I have a model OutcomeData with controller OutcomeDatas.
In OutcomeData, I have a method as_cleaned_hash that right now, doesn't do a damn thing. Let's just pretend it returns 'hello'
class OutcomeData < ActiveRecord::Base
attr_accessible :key, :outcome_uid, :task_id, :value
belongs_to :task
belongs_to :outcome
def as_cleaned_hash
'hello i am alive'
end
This is the method that as_cleaned_hash is supposed to follow, if it matters:
#outcome_data = OutcomeData.find_all_by_outcome_uid(params[:outcome_uid])
hash = Hash.new
#outcome_data.each do |p|
unless p[:value].blank? || p[:key] == 'raw'
hash[p[:key]] = p[:value]
end
end
This works fine -- right now I'm throwing it into my controller actions, but since it needs to be used throughout my app, I can't let this happen.
So, for whatever reason, I get an undefined method error.
I called OutcomeData.methods to see if the method was even there, and, nope. (see list here: http://pastebin.com/B3y1r2w7)
OutcomeData.respond_to?('as_cleaned_hash') returns false.
There's nothing fancy going on either, so I'm not quite sure what's happening.
Rails 3.2.12 with Ruby 2.0.0-p195
To define a class method, the syntax is
def self.foo
end
You have defined an instance method.
Related
I'm was trying to add multiple objects like so:
class Person < ActiveRecord::Base
has_many :interests
...
def add_interests(interest_hashes)
interest_hashes.each do |interest|
Rails.logger.debug "person.apply_interests: interest: #{interest.inspect}"
interests.build(:name => interest.name, :category => interest.category)
end
save!
end
...
end
However in the log when calling <some person>.add_interests(<some hashes>) all I see is the first hash - no error or exception. If I remove the build method the loop works as expected.
What is happening when calling the build method?
What's a better way to achieve what I'm trying?
Edit:
interest_hashes.inspect output example:
[{"category"=>"Interest", "name"=>"Formula One"}, {"category"=>"Musical instrument", "name"=>"Guitar"}]
You should get a NoMethodError when calling name and category on interest, since hashes are accessed using the [] method. Replace
interest.name
with
interest["name"]
Or use an Struct, which may be preferable.
This is the weirdest thing ever happened to me with ruby/rails.
I have a model, Store, which has_many Balances. And I have a method that gives me the default balance based on the store's currency.
Store model.
class Store < ActiveRecord::Base
has_many :balances, as: :balanceable, dependent: :destroy
def default_balance
#puts self.inspect <- weird part.
balances.where(currency: self.currency)[0]
end
...
end
Balance model.
class Balance < ActiveRecord::Base
belongs_to :balanceable, :polymorphic => true
...
end
Ok, so then in the Balance controller I have the show action, that will give me a specific balance or the default one.
Balance controller.
class Api::Stores::BalancesController < Api::Stores::BaseController
before_filter :load_store
# Returns a specific alert
# +URL+:: GET /api/stores/:store_id/balances/:id
def show
#puts #store.inspect <- weird part.
#balance = (params[:id] == "default") ? #store.default_balance : Balance.find(params[:id])
respond_with #balance, :api_template => :default
end
...
private
# Provides a shortcut to access the current store
def load_store
#store = Store.find(params[:store_id])
authorize! :manage, #store
end
end
Now here is where the weird part comes...
If I make a call to the show action; for example:
GET /api/stores/148/balances/default
It returns null (because the currency was set as null, and there is no Balance with null currency), and the SQL query generated is:
SELECT `balances`.* FROM `balances` WHERE `balances`.`balanceable_id` = 148 AND `balances`.`balanceable_type` = 'Store' AND `balances`.`currency` IS NULL
So I DON'T know why... it is setting the currency as NULL. BUT if in any where in that process I put
puts #store.inspect
or inside the default_balance method:
puts self.inspect
it magically works!!!.
So I don't know why is that happening?... It seems like the store object is not getting loaded until I "inspect" it or something like that.
Thanks
Sam and Adrien are on the right path.
ActiveRecord overrides method_missing to add a whole bunch of dynamic methods including the accessors for the column-backed attributes like Store#currency. While I'm glossing over a lot, suffice it to say that when the logic is invoked then the dynamic class/instance methods are added to the Store class/instances so that subsequent calls no longer require the method_missing hook.
When YOU overrode method_missing without calling super, you effectively disabled this functionality. Fortunately, this functionality can be invoked by other means, one of which you tripped upon when you called store#inspect.
By adding the call to super, you simply assured that ActiveRecord's dynamic methods are always added to the class when they're needed.
OK finally after a lot of debugging, I found the reason...
In the Store model I have a method_missing method and I had it like this:
def method_missing method_name, *args
if method_name =~ /^(\w+)_togo$/
send($1, *args).where(togo: true)
elsif method_name =~ /^(\w+)_tostay$/
send($1, *args).where(tostay: true)
end
end
So when I was calling self.currency it went first to the method_missing and then returned null. What I was missing here was the super call.
def method_missing method_name, *args
if method_name =~ /^(\w+)_togo$/
send($1, *args).where(togo: true)
elsif method_name =~ /^(\w+)_tostay$/
send($1, *args).where(tostay: true)
else
super
end
end
But I continue wondering why after I had called puts #store.inspect or puts self.inspect it worked well?. I mean, why in that case that super call wasn't needed?
class Person < ActiveRecord::Base
validates :terms_of_service, :acceptance => true
end
In the above, what is validates from a Ruby standpoint? It's not a method definition, it's not a data definition or declaration. So, evidently it's a method invocation, right there in the body of the class. I have never seen a method invoked like that directly in a class (i.e. outside of a method definition), not even in the Ruby programming tutorial I'm going through now: http://ruby-doc.org/docs/ProgrammingRuby/.
So, if it's a method invocation, at what point is it being invoked? I tried the following as a test:
class Person
print "cat"
end
#p = Person.new
#q = Person.new
print "cat" is being executed exactly once, regardless if any actual Person objects are being declared or not, so evidently just when parsing the class definition, Ruby sees the method print and says, "OK I will just go ahead and execute this now" but never does so again.
So, where is the Ruby documentation that will help me understand what is going on with validates above?
In Ruby, class declarations are just chunks of code, executed in order.
It's important to remember that inside a class definition, self points to the class itself. validates is a class method of ActiveRecord. As the class is being defined, code in the definition is executed. The validates method resolves to a class method of ActiveRecord, so is called during class definition.
In your Person example, it will only print once, because you only define the class once.
Consider the following:
class Foo
def self.validates_nothing(sym)
(##syms ||= []) << sym
puts "!!! Here there be logic"
end
def validate
##syms.each { |s| puts s }
end
end
This defines a class with a class method validates_nothing, and an instance method, validate. validates_nothing just gathers whatever arguments are given it, validate just dumps them out.
class Bar < Foo
validates_nothing :anything
validates_nothing :at_all
end
This defines a subclass. Note that when the class method validates_nothing is called, it prints:
Here there be logic
Here there be logic
If we create a new bar and call validate, we get the expected output:
> Bar.new.validate
!!!anything
!!!at_all
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
attr_accessor default values
I am using Ruby on Rails 3.0.9 and I would like to initialize some attr_accessor attribute values in my class\model that inherits from ActiveRecord::Base. That is,
... in my module I have:
class User < ActiveRecord::Base
attr_accessor :attribute_name1,
:attribute_name2,
:attribute_name3,
...
end
and I would like to set to true all attr_accessor attribute values. How can I do that?
P.S.: Of course I would like to solve the above issue approaching "à la Ruby on Rails Way". I know about the after_initialize callback but by using that method I should repeat each attribute_name<N> statement for which I would like to set the value to true inside that after_initialize statement (... and this is not DRY - Don't Repeat Yourself). Maybe there is a better way to achieve this. Is there a way to set attr_accessor attribute values "on the fly" when you state those attributes? That is, I expect to declare and set attr_accessor attributes at once!
Did you try:
class User < ActiveRecord::Base
attr_accessor :attribute_name1,
:attribute_name2,
:attribute_name3,
...
after_initialize :set_attr
def set_attr
#attribute_name1 = true
...
end
end
For Rails 3.2 or earlier, you could use attr_accessor_with_default:
class User < ActiveRecord::Base
attr_accessor_with_default :attribute_name1, true
attr_accessor_with_default :attribute_name2, true
attr_accessor_with_default :attribute_name3, true
...
end
Since your default value is an immutable type (boolean), this form of the method is safe to use here. But don't use it if the default value is a mutable object, including an array or string, because all of your new model objects will share the exact same instance, which is probably not what you want.
Instead, attr_accessor_with_default will accept a block, where you can return a new instance each time:
attr_accessor_with_default(:attribute_name) { FizzBuzz.new }
I would just define a getter that lazily loads the value you are interested in, and use attr_writer to define the setter. For instance,
class Cat
attr_writer :amount_of_feet
def amount_of_feet; #amount_of_feet ||= 4; end # usually true
end
Which, if you really mean it, can be rewritten with some meta-programming:
class Cat
# The below method could be defined in Module directly
def self.defaulted_attributes(attributes)
attributes.each do |attr, default|
attr_writer attr
define_method(attr) do
instance_variable_get("##{attr}") ||
instance_variable_set("##{attr}", default)
end
end
end
defaulted_attributes :amount_of_feet => 4
end
calin = Cat.new
print "calin had #{calin.amount_of_feet} feet... "
calin.amount_of_feet -= 1
puts "but I cut one of them, he now has #{calin.amount_of_feet}"
This works because, usually, computing the default value won't have any side effect making the order matter and it won't be needed to compute the value until you first try to access it.
(Câlin is my cat; he's doing well, and still has the four of his feet)
Brutal solution
class User < ActiveRecord::Base
##attr_accessible = [:attribute_name1, :attribute_name2, :attribute_name3]
attr_accessor *##attr_accessible
after_initialize :set_them_all
def set_them_all
##attr_accessible.each do |a|
instance_variable_set "##{a}", true
end
end
end
or little more conceptual: Ruby: attr_accessor generated methods - how to iterate them (in to_s - custom format)?
For example, if I have a user model and I need to validate login only (which can happen when validating a form via ajax), it would be great if I use the same model validations defined in the User model without actually instantiating a User instance.
So in the controller I'd be able to write code like
User.valid_attribute?(:login, "login value")
Is there anyway I can do this?
Since validations operate on instances (and they use the errors attribute of an instance as a container for error messages), you can't use them without having the object instantiated. Having said that, you can hide this needed behaviour into a class method:
class User < ActiveRecord::Base
def self.valid_attribute?(attr, value)
mock = self.new(attr => value)
unless mock.valid?
return mock.errors.has_key?(attr)
end
true
end
end
Now, you can call
User.valid_attribute?(:login, "login value")
just as you intended.
(Ideally, you'd include that class method directly into the ActiveRecord::Base so it would be available to every model.)
Thank you Milan for your suggestion. Inspired by it I created a simple module one can use to add this functionality to any class. Note that the original Milans suggestion has a logic error as line:
return mock.errors.has_key?(attr)
should clearly be:
return (not mock.errors.has_key?(attr))
I've tested my solution and it should work, but ofc I give no guarantees. And here's my glorious solution. Basically a 2-liner if you take away the module stuff.. It accepts method names as stings or symbols.
module SingleAttributeValidation
def self.included(klass)
klass.extend(ClassMethods)
end
module ClassMethods
def valid_attribute?(attr, value)
mock = self.new(attr => value)
(not mock.valid?) && (not mock.errors.has_key?(attr.class == Symbol ? attr : attr.to_sym))
end
end
end
To use your standard validation routines:
User.new(:login => 'login_value').valid?
If that does not work for you, build a custom class method for this:
class User < ActiveRecord::Base
validate do |user|
user.errors.add('existing') unless User.valid_login?(user.login)
end
def self.valid_login?(login)
# your validation here
!User.exist?(:login=> login)
end
end
I had a hell of a time getting this to work in Rails 3.1. This finally worked. (Not sure if it's the best way to do it, I'm kind of a newb.). The problem I was having was that value was being set to type ActiveSupport::SafeBuffer, and was failing validation.
def self.valid_attribute?(attr, value)
mock = User.new(attr => "#{value}") # Rails3 SafeBuffer messes up validation
unless mock.valid?
return (not mock.errors.messages.has_key?(attr))
end
return true
end
I have gone with the custom class solution but I just wanted to make sure there was no better way
class ModelValidator
def self.validate_atrribute(klass, attribute, value)
obj = Klass.new
obj.send("#{attribute}=", value)
obj.valid?
errors = obj.errors.on(attribute).to_a
return (errors.length > 0), errors
end
end
and I can use it like
valid, errors = ModelValidator.validate_attribute(User, "login", "humanzz")
class User < ActiveRecord::Base
validates_each :login do |record, attr, value|
record.errors.add attr, 'error message here' unless User.valid_login?(value)
end
def self.valid_login?(login)
# do validation
end
end
Just call User.valid_login?(login) to see if login itself is valid
An implementation of the 'valid_attribute' method you are suggesting:
class ActiveRecord:Base
def self.valid_attribute?(attribute, value)
instance = new
instance[attribute] = value
instance.valid?
list_of_errors = instance.errors.instance_variable_get('#errors')[attribute]
list_of_errors && list_of_errors.size == 0
end
end
How about:
User.columns_hash.has_key?('login')