Rails get objects in front of dot of a class method - ruby-on-rails

students = Student.limit(3)
students.approve()
def self.approve
# ...
end
I want to get students and use each_with_index, but it fails.
I tried self, it points out
"NoMethodError: undefined method `each_with_index' for #<Class:0x00000007a1b920>".
I just want the objects in front of my method, how can I do this, thanks !

In the approve method you have an ActiveRecord::Relation, but you need an array instead:
def self.approve_all
all.each_with_index do |user, index|
...
end
end
Class methods in ActiveRecord models can be called not only on the class itself, but also on ActiveRecord::Relation objects.
So for example, we can call the .approve_all method not only directly on the class: Student.approve_all, but also on any relation for the Student class: Student.order(name: :asc).limit(10).where(state: "not_approved").approve_all
But i think the method should be at least named update_all, and i would try to figure out a better solution for this problem.

Related

Rails ActiveRecord: Where to define method?

I have a question about ActiveRecord of Rails.
For example I have a Service model, and Service has name as a column.
This is app/model/service.rb
class Service < ActiveRecord::Base
def self.class_method
puts 'This is class method'
end
def instance_method
puts 'This is instance method'
end
end
Then I can do,
Service.class_method #=> 'This is class method'
Service.find(1).instance_method #=> 'This is instance method'
This is easy. But when I get ActiveRecord Instance in Array, for example
Service.where(id: [1,2,3])
and I need method like,
Service.where(id: [1,2,3]).sample_method
def sample_method
self.length
end
but how and where to define method for Active Record Array? I want to handle this object just like other Service class or instances.
Thanks.
First of all, where returns an ActiveRecord::Relation object, not an array. It behaves similar to an array, but inherits a load more methods to process data/ construct SQL for querying a database.
If you want to add additional functionality to the ActiveRecord::Relation class, you can do something like this:
class ActiveRecord::Relation
def your_method
# do something
end
end
This will need to reside somewhere meaningful, such as the lib directory or config/initializers.
This should allow you to do something like
Service.where(id: [1,2,3]).your_method
You can do something similar for any Ruby class, like the Hash, or Array class as well.
However, there's almost ALWAYS a better solution than extending/ overriding Rails/ Ruby source classes...
Your methods like get_count and get_name are a bit pointless... Why not just do:
Service.count
Service.find(1).name
Class methods like count, and instance methods like name (i.e. database column names) are all public - so you don't need to define your own getter methods.
As for your second example, you could just write the following:
Service.where(id: [1,2,3]).map{ |s| s.name }
Or equivalently:
Service.where(id: [1,2,3]).map(&:name)
But the following is actually more efficient, since it is performing the calculation in SQL rather than in ruby. (If you're confused what I mean by that, try running both versions and compare what SQL is generated, in the log):
Service.where(id: [1,2,3]).pluck(:name)

Use instance method in ActiveRecord where clause

I have an instance method that has logic I want to use in a query. My attempts have not worked. I'd rather not duplicate the logic inside is_thing when building my where clause.
class Foo < ActiveRecord::Base
def is_thing?
#... some logic
end
end
I tried
Foo.where(is_thing?)
and got
NoMethodError: undefined method `is_thing?' for main:Object
The Approach I would recommend
I do believe that method is not good practice (to chain a where query). It will only add unnecessary complexity.
The better approach is using scope
scope :is_thing?, -> { where(thing: true) }
and call it
Foo.is_thing?.where()
The Why
The reason it is returned
NoMethodError: undefined method `is_thing?' for main:Object
Is because is_thing? is instance variable of Foo
Instance variable can be called on an instance of the class. And not availabe on main object.
You, however could do
Foo.where(Foo.new.is_thing?)
It is posible to use that if you convert is_thing? to class method. e.g
def self.is_thing?
# code
end
and use it this way
Foo.where(Foo.is_thing?)
try this:
class Foo < ActiveRecord::Base
def is_thing?
#... some logic
end
def things
Foo.where('thing_check = ?', self.is_thing?)
end
end
create another instance method to wrap your where query. Then access it as Foo.last.things

NoMethodError - undefined method error - ActiveRecord

I'd like to return a random webpage that I can display as a link in a view but I can't get the method to work properly:
Error:
NoMethodError: undefined method `randomize_webpages' for #<ActiveRecord::Relation::ActiveRecord_Relation_Webpage:0x00000101d84e68>
Webpage Model
def randomize_webpages
shuffle.first
end
WebpagesController
def index
#webpages = Webpage.all
end
CSV seeder for Webpage
link
http://www.buzzfeed.com
http://www.reddit.com
http://boston.com
http://phys.org
http://www.popsci.com
http://www.technologyreview.com
http://techcrunch.com
View Index.html.erb
<%= link_to 'Take Me Anywhere But Here', #random_page %>
You need to define the method as a class method of the model
class Webpage
def self.randomize_webpages
shuffle.first
end
end
instead of
class Webpage
def randomize_webpages
shuffle.first
end
end
Please note that the method is very inefficient. You are loading all the records and then selecting one. There are more efficient ways.
One possible improvement is to select only the ID, then query for that ID. Another alternative is to delegate to the underlying driver, assuming it supports the query.
Aren't you calling randomize_webpages on #webpages, are you? This is a collection, so you have to do something like #webpages.first.randomize_webpages or iterate over them (you didn't include relevant code so I can't tell what you want to do) to get this working.
It needs to be a class method, then you could use sample:
def self.randomize_webpages
all.sample
end
You could add a class method to your model
class WebPage < ActiveRecord::Base
def self.random
order('RAND()').first
end
....
end
Then you can call it from as WebPage.random in your controller, save to an instance variable, and use that instance variable in your view.
Please: note that 'RAND()' is MySQL specific. If you do not use MySql, search for the correct syntax (if any)

Define method on activerecord relation

I want to define a custom method on an activerecord relation, eg:
Transaction.all.summed_values
A simple example would be where summed_values should evaluate sum(:value) on the relation.
Where should I define the method summed_values? Looks like it should be on ActiveRecord::Relation. If it should be directly there, which file should I put it in?
Also, if this new method only has meaning for Transactions, is there any way to tell rails to only define this method for ActiveRecord::Relations that consist of Transactions?
Create a self.summed_values method directly in the transaction model.
You should use extending
Transaction.all.extending do
def summed_values
sum(:what_you_want)
end
end
For more info: ActiveRecord::QueryMethods
Is there any specific reason why you want to create this method as extension to ActiveRecord::Relation? I would propose a class method like so:
class Transaction ...
def self.summed_values(transactions=nil)
if transactions.nil?
all.sum(...)...
else
where(id => transactions).sum(...)...
end
end
end
This also has the advantage that it is only available for transactions.

Can I define_method in rails models?

My rails model has code that is attempting to define_method(method_name) inside the model.
I keep getting:
NoMethodError: undefined method `define_method'
What am I doing wrong? Am I doing this in the wrong place. I need this method attached to this model. Where else can I define this method?
EDIT:
For those asking to see the code:
for field in rdev_fields
next if self.attributes.include?(field)
count = count + 1
rdev_hash[field.to_sym] = self.attributes["attribute#{count}"]
if !self.respond_to?(field) then
define_method("#{field}") do
self.send("attribute#{count}".to_sym)
end
end
end
There's nothing magical or about a rails model, it's just a normal class with a bunch of pre-existing methods,
So, the question is "can I define_method in a class"?
Part 1: Yes you can.
The important distinction is than you can define method in a class not in an instance method
For example:
class Cow
define_method "speak" do
"MOOOO"
end
end
Cow.new.speak
=> "MOOOO"
This should work fine. Note you're defining it on the class Cow, so any other Cows that you already have will automatically get that method added.
Part 2: What do you do if you want to define a method from within an instance method?
You can't define methods from an instance method, so you have to grab the class, and use that to define the method. Like this:
class Cow
def add_speak
self.class.send(:define_method, :speak) do
"MOOOO added"
end
end
end
Cow.new.speak
NoMethodError: undefined method 'speak' for #<Cow:0xb7c48530>
Cow.new.add_speak
Cow.new.speak
=> "MOOOO added"
Problem solved. Astute readers will note that in this example I'm using send(:define_method) - this is needed because define_method is private, and private methods are only accessible to the thing they're in. In this case, define_method is in the class, we are in the instance, so we can't directly access it.
As above though, we're adding the method directly to the class, so all other Cows which already exist will automatically also get the speak method added.
Part 3: What do you do if you want to define a method for only 1 object, not all objects of that class?
Example:
class Cow
def add_speak_just_me
class << self
define_method "speak" do
"MOOOO added for just me"
end
end
end
end
Cow.new.speak
NoMethodError: undefined method 'speak' for #<Cow:0xb7c72b78>
c = Cow.new
c.add_speak_just_me
c.speak
=> "MOOOO added for just me" # it works, hooray
Cow.new.speak # this new cow doesn't have the method, it hasn't been automatically added
NoMethodError: undefined method `speak' for #<Cow:0xb7c65b1c>
How does this work? Down the rabbithole you go!
Read this: http://dannytatom.me/metaid/ and good luck. It helps when you realise that 'adding a method' to an instance isn't actually adding it to the instance at all :-)
If you came here searching for how to dynamically define a CLASS method, because define_method wasn't working (because it defines INSTANCE methods), here is your answer:
Use define_singleton_method :)
was able to cobble this together. Very little understanding of what's actually going on though.
My instance method foo is opening the class and defining bar on it so that I can then call that on my instance. More experienced folks will let us know if this is opening a can of worms at the same time.
Would be useful to know your specific use for this though.
class User < ActiveRecord::Base
def foo
(class << self; self; end).class_eval do
define_method(:bar) {puts "bar"}
end
end
end
u = User.first
u.foo
u.bar #=> "bar"
The answer to your question is "yes, you can". As for why it's not working for you - it's impossible to say for sure why, if you don't provide some context for the code.

Resources