1

I am trying to write up a dynamic query to search for sent messages. I have written up something like this right off my head and i know it has more errors.

def create          
        scope :before, lambda { |value| where('created_at <= (?)', value) if value }
        scope :after, lambda { |value| where('created_at >= (?)', value) if value }
        scope :from, lambda { |value| where('from LIKE (?)', value) if value }
        scope :to, lambda { |value| where('to LIKE (?)', value) if value }
        scope :with_message, lambda { |value| where('message LIKE (?)', value) if value }


        @report = SentMessage.create_before(params[:report][:before])
                     .created_after(params[:report][:after])
                     .from(params[:report][:from])
                     .to(params[:report][:to])
                     .with_message(params[:report][:with_message])
                     .all.reverse

        respond_with(@report)
  end

I get an error like this;

undefined method `scope' for #<ReportsController:0xde24d2c>

Thank you in advance.

After improving the code; I am now getting this;

undefined method `default_scoped?' for #<String:0xcfa2678>

Improved code looks like this;

scope :created_before, lambda { |value| where("created_at <= (?)", value) if value }
    scope :created_after, lambda { |value| where("created_at >= (?)", value) if value }
    scope :sent_from, lambda { |value| where("from like (?)", "%#{value}%").to_sql if value }
    scope :sent_to, lambda { |value| where("to like (?)", "%#{value}%").to_sql if value }
    scope :with_message, lambda { |value| where("message like (?)", "%#{value}%").to_sql if value }

and the controller action looks like this;

def create              
        @report = SentMessage.created_before(params[:report][:before])
                             .created_after(params[:report][:after])
                             .sent_from(params[:report][:from])
                             .sent_to(params[:report][:to])
                             .with_message(params[:report][:message])
                             .all.reverse       
        respond_with(@report)       
  end

and the new error is

undefined method `default_scoped?' for #<String:0xd4c928c>
Kirti Thorat
  • 52,578
  • 9
  • 101
  • 108
acacia
  • 1,375
  • 1
  • 14
  • 40
  • 1
    scope is an ActiveRecord method and won't work in the controller. Copy those into your model class – DiegoSalazar Feb 06 '14 at 18:57
  • What do you expect, say, `created_before` to do to your query if `value.nil?`? Why the `.to_sql` calls in the scopes? Why `.all.reverse` rather than telling the database what order you want things in? Scopes really aren't the right tool for this job BTW. – mu is too short Feb 06 '14 at 19:54
  • @muistooshort thank you for the pointers, so what is the best tool? – acacia Feb 06 '14 at 19:57
  • For your new error http://stackoverflow.com/questions/12365128/undefined-method-default-scoped-while-accessing-scope?rq=1 post might be helpful... – Sumit Munot Feb 06 '14 at 19:59

3 Answers3

3

Scopes belong in the model they scope. This way you can reuse them all over the app. Note that the name of the scope is what you define (:before translates into SentMessage.before not SentMessage.create_before)

Eg:

@user = User.valid
rlecaro2
  • 755
  • 7
  • 14
2

Scopes are created in models not in controllers. You can reference them from your controllers. Refer to the link for examples :Rails Guides: ActiveRecord

Kirti Thorat
  • 52,578
  • 9
  • 101
  • 108
2

Scopes aren't the right tool for something like this. Scopes are meant to be general purpose query builders but these look like they're specific to a single controller.

I'd probably start with a private query builder in your controller:

def messages_matching(query)
  query ||= { }

  # This can be generalized with some Hashes and iteration but there's
  # little point to that when there are so few options.
  msgs = SentMessage
  msgs = msgs.where('created_at <=   ?',     query[:before      ]   ) if(query[:before      ])
  msgs = msgs.where('created_at >=   ?',     query[:after       ]   ) if(query[:after       ])
  msgs = msgs.where('from       like ?', "%#{query[:from        ]}%") if(query[:from        ])
  msgs = msgs.where('to         like ?', "%#{query[:to          ]}%") if(query[:to          ])
  msgs = msgs.where('message    like ?', "%#{query[:with_message]}%") if(query[:with_message])

  msgs.order('created_at desc') # Or whatever order you really want
end

And then:

def create
  @report = messages_matching(params[:report])
  respond_with @report
end

Later, if you find yourself using any of those snippets over and over again then convert them to scopes using class methods on SentMessage.

mu is too short
  • 426,620
  • 70
  • 833
  • 800