Custom model name in Rails 4 validation - ruby-on-rails-4

I have a class that looks something like this:
class OrganicBipedalLifeform < ActiveRecord::Base
# Has the field 'name'
validate :presence_of_name
private
def presence_of_name
errors.add(:base, "name can't be blank") unless name.present?
end
end
And I want the validation error message to use a custom string that excludes (or modifies) the model name, say 'Human/Vulcan name can't be blank'.
If I want this to be the default message for validation errors on this model, is there a better approach than changing the flash details in every view which might display validation errors? Ie by changing something on the model itself?
Apologies if this has been answered elsewhere. I've found a lot of posts about customising the field name, but none about modifying the name of the model itself.
ETA: #TomDunning #Dan, I think I misidentified the source of the problem (or at least didn't make it sufficiently specific), so am creating a new thread to ask what I hope is a better question.

I think you can replace :base with self.class_name or self.class.table_name or a similar class method.

That is bad design, just use this:
validate :name, presence: true
"name can't be blank" would be the default error anyway.
If you then want to extract these later just call my_record.errors or similar.
For a custom error message
validate :name, presence: { message: 'must not be blank' }

Related

How to customize Devise Invitable for different use cases

I am trying to follow the documentation for Devise Invitable here to send different email for different user types, in my case partners and clients.
So it says to add in your Devise model, which in my case is User.rb, the following code.
....
attr_accessor :invitation_instructions
....
def self.invite_partner!(attributes={}, invited_by=nil)
self.invite!(attributes, invited_by) do |invitable|
invitable.invitation_instructions = :partner_invitation_instructions
end
end
def self.invite_client!(attributes={}, invited_by=nil)
self.invite!(attributes, invited_by) do |invitable|
invitable.invitation_instructions = :client_invitation_instructions
end
end
Then from my controller, when a new user signs up I am calling
....
if current_user.is_client?
user.invite_client!(user, current_user)
else
user.invite_partner!(user, current_user)
end
When I do that the error I get back is
undefined method 'invite_client!' for #<User:0x007ffbcdfabd08>
Which is a little confusing because the method is defined in the user model, so I would think that, at least, it was defined.
Any help on fixing this and getting this setup to work would be greatly appreciated!
I think those are class methods and you should call it like User.invite_client! and also pass your arguments in method.
Same goes for invite_partner!

NoMethodError - new since upgrading to Rails 4

I'm at my wit's end. I upgraded to Rails 4.2.10, and everything is terrible.
Here is the relevant part of /models/product.rb
class Product < ActiveRecord::Base
delegate_attributes :price, :is_master, :to => :master
And here is /models/variant.rb:
class Variant < ActiveRecord::Base
belongs_to :product
The variants table has fields for "price" and "is_master". Products table does not.
It used to be the case that one could access Product.price and it would get/set the price for the master variant (there's really only one variant per product, the way things are currently set up).
Now it complains that:
NoMethodError: undefined method `price=' for #<Product:0x0000000d63b980>
It's true. There's no method called price=. But why wasn't this an issue before, and what on earth should I put in that method if I create it?
Here's the code to generate a product in db/seeds.rb:
product = Product.create!({
name: "Product_#{i}",
description: Faker::Lorem.sentence,
store_id: u.store.id,
master_attributes: {
listing_folder_id: uuids[i],
version_folder_id: uuids[i]
}
})
product.price = 10
product.save!
end
delegate_attributes isn't a Rails method and looks like it comes from a gem (or gems) that aren't actively maintained?
If there's a new version of whatever gem you're using that might help, because the short answer is that part of the "delegating" of an attribute would involve getting and setting the attribute, so it would generate #price= for you.
If you want to define it yourself, this should do it (within your Product class):
def price=(*args)
master.price=(*args)
end
or if you want to be more explicit:
def price=(amount)
master.price = amount
end

How to make attribute_names list all attribute names in a document with dynamic attributes

I have a Rails 4.2 application with mongoid in which I'm importing csv files with test results. I can't define all fields in the model because they change from test to test and theres always around 700 of them. I use Dynamic Attributes and importing and displaying works fine.
I'm trying to use attribute_names method to get all attribute names but all I get is those defined in the model. If I don't define anything in the model it comes back with "_id" only. attributes method on the other hand can see attributes in the actual document on the other hand.
>> #results.first.attributes.count
=> 763
>> #results.first.attribute_names
=> ["_id"]
I also tried fields.keys, same problem
>> #results.first.fields.keys
=> ["_id"]
My model at the moment looks like this
class Result
include Mongoid::Document
include Mongoid::Attributes::Dynamic
def self.import(file)
CSV.foreach(file.path, headers: true) do |row|
Result.create! row.to_hash
end
end
end
Can somebody explain how to make it work?
Any help greatly appreciated.
This part is not very clear in the documentation.
and this answer doesn't address how you can make your case works ( I really don't know)... but it has one monkey patch at the end...
all I know is why this case not working...
as the documentation states
When dealing with dynamic attributes the following rules apply:
If the attribute exists in the document, Mongoid will provide you with your standard getter and setter methods.
For example, consider a person who has an attribute of "gender" set on the document:
# Set the person's gender to male.
person[:gender] = "Male"
person.gender = "Male"
# Get the person's gender.
person.gender
this is not your case... cause as it appears you are not defining any attributes in your model...
what applies in your case (from the code you showed and problem you described)
If the attribute does not already exist on the document,
Mongoid will not provide you with the getters and setters and will enforce normal method_missing behavior.
In this case you must use the other provided accessor methods: ([] and []=) or (read_attribute and write_attribute).
# Raise a NoMethodError if value isn't set.
person.gender
person.gender = "Male"
# Retrieve a dynamic field safely.
person[:gender]
person.read_attribute(:gender)
# Write a dynamic field safely.
person[:gender] = "Male"
person.write_attribute(:gender, "Male")
as you can see... there is no way for mongoid to add the setter and getter methods in runtime...
Monkey Patch
you could add a field (maybe string, array, hash, whatever suites you) to the document (attribute exists in the document)
on populating the document from the CSV row.. just save what are the fields of the CSV in that field... (hold the CSV keys in it)
use your predefined field (that holds the keys) instead of using .keys.
code example in your case.
class Result
include Mongoid::Document
include Mongoid::Attributes::Dynamic
field :the_field_that_holds_the_keys, type: Array
# ...
end
and in your controller:
#results.first.some_attribute
#=> method missing error
#results.first[:some_attribute]
#=> some_value
#results.first.the_field_that_holds_the_keys
#=> [:some_attribute, :some_other_attribute, :yada]

Rails 4: strong_params,nested_attributes_for and belongs_to association trouble

I really can't get my head around Rails 4 strong parameters, belongs_to association and form with fields_for.
Imagine I have model for quoting some price:
class Quote < ActiveRecord::Base
belongs_to :fee
accepts_nested_attributes_for :fee
Now, I have seeded some fees into the db, and have put some radiobuttons on my form_for #quote using fields_for. The values of the radiobuttons are simply ids of the records.
Here is the troubling part, the controller:
def create
#quote = Quote.new(quote_params)
...
end
def quote_params
params.require(:quote).permit(:amount_from, fee_attributes: [:id])
end
From my understanding, automagically Rails should fetch fee record with some id, but there is some mystic error instead.
params hash is: "quote"=>{"amount_from"=>"1200", "fee_attributes"=>{"id"=>"1"}}
Log tail:
Completed 404 Not Found in 264ms
ActiveRecord::RecordNotFound (Couldn't find Fee with ID=1 for Quote with ID=)
app/controllers/quotes_controller.rb:14:in `create'
I really don't understand what is going on here, have read Rails association guide, googled for hour for all info, but to no avail.
What I want to achieve here is to understand the correct "Rails way" to fetch some associations for new Quote object using some params I've put in the form.
Guess I got nested_attributes_for wrong, somehow thought it would call Fee.find automagically.
I've opted for ditching fields_for helpers from the form and rendering fields manually like
radio_button_tag 'fee[id]', fee.id
Then in controller I have 2 params methods now:
def quote_params
params.require(:quote).permit(:amount_from)
end
def fee_params
params.require(:fee).permit(:id)
end
And my action looks like
def create
#quote = Quote.new(quote_params)
#quote.fee = Fee.find(fee_params[:id])
...
Any additions on best practices when one has to handle lots of different objects with not so straight init logic are welcome.

Fake select field for Simple Form

I'm using Simple Form, and I have a few fields that are not associated with my model. I found using this fake field option to be a good solution:
https://github.com/plataformatec/simple_form/wiki/Create-a-fake-input-that-does-NOT-read-attributes
I thought this was cleaner than adding an attr_accessor value for my fields, and it works great for text input fields. I'm hoping to accomplish the same thing with a Select Field.
I found this thread, but I couldn't find anything else:
https://github.com/plataformatec/simple_form/issues/747
Has anyone found a similar option for a Fake Select Input? Thanks!
Assuming you'll use that "fake select" for UI purposes (probably as a mean to modify the form fields to present the user using Javascript?) and you don't want to deal with the value in the controller, you could just use select_tag with any field name, instead of the simple_form f.input. The value will be sent to the server, but it will be outside the model params, so you can just ignore it in the controller.
If I misunderstood your question, please clarify.
If your just trying to get the name='whatever' instead of having name='model[whatever]' I've found it easiest to just specify the name attribute in input_html { name: 'whatever', id: 'whatever' } hash which over rides the default model[attribute].
Otherwise you could create a fake_select_input.rb which would be similar to fake_input.rb but obviously use a select_tag instead and do something like as: :fake_select