I am working on a Rails project, using Mongo. I am faced with an issue in my view form helper, in that (whether this access is through Rails or Mongoid, I am not sure) I am attempting to access a value within a Hash field on my model, but I am getting an undefined method error, because it is attempting to access the hash using dot notation, instead of bracket notation.
The model:
class AgencySetting
include Mongoid::Document
include Mongoid::Timestamps
field :uuid, type: String
field :mailing_address, type: Hash
field :service_address, type: Hash
field :active, type: Boolean, default: true
embeds_many :contracts
validates :uuid, presence: true, uniqueness: true
end
The form (this is probably not the ideal way to handle a Hash attribute field, there is another f.fields_for
attached to service_address
that I have excluded for brevity):
= form_with model: @agency_setting, url: agency_setting_url, method: :post do |f|
= f.fields_for :mailing_address, @agency_setting.mailing_address do |m_fields|
= m_fields.text_field :line_1, class: 'form-control my-1', placeholder: 'Contract street line one'
= m_fields.text_field :line_2, class: 'form-control my-1', placeholder: 'Contract street line two'
= m_fields.text_field :city, class: 'form-control my-1', placeholder: 'Contract city'
= m_fields.text_field :state, class: 'form-control my-1', placeholder: 'Contract state'
= m_fields.text_field :zip, class: 'form-control my-1', placeholder: 'Contract zip'
= f.submit 'Save settings', class: 'btn btn-primary mt-2'
It throws the error on the first line of m_fields
, specifically it gives, undefined method 'line_1' for {"line_1"=>"123", "line_2"=>"123", "city"=>"123", "state"=>"123", "zip"=>"123"}:BSON::Document
. Clearly, the reason for this is that it is attempting to access mailing_address
with mailing_address.line_1
instead of mailing_address[:line_1]
which runs fine in the console. Is it possible to force the helper to use the latter syntax instead?
Is it possible to force the helper to use the latter syntax instead?
No. But you can provide the value manually:
= form_with model: @agency_setting, url: agency_setting_url, method: :post do |f|
= f.fields_for :mailing_address, @agency_setting.mailing_address do |m_fields|
= m_fields.text_field :line_1,
class: 'form-control my-1',
placeholder: 'Contract street line one',
value: m_fields.object[:line_1]
Or you can convert the Hash into a OpenStruct:
= form_with model: @agency_setting, url: agency_setting_url, method: :post do |f|
= f.fields_for :mailing_address, OpenStruct.new(@agency_setting.mailing_address) do |m_fields|
= m_fields.text_field :line_1,
class: 'form-control my-1',
placeholder: 'Contract street line one'
# ...
But this really just begs the question why you're shooting yourself in the foot instead of just modeling your data better.
class Address
include Mongoid::Document
include Mongoid::Attributes::Dynamic
include Mongoid::Timestamps
field :line_1, type: String
field :line_2, type: String
# ...
end
class AgencySetting
include Mongoid::Document
include Mongoid::Timestamps
field :uuid, type: String
field :active, type: Boolean, default: true
embeds_many :contracts
embeds_one :mailing_address, class_name: 'Address'
embeds_one :service_address, class_name: 'Address'
end
Having an actual model will make your life so much easier when it comes to adding validations and dealing with the input.