How to validate presence of associated object in mongoDB? - ruby-on-rails-3

In my application I have Link model like this:
class Link
include Mongoid::Document
field :url, :type => String
validates_presence_of :url
belongs_to :link_bucket
end
and LinkBucket model, which is inherited from FeedItem model (in my app FeedItem could contain links, message, audio_track and so on, this is why I use inheritance).
class LinkBucket < FeedItem
has_many :links
end
So how can I verify if there is a link before I create LinkBucket object?

You can't make the association unless the objects exist or are being created at the time. But you can create a Link without having an associated LinkBucket, and then later create the LinkBucket and associate them. In other words, only create a LinkBucket when you are sure you have a Link and you need to create one. Does that help?

Related

What is better way to create Client record after User is registered (corresponding client record)

Basically i have user registering himself to the app , by using devise gem.
Instead of having standard sign up form like (email, password) i have an extra 2 fields (name, contact_nr) in total used (name, contact_nr, email, password, password_confirm) fields, :name and :contact_nr attributes exists in 'clients' table only.
Table name: clients
id :integer not null, primary key,
name :string(255)
surname :string(255)
contact_nr :string(255)
user_id :integer
class Client < ActiveRecord::Base
belongs_to :user
end
class User < ActiveRecord::Base
has_one :client, dependent: :destroy
after_create :update_user_client
def name
return unless client
client.name
end
def contact_nr
return unless client
client.contact_nr
end
def update_user_client
Client.last.update_attributes(user: self)
end
end
In my RegistrationsController I have only one method
class RegistrationsController < Devise::RegistrationsController
before_action :create_client
private
def create_client
return if params[:user].blank?
Client
.new(name: params[:user][:name],
contact_nr: params[:user][:contact_nr])
.save(validate: false)
end
end
What bothers me is that kind of writing code, it feels like code smell.
How would you implement it?
Thanks guys looking forward to your answers..
First advice I can give is do not separate client and user into two tables if you don't have valid reasons and/or requirements for now. That would make things much easier.
If you have valid reasons, here are my advices on how to improve your existing state of this code piece:
Rails and all mature gems around it rely on 'convention over configuration', so you should check if there are conventional ways to achieve same results.
In your RegistrationsController instead of doing params[:user].blank? check, you should use Devise's way of doing this, provided with inherited methods as devise_parameter_sanitizer.permit within a before_action callback.
Instead of creating client in your controller, move that to model logic, and in your user model put accepts_nested_attributes_for :client.
Since both of your models(client and user) share same name, put a before_save callback, so that you can pass user's name attribute to client itself.
after_create callback is very risky, since it is not an atomic save (no guarantee that client will be updated after user record is updated.). So don't use it. accepts_nested_attributes_for will handle both create and update calls.
If name attribute for user would be fetched through client only, there is no need to keep name within user.
If you want to access client's contact_nr and name attributes directly from user model, then use delegate method inside it.
Putting all together, I would refactor that code piece as this:
class User < ActiveRecord::Base
has_one :client, dependent: :destroy
accept_nested_attributes_for :client
delegate :name, to: :client
delegate :contact_nr, to: :client
# optional. if you want to keep name attr in both models.
before_save :sync_names
private
def sync_names
self.client.name = name if client.present?
end
end
class RegistrationsController < Devise::RegistrationsController
before_action :configure_permitted_parameters
protected
def configure_permitted_parameters
added_attrs = [:name, :email, :password, :password_confirmation, client_attributes: [:contact_nr]]
devise_parameter_sanitizer.permit :sign_up, keys: added_attrs
devise_parameter_sanitizer.permit :account_update, keys: added_attrs
end
end
Don't forget to update your signup and account update forms to accept nested attributes for client resource.
As far as you are validating the data with JS and filtering with params.require(:client).permit, the code looks fine. Try to create many differente scenarios in your Rspec. The test usually reveals unexpected flaws.

has_many :through and build

I have three models, Account, User and Contact:
class User < ActiveRecord::Base
has_one :account
has_many :contacts, :through => :account
end
class Account < ActiveRecord::Base
belongs_to :owner, :class_name => 'User'
has_many :contacts
end
class Contact < ActiveRecord::Base
belongs_to :account
end
I'm trying to scope build a new contact through the user record, like this in my contacts controller.
def create
#contact = current_user.contacts.build(params[:contact])
respond_to do |format|
if #contact.save
...
else
...
end
end
end
When I do this, I don't receive any errors, the contact record is saved to the database however the account_id column is not set on the contact, and it is not added to the collection so calling #current_user.contacts returns an empty collection.
Any suggestions?
Using build makes a new instance of Contact in memory, but you would need to manually set the account_id on the record (e.g. #contact.account_id = current_user.account.id), or perhaps set it in a hidden field in the new form used to display the contact for creation such that it is picked up in the params array passed to the build method.
You might also want to consider whether accepts_nested_attributes_for may be helpful in this case. Another option may be to use delegate, although in both cases, your use may be sort of the opposite of what these are intended for (typically defined on the "parent").
Update:
In your case, the build method is added to both the User instance and to the Account (maybe "Owner") instance, because you have both a many-to-many relationship between User and Contact, as well as a one-to-many relationship between Account and Contact. So to get the account_id I think you would need to call Account's build, like
#contact = current_user.accounts.contacts.build(params[:contact])
Does this work?

Associated records via custom query in Rails

I have two models that are connected via a has_many/belongs_to association:
Class Project < ActiveRecord::Base
has_many :tasks
end
Class Tasks < ActiveRecord::Base
belongs_to :project
end
Each of the tasks are tagged with a HABTM relationship:
Class Tasks < ActiveRecord::Base
belongs_to :project
has_and_belongs_to_many :tags
end
I am trying to get a list of projects based on a tag id. I can get a list of projects that have tasks with a specific tag by using a class method on my Project model:
def by_tag(tag_id)
Project.joins(:tasks => :tags).where(:tags => {:id = tag_id})
end
Ideally, I'm looking to be able to list all the projects and their associated tasks for a given tag in my view. I could normally get a list of tasks belonging to a given project by using project.tasks if I used a typical find with project like Project.find(1).
However, when I try project.tasks on results found using my new class method Project.by_tag(1), I get a "NoMethodError: Undefined Method 'tasks'" error.
I looked into Named Scopes to get the Project by Tag results but it seems like people are moving away from that approach in favor of class methods. Is that true?
On your project model you need to add it to the class not the instance. Also note that this raises the self object to the class so you can eliminate "Project." unless you want to be explicit.
class << self
def by_tag(tag_id)
joins(:tasks => :tags).where(:tags => {:id = tag_id})
end
end
There is always debate over what is the best method. I myself prefer whatever gets the job done quicker. I like scopes personally but to each his own.

Rails model with Mongoid Embedded and Standalone

How would I create a Mongoid model that has the ability to be saved in it's own collection, and be embedded in another document?
The short answer: you can't.
When you use an embedded relationship between two Mongoid documents, this is because you don't want the child model in its own collection. An embedded document it literally that: embedded in its parent.
I'm not sure if you're new to Mongoid, so what you may actually be looking for is a referenced relationship, which behaves more like a traditional RDBMS relationship, where the child document stores a reference to the parent document's ID. The Mongoid documentation for this starts here.
It's pretty easy to switch between the two, given these embedded models:
class Person
include Mongoid::Document
field :name
embeds_many :phone_numbers
end
class PhoneNumber
include Mongoid::Document
field :area_code
field :number
embedded_in :person
end
You can just change the embeds_many and embedded_in, so it becomes:
class Person
include Mongoid::Document
field :name
has_many :phone_numbers
end
class PhoneNumber
include Mongoid::Document
field :area_code
field :number
belongs_to :person
end
And it will just work. Now you'll be able to do things like query directly for phone numbers with statements like: PhoneNumber.where(:area_code => "212").

Modeling inheritance with Ruby/Rails ORMs

I'm trying to model this inheritance for a simple blog system
Blog has many Entries, but they may be different in their nature. I don't want to model the Blog table, my concern is about the entries:
simplest entry is an Article that has title and text
Quote, however, does not have a title and has short text
Media has a url and a comment...
etc...
What is a proper way to model this with Ruby on Rails? That is
Should I use ActiverRecord for this or switch to DataMapper?
I would like to avoid the "one big table" approach with lots of empty cells
When I split the data into Entry + PostData, QuoteData etc can I have belongs_to :entry in these Datas without having has_one ??? in the Entry class? That would be standard way to do it in sql and entry.post_data may be resolved by the entry_id in the postdata table.
EDIT: I don't want to model the Blog table, I can do that, my concern is about the entries and how would the inheritance be mapped to the table(s).
I've come across this data problem several times and have tried a few different strategies. I think the one I'm a biggest fan of, is the STI approach as mentioned by cicloon. Make sure you have a type column on your entry table.
class Blog < ActiveRecord::Base
# this is your generic association that would return all types of entries
has_many :entries
# you can also add other associations specific to each type.
# through STI, rails is aware that a media_entry is in fact an Entry
# and will do most of the work for you. These will automatically do what cicloon.
# did manually via his methods.
has_many :articles
has_many :quotes
has_many :media
end
class Entry < ActiveRecord::Base
end
class Article < Entry
has_one :article_data
end
class Quote < Entry
has_one :quote_data
end
class Media < Entry
has_one :media_data
end
class ArticleData < ActiveRecord::Base
belongs_to :article # smart enough to know this is actually an entry
end
class QuoteData < ActiveRecord::Base
belongs_to :quote
end
class MediaData < ActiveRecord::Base
belongs_to :media
end
The thing I like about this approach, is you can keep the generic Entry data in the entry model. Abstract out any of the sub-entry type data into their own data tables, and have a has_one association to them, resulting in no extra columns on your entries table. It also works very well for when you're doing your views:
app/views/articles/_article.html.erb
app/views/quotes/_quote.html.erb
app/views/media/_media.html.erb # may be medium here....
and from your views you can do either:
<%= render #blog.entries %> <!-- this will automatically render the appropriate view partial -->
or have more control:
<%= render #blog.quotes %>
<%= render #blog.articles %>
You can find a pretty generic way of generating forms as well, I usually render the generic entry fields in an entries/_form.html.erb partial. Inside that partial, I also have a
<%= form_for #entry do |f| %>
<%= render :partial => "#{f.object.class.name.tableize}/#{f.object.class.name.underscore}_form", :object => f %>
<% end %>
type render for the sub form data. The sub forms in turn can use accepts_nested_attributes_for + fields_for to get the data passed through properly.
The only pain I have with this approach, is how to handle the controllers and route helpers. Since each entry is of its own type, you'll either have to create custom controllers / routes for each type (you may want this...) or make a generic one. If you take the generic approach, two things to remember.
1) You can't set a :type field through update attributes, your controller will have to instantiate the appropriate Article.new to save it (you may use a factory here).
2) You'll have to use the becomes() method (#article.becomes(Entry)) to work with the entry as an Entry and not a subclass.
Hope this helps.
Warning, I've actually used Media as a model name in the past. In my case it resulted in a table called medias in rails 2.3.x however in rails 3, it wanted my model to be named Medium and my table media. You may have to add a custom Inflection on this naming, though I'm not sure.
You can handle this easily using ActiveRecord STI. It requires you to have a type field in your Entries table. This way you can define your models like this:
def Blog > ActiveRecord::Base
has_many :entries
def articles
entries.where('Type =', 'Article')
end
def quotes
entries.where('Type =', 'Quote')
end
def medias
entries.where('Type =', 'Media')
end
end
def Entry > ActiveRecord::Base
belongs_to :blog
end
def Article > Entry
end
def Quote > Entry
end
def Media > Entry
end