Undefined local variable inside a Struct in rails - ruby-on-rails

I have a class as follow:
class TempUser < Struct.new(:user)
user.attributes.keys.each do |attribute|
delegate attribute, to: :user
end
end
When I try to use it in rails console I get:
$> tmp = TempUser.new(User.last)
NameError: undefined local variable or method `user' for TempUser:Class
But if I replace user.attributes by User.new.attributes it works.
It's not due to the loop, actually I get the same error by simply doing:
class TempUser < Struct.new(:user)
test = user
end
I guess I missed something but I have no clue what it is, any idea?
EDIT
It might not be relevant in my example, but for my needs I will need the users' attributes, so I can't stay with something like User.columns.map(&:name).

You have this error, because when ruby parse this class it understand user as class variable that is clear from error message: 'user' for TempUser:Class
And :user it is an instance variable:
TempUser = Struct.new(:user)
tmp = TempUser.new(User.last)
=> #<struct TempUser user=#<User id: 1>>
All users have the same attributes, I think you can use something like this: User.columns.map(&:name)
EDIT
If each user may have different attributes, I think you can do it with method_missing
TempUser = Struct.new(:user) do
def method_missing(m, *args, &block)
return user.send(m) if user.respond_to?(m)
super
end
end

Related

Print variables coming to model's methods

Having following code:
class MyModel < ActiveRecord::Base
before_create :do_process
def do_process
#I want to print here everything that came to this method
self.my_stuff = my_stuff
end
Say we have model with name and description attributes and
I'm going into console and enter something like
MyModel.create! name: 'test', description: 'test'
So how can I view what arguments passed to method do_process?
You can do it this way:
def do_process(*args)
puts args
# body of method
end
It seems that what you really want is to see the attributes of the object as it enters the do_process method. For that, you can use the attributes method:
def do_process
$stderr.puts attributes
self.my_stuff = my_stuff
end

Rails model definition using related field

I have a Rails app that is multi-tenant. In the Tenant record, I store codes that are particular to that Tenant.
The following works well (PS - scope doesn't work):
class Worequest < ActiveRecord::Base
acts_as_tenant(:tenant)
def self.closed
where("statuscode_id = ?", ActsAsTenant.current_tenant.request_closed)
end
What I really need is not worequest.statuscode_id but instead worequest.statuscode.position.
I tried this:
def self.closed
self.statuscode.position = ActsAsTenant.current_tenant.request_closed
end
But, that gives this error:
undefined method `statuscode'
Thanks for the help!
Your method is a class method. It means that your code is referring to the class via self object.
def self.closed
self.statuscode.position = ActsAsTenant.current_tenant.request_closed
end
self here is class Worequest and it doesn't have an attribute statuscode.
What are you trying to with self.statuscode.position = ActsAsTenant.current_tenant.request_closed?

rails find_by_looking_in_every_field ...or... "why is my function missing?"

This is a two parter. I'd be happy with either of the approaches below or other suggestions.
I'd like to be able to retrieve records/objects using my model by passing it a search term and having it look for that search term in any field in the model, or any field that the model deems viable. So, as an example:
class Product < ActiveRecord::Base
...
def search_all_fields(search_term)
return search_term.length == 0 ? nil : Product.where("serial_number like :find_me", { :find_me => search_term })
end
end
This is from a Product model. The same function in the Company model might look like:
class Company < ActiveRecord::Base
...
def search_all_fields(search_term)
return search_term.length == 0 ? nil : Company.where("customer_number like :find_me or name like :find_me", { :find_me => search_term })
end
end
I would love a "railsy" way to do this, such as "find_by_looking_everywhere" but I haven't been able to find such a thing. I've found lots of suggestions for searching a single field for multiple values, but not searching multiple fields for a single value. So that's "Part 1," is there a "railsy" way to do this?
"Part 2" ... using the code above, why am I getting the following exception?
undefined method `search_all_fields` for #<Class:0xa38f2ac>
I'm calling the methods using #products = Product.search_all_fields("xy3445") or #companies = Company.search_all_fields("high")?? The trace shows that the exception is being raised by just a generic class. It doesn't say #<Product...> or #<Company...>
I'm a little lost... any and all help appreciated.
Thanks, gang.
Your method is an instance method (the Model need to be instanciated to access this method). You need a Class method (means you don't need an instance of Company to call it, like the methods where(), find() etc).
class Company < ActiveRecord::Base
def say_hello
return "Hello world!"
end
end
This method say_hello can only be called from an instance of Company (instance method):
company = Company.first
company.say_hello #=> "Hello world!"
# but this will raise a NoMethodError:
Company.say_hello #=> NoMethodError
In order to define a method as a class method, you can do the following:
class Company < ActiveRecord::Base
def self.say_hello
return "Hello world!"
end
# OR you can use the name of the model instead of the self keyword:
def Company.say_hello
return "HEllo World!"
end
end
Now you can do:
Company.say_hello
#=> "HEllo World!"
# but this will fail:
Company.first.say_hello
#=> NoMethodError

ActiveRecord Relationships : undefined method for nil:NilClass

Consider the following:
class Manager < ActiveRecord::Base
has_many :employees
end
class Employee < ActiveRecord::Base
belongs_to :manager
end
employee = Employee.first
puts employee.manager.name
If for some reason an employee does not have a manager I get this:
undefined method `name' for nil:NilClass
Which makes sense. However, is there a clean/suggested way to handle this, so I don't always have to check and see if an employee actually has a manager before I ask for the manager's name?
Try:
puts employee.manager.name unless employee.manager.nil?
Or:
puts (employee.manager.nil? ? "No manager" : employee.manager.name)
Which is equivalent in this case to:
puts (employee.manager ? employee.manager.name : "No manager")
(Equivalent as long as employee.manager can't return false.)
Don't try to be too clever. If you're going to use this repeatedly, make it a function.
class Employee
def manager_name
manager.try(:name).to_s # force empty string for nil
end
end
You can check using the has_attribute? method:
employee.has_attribute? :manager
So something like:
puts employee.manager.name if employee.has_attribute? :manager
I tend to prefer puts employee.manager.try(:name)
The try method returns nil if it was called on nil, but will execute the method properly if manager wasn't nil.
If you want default text:
puts employee.manager.try(:name) || "No Manager"

Rails Validation Error

While trying to add an error message using add_to_base, I am getting an undefined method 'errors' message. I am defining it in my model. Am I supposed to include any other file in order to access the errors variable.
Model File - I am defining it inside a method
self.errors.add_to_base("Invalid Name")
Error Message
undefined method `errors' for #<Class:0x0000010179d7a0>
I tried by calling it as errors.add_to_base("Invalid Name") also but still getting the same error.
Thanks.
you should call it in your callback method, something like following
def validate
if !self.interests.blank? && !self.interests.match("<").nil?
self.errors.add :base, 'Please ensure that Interest field do not contain HTML(< and >) tags'
end
end
I suspect that you have defined your method as a class method, instead of as an instance method.
Class methods look like this on ruby:
def self.checkFoo()
...
end
Instance methods looks like this:
def checkFoo()
...
end
Check that your checkFoo method is an instance method, and then use it like this:
class MyModel < ActiveRecord::Base
validate :foo
private
def checkFoo()
self.errors.add etc..
end
end
Typically used the validation callbacks, model errors are used both to cause the prospective database save to fail and to set up a contextual error messages for the end-user. The add_to_base variant is intended for general, non-specific error conditions (i.e. not associated with a particular model attribute).
class MyModel < ActiveRecord::Base
validate do |my_model|
if my_model.some_attribute.blank? # For example
my_model.errors.add :my_model, "must be filled in"
end
end
end
Subsequently
#my_model = MyModel.create(:some_attribute => "")
would fail and the #my_model.errors.full_messages array would contain
[ ..., "Some_attribute must be filled in", ... ]
There is however a shorthand for the above example as follows
class MyModel < ActiveRecord::Base
validates_presence_of :some_attribute, :msg => "must be filled in"
end
Looks like your 'self.errors.add_to_base("Invalid Name")' doesn't have any problem
But your model should inherit from ActiveRecord::Base
cheers
sameera

Resources