ArgumentError: The scope body needs to be callable - ruby-on-rails

I have the following methods defined in a plugin:
class ReArtifactProperties < ActiveRecord::Base
unloadable
#attr_accessible :artifact_type
scope :without_projects, :conditions => ["artifact_type != ?", 'Project']
scope :of_project, lambda { |project|
project_id = (project.is_a? Project) ? project.id : project
{:conditions => {:project_id => project_id}}
}

Your code has several issues, some of which are violations of syntax rules of Ruby, while others violate return value requirements of Rails.
The cause of your current error is a syntax error. The block to the lambda must be defined in the same line as the lambda method.
Now, if you have fixed this, you will notice that your code will throw other exceptions once you use the scope. The reason for that is that the return value of a scope is expected to be an ActiveRecord relation, not just a simple Hash.
Your scope definition should thus look similar to this:
scope :of_project, lambda { |project|
project_id = (project.is_a? Project) ? project.id : project
where(:project_id => project_id)
}
Now given that Rails is smart enough to figure out how to get the ID from an object for a query, you can even get rid of the project_id logic in it and reduce your scope definition to
scope :of_project, lambda { |project|
where(:project_id => project)
}

Your code has several issues, some of which are violations of syntax rules of Ruby, while others violate return value requirements of Rails.
Yes you were right about the lambda block
Initially the code was:
scope :of_project, lambda { |project|
project_id = (project.is_a? Project) ? project.id : project
{:conditions => {:project_id => project_id}}
}
After Correction, it reduced down to this:
scope :of_project, lambda { |project|
project_id = (project.is_a? Project) ? project.id : project
where(:project_id => project_id)
}
Now given that Rails is smart enough to figure out how to get the ID from an object for a query,
Knowing the MVC architecture which I learned from http://rubyonrails.org/
How do I understand the core functions of RAILS w.r.t Redmine Tool?The mysql default database that it uses has a very huge model structure currently.Could it be reduced down to understand the core functions of Rails?

Related

Issue including a second and a third level association

This query is not working, pease help. I'm trying to include a second and a third deep-level of association.
Pedido > has_one(products_pedido) > has_one(product_size)
#pedidos = Pedido.includes(:pedidos_payments, :products_pedidos => { :product_size } , :estado, :brand, :customer ).where(:is_quote => false)
Ps: I know products_pedido is mispelled according to ActiveRecord good practices :).
Without a stacktrace here's what I suggest:
Assuming your has_one method name is products_pedidos, your issue looks like a problem with your hash syntax.
Your syntax creates a hash with key products_pedidos that returns a hash without a value. This is probably where the error is occurring.
#pedidos = Pedido.includes(:products_pedidos => { :product_size })
What you likely want is this which returns a hash with key products_pedidos with value product_size
#pedidos = Pedido.includes({products_pedidos: :product_size })
The Entire query might look like:
#pedidos = Pedido.includes(
:pedidos_payments,
{products_pedidos :product_size},
:estado,
:brand,
:customer
).where(is_quote: false)
Here's a great post explaining a bit more about ActiveRecord nested relationship loading: Rails - Nested includes on Active Records?. I'd also suggest fixing the naming on products_pedido to follow good naming practices.

Rails CanCanCan & Defining Abilities

I am trying to make an app in Rails 4.
I am using CanCanCan for permissions and Role_Model for roles management.
In my ability.rb, I have defined student abilities as:
elsif user.try(:profile).present? && user.profile.has_role?(:student)
student_abilities
and then:
def student_abilities
can :read, Project.visible.current.available
In my project.rb I have defined scopes as:
scope :visible, lambda { joins(:sweep => :disclosure).where('disclosures.allusers' => 'true')
.joins(:sweep => :finalise).where('finalises.draft' => 'false') }
scope :current, lambda { where('project.start_date >= ?', Date.today)}
scope :available, lambda { where('closed =', 'false')}
When I try to start the server and generate a view, I get this error:
NoMethodError at /project_invitations
undefined method `available' for #<Project::ActiveRecord_Relation:0x007fdde41f2ee8>
When I try removing available from the end of the ability, so that its just:
can :read, Project.visible.current
I get this error:
entry for table "project"
LINE 1: ..." = 'true' AND "finalises"."draft" = 'false' AND (project.st...
^
I don't know why it won't let me read the end of the error message.
Can anyone see what I've done wrong?
Check the table name. Is it really called "project", not "projects"?
The way you describe scopes is a bit weird. E.g. instead of where('closed
=', 'false') I would describe it as where(closed: false), minimizing the number of SQL-aware fragments

Argument Error: The scope body needs to be callable

I'm working through the 'Ruby On Rails 3 Essential Training' and have received a problem when using name scopes. When finding records and using queries withing the Rails console everything went smoothly until I tried to use a name scope in my subject.rb file. This is my code in the subject.rb file.
Class Subject < ActiveRecord::Base
scope :visible, where(:visible => true)
end
I saved the .rb file and restarted my Rails console but when I run from my rails console:
subjects = Subject.visible
I get: ArgumentError: The scope body needs to be callable.
Does anyone know why I'm getting this error.
The scope's body needs to be wrapped in something callable like a Proc or Lambda:
scope :visible, -> {
where(:visible => true)
}
The reason for this is that it ensures the contents of the block is evaluated each time the scope is used.
I got the same error , while before my solution I had a space between where and ( like below
scope :registered , -> { where ( place_id: :place_id , is_registered: :true ) }
after i removed the space between where and ( like below i made my page working
scope :registered , -> { where( place_id: :place_id , is_registered: :true ) }
Yes, indeed, this is rails 4 way of calling scopes. You'd need to change it, if you're upgrading to Rails 4 from Rails 3.
What you are using: scope :visible, where(:visible => true) goes for eager loading, and has been deprecated in Rails 4.
scope :visible, where(:visible => true)
This line of code gets evaluated when the particular class is loaded, and not at the time, this very scope is called.
There are a few cases when this thing does matter, like:
scope :future, where('published_at > ?', Time.now)
scope :future, -> { where('published_at > ?', Time.now) }
In first case, ? will be replaced with the very time the class would have been loaded, but the second & correct case, that time will be used at which the scope would have been called on the class.

How do I create a rails scope with an or or a not?

I have a two scopes in my user model:
scope :hard_deactivated, where(:hard_deactivated => true)
scope :soft_deactivated, where(:soft_deactivated => true)
So far so good
OR
I want to create a scope :deactivated, which will include all users where hard_deactivated is true OR soft deactivated is true. Obviously I could just do this:
scope :deactivated, where("hard_deactivated = ? or soft_deactivated = ?", true, true)
but this does not feel very dry.
NOT
Also I would like to create an inverse scope :not_hard_deactivated. I could do this:
scope :not_hard_deactivated, where(:hard_deactivated => false)
but again, this feels bad, especially if my scope becomes more complex. There should be some way or warpping the SQL generated by the previous scope in a not clause.
Use an arel table:
hard_deactivated_true = arel_table[:hard_deactivated].eq(true)
soft_deactivated_true = arel_table[:soft_deactivated].eq(true)
scope :deactivated, where(hard_deactivated_true.and(soft_deactivated_true))
scope :not_hard_deactivated, where(hard_deactivated_true.not)
See: Is it possible to invert a named scope in Rails3?
For the "NOT" part, you can do something like this:
extend ScopeUtils
positive_and_negative_scopes :deactivated do |value|
where(:hard_deactivated => value)
end
And implement this method in a separate module:
module ScopeUtils
def positive_and_negative_scopes(name)
[true, false].each do |filter_value|
prefix = ("not_" if filter_value == false)
scope :"#{prefix}#{name}", yield(filter_value)
end
end
end
Regarding the "OR" case, you might be something similar, depending on what your recurring pattern is. In the simple example above it's not worth it, as doesn't help readability.
scopes_with_adjectives_and_negatives :deactivated, [:soft, :hard]
module ScopeUtils
def scopes_with_adjectives_and_negatives(name, kinds)
kinds.each do |kind|
positive_and_negative_scopes name do |filter_value|
where("#{kind}_#{name}" => filter_value)
end
end
scope :"#{name}", where(kinds.map{|kind| "#{kind}_#{name} = ?"}.join(" OR "), true, true)
scope :"not_#{name}", where(kinds.map{|kind| "#{kind}_#{name} = ?"}.join(" AND "), false, false)
end
end
You should use sql snippet in where method (like in your second example), or more 'sugar' gems like squeel

Rails as_json includes with conditions

I have problems to restrict an as_json include by a dynamic attribute:
#pirates_ships = #current_account.pirates.as_json(:include => {:ships => {:only => [:id, :name]}}, :only => [:id, :last_name])
This for sure gives me all pirates with or without their ships.
But I also need to restrict the ships by e.g. ships.ocean_id
I tried resolving it by includes with conditions:
pirates.includes(:ships).where("ships.ocean_id = ?", #ocean.id).as_json(...)
The restriction works, but now all pirates without a ship are lost.
Also no luck with my own JOIN Syntax.
Any ideas?
Ahoy
UPDATE
My solution so far is to manually eager load. This way I can have my dynamic conditions:
#pirates = #current_account.pirates
#ships = #current_account.ships.where({:pirate_id.in => #pirates, :ocean_id => #ocean.id})
render :json => { :pirates => #pirates.as_json(...), :ships => #ships.as_json(...) }
My Ajax callback can now iterate over :pirates and add for each pirate his ships if any.
(I use a JS template engine clientside to generate the view from the JSON response)
Not very elegant, but performance is important in my case.
I'am still open for better ideas.
I tried dynamic has_many :ships, :conditions => ...
but that's a bit fiddly.
I think your best bet would be altering the #pirates_ships hash after generating it from as_json (I tried multiple variations of includes, etc. and couldn't get anything to work).
#pirates_ships = #current_account.pirates.as_json(:include => :ships)
#pirates_ships.each do |pirate|
pirate[:ships].delete_if{ |ship| ship.ocean_id != #ocean.id }
end
# Now, #pirates_ships should contain ALL pirates, as well as ships for #ocean

Resources