To do a special kind of validation in an ActiveRecord-based model I need to manually do the update_attributes-steps:
load(params) && save
But when I try to call "load" on my Model's instance (mymodel.load(params)) it calls "load" on ActiveSupport, trying to load a file.
Is there a way to tell Rails to use the ActiveRecord-method?
Thanks!
Regards
Marc
I think you want the assign_attribute method of ActiveRecord::Base.
I'm using Rails 3.1.3, but hopefully this is correct for you.
The update_attributes source you pasted belongs to ActiveResource::Base, not toActiveRecord::Base. I just made that mistake myself: Is it possible to call ActiveResource::Base#load? ActiveRecord::Base does not supply a load method. Instead the load method of Object is called, which appears to be supplied by ActiveSupport's Loadable module in activesupport-3.1.3/lib/active_support/dependencies.rb.
Straight from the source:
# File activerecord/lib/active_record/base.rb, line 2665
def update_attributes(attributes)
self.attributes = attributes
save
end
Actually it's rails 2, but I imagine it didn't change all that much.
Related
I'm looking for way to see currently executing code. My reason is: I have ruby classes, that are been monkey patched by lots of patches, so its close to impossible to track which methods were changed. Is there any way to check what code has been loaded into memory and currently executing?
Ask for more details if you need
Another way besides the debugger mentioned by tessi, would be to use ruby-prof to benchmark the whole application and analyze the generated tree.
Every called method is in there, so the desired or not desired versions as well.
Getting the Origin of a Method
In ruby 1.9 and newer, you can open a debugger/console at any point and ask the method for it's source location.
For example, when looking for the definition of the admin? method of my User class I can do the following:
user = User.first
=> #<User id: 1, ...>
user.method(:admin?).source_location
=> ["/Users/tessi/.rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/activemodel-4.0.13/lib/active_model/attribute_methods.rb", 382]
It tells me that the admin? method is defined in ActiveModel in the file above and in line 382 of that file.
In a similar way you could iterate over all methods of your class and check the origin of the methods:
user.methods.map {|method_name| user.method(method_name).source_location}
Monkey Patching
This still works with patched classes. When opening a rails console, I can edit my User class and look at the source_location again:
class User < ActiveRecord::Base
def admin?
puts 'the patched admin? method'
super
end
end
User.first.method(:admin?).source_location
=> ["(pry)",2]
Now, the method's location is in my console (pry) at statement 2. This works, because my patch creates a new Method-object which replaces the old method in the method dictionary of the User class. The new method object returns a different source_location.
Throughout my application self. is not necessary to refer to a User's name. name works fine.
Why does the following code require self to work as intended?
class User< ActiveRecord::Base
before_save :validate_name
def validate_name
if self.name.nil? || self.name.empty?
self.name= "Mr. No Name"
end
end
By the way, I know that validates_presence_of can be used to prevent the save, but I want to save with a default if no name is given.
Rails 3.0.7.
Often the use of self is to force Ruby to recognize that as a method call and not mis-interpret it as a variable. Without prior knowledge of a method called day=, then day = "x" looks to Ruby like a variable assignment. self.day = "x" is always a method call.
The reason this is trouble is because the name and name= methods are added dynamically after the User class file has been parsed. The first thing Rails does when using a model is make methods for the associated database fields, but this happens after your user.rb file is parsed.
in previous released of Mongoid (2.0.beta.20), I could pass a class type as the
2nd parameter of the .find_or_create_by block on embedded document collections.
this doesn't appear to be the case any more, with v2.0.1, yet I still
need to do polymorphic find_or_create_by. any suggestions / pointers
on how to do this?
I used to do this:
SomeClass.childclass.find_or_create_by({:key => "value"},
InheritingChildClass)
now I get an exception saying too many arguments (2 for 1)
on .find_or_create_by.
how can I tell the collection to create an object of the correct type, when using find_or_create_by? Or, how can i create my own method that will be functionaly equivalent to what I want, and be re-usable across my embedded document collections?
any help is appreciated.
thanks.
I ended up rolling my own solution for this
module Mongoid::Relations
class Many
def find_or_new(attrs, type, &block)
inst = self.where(attrs).first
unless inst
inst = type.new
inst.write_attributes attrs
self << inst
end
inst
end
end
end
Not sure I really understood your need to inform the subclass, but check this gist: https://gist.github.com/960684
I do search subclass instances without needing to inform it. Perhaps your scenario really needs it, but if it does, why don't you call subclass's find_or_create_by?
I have a basic has_many :through relationship that is bi-directional:
calendars have many calendar_calendar_events
calendars have many events through calendar_calendar_events
events have many calendar_calendar_events
events have many calendars through calendar_calendar_events
I'm wanting to assign calendars to an event with the basic calendar_ids= function that has_many :through sets up, however, I want to override this function to add some extra magic. I've had a look through the rails source and can't find the code for this function. I'm wondering if someone could point me to it. I'll then override it for this class to add the stuff that I want :)
You can find the source code in the file lib/active_record/associations.rb at line 1295
def collection_accessor_methods(reflection, association_proxy_class, writer = true)
collection_reader_method(reflection, association_proxy_class)
if writer
define_method("#{reflection.name}=") do |new_value|
# Loads proxy class instance (defined in collection_reader_method) if not already loaded
association = send(reflection.name)
association.replace(new_value)
association
end
define_method("#{reflection.name.to_s.singularize}_ids=") do |new_value|
ids = (new_value || []).reject { |nid| nid.blank? }
send("#{reflection.name}=", reflection.class_name.constantize.find(ids))
end
end
end
You should definitely avoid to overwrite such this method to add magic stuff.
Rails is already "too much magic" sometimes. I would suggest to create a virtual attribute with all your custom logic for several reasons:
some other rails methods might rely on the default implementation
you rely on a specific API that might going to change in future ActiveRecord versions
After a bit of a hunt I found it:
http://apidock.com/rails/ActiveRecord/Associations/ClassMethods/collection_accessor_methods
It didn't look like what I thought it would look like, so that's why I probably missed it. I ended up overriding the calendars= method instead of the calendar_ids= method and everything works well.
In response to the answer above, I used alias_method_chain to override the default setter and add my feature. Works quite well, though I'm not sure why I have to send the method setter instead of just using it normally. It didn't seem to work though so this will do :)
def calendars_with_primary_calendar=(new_calendars)
new_calendars << calendar unless new_record?
send('calendars_without_primary_calendar=', new_calendars) # Not sure why we have to call it this way
end
alias_method_chain :calendars=, :primary_calendar
I've got a legacy database with a bunch of idiotically named columns like:
some_field_c
some_other_field_c
a_third_field_c
I would very much like to make a Rails ActiveRecord sub-class that automatically aliases these attributes to their name minus the underscore and "c". However, when I tried:
attributes.each_key do | key |
name = key
alias_attribute key.to_sym, key[0, (key.length -2)].to_sym if key =~ /_c$/
end
in my class definition I got an "undefined local variable or method `attributes'" error. I also tried overwriting these methods:
method_missing
respond_to?
but I kept getting errors with that route too.
So my question (actually questions) is/are:
Is what I'm trying to do even possible?
If so, what's the best way to do it (iterative aliasing or overwriting method missing)?
If it's not too much trouble, a very brief code sample of how to do #2 would be awesome.
Thanks in advance for any answers this post receives.
Your problem is probably that attributes is an instance method and you're doing this in the context of the class. The class method that's closest to what you want is column_names.
you could do something like:
methods.each do |method|
if method.ends_with("_c") then
self.send(:defind_method,method.slice(0,-2)){self.send(method)}
end
end
Hmm... I cooked up this little solution that I thought ended up pretty elegantly...
Not sure if this will work, but I aliased method_missing to still allow active_record to do it's thang:
module ShittyDatabaseMethods
alias :old_method_missing :method_missing
def method_missing(method)
if methods.include?("#{method}_c")
send("#{method}_c".to_sym)
else
old_method_missing(method)
end
end
end
class Test
attr_accessor :test_c
include ShittyDatabaseMethods
end
You may not get to name your module "ShittyDatabaseMethods", but you get the idea ;) Once you define that module and stuff it into lib, you just need to include this module and off you go :D
Would love to hear if this works out for you :)