Nested attribute localization for error messages in Rails 4 - ruby

I am using CarrierWave to upload the images.
i have 2 models
class Book < ActiveRecord::Base
has_many :book_attachments, dependent: :destroy
accepts_nested_attributes_for :book_attachments, allow_destroy: true, reject_if: proc { |attributes| attributes['image'].blank?}
end
class BookAttachment < ActiveRecord::Base
belongs_to :book
has_many :images
validates :image,
:file_size => {
:maximum => 3.megabytes.to_i
}
mount_uploader :image, ImageUploader
end
i need to localize the validation message for image size validation.
i give like the following in the en.yml:
en:
activerecord:
errors:
models:
book_attachment:
attributes:
image:
too_big: The image is too big. The maximum size is 3 MB
The following message will get by default if image size is more:
"Book attachments image is too big (should be at most 3 MB)".
But. i need to get the message shown in the en.yml file.
Please help.

Assuming you've implemented the custom validator from the wiki (https://github.com/carrierwaveuploader/carrierwave/wiki/How-to:-Validate-attachment-file-size).
Try this:
en:
activemodel:
errors:
models:
book_attachment:
attributes:
image:
size_too_big: "The image is too big. The maximum size is %{file_size} MB"
Alternatively you could also try a validation on the model directly:
class BookAttachment < ActiveRecord::Base
belongs_to :book
has_many :images
mount_uploader :image, ImageUploader
validate :image_size
def image_size
if image.file.size.to_i > 3.megabytes.to_i
errors.add(:image, :size_too_big)
end
end
end

en:
activerecord:
errors:
models:
book_attachment:
attributes:
image:
size_too_big: "is too big (should be at most 3 MB)"
The above worked for me . Thanks #Youri for the help. i used activerecord instead of activemodel.

Related

Modifying a Lib Model

I would like to override a Lib Model in my Models and add a relation.
What is the best way to do it ?
Example of a model in rpush lib:
https://github.com/rpush/rpush/blob/f82cc6a25861612ce118b2661f5a47bceb7ebd86/lib/rpush/client/active_record/app.rb
module Rpush
module Client
module ActiveRecord
class App < ::ActiveRecord::Base
self.table_name = 'rpush_apps'
if Rpush.attr_accessible_available?
attr_accessible :name, :environment, :certificate, :password, :connections, :auth_key, :client_id, :client_secret
end
has_many :notifications, class_name: 'Rpush::Client::ActiveRecord::Notification', dependent: :destroy
validates :name, presence: true, uniqueness: { scope: [:type, :environment] }
end
end
end
end
I would like to add a has_many relation without editing the gem
So I thought creating a models/app.rb with this would be a start:
class Rpush::Client::ActiveRecord::App
has_many :rel_group_apps
has_many :groups, :through => :rel_group_apps
end
I tried this but nothing changed. Maybe my models/app.rb is not called ?:
module Rpush
module Client
module ActiveRecord
module App
def self.included(includer)
includer.class_eval do
has_many :rel_group_apps
has_many :groups, :through => :rel_group_apps
end
end
end
end
end
end
How should I do it ? Is there a way to extend a lib model without removing the original behavior ?
Thanks !
EDIT
I Made it work but only by putting this code directly in config/initializers/rpush.rb
It wasn't working in models/app.rb
class Rpush::Client::ActiveRecord::App
has_many :rel_group_apps
has_many :groups, :through => :rel_group_apps
end
If someone has a nicer idea, I'll take it !
Extend the class with class << self
class Rpush::Client::ActiveRecord::App
class << self
[your methods here]
end
end

Rails 4 Nested Validations not working

I have this model:
class CompetitionEntry < ActiveRecord::Base
has_many :participants
has_one :address
has_many :music_programs
accepts_nested_attributes_for :address
accepts_nested_attributes_for :participants, :music_programs,
:allow_destroy => true,
:reject_if => :all_blank
end
and this one:
class Participant < ActiveRecord::Base
belongs_to :competition_entry
has_one :birthplace
validates :name, :surname, :instrument, presence: true
end
Now the problem is that, if I create a new competition entry, it goes through.
But if I fill ONE field, i.e name, then it comes up with an error!
Why is this happening? It should fail when all are empty!
When you use accepts_nested_attributes_for, you are able to create the participants records at the same time that the competition_entry record, considering that the hash passed to competition_entry.create contains participants_attributes. When you pass only name, it validates the participant to be created and fail because it has no surname and instrument. When you leave all field empty, the behaviour should be the same, but it isn't because you explicitly set :reject_if => :all_blank.
:reject_if => :all_blank states that the participant_attributes hash should be ignored if it is blank?, which happens when you don't fill any field. What is happening then is that a competition_entry is being created without trying to create a participant because the accepts_nested_attributes_for is just ignored.

Manually updating attributes mounted by Carrierwave Uploader

I am unable to use model.update_attribute on an attribute that is mounted by a carrierwave uploader. The SQL statement wont accept the value and adds NULL to the placeholder. If I remove the mount_uploader statement from the model class it works as normal. I am troubleshooting things from the console and trying to add some attributes while seeding the DB and this is thwarting my efforts. Ideas?
Thanks.
Update:
Relevant code:
class Profile < ActiveRecord::Base
belongs_to :user
has_and_belongs_to_many :sports
has_and_belongs_to_many :interests
has_and_belongs_to_many :minors
has_and_belongs_to_many :majors
has_and_belongs_to_many :events
has_and_belongs_to_many :groups
attr_accessible :description, :username, :avatar, :bio, :first_name, :last_name, :major, :minor, :graduation_date, :living_situation, :phone, :major_ids, :minor_ids, :sport_ids
mount_uploader :avatar, AvatarUploader
end
I am simply trying to rewrite the :avatar string from a db seed file and while testing from the rails console like so:
Profile.first.update_attribute(:avatar, 'foo')
Both work when I comment out the mount_uploader line.
Does adding the mount_uploader method freeze the string or make it immutable?
I found a solution to this.
My issue was that I was not able to alter the attribute mounted my the CarrierWave uploader from my seeds.rb file.
This works:
user.profile.update_column(:avatar, 'foobar/image.png')

Rails 3 - Model structure

I would like to have nice and clean structure in my Rails App.
Now I have 4 files in models folder: Post, PostTranslation, PostCategory and PostCategoryTranslation.
This is my post.rb
class Post < ActiveRecord::Base
attr_accessible :image, :image_cache, :remove_image, :post_category_ids, :post_categories_attributes, :post_translations_attributes
validates :post_translations, :post_categories, presence: :true
translates :name, :content
has_many :post_translations, dependent: :destroy
accepts_nested_attributes_for :post_translations, allow_destroy: true
end
This is post_translation.rb
class PostTranslation < ActiveRecord::Base
attr_accessible :locale, :name, :content
validates :name, length: { maximum: 255 }, presence: true
validates :content, :locale, presence: true
belongs_to :post
end
What should I do? What's the best practice? Make post folder and move translation into this folder and create sub model? Like this: class Translation < Post
Thanks for your advice
The main Best Practice here is to define your domain model properly, and this holds good regardless of Rails.
You need to decide what relation do Post and PostTranslation have with each other. If PostTranslation < Post, then belongs_to :post should probably not be there inside PostTranslation.
Once you have a clearer modelling, put all the classes in models folder itself.
I figured it out. I added namespace Blog..
Now I have these files
blog/post.rb - Blog::Post
blog/post/translation.rb - Blog::Post::Translation
blog/category.rb - Blog::Category
blog/category/translation.rb - Blog::Category::Translation
class Blog::Post < ActiveRecord::Base
validates :translations, :categories, presence: true
translates :name, :content
accept_nested_attributes_for :translations, allow_destroy: true
end
class Blog::Post::Translation < Globalize::ActiveRecord::Translation
validates :name, presence: true
validates :locale, presence: true, uniqueness: { scope: :blog_post_id }
end

Rails multiple belongs_to assignment

Given
User:
class User < ActiveRecord::Base
has_many :discussions
has_many :posts
end
Discussions:
class Discussion < ActiveRecord::Base
belongs_to :user
has_many :posts
end
Posts:
class Post < ActiveRecord::Base
belongs_to :user
belongs_to :discussion
end
I am currently initializing Posts in the controller via
#post = current_user.posts.build(params[:post])
My question is, how do I set/save/edit the #post model such that the relationship between the post and the discussion is also set?
Save and edit discussions along with post
Existing Discussion
To associate the post you're building with an existing discussion, just merge the id into the post params
#post = current_user.posts.build(
params[:post].merge(
:discussion_id => existing_discussion.id
)
You will have to have a hidden input for discussion id in the form for #post so the association gets saved.
New Discussion
If you want to build a new discussion along with every post and manage its attributes via the form, use accepts_nested_attributes
class Post < ActiveRecord::Base
belongs_to :user
belongs_to :discussion
accepts_nested_attributes_for :discussion
end
You then have to build the discussion in the controller with build_discussion after you built the post
#post.build_discussion
And in your form, you can include nested fields for discussions
form_for #post do |f|
f.fields_for :discussion do |df|
...etc
This will create a discussion along with the post. For more on nested attributes, watch this excellent railscast
Better Relations
Furthermore, you can use the :through option of the has_many association for a more consistent relational setup:
class User < ActiveRecord::Base
has_many :posts
has_many :discussions, :through => :posts, :source => :discussion
end
class Discussion < ActiveRecord::Base
has_many :posts
end
class Post < ActiveRecord::Base
belongs_to :user
belongs_to :discussion
end
Like this, the relation of the user to the discussion is maintained only in the Post model, and not in two places.

Resources