Octojson
Supercharge jsonb
attributes with defaults and schemas based on another ActiveRecord
attribute. Inspired by jsonb_accessor
Installation
Add to your Gemfile:
gem 'octojson'
Then you can add octojson
to your model(s):
class Post < ActiveRecord::Base
YOUR_SCHEMA = {
type_one: {
title: { type: :string, default: 'Title -- one' },
text_one: { type: :string, default: 'something cool -- one' },
boolean_one: { type: :boolean, default: false },
number_one: { type: :integer, default: 3, validates: { numericality: { only_integer: true, greater_than_or_equal_to: 1, less_than_or_equal_to: 5 } } },
json_one: { type: :json, default: {}, nested_attributes: [:nested_one, :nested_two, :nested_three] },
},
type_two: {
title: { type: :string, default: 'Title -- two' },
text_two: { type: :string, default: 'something cool -- two' },
boolean_two: { type: :boolean, default: true },
},
type_three: {
title: { type: :string, default: 'Title -- three' },
text_three: { type: :string, default: 'something cool -- three' }
}
}.freeze
octojson :settings, YOUR_SCHEMA, :post_type
end
post = Post.new(post_type: 'type_one')
post.save
post.settings['title'] # => "Title -- one"
post.settings['text_one'] # => "something cool -- one"
post.settings['boolean_one'] # => false
post.settings['number_one'] # => 3
post.settings['json_one'] # => {}
post = Post.new(post_type: 'type_two')
post.save
post.settings['title'] # => "Title -- two"
post.settings['text_two'] # => "something cool -- two"
post.settings['boolean_two'] # => true
post.settings['text_one'] # => nil
Usage
Validations are cool too! Use Rails validations as you would on directly on record's attributes.
class Post < ActiveRecord::Base
YOUR_SCHEMA = {
type_one: {
count: { type: :integer, default: 3, validates: { numericality: { only_integer: true, greater_than_or_equal_to: 1, less_than_or_equal_to: 5 } } },
},
type_two: {
count: { type: :integer, default: 10, validates: { numericality: { only_integer: true, greater_than_or_equal_to: 5, less_than_or_equal_to: 15 } } },
}
}.freeze
octojson :settings, YOUR_SCHEMA, :post_type
end
Sometimes you don't need support for different attributes values. _default
can be used instead to get the benefits of a schema with defaults and enforced nested_attributes.
class Post < ActiveRecord::Base
OPTIONS_SCHEMA_AS_DEFAULT = {
_default: {
title: { type: :string, default: 'default title' },
number_default: { type: :integer, default: 3, validates: { numericality: { only_integer: true, greater_than_or_equal_to: 1, less_than_or_equal_to: 5 } } },
sub_options: { type: :array, default: [], nested_attributes: [:name, :type] }
}
}.freeze
octojson :options, OPTIONS_SCHEMA_AS_DEFAULT
end
Use JSONB_ATTRIBUTE_strong_params
with your controller strong params
class PostsController < ActiveController::Base
def set_post
@post = @post.find(params[:id])
end
def post_params
params.require(:post).permit(settings: @post.settings_strong_params)
end
end
Dependencies
- ActiveRecord >= 6.0
- Postgres >= 9.4 (in order to use the jsonb column type).
Tests
Run bin/setup
to install dependencies.
$ rake test
** ensure postgres is running
Contributing
- Fork it
- Create your feature branch (
git checkout -b my-new-feature
) - Add tests and changes (run the tests with
rake
) - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create a new Pull Request