How do I use except on a nested hash in a reject - ruby-on-rails

I'm trying to reject an empty form entry, but I'm having difficulties.
Users can either choose for an existing location, or create a new one. I want the form to actually show empty fields, but reject them when they are all empty. As '_destroy' will never be empty, I need to make an exception. Also if only the quantity is filled out, the entry can be rejected.
The form submits the following information:
Parameters:
{"product"=>
{...,
"product_locations_attributes"=>
{
"0"=>{"location_attributes"=>{"_destroy"=>"false", "street"=>"", "number"=>"", "zipcode"=>"", "city"=>"", "country"=>""}, "quantity"=>""},
"1"=>{"_destroy"=>"false", "location_id"=>"", "quantity"=>""}}
}
, "commit"=>"Create Product"
}
AI'm trying to get the empty location removed in the Product model as follows:
accepts_nested_attributes_for :product_locations, :allow_destroy => true,
:reject_if => proc {|a| a.except('_destroy', 'quantity').values.all?( &:blank? )}
As it is nested, it doesn't work like this.
So how can I check if everything is blank except the quantity and _destroy?
It should be possible to do it in one go right?
Thanks for any help.
* updated to make it more clear *

I would explicitly check all the fields that may or may not be blank, rather than try and do some kind of "are they all blank". Much more explicit and readable.
:reject_if => proc {|a|
location_attributes = a[:location_attributes]
a[:street].blank? && a[:number].blank? && a[:location_id].blank?
}
It will be verbose but better in the long run!

Thanks to the anser of #RobHeaton I finally was able to make this work. Myabe his answer could work, but it did not work for me. If it should and I did something wrong, let me know, and I will accept his answer. I finally got it to work with the following code:
accepts_nested_attributes_for :product_locations, :allow_destroy => true, :reject_if => :empty_fields
def empty_fields(a)
if la = a[:location_attributes]
la[:street].blank? && la[:number].blank? && la[:city].blank? && la[:zipcode].blank? && la[:country].blank?
else
a[:location_id].blank?
end
end
It is now very clear on what needs to be blank in order for it to reject. With other things I tried I ended up with either too many things accepted or rejected.
Just writing this down in case other people run into the same problem.

Related

Issue including a second and a third level association

This query is not working, pease help. I'm trying to include a second and a third deep-level of association.
Pedido > has_one(products_pedido) > has_one(product_size)
#pedidos = Pedido.includes(:pedidos_payments, :products_pedidos => { :product_size } , :estado, :brand, :customer ).where(:is_quote => false)
Ps: I know products_pedido is mispelled according to ActiveRecord good practices :).
Without a stacktrace here's what I suggest:
Assuming your has_one method name is products_pedidos, your issue looks like a problem with your hash syntax.
Your syntax creates a hash with key products_pedidos that returns a hash without a value. This is probably where the error is occurring.
#pedidos = Pedido.includes(:products_pedidos => { :product_size })
What you likely want is this which returns a hash with key products_pedidos with value product_size
#pedidos = Pedido.includes({products_pedidos: :product_size })
The Entire query might look like:
#pedidos = Pedido.includes(
:pedidos_payments,
{products_pedidos :product_size},
:estado,
:brand,
:customer
).where(is_quote: false)
Here's a great post explaining a bit more about ActiveRecord nested relationship loading: Rails - Nested includes on Active Records?. I'd also suggest fixing the naming on products_pedido to follow good naming practices.

Controller Chokes on Nested Model's Attributes

I'm using Rails 4 + Ruby 2
Nested models in Rails are a huge pain. There, I said it.
Okay, so I have an entry model with approaches nested inside.
# --- entry.rb ---
has_many :approaches, :dependent => :destroy
accepts_nested_attributes_for :approaches, :reject_if => lambda { |a| a[:approach_type].blank? }, :allow_destroy => true
The approach parameters come over to the controller's create method like this (from the log):
{"utf8"=>"✓",
"entry"=>{
"aircraft_registration"=>"N384HA",
"flight_date"=>"2013-12-10",
"departure"=>"KSAD",
...
"approaches"=>{
"1386633324306"=>{
"approach_type"=>"GLS",
"holding"=>"false",
"quantity"=>"2",
"airport"=>"FFS",
"runway"=>"12L",
"updated_flag"=>"true"
},
"1386633813852"=>{
"approach_type"=>"TACAN",
"holding"=>"false",
"quantity"=>"1",
"airport"=>"DFD",
"runway"=>"12L",
"updated_flag"=>"true"
}
},
}
For testing purposes, I do blanket param allowance:
params.require(:entry).permit!
...and I get this error from Satan himself:
ActiveRecord::AssociationTypeMismatch - Approach(#70114475640640) expected,
got Array(#70114477494560)
It seems that the problem is that the Entry model doesn't like the approaches being an array (which actually looks like a hash with id numbers, but what do I know).
My Question
How should the params look on a nested model when they come over from the view to the controller?
I'm trying to narrow down whether I have mis-formatted data coming from my form, or a problem in my controller.
I'm new to Rails, so please be gentle. :)
To answer your question, a nested model should come through something like this:
params = {
:entry => {
:approaches_attributes => [
{:approach_one_attr => ...},
{:approach_two_attr => ...}
]
}
}
Looks to me like the problem here is with the form in your view not the controller.

skip validation_uniqueness for certain values

I have validation on uniqueness and I want skipping certain value or values(for example 0000):
validates_uniqueness_of :gtin, :scope => [:user_id, :item_id]
I'm tried to use next construction, but she don't work:
validates_uniqueness_of :gtin, :scope => [:user_id, :item_id], :unless => Proc.new{|base_item| base_item.gtin == '0000'}
How I can skip certain value or values?
Thanks.
P.S. update!!!
did not see a manual migration, which change behaviour
using the :unless option is certainly the right way, but i think you get the whole object as proc argument so it should be
validates_uniqueness_of :gtin, :scope => [:user_id, :item_id], :unless => Proc.new{|obj| obj.gtin == '0000'}
Not sure if this is a gotcha or not. Is the value of gtin a string or an integer? It looks like what your doing should work, but if it's an integer you would want to change to:
validates :gtin, :uniqueness => {:scope => [:user_id, :item_id]}, :unless => Proc.new{|base_item| base_item.gtin == 0000}
I'm trying to do the same thing, and I think I know what's wrong. The problem is, the if or unless "base_item" object refers to the value you're checking the uniqueness for, not the prospective match object.
Maybe you really do mean to check the item you're validating (in which case I'm barking up the wrong tree), but it seems more natural in the uniqueness case to want to exclude certain matches. For instance, I have a field is_deleted, and I want to allow a uniqueness violation if the matching object has been deleted.
I can't find any way to reference the matching object that was found in the proc. You can accomplish this by making your own a custom validation function though. For instance, if you want to validate the uniqueness of 'name', you might try something like this:
validate :full_validation
def full_validation
matches = self.class.find_all_by_name(self.name).select {|match| match.id != self.id && match.is_deleted==false}
return (matches.size>0)
end

searching a model in rails for 2 values?

I wrote this retrieval statement to check if an appointment being saved or created dosent conflict with one thats already saved. but its not working, can someone please point me to where I'm going wrong?
#new_appointment = :appointment #which is the params of appointment being sent back from submit.
#appointments = Appointment.all(:conditions => { :date_of_appointment => #new_appointment.date_of_appointment, :trainer_id => #new_appointment.trainer_id}
)
the error is from the :date_of_appointment => #new_appointment.date_of_appointment this will always be false as:
thank you
At face value, there doesn't appear to be anything wrong with your syntax. My guess is that #new_appointment isn't containing the values you're expecting, and thus the database query is returning different values than you expect.
Try dumping out #new_appointment.inspect or check the logfiles to see what SQL the finder is producing, or use
Appointment.send(:construct_finder_sql, :conditions => {
:date_of_appointment => #new_appointment.date_of_appointment,
:trainer_id => #new_appointment.trainer_id
})
to see the SQL that will be generated (construct_finder_sql is a protected ActiveRecord::Base method).
Update based on your edit
#new_appointment = :appointment should be something like #new_appointment = Appointment.new(params[:appointment]). :appointment is just a symbol, it is not automatically related to your params unless you tell it to.

Getting rails to accept European date format (dd/mm/yyyy)

I want my rails app to accept dates for a date field in the format dd/mm/yyyy.
In my model I have tried to convert the date to the American standard which I think the Date.parse method that Rails will call on it is expecting:
before_validation :check_due_at_format
def check_due_at_format
self.due_at = Date.strptime(self.due_at,"%d/%m/%Y").to_time
end
However, this returns:
TypeError in OrdersController#update
can't dup NilClass
If it is useful to know, the Items form fields are a nested for within Orders, and Orders are set to:
accepts_nested_attributes_for :items, :reject_if => lambda { |a| a[:quantity].blank? && a[:due_at].blank? }, :allow_destroy => :true
So the items are being validated and saved/updated on #order.save/#order.update_attributes
Thank you!
It may be just a case of the due_at value being nil. In your case it's an empty string, but ignored because of the :reject_if option on accepts_nested_attributes_for, and so it remains as nil.
>> Date.strptime(nil, "%d/%m/%Y")
TypeError: can't dup NilClass
Take care of it with some conditional then.
self.due_at = Date.strptime(self.due_at,"%d/%m/%Y").to_time unless self.due_at.nil?
I have struck exactly the same problem in Ruby 1.8.7 and the only way that I could solve it was to do the self assignment in two steps!!
xxx = Date.strptime(self.due_at,"%d/%m/%Y").to_time
self.due_at = xxx
I can' believe that this should be necessary. The only thing I can think of is that ruby is assigning fields in the new target date class on a piecemeal basis before it has finished using the due_at source string on the right hand side.
The problem does not seem to exist in Ruby 1.9.3

Resources