Building a rails Model using initialize - ruby-on-rails

I have a model like so:
class Phrase < ActiveRecord::Base
attr_accessible :phrase, :emotion, :category
end
This model corresponds to it's appropriate table. Every time I want to create a new Phrase, I run this code in a controller:
Phrase.where(:phrase => values['phrase']).first_or_create do |phrase|
phrase.emotion = values['emotion']
phrase.category = values['category']
end
I feel like I should be using the initialize method on my model, or perhaps creating a new method on my model to build them up. The above method seems pretty bad, especially when I start building models with 20+ attributes.
Is there a best/better practice around building models? Should the above controller code actually live in the model somewhere?

def create
#phrase = Phrase.new(params[:phrase])
end
or if you are using strong parameters
def create
#phrase = Phrase.new(phrase_params)
end
private
def phrase_params
params.require(:phrase).permit(:phrase, :emotion, :category)
end
end

Since your model allows for mass assignment of those attributes via attr_accessible you can just pass a hash to new:
Phrase.new(values)
You can also use find_or_create_by with a hash of options:
Phrase.find_or_create_by_phrase(values[:phrase], values)

Related

How to get the scoped attributes when creating a new object through a has_many association

When creating objects through a has_many association like User.first.books.create!(...), the new book gets the user_id automatically from the association.
Is there any way to get that user_id if I call my own create method? i.e. User.first.books.own_create_method
def self.own_create_method
# how to get the user object?
end
Thanks!
To define User.first.books.own_create_method you would use:
def self.own_create_method
book = build
# something custom you want to do with book
book.save
end
self. allows you to define class methods in Ruby.
Digging into ActiveRecord new method, I found that you can call scope_attributes and you'll get a hash with all the attributes that are scoped.
def self.own_create_method
attributes = scope_attributes
# attributes["user_id"] would be the user_id that is scoped by
...
end
Not sure if this is a good practice though...

RoR: How to access a model object in another model

I have created a new model in my app - PaypalOrder. Now in one of the methods of this model I want to be able to access current_order object. Where Order is an existing model. How can I do this in ruby on rails?
I read about associations, but they seem a bit complicated.
EDIT:
The problem with using associations is that not every Order will have a corresponding PaypalOrder. But whenever there is a PaypalOrder I want to access Order. How can I setup this association
what about:
class PaypalOrder
belongs_to :order
end
?
you need an "order_id" column in paypal_orders table
and that's it
you then create a PaypalOrder with
def some_action
current_order = Order.find(some_id)
paypal_order = PaypalOrder.new(order: current_order)
#do what you want with paypal_order
end
if you don't have the order_id do
bundle exec rails g migration AddUserToPaypalOrder
and the change method
add_column :paypal_orders, :user, :references
or
add_column :paypal_orders, :user_id, :integer
The way to go is to use concerns, it works like this:
Model:
# app/models/PayPayOrder.rb
class PayPalOrder < BaseModel
# concerns
include MyMultipleNeededMethods
...
def now_i_use_the_concern_method
concern_method
end
...
end
Concern:
# app/models/concerns/MyMultipleNeededMethods.rb
module MyMultipleNeededMethods
extend ActiveSupport::Concern
def concern_method
puts "refactored like a boss"
end
...
end
Never ever try to cross reference methods this way. Use the given rails framework, its awesom ;-)
Hm... current_order and curren_user and usually current_s are tight to the session. So they can be accessed only by the controller. Since the models are handling business-domain logic they shouldn't access these objects...

Proper way to initialize nested fields in Rails forms

I'd like to understand what's the "proper" way to initialize the nested fields of a model.
Let's say you have some nested fields for a model:
class User
has_one :address
accepts_nested_attributes_for :address
end
And you need to initialize those attributes (address in this case) to use them in a fields_for call.
So far I've thought of three ways to do this.
First, after_initialize hook on the model:
class User
after_initialize :init_address
protected
def init_address
address ||= build_address
end
Then we have initialization in the controller:
class UsersController
def new
#user = User.new
#user.build_address
end
end
And finally, we can have a helper method to do it for us:
module FormHelpers
def setup_user(user)
user.address ||= user.build_address
user
end
end
# view
<%= form_for setup_user(#user)... %>
Is there anything resembling a standard or a "best practice" for this scenario? How do you do it and why?
I think that if the nested attribute doesn't make sense at all without the parent model, building and initialization of these nested models should be the responsibility of the parent model.
I don't see why the UsersController should care about how the #user.addresses are built or initialized. For me, giving the controller this responsibility, would probably imply that on create he should be the one that parsed and built the nested attributes (which, happens in the model).
I would go for the first approach.
i believe that build_address is already built in for rails after u declare a has_one association, so you don't need to write that bit urself.
and if the form is called only from the new action, what u really need is only the controller bit, and nothing else

Using one form to create two models with overlapping attributes in Rails

This post seems good for how to create two models with one form. But how would you do it if the two models share one or more of the attributes?
That post seems fairly outdated, I would recommend using accepts_nested_attributes_for and fields_for in your form instead. That said, overlapping attributes should probably be set in your model's callbacks. Say you want a project's name to be automatically set to first task's name.
class Project < ActiveRecord::Base
has_many :tasks
accepts_nested_attributes_for :tasks
before_validation :set_name_from_task
private
def set_name_from_task
self.name = tasks.first.name
end
end
If your 2 models are completely unrelated, you can assign certain params to them directly in the controller.
def create
#foo = Foo.new(params[:foo])
#bar = Bar.new(params[:bar])
#bar.common_attr = params[:foo][:common_attr]
# validation/saving logic
end
Although this is not a great practice, this logic should ideally be moved into models.

Ruby on Rails - Overriding the association id creation process

I'm trying to override the way rails apply and id to an associated object, for example:
There are 2 simple models:
class Album < ActiveRecord::Base
has_many :photos
end
class Photo < ActiveRecord::Base
belongs_to :album
end
And then I want to do this:
album = Album.new :title => 'First Album'
album.photos.build
album.save #=> true
On this case I've created a plugin that overrides the id property and replaces it to a hashed string, so what I want to do is find the methods where this album_id is being replaced for my custom method instead of the int and be able to converted before it's saved.
But I want to act globally inside Rails structure because since it will be a sort of plugin I want to make this action work on dynamic models, that's why I can't create an before_save validation on the model.
I'm not sure if it's easy to understand, but I hope someone could help me on that..
Here's a screenshot of my current table so you can see what is happening:
SQLite3 DB http://cl.ly/1j3U/content
So as you can see the album_id it's being replaced for my custom ruby object when its saved...I've disabled the plugin and then it saved normally with records 11 and 12...
I want just act on a rails action and converted with my custom methods, something like
def rails_association_replaced_method(record)
#take the record associations and apply a to_i custom method before save
super(record)
end
something like this :)
Well I hope this didn't get too complicated
Cheers
It seems if I only override theActiveRecord::Base save method do the job if handled properly
define_method 'save' do
int_fields = self.class.columns.find_all { |column| column.type == :integer }
int_fields.each do |field|
if self.attributes[field.name]
self.attributes[field.name] = self.attributes[field.name].to_i
end
end
super
end
And this shall replace all the integer fields from the Current Model applying a to_i method over the result.
Rails is unfriendly to that kind of change to the defaults. What's your end goal here?

Resources