I have a model user that has one traversal. The Traversal model has an attribute, frame. I can access it using
user.traversal.frame
but I seem to recall there is a ruby on rails means for making the following work.
user.traversal_frame
without an extra method in the User model i.e. without doing
def traversal_frame
self.traversal.frame
end
Is there another way to do this?
Use delegate method. Something like following
class User < ActiveRecord::Base
has_one :traversal
delegate :frame, to: :traversal
end
You can then use it like following
user.frame # => Same as user.traversal.frame
Note:- when user has no traversal then user.frame will throw an error raises NoMethodError: undefined method 'frame' to fixed this use following
delegate :frame, to: :traversal, allow_nil: true
To access it using user.traversal_frame you have to use prefix option as below
delegate :frame, to: :traversal, prefix: true, allow_nil: true
Related
I am learning both Ruby (2.3.x) & Rails (4.x). I was going through the Ruby On Rails Tutorial and I encountered this syntax and am having trouble reading it:
class User < ApplicationRecord
validates :name, presence: true
validates :email, presence: true
end
Does this class define validates as a method which takes in a :name symbol and a hash presence:true? The same thing applies to line 3.
Or it is something entirely else? All attempts to run it resulted in:
uninitialized constant ApplicationRecord.
I looked at the source(maybe?) but am still not clear.
This is a special DSL introduced by ApplicationRecord. What you are actually doing is calling those methods inside class during declaration. It adds those validations to your class, so whenever you try to save a record it will fail if you don't have email or name
Try this
user = User.new
user.save
user.valid? # false
And try to do the same without validates.
If it will make things more clear for you, you can try write this class like this
class User < ApplicationRecord
validates(:name, presence: true)
validates(:email, presence: true)
end
validates is implemented as a class method in ActiveModel::Validations.
The ActiveModel::Validations module is included in ApplicationRecord, therefore you are able to call that method when your User class is loaded.
validates accepted an array and treats the last element of that array as an options hash (if the last element is an hash).
validates is a pre-defined helper that Active Record offers to use in Rails to make the validation work easier, this way you can with some single lines of code manage several validations of several attributes.
As it's a helper within Rails it's also a method defined in the ActiveModel module, in the core of the framework, see: active_model/validations.rb
The most common is the presence attribute which you're facing the trouble with, that specifies that the attribute used isn't empty, doing it in the Ruby way through the blank? method to check if the value passed isn't blank nor nil.
In order to access the same array in different parts of my app, I stored the array in the corresponding Model, using class method to retrieve the array.
In the code bellow, codes are used in views (to generate the select drop down) and in the same model (to validate passed value).
class Request < ActiveRecord::Base
validates :code, presence: true, inclusion: { in: self.codes }
def self.codes
return ["OBJ", "SER", "REC"]
end
end
But using this, generates the following error:
undefined method `codes' for #<Class:0x000001074ddc50>
Even removing the self. in the inclusion, doesn't solve the problem (undefined local variable).
Do you have an idea?
Your codes method is declared after you've used it in your validation - when the validation line is executed the method has not yet been defined, hence the error.
If you place the validation after the codes method it should work.
You can define it as a constant at a top of your model
class Request < ActiveRecord::Base
CODES = ["OBJ", "SER", "REC"]
Than you can access it like this Request::CODES
validations will look like this
validates :code, presence: true, inclusion: { in: CODES }
I wonder what the beautiful way to output this in Rails.
order.items.min.user.email
I want to show the value of email, the only thing I know is order is not nil, however items and user might be nil.
The only way I see is
if !order.items.empty?
if !order.items.min.user.nil?
if !order.items.min.user.email.nil?
order.items.min.user.email
end
end
end
It looks like not the best way, do you know the better way?
You could use try (or try! depending on your Rails3 version and how you want unknown methods to be handled):
order.items.try(:min).try(:user).try(:email)
try is an easy way to swallow nils (and unknown methods depending on your Rails version) in a long method call chain.
Consider this more elegant approach:
Order:
class Order < ActiveRecord::Base
has_many :items
end
Item:
class Item < ActiveRecord::Base
belongs_to :user
delegate :email, to: :user, allow_nil: true, prefix: true # You can remove the prefix true and call item.email instead item.user_email
end
Then:
order.items.min.try(:user_email)
or:
order.items.min.try(:user_email).presence || "Not found"
Which will return the user's email or "Not found" in case items.min is nil, user is nil, or email is nil.
Is there a way to get embedded documents to initialize automatically on construction in mongoid? What I mean is given that User which embeds a garage document. I have to write the following code to fully set up the user with the garage:
user = User.create!(name: "John")
user.build_garage
user.garage.cars << Car.create!(name: "Bessy")
Is there a way I can skip calling user.build_garage?
Thanks
Mongoid 3 have autobuild option which tells Mongoid to instantiate a new document when the relation is accessed and it is nil.
embeds_one :label, autobuild: true
has_one :producer, autobuild: true
You can add a callback to the User model like this:
class User
...
after_initialize do |u|
u.build_garage unless u.garage
end
...
end
This callback fires after each instantiation of the class, so it fires after 'find' and 'new'.
I have a child model which accepts_nested_attributes_for another model in a has_one / belongs_to relationship. I am trying to configure the activescaffold controller like this:
config.create.columns = [:name, :birthdate, :device_attributes]
But it just throws this error:
undefined method `device_attributes' for #<Child:0xc103e28>
Note: I have overridden the default create_form with a custom implementation.
I found a way to make it works. I just added this to the activescaffold controller:
def before_create_save(record)
record.device_attributes = params[:record][:device_attributes]
end
def before_update_save(record)
record.device_attributes = params[:record][:device_attributes]
end
It's not the cleaner way to do it, but i didn't find other way.