1

Lets say I have this 3 scopes:

scope :assembly_reclamation, -> { where(status: 5) }
scope :customer_reclamation, -> { where(status: 9) }
scope :wrong_delivery,       -> { where(status: 12) }

Now let's say that I want to make a scope that joins those 3 categories. I am doing it right now like this:

scope :returnable, -> { where(status: [5, 9, 12]) }

While this works, it has some disadvantages. if I were to change the conditions of one of the 3 categories, I would have to rework the scope that contains them all as well.

Something like this seems more DRY:

scope :returnable, -> { assembly_reclamation.or.customer_reclamation.or.wrong_delivery }

But this is not valid code.

Is there a way to code it in such a fashion?

UPDATE

I know I have used an example using categories and ids, but please do not make it about it. It is about merging scopes.

UPDATE 2 I have changed the name of the attributes from idto status, because everyone was concentrating on the issue of the id, and that is not the point of the question

Enrique Moreno Tent
  • 24,127
  • 34
  • 104
  • 189
  • http://stackoverflow.com/questions/6686920/activerecord-query-union can help you – Ilya Nov 22 '16 at 10:46
  • Hardcoding database generated IDs into your application code is a bad idea. Primarily because it does not lend itself well to testing as it requires all the records to be inserted into the database in the same order. – max Nov 22 '16 at 10:55
  • 1
    Also remember that scopes are just class methods. If what you are writing does not fit into a lambda block it should be refactored into a regular class method. – max Nov 22 '16 at 10:56
  • The thing with the `id` is not the point of the question. I have updated the body. – Enrique Moreno Tent Nov 22 '16 at 12:28
  • This really depends on what you want the actual query to do. In the case above you want a `WHERE things.id IN (?)` which you cannot get by merging scopes together. If you are looking to get an OR query it takes either a simple raw sql string or quite a bit of Arel trickery in Rails 4. I don't really think this question is answerable as is since the scenario and desired outcome are not very well defined. – max Nov 22 '16 at 13:51
  • I want that my "merged" scope to be independent from the actual queries that the other scopes have. Just use the names of the scopes in some way, to implement it. Less knowledge as possible as of how they work. – Enrique Moreno Tent Nov 22 '16 at 14:59

1 Answers1

1

If you really are dealing with category_ids, it would be more meaningful for anyone, who reads the code if you have these ids as constants in Category model:

class Category
  ASSEMBLY_RECLAMATION_ID = 5
  CUSTOMER_RECLAMATION_ID = 9
  WRONG_DELIVERY_ID       = 12
  RETURNABLE_IDS          = [ASSEMBLY_RECLAMATION_ID, CUSTOMER_RECLAMATION_ID, WRONG_DELIVERY_ID]
end

From now on, you can use these constants in your scopes, and only change the constants, not scopes' implementation.

P.S. Rails 5 added OR support, but it's not that relevant for you until you upgrade.

EDIT

One way to combine few scopes is as follows:

scope :returnable, lambda {
  where(id: assembly_reclamation.ids + customer_reclamation.ids + wrong_delivery.ids)
}
Community
  • 1
  • 1
Andrey Deineko
  • 51,333
  • 10
  • 112
  • 145
  • It was just an example. I was looking just for a way to merge scopes. For something to a scope criteria, I could be adding more stuff than just searching for an id. – Enrique Moreno Tent Nov 22 '16 at 10:22
  • It doesn't solve the problem. It is still depending on the logic used to meet that criteria (it can be more than just meeting an `id`) – Enrique Moreno Tent Nov 22 '16 at 10:38
  • @EnriqueMorenoTent it does solve you problem. Look closer. `assembly_reclamation.ids` returns the ids of the collection of objects, returned by **whatever condition** you had in the `assembly_reclamation` scope. – Andrey Deineko Nov 22 '16 at 10:42
  • @EnriqueMorenoTent So if your scope `assembly_reclamation` looked as follows: `scope : assembly_reclamation, -> { where(id: 2, name: 'foo', email: 'bar', baz: 'qux') }`, `assembly_reclamation.ids` would still return `ids` of all objects, returned by this scope. – Andrey Deineko Nov 22 '16 at 10:44
  • @EnriqueMorenoTent just run the `returnable` scope and see generated SQL is correct ;) – Andrey Deineko Nov 22 '16 at 10:57
  • Isn't that making more than 1 request? If that is the case, is not very efficient. – Enrique Moreno Tent Nov 22 '16 at 12:30
  • @EnriqueMorenoTent the solution will work as long as you have `id` column it the database table **despite** of whether you used it in scope or not. Why are you so reluctant to realize, that `ids` method is not what you are afraid it is? :) – Andrey Deineko Nov 22 '16 at 12:31
  • @EnriqueMorenoTent Show me where is it not DRY enough? :) – Andrey Deineko Nov 22 '16 at 12:33
  • @EnriqueMorenoTent yep, it fires few queries. I suggest you to consider upgrading to rails 5, since in that case you would be able to do the OR merging and firing single query in db. – Andrey Deineko Nov 22 '16 at 12:33
  • Upgrading to v5 is sadly out of my power. That is why Im searching for a Rails 4 solution – Enrique Moreno Tent Nov 22 '16 at 13:03