ActiveRecordToHash
Summary
Add to_hash
method to ActiveRecord of Rails. The to_hash
method can also acquire related records with relations by Hash, by passing the options to the argument.You can filter the values to retrieve, and change the keys of Hash using options.
ActiveRecordToHash will be useful when creating a Web API that returns a response with JSON.
Installation
gem 'active_record_to_hash', '~>0.1'
Usage
Options
Key | Description | Type |
---|---|---|
attrs_reader | Specify a method to get the hash of the value and column name. If you omitt it, attributes is used. |
Symbol |
key | Change the key of the hash. | Symbol |
except | Remove from the hash. | Symbol Array |
only | Retrieve only the specified key. | Symbol Array |
with_[attribute_name] | The attribute name is passed to public_send. If the return value is ActiveRecord or ActiveRecord_Relation, call to_hash . The Hash specified for this value is passed to that to_hash . |
Boolean Hash :exists |
scope | You can specify scope when acquiring related records. | Symbol Array Proc |
Examples
Examples are shown assuming the following tables.
create_table :wide_areas do |t|
t.string :name
t.timestamps
end
create_table :areas do |t|
t.string :name
t.references :wide_area, foreign_key: true
t.timestamps
end
create_table :shops do |t|
t.string :name
t.timestamps
end
create_table :shop_areas do |t|
t.references :shop, foreign_key: true
t.references :area, foreign_key: true
t.timestamps
end
class Shop < ApplicationRecord
has_many :shop_areas
has_many :areas, inverse_of: :shops, through: :shop_areas
def to_api_hash
{
id: id,
name: name
}
end
end
p shop.to_hash
# {
# :id=>1,
# :name=>"Shop No1",
# :created_at=>Mon, 26 Mar 2018 07:53:26 UTC +00:00,
# :updated_at=>Mon, 26 Mar 2018 07:53:26 UTC +00:00
# }
p shop.to_hash(attrs_reader: to_api_hash)
# {
# :id=>1,
# :name=>"Shop No1",
# }
p shop.to_hash(attrs_reader: ->(shop){ {shop_id: shop.id} })
# {
# :shop_id=>1,
# }
p shop.to_hash(only: :name)
# {:name => "Shop No1"}
p shop.to_hash(except: [:created_at, :updated_at])
# {:id => 1, :name => "Shop No1"}
p shop.to_hash(only: [:id], with_name: {key: :foobar})
# {:id=>1, :foobar=>"Shop No1"}
p shop.to_hash(only: [:id, :name], with_areas: true)
# {
# :id=>1,
# :name=>"Shop No1",
# :areas=>[
# {:id=>1, :name=>"Area No1", :wide_area_id=>1, :created_at=>Mon, 26 Mar 2018 07:53:26 UTC +00:00, :updated_at=>Mon, 26 Mar 2018 07:53:26 UTC +00:00},
# {:id=>2, :name=>"Area No2", :wide_area_id=>2, :created_at=>Mon, 26 Mar 2018 07:53:26 UTC +00:00, :updated_at=>Mon, 26 Mar 2018 07:53:26 UTC +00:00},
# {:id=>3, :name=>"Area No3", :wide_area_id=>3, :created_at=>Mon, 26 Mar 2018 07:53:26 UTC +00:00, :updated_at=>Mon, 26 Mar 2018 07:53:26 UTC +00:00}
# ]
# }
p shop.to_hash(only: [:id, :name], with_areas: false)
# {
# :id=>1,
# :name=>"Shop No1"
# }
p shop.to_hash(only: [:id, :name], with_areas: {key: :area_list})
# {
# :id=>1,
# :name=>"Shop No1",
# :area_list=>[
# {:id=>1, :name=>"Area No1", :wide_area_id=>1, :created_at=>Mon, 26 Mar 2018 07:53:26 UTC +00:00, :updated_at=>Mon, 26 Mar 2018 07:53:26 UTC +00:00},
# {:id=>2, :name=>"Area No2", :wide_area_id=>2, :created_at=>Mon, 26 Mar 2018 07:53:26 UTC +00:00, :updated_at=>Mon, 26 Mar 2018 07:53:26 UTC +00:00},
# {:id=>3, :name=>"Area No3", :wide_area_id=>3, :created_at=>Mon, 26 Mar 2018 07:53:26 UTC +00:00, :updated_at=>Mon, 26 Mar 2018 07:53:26 UTC +00:00}
# ]
# }
p shop.to_hash(only: [:id, :name], with_areas: {scope: ->{ where(id: 1) }})
# {
# :id=>1,
# :name=>"Shop No1",
# :areas=>[
# {:id=>1, :name=>"Area No1", :wide_area_id=>1, :created_at=>Mon, 26 Mar 2018 07:53:26 UTC +00:00, :updated_at=>Mon, 26 Mar 2018 07:53:26 UTC +00:00}
# ]
# }
p shop.to_hash(
only: [:id, :name],
with_areas: {
only: [:id, :name],
with_wide_area: {
only: [:id, :name]
}
}
)
# {
# :id=>1,
# :name=>"Shop No1",
# :areas=>[
# {:id=>1, :name=>"Area No1", :wide_area=>{:id=>1, :name=>"Wide Area No1"}},
# {:id=>2, :name=>"Area No2", :wide_area=>{:id=>2, :name=>"Wide Area No2"}},
# {:id=>3, :name=>"Area No3", :wide_area=>{:id=>3, :name=>"Wide Area No3"}}
# ]
# }
p shop.to_hash(
only: [:id, :name],
with_areas: {
alter: ->(areas){ areas.each_with_object({}) {|area, memo| memo[area[:id]] = area[:name] } }
}
)
# {
# :id=>1,
# :name=>"Shop No1",
# :areas=>{1=>"Area No1", 2=>"Area No2", 3=>"Area No3"}
# }
shop.to_hash(only: [:id, :name], with_areas: :exists)
# {:id=>1, :name=>"Shop No1", :areas=>true}
# You can use this option only with ActiveRecord::Relation
shop.to_hash(ignore_nil: true)
# When the value is nil, it skips that key.
shop.to_hash(with_category_name: { delegate: { category: :name } })
# `category_name` is `shop.category.name`
Configuration
method_name
You can change the method name from :to_hash
to you want.
# config/application.rb
module YourApp
class Application < Rails::Application
...
config.active_record_to_hash.method_name = :to_your_hash
end
end
aliases
You can set an aliases for the method.
# config/application.rb
module YourApp
class Application < Rails::Application
...
config.active_record_to_hash.aliases = [:to_api_hash]
end
end
This is useful for overriding and preparing hashed methods of various patterns. The same alias method is also called for the related record specified in with_[attribute_name]
option.
# app/model/application_record.rb
def to_api_hash(options = {})
options = {
except: [:created_at, :updated_at, :sequence],
attrs_reader: :attributes_for_api
}.merge(options)
super(options)
end
# Time type value is converted to timestamp.
def attributes_for_api
hash = attributes.each_with_object({}.with_indifferent_access) do |(k, v), obj|
v = v.to_i if v.is_a? Time
obj[k] = v
end
hash
end
Use without ActiveRecord
If you want to use without ActiveRecord, just include ActiveRecordToHash::ActiveRecord
class ShopData
include ActiveModel::Model
include ActiveModel::Attributes
include ActiveRecordToHash::ActiveRecord
...
end
Contributing
- Run rspec test.
- Check source code with the rubocop.
License
The gem is available as open source under the terms of the MIT License.