Rails 3 - Model structure - ruby

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

Related

Nested attribute localization for error messages in Rails 4

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.

Two files requiring each other in ruby

I'm building a web API with Ruby and Grape. I have two classes that requires each other which leads to a situation where I get uninitialized constant class errors. The place where I get the error is in the Entity class for Connector, see the example code below, which requires Card::Entity before it has been inintialized. Is there any way to solve this probelm without moving the Entity definitions to another file?
#card.rb
require_relative 'connector'
require_relative 'caption'
class Card < ActiveRecord::Base
belongs_to :medium
belongs_to :storyline
has_many :connectors, autosave: true
has_many :connected_cards, class_name: "Connector", foreign_key: "connected_card_id"
has_many :captions
accepts_nested_attributes_for :connectors, :captions
class Entity < Grape::Entity
expose :id, documentation: { readonly: true }
expose :cardtype
expose :connectors, using: Connector::Entity
expose :captions, using: Caption::Entity
end
end
#connector.rb
require_relative 'card'
class Connector < ActiveRecord::Base
has_one :card
has_one :connected_card, :class_name => "Card", :foreign_key => "connected_card_id"
class Entity < Grape::Entity
expose :id, documentation: { readonly: true }
expose :title
expose :card, using: Card::Entity
expose :connected_card, using: Card::Entity
end
end
I don't know a lot about grape, but this could be solved by "pre declaring" the class:
#card.rb
require_relative 'caption'
class Connector < ActiveRecord::Base
# empty declaration just to make the import works
end
class Card < ActiveRecord::Base
belongs_to :medium
belongs_to :storyline
has_many :connectors, autosave: true
has_many :connected_cards, class_name: "Connector", foreign_key: "connected_card_id"
has_many :captions
accepts_nested_attributes_for :connectors, :captions
...
end
Still, I think that QPaysTaxes may have a valid point about design here.

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.

Rails nested attributes in department store pattern not autosaving foreign key

In the following "department store pattern" I have three models:
class Store
has_many :items, inverse_of: :store, autosave: true
has_many :departments, inverse_of: :store, autosave: true
accepts_nested_attributes_for :departments, allow_destroy: true
class Department
belongs_to :store, inverse_of: :departments
has_many :items, autosave: true, inverse_of: department
accepts_nested_attributes_for :items, allow_destroy: true
class Item
belongs_to :store, inverse_of: :items
belongs_to :department, inverse_of: :items
When I try the following:
store = Store.new
department = store.departments.build
item = department.items.build
store.save
Then the item does not associate with the store.
My solution to the problem was to add the following to the Item model:
class Item
before_validation :capture_store_info
def capture_store_info
self.store = self.department.store
end
I added it to the before_validation callback because in my non-trivial code I have a bunch of validations, including one that checks for the presence of the store model.
Question: My solution works, but is it the correct (ie. Rails conventional) way of solving this problem? Is there a better solution. This feels kinda dirty, and every time I have done something in Rails that felt "kinda dirty" it has come back to bite me later.
Thanks,
JB

Resources