In controller i have such code:
#bank_exchangers = ExchangerList.find(:all, :conditions => {:bank_id => params[:id]})
#currency_list = CurrencyList.all
#currencies = []
#currency_list.each do |c|
#currencies << CurrencyValue.find(:all, :conditions => {:currency_list_id => c.id}, :order => :updated_at)
end
#currencies.flatten!
and i have such models:
class CurrencyList < ActiveRecord::Base
attr_accessible :code, :country, :name
has_many :currency_values
end
class CurrencyValue < ActiveRecord::Base
attr_accessible :currency_list_id, :direction_of_exchange_id, :value
belongs_to :currency_list
belongs_to :exchanger_list
end
class ExchangerList < ActiveRecord::Base
attr_accessible :address, :bank_id, :exchanger_type_id, :latitude, :location_id, :longitude, :name
has_many :currency_values
end
i need to display for each ExchangerList it's CurrencyValue with some conditions, as i provided below... But main trouble is with rabl output:
i have such code:
collection #bank_exchangers, :root => "bank_exchangers", :object_root => false
attributes :id, :name, :address, :location_id, :latitude, :longitude, :exchanger_type_id
child #currencies do
attribute :value
end
as you can see, here for each #bank_exchangers i create node with it's #currencies... But i need to display node only for this #bank_exchangers iterator, if i would write in controller i would write something like:
#currencies << CurrencyValue.find(:all, :conditions => {:currency_list_id => c.id, :exchanger_list_id => param}, :order => :updated_at)
How to set something like this in view?
Becouse now my output is like:
{"bank_exchangers":[{"id":3,"name":"Банк *** ЦБУ №1","address":"г. Минск, ул. Московская, 13","location_id":8,"latitude":null,"longitude":null,"exchanger_type_id":1,"location_name":"Минск","exchanger_type_name":"normal","currency_values":[{"currency_value":{"value":8620.0}},{"currency_value":{"value":8620.0}},{"currency_value":{"value":8700.0}},{"currency_value":{"value":8700.0}},{"currency_value":{"value":8620.0}},{"currency_value":{"value":8700.0}},{"currency_value":{"value":11500.0}},{"currency_value":{"value":11100.0}}]},{"id":4,"name":"Банк ***","address":"г. Минск, Мясникова, 32","location_id":8,"latitude":null,"longitude":null,"exchanger_type_id":1,"location_name":"Минск","exchanger_type_name":"normal","currency_values":[{"currency_value":{"value":8620.0}},{"currency_value":{"value":8620.0}},{"currency_value":{"value":8700.0}},{"currency_value":{"value":8700.0}},{"currency_value":{"value":8620.0}},{"currency_value":{"value":8700.0}},{"currency_value":{"value":11500.0}},{"currency_value":{"value":11100.0}}]}]}
as you can see, for each bank_exchangers i create node with all currency_values data, but i need to put for each bank_exchangers in node only currency_values for this bank_exchangers parent....
How could i do this?
Sorry if something is not clear... i'm new...
Just how to set for my child #currencies in view some condition?
You can take advantage of the relation between ExchangerList and CurrencyValues:
child :currency_values do
attribute :value
end
If you have conditions, you can include these using a lambda:
if => lambda { |child| child.something? })
child(:currency_values, if => lambda { |currency_value| currency_value.something? }) do
attribute :value
end
You may also want to define a view for just the one ExchangerList object (i.e. show):
object #bank_exchanger, :object_root => false
attributes :id, :name, :address, :location_id, :latitude, :longitude, :exchanger_type_id
child(:currency_values, if => lambda { |currency_value| currency_value.something? }) do
attribute :value
end
Then simply have the collection extend this:
collection #bank_exchangers, :root => "bank_exchangers", :object_root => false
extends 'bank_exchangers/show'
Alternatively, if you add a 'currencies' method to to the ExchangerList model, you can call the method directly as an attribute via RABL. Some test code I wrote:
def test
test_array = []
1.upto(3) do |i|
test_array << {qty: i, px: 2*i}
end
return test_array
end
You can then simply call this as an attribute:
object #object
attribute :test
This results in the following JSON, which I believe is similar to the format you are trying to achieve:
test: [
{
qty: 1,
px: 2
},
{
qty: 2,
px: 4
},
{
qty: 3,
px: 6
}
],
Related
I need to merge to different object in my Ruby on Rails application. I have my Invoice object:
class Invoice < ActiveRecord::Base {
:id => :integer,
:user_id => :integer,
:description => :text,
......
:status => :string,
:price => :float
}
And my Payment object:
class Payment < ActiveRecord::Base {
:id => :integer,
:token => :string,
:invoice_id => :integer,
:created_at => :datetime,
:updated_at => :datetime,
:email => :string
}
With 1 to many relationship between them:
class Invoice < ActiveRecord::Base
has_many :payments
class Payment < ActiveRecord::Base
belongs_to :invoice
Now what I would like to do is to return the Invoice object and the :email and :created_at field of the payment object associated. Right now I return the two object with the zip function:
:invoices => (user.invoices.where(:hide => false).zip user.invoices.map{|x| x.payments.last}),
but that return an array of array:
[
[{invoice},{payment}],
[{invoice},{payment}],
...
]
What I want to return is something like:
[
{invoice, :email_payment, :created_at_payment},
{invoice_1, :email_payment, :created_at_payment},
...
]
How can I do that?
I would add email_payment and created_at_payment as methods to the invoice model, but you can achieve it with the following:
user.invoices.where(hide: false).map do |invoice|
invoice.attributes.merge({
email_payment: invoice.payments.last.email,
created_at_payment: invoice.payments.last.created_at
})
end
I've got a form view of an Order model (orders_form.html.erb) with a select option:
<%= f.select :pay_type, PaymentType.array_of_payment_types,
:prompt => 'Select a payment method' %>
PaymentType is another model and .array_of_payment_types is an array created out of the entries in the payment_type_name column, like so:
def self.array_of_payment_types
#array_of_payment_types ||= PaymentType.pluck(:pay_type_name)
end
... from models\payment_type.rb
But I get a proc 'empty?' error:
undefined method `empty?' for #
I hope my problem is clear, it seems like there is an obvious solution but I haven't found one reading other questions so far...
I will update with the relationships in the models...
My models:
payment_type.rb:
class PaymentType < ActiveRecord::Base
attr_accessible :pay_type_name
has_many :orders
validates :pay_type_name, :uniqueness
def self.names
all.collect { |pt| pt.pay_type_name }
end
def self.array_of_payment_types
PaymentType.all.map{ |p| [p.pay_type_name, p.id] }
end
end
order.rb:
class Order < ActiveRecord::Base
attr_accessible :address, :email, :name, :pay_type, :payment_type_id, :cart_id,
:product_id
has_many :line_items, :dependent => :destroy
belongs_to :payment_type
#PAYMENT_TYPES = ['Check','Purchase order','Credit card']
validates :name, :address, :email, :presence => true
validates :pay_type,
:presence => true,
:inclusion => { :in => proc { PaymentType.array_of_payment_types } }
def add_line_items_from_cart(cart)
cart.line_items.each do |item|
item.cart_id = nil
line_items << item
end
end
end
Try using the options_for_select:
# in the view:
<%= f.select :pay_type, options_for_select(PaymentType.array_of_payment_types),
:prompt => 'Select a payment method' %>
# in the PaymentType model:
def self.array_of_payment_types
PaymentType.all.map{ |p| [p.pay_type_name, p.id] }
end
You also need to update your validates statement in the Order model:
validates :pay_type,
:presence => true,
:inclusion => { :in => proc { PaymentType.pluck(:pay_type_name) } }
I have model with virtual attributes, for use in simple_form:
class Sms < ActiveRecord::Base
attr_accessor :delayed_send, :send_time_date, :send_time_time
I have form for /smses/new:
= simple_form_for([:admin, resource]) do |f|
...
.clear
.field.grid_3
= f.input :delayed_send, :as => :boolean, :label => "Отложенная отправка на:"
.clear
.field.grid_3
= f.input :send_time_date, :as => :string, :input_html => { :class => 'date_picker' }, :disabled => true, :label => "Дату:"
.clear
.field.grid_1
= f.input :send_time_time, :as => :string, :disabled => true, :label => "Время:", :input_html => { :value => (Time.now + 1.minute).strftime("%H:%M") }
.clear
.actions.grid_3
= f.submit "Отправить"
And i wanna validate all that virtual attributes inside my SmsesController, in create action, and if it invalid - show error. But that doesn't work:
class Admin::SmsesController < Admin::InheritedResources
def create
#sms.errors.add(:send_time, "Incorrect") if composed_send_time_invalid?
super
end
How should i add my custom errors, if i using inherited_resources?
If there's not a specific reason you're validating in the controller, the validation should be in the model:
class Sms < ActiveRecord::Base
#two ways you can validate:
#1.use a custom validation routine
validate :my_validation
def my_validation
errors.add(:send_time, "Incorrect") if composed_send_time_invalid?
end
#OR 2. validate the attribute with the condition tested in a proc.
validates :send_time, :message=>"Incorrect", :if=>Proc.new{|s| s.composed_send_time_invalid?}
end
In the controller, a save (or call to object.valid?) will trigger these validations to run. You can then handle the response in your controller to re-render the action if warranted.
How can I return errors messages from a cross reference table with multiple records when I trying to create those? I'm trying this:
## activity_set.rb
class ActivitySet < ActiveRecord::Base
has_many :activity_set_lessons
has_many :lessons, :through => :activity_set_lessons
validates :name, :presence => true
def activity_set_lessons=(data)
data.each_with_index do |v, i|
activity_set_lessons.build(
:lesson_id => v[:lesson_id],
:sort_order => i,
:weight_percentage => v[:weight_percentage]
)
end
end
end
## activity_set_lesson.rb
class ActivitySetLesson < ActiveRecord::Base
belongs_to :activity_set
belongs_to :lesson
validates :lesson_id, :presence => true
validates_each :weight_percentage do |record, attr, value|
record.errors.add :base, "woot" if value.blank?
end
end
This is the request data:
## params[:activity_set]
"activity_set" => {
"name" => "hshshshs",
"keywords" => "",
"activity_set_lessons" => [
{"weight_percentage" => "", "lesson_id"=>"4"},
{"weight_percentage" => "", "lesson_id"=>"5"}
]
}
Error messages from #activity_set when I do #save:
{
"errors":{
"activity_set_lessons":["is invalid","is invalid"]
},
"full_messages":[
"Activity set lessons is invalid","Activity set lessons is invalid"
]
}
I always got the same error message even if I'm adding a custom one in the join table. How can I return a message like: "woot 1 is wrong" or something like that, per validation?.
Thanks.
make use of accepts_nested_attributes_for
## activity_set.rb
class ActivitySet < ActiveRecord::Base
has_many :activity_set_lessons
has_many :lessons, :through => :activity_set_lessons
validates :name, :presence => true
accepts_nested_attributes_for :activity_set_lessons
end
view will look like
= form_for #activity_set do |f|
[activity_set form fields ]
= f.fields_for :activity_set_lessons do |p|
= p.select :lession_id
= p.select :weight_percentage
What am I doing wrong here? The forms work but keep getting "undefined method `to_i' for :street1:Symbol" when trying to seed data.
EDIT = If I do everything as a singular address (has_one instead of has_many) seed works.
EDIT 2 = See answer below for others...
address.rb
class Address < ActiveRecord::Base
attr_accessible :street1, :street2, :city, :state, :zipcode, :deleted_at, :addressable_type, :addressable_id, :current, :full_address, :address_type
belongs_to :addressable, :polymorphic => true
scope :vendor, where("address_type='Vendor'")
before_save :update_full_address
def update_full_address
unless self.street2.blank?
street = self.street1 + "<br />" + self.street2 + "<br />"
else
street = self.street1 + "<br />"
end
citystatezip = self.city + ", " + self.state + " " + self.zipcode
self.full_address = street + citystatezip
end
end
vendor.rb
class Vendor < ActiveRecord::Base
attr_accessible :name, :contact, :phone, :addresses_attributes
has_many :addresses, :as => :addressable
accepts_nested_attributes_for :addresses, :allow_destroy => true, :reject_if => proc { |obj| obj.blank? }
end
seed data
require 'faker'
Vendor.delete_all
["Company A", "Company B", "Company C", "Company D"].each do |c|
params = {:vendor =>
{
:name => c,
:contact => Faker::Name.name,
:phone => Faker::PhoneNumber.phone_number,
:addresses_attributes => {
:street1 => Faker::Address.street_address,
:city => Faker::Address.city,
:state => Faker::Address.us_state_abbr,
:zipcode => Faker::Address.zip_code,
:address_type => "Vendor"
}
}
}
Vendor.create!(params[:vendor])
end
Note the [] for an array when dealing with has_many.
require 'faker'
Vendor.delete_all
["Company A", "Company B", "Company C", "Company D"].each do |c|
params = {:vendor =>
{
:name => c,
:contact => Faker::Name.name,
:phone => Faker::PhoneNumber.phone_number,
:addresses_attributes => [{
:street1 => Faker::Address.street_address,
:city => Faker::Address.city,
:state => Faker::Address.us_state_abbr,
:zipcode => Faker::Address.zip_code,
:address_type => "Vendor"
}]
}
}
Vendor.create!(params[:vendor])
end
accepts_nested_attributes_for :foo is so that you can create forms which create associated records. When you're building things in code, there's no need to use this. You can create the associated records using the association names instead of "address_attributes". Here's one way of doing it, but Rails does expose a bunch of ways of doing this same thing...
["Company A", "Company B", "Company C", "Company D"].each do |c|
vendor_address = Address.new :street1 => Faker::Address.street_address,
:city => Faker::Address.city,
:state => Faker::Address.us_state_abbr,
:zipcode => Faker::Address.zip_code,
:address_type => "Vendor"
Vendor.create! :name => c,
:contact => Faker::Name.name,
:phone => Faker::PhoneNumber.phone_number,
:addresses => [vendor_address]
end
If you are wanting to try and use the nested attributes way, then you don't need the :vendor => {} part of the hash, you can go straight into the params, and you need addresses_attributes to be an array, not a hash.