How to monkey patch enums in rails 4.1? - ruby-on-rails-4

Say I have a class that has an enum
class Course < ActiveRecord::Base
enum status: { active: 1, in_progress: 2, disabled: 3, ... }
end
and I want to patch the enum functionality so I could do
Course.statuses.capitalize
which would return a list of all statuses - capitalized and without underscores.
I have tried a couple of things but nothing seems to be helping. Any ideas?
Thanks
Below is what I tried. My experience with monkey patching is limited so not really sure if I am on the correct path or not.
module ActiveRecord
module Enum
def capitalize
self.map{ |s| [s[0].capitalize, s[1]] }
end
end
end

I don't see the point of monkey patching this, because what you want already exists in Rails.
class Course < ActiveRecord::Base
enum status: { active: 1, in_progress: 2, disabled: 3 }
end
Course.statuses.transform_keys{|k| k.to_s.humanize}
# => {"Active": 1, "In progress": 2, "Disabled": 3}

Related

Rails - Nested association - scope order - with I18n translation

I am using globalize and i18n.
I have the following models:
class Result < ActiveRecord::Base
belongs_to :test
...
scope :sort_with_teaser, lambda { |direction|
joins(test: :test_translations).
where(test_translations: {locale: 'fr'}).
order("test_translations.teaser #{ direction }")
}
end
class Test < ActiveRecord::Base
translates :teaser
...
end
So with Globalize and I18n, I have also a test_translations table.
I want to implement a scope sort_with_teaser inside my Result model to sort result with their result.test.teaser. But I this code doesn't work.
Any help welcome.
Ok I just fixed it :
joins(test: :translations).where(test_translations: {locale: 'fr'}).order("test_translations.teaser #{ direction }")

How to test parameterized scope method using rspec(v3.4.1) in rails(v4.2.5)?

Model:
scope :recent, ->(n) {where(:created_at => 6.months.ago..Time.now).order('created_at DESC').limit(n)}
I would do something like this in Rspec:
before(:each) do
#song = double(Song,id: 1, name: "Vegan artisan.", album_id: 1, artist_id: 20, created_at: "2016-03-04 13:15:36", updated_at: "2016-03-04 13:15:36")
end
it "should return the recent most songs within range" do
Song.stub_chain(:where,:order,:limit).with(created_at:6.months.ago..Time.now).with('created_at DESC').with(2).and_return([#song,#song])
Song.recent.should == [#song,#song]
end
The above case is failing , giving the below mentioned error:
ArgumentError:
wrong number of arguments (0 for 1)
I tried different approaches, but still hard luck. Any help on this would be appreciated. Thanks in advance!!
recent scope requires you to pass a parameter that limits the search result, thus you need to change this line appropriately:
Song.recent(2).should eq([#song,#song])
or
expect(Song.recent(2)).to match_array([#song,#song])

ActiveModel::ForbiddenAttributesError when executing a build inside an update action

I am getting a pesky ActiveModel::ForbiddenAttributesError, and I can't find how to fix it.
I was following this guide:
https://rbudiharso.wordpress.com/2010/07/07/dynamically-add-and-remove-input-field-in-rails-without-javascript/
and everything works great except when I am updating the record.
My controller:
if params[:add_item]
# rebuild the baixa_itens attributes that doesn't have an id
unless params[:baixa][:baixa_itens_attributes].blank?
for attribute in params[:baixa][:baixa_itens_attributes]
#baixa.baixa_itens.build(attribute.last.except(:_destroy)) unless attribute.last.has_key?(:id)
end
(...)
# Never trust parameters from the scary internet, only allow the white list through.
def baixa_params
params.require(:baixa).permit(:prontuarios_id, :data, :profissional_id, :comentario, baixa_itens_attributes: [ :id, :item_id, :quantidade, :preco, :_destroy ])
end
end
When I got my crash, I tested it in the console:
>> attribute
=> ["1", {"item_id"=>"3", "quantidade"=>"1", "preco"=>"1", "_destroy"=>"0"}]
>> attribute.last.except(:_destroy)
=> {"item_id"=>"3", "quantidade"=>"1", "preco"=>"1"}
>> #baixa.baixa_itens.build(attribute.last.except(:_destroy))
!! #<ActiveModel::ForbiddenAttributesError: ActiveModel::ForbiddenAttributesError>
But if I try to make this build MANUALLY, it works:
>> #baixa.baixa_itens.build({"item_id"=>"3", "quantidade"=>"1", "preco"=>"1"})
=> #<BaixaItem id: nil, baixa_id: 6, item_id: 3, quantidade: 1, preco: #<BigDecimal:7ff97147b6f8,'0.1E1',9(18)>, created_at: nil, updated_at: nil>
>>
What am I missing?
I workaround this problem doing
for attribute in params[:baixa][:baixa_itens_attributes].permit!
in my controler (not using ! would give me error: "Unpermitted parameters: 0, 1", which i cant understand)
I still wish a better solution.

Customize JSON output with HAS_MANY related output

I have 2 models Prize and Staff, One staff HAS_MANY prizes
I want to export noy only the prize, but also embedded the prize's owner (staff) in the JSON response.
How to do it ?
render json: Oj.dump( Prize.where(:staff_id => nil) )
Sample output (But not including the staff information)
{
can_accept_prize_now: true,
name: "Apple MacBook Pro Retina",
sn: 3,
}
Expected output
{
can_accept_prize_now: true,
name: "Apple MacBook Pro Retina",
sn: 3,
staff_id: 80,
staff_name: "Eric"
}
You could probably could add something like the following to the Prize model:
def serializable_hash(options={})
hash = super
hash['staff_id'] = staff.id
hash['staff_name'] = staff.name
hash
end
If you're using ActiveModel::Serializers, you could probably do something like the following
class PrizeSerializer < ActiveModel::Serializer
attributes :can_accept_prize_now, :name, :sn, :staff_id, :staff_name
def staff_id
object.staff.id
end
def staff_name
object.staff.name
end
end

Count an Attribute on a nested form?

Rails 3.2.12 and Ruby 1.9.3 and Haml
I would like to use the count of attribute to control the display of a 'link_to "remove"', but I am having problems with setting up the logic.
Following is some code from my form as it is currently:
.field
= codeline.label :name, "Units Alloc"
%br/
= codeline.text_field :units_alloc, :precision => 6, :scale => 2, :size => 10,
:class => "ui-state-default"
= codeline.hidden_field :_destroy
= link_to "remove", '#', class: "remove_fields"
this works well but I have the 'remove' link showing up and I would prefer it to only show if there are two :units_alloc attributes.
This is what I tried:
.field
= codeline.label :name, "Units Alloc"
%br/
= codeline.text_field :units_alloc, :precision => 6, :scale => 2, :size => 10,
:class => "ui-state-default"
- if :units_alloc.count > 1
= codeline.hidden_field :_destroy
= link_to "remove", '#', class: "remove_fields"
and here is my error:
NoMethodError in Contracts#new
Showing /home/tom/rails_projects/tracking/app/views/contracts
/_codeline_fields.html.haml where line #9 raised:
undefined method `count' for :units_alloc:Symbol
if I use units_alloc in the argument instead of the symbol, I still get an error:
NameError in Contracts#new
Showing /home/tom/rails_projects/tracking/app/views/contracts
/_codeline_fields.html.haml where line #9 raised:
undefined local variable or method `units_alloc' for
#<#<Class:0xadbde90>:0xa8956e8>
I tried to use 'codeline.units_alloc' but this did not work and the same error was flagged.
Any suggestions, or pointers to help me resolve this issue?
Thanks.
Solution: Thanks to James Scott Jr.
app/controller/contracts_controller.rb
def New
#show_remove = false
....
....
end
app/views/contracts/_codelines_fields.html.haml
.field
= codeline.label :name, "Units Alloc"
%br/
= codeline.text_field :units_alloc, :precision => 6, :scale => 2, :size => 10,
:class => "ui-state-default"
- if #show_remove
= codeline.hidden_field :_destroy
= link_to "remove", '#', class: "remove_fields"
- else
- #show_remove = true
And that did it ... the remove button only shows in the second and subsequent row of attributes.
By the time you're in the form (partial), codeline doesn't refer to an instance the instance of Codeline that the form (partial) is for, but an instance of an ActionView::Helpers::FormBuilder that simple knows how to associate information the the instance of Codeline. You knew that because in the first line of the partial, you have codeline.object.build_code.
So, if you want to access the information about the units_alloc associated, you would access them with codeline.object.units_alloc. That will give you your data for your conditional.
I would just like to add that if the purpose of your anchor tag is to remove elements from a form list using some javacscript, you might be using the wrong control for it. Anchor tags are not form elements, they should point to resources/content and are not there to be used as animation/client side behaviour triggers. According to the use case you describe, an input tag type=button would be a much more appropriated element for what you seem to be trying to achieve.