Create complex objects - ruby-on-rails

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

Related

before validation do something, no method error

I have an model for notifications.
An notification can be posted by an User or an Contact.
and the notification can go to either a business or a notification_area.
people has to be filled, so when there is no user added it has to fill in the Contact that is logged in.
the notification_to has to be filled to so when there is no business added it has to take the latitude and the longitude and add the right area.
I have written the code but it won't work.
First I added it in the controller. But after looking around on google and this site I found I had to add it to the model.
But it still won't work.
What do I do wrong?
I get an error
class Notification < ActiveRecord::Base
belongs_to :people, :polymorphic => true
belongs_to :notification_to, :polymorphic => true
belongs_to :App_of_area
belongs_to :kind
before_validation :if_empty_add_the_other
#validates :photo, presence: true
validates :message, :status, :people_id, :people_type, :notification_to_id, :notification_to_type, :App_of_area_id, :kinds_id, presence: true
def if_empty_add_the_other
unless self.people_type.present?
self.people = current_contact
end
unless self.notification_to_id.present?
if self.longitude && self.latitude
#noa = NotificationArea.where( "latitude_A <= #{self.latitude} AND latitude_B >= #{self.latitude} AND longitude_A <= #{self.longitude} AND longitude_B >= #{self.longitude}")
self.notification_to = #noa.first
end
end
end
end
end
First thing first you should DRY up your code:
belongs_to :business
belongs_to :app_of_area
has_many :people, source_type: "Notification"
has_many :api_keys
before_validation :if_empty_add_the_other
has_secure_password
validates :name, :email, :rights, :password_confirmation, :app_of_area_id, :business_id, presence: true
validates_format_of :email, :with => /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
validates :email, uniqueness: true
validates :password, confirmation: true, length: { minimum: 6 }
:as is used for polymorphic associations, I believe you want to use :source_type as shown above. This would allow you to perform self.people << current_contact, but I address this more below.
Why was :App_of_area capitalized? Same with :App_of_area_id?
Your if_empty_add_the_other validation method has a lot wrong with it.
Use unless rather than if not.
Can the two if statements testing latitude and longitude be combined to if self.longitude && self.latitude?
You have to ask yourself, how is current_contact being passed to this function? Also, you're trying to set self.people equal to this phantom current_contact; self.people would contain multiple records, an array if you will, so setting an array equal to an object won't work, hence the self.people << current_contact above.

How to properly display value in edit form while using bootstrap3_autocomplete_input and association

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

Search object by generated string by has_one associations

I'm trying to do a search on the generated model string, which is formed by has_one associations.
Here my models:
Street.rb
class Street < ActiveRecord::Base
has_many :houses
validates :street_type, presence: true
validates :name, presence: true, uniqueness: {:scope => :street_type, :case_sensitive => false }
def text
street_type + " " + name
end
end
House.rb
class House < ActiveRecord::Base
belongs_to :street
has_many :apartments
validates :street_id, presence: true
validates :number, presence: true
def text
street.text + ", " + number
end
end
Apartment.rb
class Apartment < ActiveRecord::Base
belongs_to :house
has_one :street, through => :house
has_many :accounts
validates :house_id, presence: true
validates :number, presence: true, uniqueness: {:scope => :house_id, :case_sensitive => false}
def text
house.text + ", ap. " + number
end
end
Account.rb
class Account < ActiveRecord::Base
belongs_to :appartment
has_one :house, :through => :appartment
belongs_to :resident
validates :appartment_id, presence: true
validates :resident_id, presence: true, uniqueness: {:scope => :appartment_id, :case_sensitive => false}
def text
uniq_id + " (" + appartment.text + ")"
end
end
I must search all accounts by matching some adress string. And I have no idea how to do it.
Example 1st account text "123-1123-122 (Regent street, 51, ap. 3)"
Example 2nd account text "123-1123-155 (Regent street, 51, ap. 6)"
Example search text "Regent street, 51"
Can somebody help me understand how to do that?
Search against joined fields
You are searching against Account#text, which is nowhere to be found in DB, unless you store it there (see "Search against full Apartment#text"), which (judging by the code) seems not to be the case. You can't use return values of your model methods in ActiveRecord queries. So first you need to parse street type and name, house number and apartment number from your search string.
Assuming you know street type and name, house number and apartment number, you should (minus bugs, I did not actually test that) be able to find the accounts by
Account.
joins(:apartment => { :house => :street }).
where('apartment.number' => apt_num,
'apartment.house.number' => house_num,
'apartment.house.street.name' => street_name,
'apartment.house.street.street_type' => street_type)
Your address information is higly structured. It may or may not be good. Without knowing any details I would suggest you to check if less structured or unstructured address information (plain text field) combined with full text search would work for you.
More on AR queries, see http://guides.rubyonrails.org/active_record_querying.html
Search against full Apartment#text
In case you don't want to parse names and numbers from the generated apartment text field, you need to have it stored in db. You can do it by hooking into before_save and updating a text field (let it be called cached_text) to contain Apartment#text.
class Apartment < ActiveRecord::Base
before_save :update_cached_text
def update_cached_text
self.cached_text = text
end
end
Now of course Apartment#cached_text will be out of sync if you change house number or street name or type. To keep it in sync you need to "resave" (see #touch) all associated apartments each time you save house or street.
Now you can search using full Apartment#text
Account.
joins(:apartment).
where('apartment.cached_text' => text)
or a substring of it
Account.
joins(:apartment).
where("apartment.cached_text LIKE '%?%'", text)
Try with following
Account.includes([:appartment => [:house => :street]]).where("CONCAT('accounts.uniq_id', ' (', 'streets.street_type', ' ', 'streets.name', ' ', 'houses.number', ', ap. ', 'houses.number', ')') like '%?%'", your_search_text)
your_result = Account.includes(:apartment, :apartment => :house, :apartment => {:house => :street}).where(:apartment => {:number => ...}).where(:house => {:number => ...}).where(:street => {:name => ...})

Rails - where to store collections

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

Rails model validation: How to make sure a attribute is in a list

Hi I have a job model which has an string attribute called category. In the front end, I have a form with a dropbox where a user can fill out the category attribute with the selected value from the list. This is good enough front end validation for me, but now how will I do backend validation for the model?
I have dont other validations in the past for example:
validates :name, :presence => true
But is there anyway I can do something like
validates :category, :in => {"Food", "Drink", "Rental"}
You can do it like this:
validates :category, :inclusion => { :in => %w(Food Drink Rental) }
Or shorter:
validates :category, :inclusion => %w(Food Drink Rental)
Everything is in the documentation.

Resources