I have many select options in my forms with a collection of possible options.
E.g.:
title_options = %w[Mr Mrs Miss Ms Dr]
In my view, I will render the select (using formtastic):
<%= f.input :title, :as => :select, :collection => title_options %>
Currently, though, I store the title options in a helper file with many methods for each select:
module SelectHelper
def days_options
...
end
def title_options
...
end
..
end
Then, in a model for validation I can either extend this helper:
class user < ActiveRecord::Base
extend SelectHelper
validates :title, :inclusion => {:in => title_options}
end
or duplicate the options:
class user < ActiveRecord::Base
validates :title, :inclusion => {:in = %w[Mr Mrs Miss Ms Dr]}
end
Is there a better way to store the collection, for example, as a method in the model directly? I don't see it necessary to store these options in a database as they shouldn't ever change.
Well, to simplify things, you can store this collections in a constant inside your class.
class User < ActiveRecord::Base
TITLES = %w[Mr Mrs Miss Ms Dr]
validates :title, :inclusion => {:in => TITLES}
You can try to use enumerated_attribute gem or some of same functionality
Related
I am trying to use auto-complete/type-ahead feature provided by following Rails gem https://github.com/maxivak/bootstrap3_autocomplete_input , together with https://github.com/plataformatec/simple_form
Everything works fine in case I choose "new" action for new record in form. I am able to choose value in input field by auto-complete feature. Problem is in case i choose "edit" action to edit already existing record. In that case field does not show correct value (pre-filled by form), but it shows something like: #<Airport:0x007f98b478b7a8>
Even in "show" action, I can see correct value displayed.
I tried to change f.input with f.association as I had it before I started implementing auto-complete, but this did not helped.
Records in Cargo model have correct airports_id reference stored, I checked that manually in rails console.
Question is how can I get correct Airport value pre-filled by form in case I choose "edit" action, instead some kind of reference, I got.
Rails 4.1.7
My code is:
Cargo model:
class Cargo < ActiveRecord::Base
belongs_to :airport
...
Cargo view:
...
<%= f.input :airport, :as => :autocomplete, :source_query => autocomplete_airport_city_airports_url %>
...
Airport model:
class Airport < ActiveRecord::Base
has_many :cargos, :dependent => :destroy
attr_accessible :iata_code, :name, :city
validates :iata_code, :name, :city, presence: true
validates :iata_code, :uniqueness => { :scope => :name }
validates :iata_code, length: { is: 3 }, format: { with: /\A[a-zA-Z\d\s]*\z/ }
validates :name, :city, length: { minimum: 2, maximum: 128 }
def full_airport_name
"#{city} / #{iata_code}"
end
end
Airports controller
class AirportsController < ApplicationController
autocomplete :airport, :city, { :display_value => 'full_airport_name', :full_model=>true }
...
Routes:
resources :airports do
get :autocomplete_airport_city, :on => :collection
end
Actually I found the problem. First of all I refactored Airports model, removed all columns but name, and reseed name column with data concatenated from separate strings IATA code / City. After this, there is need to specify in model, what to show as value. Simply this solved this issue:
class Airport < ActiveRecord::Base
has_many :cargos, :dependent => :destroy
attr_accessible :name
validates :name, presence: true
validates :name, :uniqueness => true
def to_s
name
end
end
This is described, I didnot understand it on first sight previously, in original documentation here https://github.com/maxivak/bootstrap3_autocomplete_input section Model.
User f.association and because rails will automatically look for :nameand you do not have that, you'll have to define it like so:
f.association :airport, label_method: :full_airport_name, value_method: :id........etc
I am working on a self-learning Rails application (the source code can be found here. I want to validate the presence of the content before posting a text or an image:
.
Those are my models or look below:
class Post < ActiveRecord::Base
belongs_to :user
default_scope { order ("created_at DESC")}
belongs_to :content, polymorphic: true
has_reputation :votes, source: :user, aggregated_by: :sum
end
class PhotoPost < ActiveRecord::Base
has_attached_file :image, styles: {
post: "200x200>"
}
end
class TextPost < ActiveRecord::Base
attr_accessible :body
end
Here are my controllers in case they have a relation with this. Any other files can be found in my Github account. I am sure it will be messy to copy the whole project (that is why I am giving links for the controllers and for my project).
So what I have tried so far. (I tried those on the Posts Model)
=> Using validates_associated
validates_associated :content, :text_post
and getting an error "undefined method `text_post' for #Post:0x517c848>"
=> Used validates
validates :content, :presence => true
and getting no error however a post is created with no text.
validates :body, :presence => true
and getting an error "undefined method `body' for #Post:0x513e4a8>"
If you need any other information please let me know and I will provide it asap.
Thank you.
It would seem you have quite a confusing model setup with some key missing relation rules. E.g. Polymorphic rule which is not being utilised and a has_many relation between User and Post with no sign a of a user_id value in the Post model. Here is how I would set it up:
User.rb
def User << ActiveRecord::Base
has_many :text_posts
has_many :photo_posts
end
TextPost.rb
def TextPost << ActiveRecord::Base
attr_accessible :body, :user_id
belongs_to :user
validates :body, :presence => true
end
PhotoPost.rb
def PhotoPost << ActiveRecord::Base
attr_accessible :image, :user_id
belongs_to :user
validates :file, :presence => true, :format => {
:with => %r{\.(gif|png|jpg)$}i,
:message => "must be a URL for GIF, JPG or PNG image."
}
end
Then in your view you would need to do:
<%= form_for #text_post do |f| %>
# ...
<% end %>
And in your controller you can modify the create method to include the current_user from devise and assign it to the new text post record (user_id attribute):
text_posts_controller.rb
def create
#text_post = current_user.text_posts.new(params[:text_post])
end
This adheres more to the DRY principle which Ruby on Rails excels at - you shouldn't be writing alot of code to just create a new record.
I would advise on reading up on some Ruby on Rails standard and best practises. You shouldn't need to create a method in the Dashboard Model in order to create a new TextPost or PhotoPost record. This is a very confusing way of going about it; instead you should be utilising the power of ActiveRecord relation.
I would advise checking out Railscasts. They have alot of fulfilling content.
What I'm trying to do is write a method that will return all of this model's outing_locations, to which it has a has_many relationship.
class Outing < ActiveRecord::Base
attr_accessible :description, :end_time, :start_time, :title, :user_id
belongs_to :user
has_many :outing_locations
has_many :outing_guests
has_one :time_range, :foreign_key => "element_id", :conditions => { :element_type => "outing" }
validates :title, :presence => true
validates :start_time, :presence => true # regex
validates :end_time, :presence => true # regex
def host_name
return User.find(self.user_id).full_name
end
end
I'm trying to get this block in particular to work.
There's another model called OutingInvite, which contains the id of a particular Outing. I need to use that to grab the proper Outing and then pull said Outing's associated outing locations.
Here's a rough sample:
<%#outing_invites.each do |invite|%>
...
<% #party = Outing.where(:id => invite.outing_id) %>
<% #party.outing_locations.each do |location| %>
And then have it output each location.
However, it's saying the method 'outing_locations' does not exist...
You can see a model's associated models by typing model_instance.associated_model_name. So in your example, an outing has_many outing_locations. After you have an instance of an outing, say by using #o = Outing.find(1), you can then use o.outing_locations to see the outing_locations associated with that specific outing.
See this example from the Ruby on Rails Guide.
EDIT
The reasons you're getting the method 'outing_locations' does not exist error is because Outing.where(:id => invite.outing_id) returns an array, and there is no outing_locations method for arrays. You'll need to either get the specific instance (like with Outing.find(invite.outing_id) or use a specific index in that array. I recommend using Outing.find(invite.outing_id) since (I'm assuming) each of your Outing's has a unique id.
I have two classes
class GpsPoint < ActiveRecord::Base
validates :longitude, :presence => true
validates :latitude, :presence => true
belongs_to :station
end
and
class Station < ActiveRecord::Base
validates :name, :presence => true,
:length => { :maximum => 50 }
validates :gps_point, :presence => true
has_one :gps_point
belongs_to :route
end
The gps points are enter separated from station when a station is created i want to be able select a gps point from a drop down list.
How can i create the dropdown list with all the gps points ?
<%= select('station', 'gps_point_id', GpsPoint.all.collect {|u| [u.name,u.id]}) %>
Check out Formtastic. It's a gem that lets you easily create forms and automatically deals with Foreign Keys really well - lets you create a drop down or radio buttons.
Otherwise there is decent article on rails select helpers:
http://shiningthrough.co.uk/Select-helper-methods-in-Ruby-on-Rails
In my rails app I have 2 models: post and post_translations.
class PostTranslation < ActiveRecord::Base
belongs_to :post
LANGUAGES = %w( en fr es de it )
validates_inclusion_of :language, :in => LANGUAGES
end
class Post < ActiveRecord::Base
has_many :post_translations
end
I want to prevent the same language translation from being submitted twice, so I want to limit the enums to the values not listed in the language column of a particular post_id.
I don't know if I should do this in model, controller or helper.
Which is the best practice?
Thanks in advance.
I'd use an attribute on the class instead of defining it on an instance.
class PostTranslation < ActiveRecord::Base
##languages = %w( en fr es de it )
cattr_reader :languages
belongs_to :post
validates :language, :inclusion => { :in => ##languages },
:uniqueness => { :scope => :post_id }
end
Now to fulfill your requirement of showing only the languages without translations, define a method on Post:
class Post < ActiveRecord::Base
has_many :post_translations
def untranslated
PostTranslation.languages - post_translations.map(&:language)
end
end
Then you can build a select menu by getting a post (#post = Post.find(params[:id]) and populating the collection from #post.untranslated.
It's perfectly valid to keep this in the model. Models should hold primary responsibility for ensuring that the data entered into them is correct.
For your particular case, you can use the :uniqueness validator with a scope passed to it. Basically your validation will ensure that languages are unique in the context of a particular post
The following should work:
validates :language, :inclusion => { :in => LANGUAGES },
:uniqueness => { :scope => :post_id }
If you prefer the Rails 2 style syntax, you can use:
validates_uniqueness_of :language, :scope => :post_id