Rails return JSON serialized attribute with_indifferent_access
use the built-in serialize
method :
class Whatever < ActiveRecord::Base
serialize :params, HashWithIndifferentAccess
end
see ActiveRecord::Base docs on serialization for more info.
Is there a way to serialize ActiveRecord's JSON properties using HashWithIndifferentAccess?
| You can use a custom serializer so you can access the JSON object with symbols as well.
# app/models/user.rb
class User < ActiveRecord::Base
serialize :preferences, HashSerializer
end
# app/serializers/hash_serializer.rb
class HashSerializer
def self.dump(hash)
hash
end
def self.load(hash)
(hash || {}).with_indifferent_access
end
end
Full credit - sans googling - goes to http://nandovieira.com/using-postgresql-and-jsonb-with-ruby-on-rails.
RSpec: comparing a hash with string keys against a hash with symbol keys?
You could do:
it "should match" do
{:a => 1, :b => 2}.stringify_keys.should =~ {"a" => 1, "b" => 2}
end
Postgresql JSON column as HashWithIndifferentAccess
Serialize alone wont work here since HashWithIndifferentAccess
does not respond to both load
and dump
methods, but you can do this:
class THEModel < ActiveRecord::Base
def my_hash_attribute
read_attribute(:my_hash_attribute).with_indifferent_access
end
end
See also Custom serialization for fields in Rails
How to get hash values from a multiple check_box in rails?
Look at the api of check_box_tag
:
check_box_tag(name, value = "1", checked = false, options = {})
- The first argument is the name: what the value is stored under, and this usually shows up in params
- The second argument is the value, and this gets stringified in order to make the html tag.
- Third is the beginning checked state
- Finally is the options
So since you are passing an object to value, rails is calling .to_s
on that object in order to create the html tag. That is why you are getting the no implicit conversion of Symbol into Integer
error, because you are calling […]
on a string, which returns the nth character of the string. It's not the best error message, unfortunately, because it doesn't mention that you are accessing a string, not a hash, and it would be the same error message for an array as well. What you probably want is to serialize the value so that you can unserialize it later:
- Try calling
.to_json
on the second argument. This should change your value inparams[:product_lots]
to['{"bol_id": 3086, "product_lot_id": 6021}']
. - Notice that this is still a string, so you need to deserialize it (although there is a chance rails could automatically deserialize this, depending on configuration and filters, etc.).
- try
params[:product_lots].map!{|data| JSON.parse(data, object_class: HashWithIndifferentAccess)}
. This should now give you an array of hashes (with indifferent access, so you can use symbol keys). (See alsoJson::parse
) - you can now get the values you wish
- consider moving this into a helper, before action/filter, etc., to be able to use it multiple times, potentially for multiple parameters
Existing data serialized as hash produces error when upgrading to Rails 5
From the fine manual:
serialize(attr_name, class_name_or_coder = Object)
[...] If
class_name
is specified, the serialized object must be of that class on assignment and retrieval. OtherwiseSerializationTypeMismatch
will be raised.
So when you say this:
serialize :social_media, Hash
ActiveRecord will require the unserialized social_media
to be a Hash
. However, as noted by vnbrs, ActionController::Parameters
no longer subclasses Hash
like it used to and you have a table full of serialized ActionController::Parameters
instances. If you look at the raw YAML data in your social_media
column, you'll see a bunch of strings like:
--- !ruby/object:ActionController::Parameters...
rather than Hashes like this:
---\n:key: value...
You should fix up all your existing data to have YAMLized Hashes in social_media
rather than ActionController::Parameters
and whatever else is in there. This process will be somewhat unpleasant:
- Pull each
social_media
out of the table as a string. - Unpack that YAML string into a Ruby object:
obj = YAML.load(str)
. - Convert that object to a Hash:
h = obj.to_unsafe_h
. - Write that Hash back to a YAML string:
str = h.to_yaml
. - Put that string back into the database to replace the old one from (1).
Note the to_unsafe_h
call in (3). Just calling to_h
(or to_hash
for that matter) on an ActionController::Parameters
instance will give you an exception in Rails5, you have to include a permit
call to filter the parameters first:
h = params.to_h # Exception!
h = params.permit(:whatever).to_h # Indifferent access hash with one entry
If you use to_unsafe_h
(or to_unsafe_hash
) then you get the whole thing in a HashWithIndifferentAccess
. Of course, if you really want a plain old Hash then you'd say:
h = obj.to_unsafe_h.to_h
to unwrap the indifferent access wrapper as well. This also assumes that you only have ActionController::Parameters
in social_media
so you might need to include an obj.respond_to?(:to_unsafe_hash)
check to see how you unpack your social_media
values.
You could do the above data migration through direct database access in a Rails migration. This could be really cumbersome depending on how nice the low level MySQL interface is. Alternatively, you could create a simplified model class in your migration, something sort of like this:
class YourMigration < ...
class ModelHack < ApplicationRecord
self.table_name = 'clubs'
serialize :social_media
end
def up
ModelHack.all.each do |m|
# Update this to match your real data and what you want `h` to be.
h = m.social_media.to_unsafe_h.to_h
m.social_media = h
m.save!
end
end
def down
raise ActiveRecord::IrreversibleMigration
end
end
You'd want to use find_in_batches
or in_batches_of
instead all
if you have a lot of Club
s of course.
If your MySQL supports json
columns and ActiveRecord works with MySQL's json
columns (sorry, PostgreSQL guy here), then this might be a good time to change the column to json
and run far away from serialize
.
Related Topics
Deleting the Current Session with Rack::Session::Cookie
How to Run and Debug Ruby on Rails from Visual Studio Code
Run a Ruby Library from the Command-Line
How to Embed Ruby in JavaScript (Rails + .Html.Erb File)
Rails 4 Strong Parameters Failing When Creating Instances in Rails Console
Get Jekyll Configuration Inside Plugin
Running Webrick Server in Background
What's the Difference Between "=" & "=>" and "@Variable", "@@Variable" and ":Variable" in Ruby
Override the Protect_From_Forgery Strategy in a Controller
Ruby Sandboxing VS. Integrating a Scripting Language
Ruby Code Beautification, Split Long Instructions on Multiple Lines
How to Pass <Arguments> to Irb If I Don't Specify <Programfile>
How Rails Delegate Method Works
Enumerator as an Infinite Generator in Ruby
"Certificate Verify Failed" Error When Installing Ruby Gems on Windows
Sunspot_Rails Gem - " Errno:: Econnrefused (Connection Refused - Connect (2)) "