How to Exclude Null Values from update_attributes in Rails - ruby-on-rails

I am trying to handle a Stripe Webhook for Invoice.created and want to save the invoice line item. My challenge is that the files variables change based on the line item type.
I received a undefined method 'name' for nil:NilClass when I tried to import the line items since, depending on the type of line item, the plan object could be null.
I was able to solve the problem by separating the update_attributes into 2, one would only happen if the plan object exists. The following is what I was to get to work. My hope is that there is a better way.
#invoice_line_item = InvoiceLineItem.where(stripe_line_item_id: line_item.id).first_or_create(invoice_id: #invoice.id)
#invoice_line_item.update_attributes(
amount: line_item.amount,
currency: line_item.currency,
period_start: Time.at(line_item.period.start).strftime("%m-%d-%Y"),
period_end: Time.at(line_item.period.end).strftime("%m-%d-%Y"),
proration: line_item.proration,
item_type: line_item.type)
if line_item.plan.present?
#invoice_line_item.update_attributes(
plan_name: line_item.plan.name,
plan_interval: line_item.plan.interval,
plan_amount: line_item.plan.amount,
trial_period_days: line_item.plan.trial_period_days)
end

You could try
line_item.plan.try(:name)
and similarly for all the line_item.plan elements
Try (in rails) will give you nil if the subject is nil http://apidock.com/rails/Object/try
It's not really excluding the nil values, but if line_item.plan is nil, then the sub-values will also be nil. If that's the right behavior, then you should try try.
Update: I just hit on this coderwall post this morning (https://coderwall.com/p/wamyow) which mentions the use of delegate with allow_nil: true. Could you do something like
class InvoiceLineItem < ActiveRecord::Base
delegate :name, :interval, :amount, :trial_period_days, to: :plan, allow_nil: true
... rest of the class ...
end
Then looking more closely, I wonder why you're updating all the plan attributes on the line_item if they are available via the relationship? Am I missing something?

Related

Rails is persisting on validates

I have an ActiveRecord model that does find and replace filtering on an attribute when its accessor is called, and I'm doing it like the answer to my question here. It looks like this:
class Post < ActiveRecord::Base
include Replaceable
profanity_attrs :body, :title
end
If you haven't looked at the link, then there's a macro called profanity_attrs that calls a profanity_filter method that does find/replace on known keywords.
So when I have something like body = "Oh my poop" and I do a replace with **** so that the filter returns "Oh my ****" then that's what it looks like, and I'm very happy. The DB shows "Oh my poop" and the view shows "Oh my ****", which is exactly what I want, until... I add validation. So if I do this:
class Post < ActiveRecord::Base
include Replaceable
validates :body, length: { maximum: 255 }
profanity_attrs :body, :title
end
Then it still does the replace properly, but the replaced value will be persisted (ie. the db contains "Oh my ****"), and that doesn't make a lot of sense to me. It appears as if the validator is mutating the field that it validates.
Is there a way to preserve the validation, but not have it persist the value returned from the accessor?
Verify that you are not setting the attribute value in your profanity_filter implementation, that you are just returning a filtered copy of it (just validating an attribute shouldn't change it's value unless you're accidently also setting its value in your profanity_filter method).
In addition I would also change a little bit what you did in the macro you defined, instead of
define_method(attr)
I would define the method as
define_method("#{attr}_clean")
and in the views access the filtered version of the attribute by calling the "clean" version, i.e.
<%= post.body_clean %>

Rails object.valid? with arguments

Is it possible to pass :symbols to the valid? method so that I can define if the object is valid up to a certain point?
Eg. if I have an object Person and want to call Person.valid?(:basic_info) and it will return true only if a certain subset of fields (say name & gender) are present?
I saw something that I thought might be of use but cannot get it working, it's conditional validations http://guides.rubyonrails.org/active_record_validations_callbacks.html#conditional-validation , in particular grouping conditional validations, but I couldn't get it working...
Can anyone help me out here please...
I don't think there already present like this however you can write a method on your own like following
def is_valid_field?(field)
self.valid?
self.errors[field].blank?
end
and then just person.is_valid_field?(:basic_info)
To validate basic_info you'll have to define a custom validator:
class Person < ActiveRecord::Base
validate :basic_info_present
def basic_info_present
if name.blank? || gender.blank?
errors.add(:basic_info, "can't be in blank")
end
end
end
If you then want to see if there are errors on the specific field, you can use #Salil's approach.
Note however that since there is no actual attribute called basic_info in your model, the validation errors here will not come up in forms, etc. (although they will be in the errors hash). That may or may not be what you want.
I got this to work using conditional validations, so now i can use .valid?(:basic) say for when i only want to check that the person has a name with the call...
validates_presence_of :name, :when => [:basic]
Documentation here: http://apidock.com/rails/ActiveRecord/Validations/valid%3F
This way I can have the object return true when calling .valid? even when it doesn't have a name, good times...

Custom Method in Rails Validation

tl;dr the validation works in the browser, but fails with rspec. I'm not sure why.
answer: I was attempting to mass assign user_id, but user_id wasn't mass-assignable. Was building it correctly in the controller, incorrectly in rspec.
This is my Listing model. Every listing has a price and a user_id associated with it.
In my "listing" model, I have "price".
In my "user" model I have a boolean attribute called "vip".
The max value for "price" depends on the User's VIP-status. If VIP, max price is 400. Else, max price is 200.
I wrote a method "listing_ceiling" that should return the correct max value depending on the user's VIP-ness.
It works in the browser, but when I run through my tests, rspec comes back with the error:
undefined method `vip?' for nil:NilClass
I'm not exactly sure where I'm falling short here. It works correctly in the browser, but fails when testing with rspec. Here's the code:
validates_numericality_of :price, :presence => true, :greater_than => 10, :less_than => :listing_ceiling
def listing_ceiling
if self.user.vip?
400.01
else
200.01
end
end
Any ideas why it would fail this way?
The Listing#user returns nil. There could be two problems.
Your listing does not have a user. i.e. foreign key user_id is nil for the Listing in question. OR
Your listing has a value in user_id column. But there is no User with that id.
Solution is to assign a user to your listing.
You should check your RSpec, the problem doesn't lie in your cutom validation method but in your test setup. As the error message states user == nil!

Rails 3: Filtering and scopes "cant convert hash to integer" problem

I have a "course" model, which has_many timeslots.
In the courses model, I have the following methods:
def available_timeslots
tsarray = []
self.timeslots.map{ |t|
if t.available then
tsarray << t
end
}
tsarray
end
def earliest_slot
self.available_timeslots.first(:order => :starting_date)
end
What I'm trying to do now is get the earliest available timeslot for each course. Without the availability filter, #course.earliest_slot works fine. But if I try #course.available_timeslots.earliest_slot brings back a "can't convert hash into integer" message.
Any suggestions appreciated
Zabba's comment is right on the money. The available_timeslots method is returning an Array and you are calling the method first on that -- but that is not the same as calling the method first on the object that a Rails association returns.
I think you want to use scopes. So delete your available_timeslots method and add this to your Timeslot class:
scope :available, where(:available => true)
then in your earliest_slot method you can do
self.timeslots.available.first(:order => :starting_date)
and it will return the first available timeslot for the course referred to by 'self'.
(Note: I am making the assumption that 'available' is a boolean. If it is something else, change the where condition in the scope appropriately.)

Rails validating virtual attributes

I this model:
class Bunny < ActiveRecord::Base
attr_accessor :number
validates_presence_of :number
validates_numericality_of :number
end
Whenever I submit a form to create this model I get the following error:
undefined method `number_before_type_cast' for #<Bunny:0x103624338>
I fixed the problem by adding this method to my Bunny model:
def number_before_type_cast
number
end
I don't like it, but I suppose it will work until someone posts a better solution.
Rails generates the FIELDNAME_before_type_cast in the model for each field. It stores the value from the form as a String before it's converted (cast) in this case to a number (it might be a date for example). This cast occurs before save, but after validation.
So when validation occurs before that cast is performed it has to use the "before type cast" value to get the value. Since this is not generated for your attribute, it fails.

Resources