Skip to content

ActiveRecord: Gives the ability to store attributes in one JSON or JSONB column in the database and provides validations, typecasting and default values on the accessors

License

MIT, MIT licenses found

Licenses found

MIT
LICENSE
MIT
MIT-LICENSE

wiseallie/has_json_attributes_on

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

17 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Introduction

The has_json_attributes_on plugin allows one to store attributes in one JSON or JSONB column in the database for ActiveRecord models and provides validations, typecasting and default values on the accessors.

Similar to ActiveRecord store(store_attribute, options = {})

Note: No tests yet. Coming soon....

Example:

# == Schema Information
#
# Table name: users
#
#  id              :integer          not null, primary key
#  email           :string
#  profile         :jsonb
#  contact_details :json
#  created_at      :datetime         not null
#  updated_at      :datetime         not null
#

class User < ActiveRecord::Base
  has_json_attributes_on :profile, {
    firstname: {type: 'String', validates: {presence: true, length: {maximum: 20, minimum: 3}}},
    lastname: {type: 'String', validates: {presence: true, length: {maximum: 20}, exclusion: {in: %w(admin ruler)}}},
    age:  {type: 'Integer', default: ->{rand(36)}, validates: {presence: true, numericality: true,  inclusion: { in: 18..35 }}},
    birthday: {type: 'Date', default: ->(u){ u.age.years.ago.to_date}, validates: {presence: true}},
    salary: {type: 'Decimal', validates: {presence: true}},
    married: {type: 'Boolean', default: ->(u){ u.age > 25 ? true : false}},
    kids_names: {type: 'Set'},
    companies: {type: 'Array'},
    extra: {type: 'Hash'}
  }

  has_json_attributes_on :contact_details, {
    google: {}, # default is type is 'String'
    facebook: {type: 'String'},
    twitter: {type: 'String'}
  }
end


User #=> User(id: integer, email: string, profile: jsonb, contact_details: json, created_at: datetime, updated_at: datetime)
u = User.new
=> #<User:0x007fa28542a318
 id: nil,
 email: nil,
 profile: #<User::ProfileDynamicType:0x007fa285429dc8 @age=23, @birthday=Thu, 07 Jan 1993, @companies=[], @extra={}, @firstname=nil, @kids_names=#<Set: {}>, @lastname=nil, @married=false, @salary=nil>,
 contact_details: #<User::ContactDetailsDynamicType:0x007fa28474c060 @facebook=nil, @google=nil, @twitter=nil>,
 created_at: nil,
 updated_at: nil>

u.age       #=> 23
u.profile.age #=> 23

u.age = 30
u.age       #=> 40
u.profile.age #=> 40

u.age = 25
u.age       #=> 25
u.profile.age #=> 25

u.married   #=> false
u.married = 'T'
u.married   #=> true
u.salary    #=> nil
u.salary = '202829.6699'  # Type casted
u.salary    #=> #<BigDecimal:7fa08a618ac8,'0.2028296699E6',18(27)>  #Type casted

Default Values

u = User.new(age: 26)
u.age       # => 26
u.married   # => true
u.birthday  # => Sun, 07 Jan 1990

Type casting

u.married = 'N'
u.married   #=> false
u.salary = '202829.6699'  # Type casted
u.salary    #=> #<BigDecimal:7fa08a618ac8,'0.2028296699E6',18(27)>  #Type casted
u.kids_names = ['jane', 'jane', 'solly']
u.kids_names #=> #<Set: {"jane", "solly"}>
u.companies = "Apple"
u.companies  #=> ["Apple"]
u.companies = ['Apple', 'Google']
u.companies  #=> ["Apple", "Google"]
u.extra = {x: 3}
u.extra  #=> {:x=>3}

Validations

u = User.new()
=> #<User:0x007fa2853e37d8
 id: nil,
 email: nil,
 profile: #<User::ProfileDynamicType:0x007fa2853daea8 @age=31, @birthday=Mon, 07 Jan 1985, @companies=[], @extra={}, @firstname=nil, @kids_names=#<Set: {}>, @lastname=nil, @married=true, @salary=nil>,
 contact_details: #<User::ContactDetailsDynamicType:0x007fa2853b1530 @facebook=nil, @google=nil, @twitter=nil>,
 created_at: nil,
 updated_at: nil>

u.valid?  #=> false
u.errors
=> #<ActiveModel::Errors:0x007fa285313038
 @base=
  #<User:0x007fa2853e37d8
   id: nil,
   email: nil,
   profile: #<User::ProfileDynamicType:0x007fa2853daea8 @age=31, @birthday=Mon, 07 Jan 1985, @companies=[], @extra={}, @firstname=nil, @kids_names=#<Set: {}>, @lastname=nil, @married=true, @salary=nil>,
   contact_details: #<User::ContactDetailsDynamicType:0x007fa2853b1530 @facebook=nil, @google=nil, @twitter=nil>,
   created_at: nil,
   updated_at: nil>,
 @messages={:firstname=>["can't be blank", "is too short (minimum is 3 characters)"], :lastname=>["can't be blank"], :salary=>["can't be blank"]}>

u.attributes = {firstname: 'James', lastname: 'King', salary: '1.6789'}
u.valid?  #=> true
u.save
(2.1ms)  BEGIN
SQL (2.7ms)  INSERT INTO "users" ("profile", "contact_details", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id"  [["profile", "{\"firstname\":\"James\",\"lastname\":\"King\",\"age\":31,\"birthday\":\"1985-01-07\",\"salary\":\"1.6789\",\"married\":true,\"kids_names\":[],\"companies\":[],\"extra\":{}}"], ["contact_details", "{\"google\":null,\"facebook\":null,\"twitter\":null}"], ["created_at", "2016-01-07 08:58:44.945408"], ["updated_at", "2016-01-07 08:58:44.945408"]]
(0.9ms)  COMMIT
=> true

u.salary = '202829.6699'  # Type casted
u.salary    #=> #<BigDecimal:7fa08a618ac8,'0.2028296699E6',18(27)>  #Type casted
u.kids_names = ['jane', 'jane', 'solly']
u.kids_names #=> #<Set: {"jane", "solly"}>
u.companies = "Apple"
u.companies  #=> ["Apple"]
u.companies = ['Apple', 'Google']
u.companies  #=> ["Apple", "Google"]
u.extra = {x: 3}
u.extra  #=> {:x=>3}

u.facebook = "https://facebook.com/someusername"
u.facebook #=> "https://facebook.com/someusername"

u
=> #<User:0x007fa2853e37d8
 id: 2,
 email: nil,
 profile:
  #<User::ProfileDynamicType:0x007fa2853daea8
   @age=31,
   @birthday=Mon, 07 Jan 1985,
   @companies=["Apple", "Google"],
   @extra={:x=>3},
   @firstname="James",
   @kids_names=#<Set: {"jane", "solly"}>,
   @lastname="King",
   @married=true,
   @salary=#<BigDecimal:7fa28ba358b0,'0.2028296699E6',18(27)>>,
 contact_details: #<User::ContactDetailsDynamicType:0x007fa2853b1530 @facebook="https://facebook.com/someusername", @google=nil, @twitter=nil>,
 created_at: Thu, 07 Jan 2016 08:58:44 UTC +00:00,
 updated_at: Thu, 07 Jan 2016 08:58:44 UTC +00:00>

 u.save
   (0.2ms)  BEGIN
  SQL (0.4ms)  UPDATE "users" SET "profile" = $1, "contact_details" = $2, "updated_at" = $3 WHERE "users"."id" = $4  [["profile", "{\"firstname\":\"James\",\"lastname\":\"King\",\"age\":31,\"birthday\":\"1985-01-07\",\"salary\":\"202829.6699\",\"married\":true,\"kids_names\":[\"jane\",\"solly\"],\"companies\":[\"Apple\",\"Google\"],\"extra\":{\"x\":3}}"], ["contact_details", "{\"google\":null,\"facebook\":\"https://facebook.com/someusername\",\"twitter\":null}"], ["updated_at", "2016-01-07 09:07:10.613237"], ["id", 2]]
   (0.2ms)  COMMIT
=> true

Installation

Rails 4.2.x / Ruby 2.0.0 and higher

The current version of has_json_attributes_on is compatible with Rails 4.2.x and less than 5.0.0, and Ruby 2.0.0 and higher.

Add it to your Gemfile:

gem "has_json_attributes_on", "~> 0.0.3"

or use the bleeding edge

gem "has_json_attributes_on", github: "wiseallie/has_json_attributes_on"

Databases

At the moment this plugin only supports postgres, other databases may be added in later

What about rails 5

This plugin will be updated for rails 5 soon.

Credits

This depends on other gems

Virtus: https://github.com/solnic/virtus

default_value_for: https://github.com/FooBarWidget/default_value_for

Thanks to MyTopDog Education for their time. http://mytopdog.co.za

About

ActiveRecord: Gives the ability to store attributes in one JSON or JSONB column in the database and provides validations, typecasting and default values on the accessors

Resources

License

MIT, MIT licenses found

Licenses found

MIT
LICENSE
MIT
MIT-LICENSE

Stars

Watchers

Forks

Packages

No packages published

Languages