jsonb_accessor 1.0.0.beta.4 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9342c69dc4817b5f014149d149fd8fb92ad650ff
4
- data.tar.gz: d3b5f27e415c190f63b9275cc28c648fc2510023
3
+ metadata.gz: 4572af0f905157b8a8540b7c4fac70426908926c
4
+ data.tar.gz: 7fc82fc4c45831589b5de1d041cab1e651783447
5
5
  SHA512:
6
- metadata.gz: 2b2e8d7ffbc17b5b8455c8e95f686a1ad24a2f3cbd7f6166d7bcf2300922316221248617dd021fb0d3f59db45f5b9bb4ff912540c4876f0b3a92484d688fc29a
7
- data.tar.gz: 6e1453d01c791b69cf5ec9c0f525819bc8eed173fdbb4f914c8e190f7a5f6b7ae0ad8e966707a023299d69f2e33df1cb0af74b3ef7374053127cbe8c430a14dd
6
+ metadata.gz: bb272c8509fce56497c5863feb5879428cab9d90e42243c791caf14928e11a40f1a3c07af10574028be601874f49a26560354c85d4f561b5fb22d9258e79e944
7
+ data.tar.gz: c053957f581dbcb37aa77c8c7cdce51193ec073927abaa43832e9ad4330a8d8d60de45ab671cf4a48dacf24a4dddbc576b743abc02c6019979bd5901257f4556
data/.gitignore CHANGED
@@ -7,3 +7,5 @@
7
7
  /pkg/
8
8
  /spec/reports/
9
9
  /tmp/
10
+ gemfiles/.bundle
11
+ gemfiles/*.gemfile.lock
@@ -5,7 +5,7 @@ AllCops:
5
5
  - db/**/*
6
6
  - gemfiles/**/*
7
7
  - vendor/**/*
8
- Lint/SpaceBeforeFirstArg:
8
+ Style/SpaceBeforeFirstArg:
9
9
  Enabled: false
10
10
  Lint/UnusedBlockArgument:
11
11
  Enabled: false
@@ -59,3 +59,5 @@ Style/SpaceAroundEqualsInParameterDefault:
59
59
  Enabled: false
60
60
  Style/StringLiterals:
61
61
  EnforcedStyle: double_quotes
62
+ Performance/EndWith:
63
+ Enabled: false
@@ -1 +1 @@
1
- 2.3.1
1
+ 2.4.1
@@ -1,7 +1,9 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.2.5
4
- - 2.3.1
3
+ - 2.3.8
4
+ - 2.4.5
5
+ - 2.5.3
6
+ - 2.6.1
5
7
  addons:
6
8
  postgresql: '9.4'
7
9
  before_install:
@@ -14,3 +16,4 @@ before_script:
14
16
  cache: bundler
15
17
  gemfile:
16
18
  - gemfiles/activerecord_5.0.0.gemfile
19
+ - gemfiles/activerecord_5.1.0.gemfile
data/Appraisals CHANGED
@@ -1,4 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  appraise "activerecord-5.0.0" do
2
- gemspec
3
4
  gem "activerecord", "~> 5.0.0"
4
5
  end
6
+
7
+ appraise "activerecord-5.1.0" do
8
+ gem "activerecord", "~> 5.1.3"
9
+ end
data/Gemfile CHANGED
@@ -1,7 +1,9 @@
1
- source 'https://rubygems.org'
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
2
4
 
3
5
  # Specify your gem's dependencies in jsonb_accessor.gemspec
4
6
  gemspec
5
7
 
6
8
  # Travis CI requires in Gemfile
7
- gem 'pg'
9
+ gem "pg"
data/README.md CHANGED
@@ -1,17 +1,13 @@
1
1
  # JSONb Accessor
2
2
 
3
- Created by &nbsp;&nbsp;&nbsp; [<img src="https://raw.githubusercontent.com/devmynd/jsonb_accessor/master/devmynd-logo.png" alt="DevMynd Logo" />](https://www.devmynd.com/)
3
+ Created by &nbsp;&nbsp;&nbsp; [<img src="https://raw.githubusercontent.com/madeintandem/jsonb_accessor/master/tandem-logo.png" alt="Tandem Logo" />](https://www.madeintandem.com/)
4
4
 
5
- [![Gem Version](https://badge.fury.io/rb/jsonb_accessor.svg)](http://badge.fury.io/rb/jsonb_accessor) &nbsp;&nbsp;&nbsp;[![Build Status](https://travis-ci.org/devmynd/jsonb_accessor.svg)](https://travis-ci.org/devmynd/jsonb_accessor) <img src="https://raw.githubusercontent.com/devmynd/jsonb_accessor/master/json-bee.png" alt="JSONb Accessor Logo" align="right" />
5
+ [![Gem Version](https://badge.fury.io/rb/jsonb_accessor.svg)](http://badge.fury.io/rb/jsonb_accessor) &nbsp;&nbsp;&nbsp;[![Build Status](https://travis-ci.org/madeintandem/jsonb_accessor.svg)](https://travis-ci.org/madeintandem/jsonb_accessor) <img src="https://raw.githubusercontent.com/madeintandem/jsonb_accessor/master/json-bee.png" alt="JSONb Accessor Logo" align="right" />
6
6
 
7
- Adds typed `jsonb` backed fields as first class citizens to your `ActiveRecord` models. This gem is similar in spirit to [HstoreAccessor](https://github.com/devmynd/hstore_accessor), but the `jsonb` column in PostgreSQL has a few distinct advantages, mostly around nested documents and support for collections.
7
+ Adds typed `jsonb` backed fields as first class citizens to your `ActiveRecord` models. This gem is similar in spirit to [HstoreAccessor](https://github.com/madeintandem/hstore_accessor), but the `jsonb` column in PostgreSQL has a few distinct advantages, mostly around nested documents and support for collections.
8
8
 
9
9
  It also adds generic scopes for querying `jsonb` columns.
10
10
 
11
- ## 1.0 Beta
12
-
13
- This README reflects the most recent 1.0 beta. Method names and interfaces may still change.
14
-
15
11
  ## Table of Contents
16
12
 
17
13
  * [Installation](#installation)
@@ -29,7 +25,7 @@ This README reflects the most recent 1.0 beta. Method names and interfaces may s
29
25
  Add this line to your application's `Gemfile`:
30
26
 
31
27
  ```ruby
32
- gem "jsonb_accessor", "1.0.0.beta.4"
28
+ gem "jsonb_accessor", "~> 1.0.0"
33
29
  ```
34
30
 
35
31
  And then execute:
@@ -141,6 +137,14 @@ For time related fields you can query using `before` and `after`.
141
137
  Product.all.data_where(reviewed_at: { before: Time.current.beginning_of_week, after: 4.weeks.ago })
142
138
  ```
143
139
 
140
+ If you want to search for records within a certain time, date, or number range, just pass in the range (Note: this is just shorthand for the above mentioned `before`/`after`/`less_than`/`less_than_or_equal_to`/`greater_than_or_equal_to`/etc options).
141
+
142
+ ```ruby
143
+ Product.all.data_where(price: 10..20)
144
+ Product.all.data_where(price: 10...20)
145
+ Product.all.data_where(reviewed_at: Time.current..3.days.from_now)
146
+ ```
147
+
144
148
  This scope is a convenient wrapper around the `jsonb_where` `scope` that saves you from having to convert the given keys to the store keys and from specifying the column.
145
149
 
146
150
  ### `jsonb_where`
@@ -164,6 +168,27 @@ Just the opposite of `jsonb_where`. Note that this will automatically exclude al
164
168
  Product.all.jsonb_where_not(:data, reviewed_at: { before: Time.current }, p: { greater_than: 5 })
165
169
  ```
166
170
 
171
+ ### `<jsonb_attribute>_order`
172
+
173
+ Orders your query according to values in the Jsonb Accessor fields similar to ActiveRecord's `order`.
174
+
175
+ ```ruby
176
+ Product.all.data_order(:price)
177
+ Product.all.data_order(:price, :reviewed_at)
178
+ Product.all.data_order(:price, reviewed_at: :desc)
179
+ ```
180
+
181
+ It will convert your given keys into store keys if necessary.
182
+
183
+ ### `jsonb_order`
184
+
185
+ Allows you to order by a Jsonb Accessor field.
186
+
187
+ ```ruby
188
+ Product.all.jsonb_order(:data, :price, :asc)
189
+ Product.all.jsonb_order(:data, :price, :desc)
190
+ ```
191
+
167
192
  ### `jsonb_contains`
168
193
 
169
194
  Returns all records that contain the given JSON paths.
@@ -284,7 +309,7 @@ From here any attributes specific to any sub-class can be stored in the
284
309
  individual fields in an `jsonb` column.
285
310
 
286
311
  This approach was originally conceived by Joe Hirn in [this blog
287
- post](http://www.devmynd.com/blog/2013-3-single-table-inheritance-hstore-lovely-combination).
312
+ post](https://madeintandem.com/blog/2013-3-single-table-inheritance-hstore-lovely-combination/).
288
313
 
289
314
  ## Validations
290
315
 
@@ -309,7 +334,7 @@ Run `bin/console` for an interactive prompt that will allow you to experiment.
309
334
 
310
335
  ## Contributing
311
336
 
312
- 1. [Fork it](https://github.com/devmynd/jsonb_accessor/fork)
337
+ 1. [Fork it](https://github.com/madeintandem/jsonb_accessor/fork)
313
338
  2. Create your feature branch (`git checkout -b my-new-feature`)
314
339
  3. Add tests and changes (run the tests with `rake`)
315
340
  4. Commit your changes (`git commit -am 'Add some feature'`)
data/Rakefile CHANGED
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require "rubygems"
3
4
  require "bundler/setup"
4
5
  require "bundler/gem_tasks"
@@ -10,4 +11,4 @@ StandaloneMigrations::Tasks.load_tasks
10
11
  RSpec::Core::RakeTask.new
11
12
  RuboCop::RakeTask.new
12
13
 
13
- task(default: [:rubocop, :spec])
14
+ task(default: %i[rubocop spec])
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
+
3
4
  # rubocop:disable Lint/UselessAssignment
4
5
 
5
6
  require "bundler/setup"
@@ -15,22 +15,22 @@ ActiveRecord::Schema.define(version: 20150407031737) do
15
15
  # These are extensions that must be enabled in order to support this database
16
16
  enable_extension "plpgsql"
17
17
 
18
- create_table "product_categories", force: :cascade do |t|
18
+ create_table "product_categories", id: :serial, force: :cascade do |t|
19
19
  t.jsonb "options"
20
20
  end
21
21
 
22
- create_table "products", force: :cascade do |t|
23
- t.jsonb "options"
24
- t.jsonb "data"
25
- t.string "string_type"
26
- t.integer "integer_type"
27
- t.integer "product_category_id"
28
- t.boolean "boolean_type"
29
- t.float "float_type"
30
- t.time "time_type"
31
- t.date "date_type"
22
+ create_table "products", id: :serial, force: :cascade do |t|
23
+ t.jsonb "options"
24
+ t.jsonb "data"
25
+ t.string "string_type"
26
+ t.integer "integer_type"
27
+ t.integer "product_category_id"
28
+ t.boolean "boolean_type"
29
+ t.float "float_type"
30
+ t.time "time_type"
31
+ t.date "date_type"
32
32
  t.datetime "datetime_type"
33
- t.decimal "decimal_type"
33
+ t.decimal "decimal_type"
34
34
  end
35
35
 
36
36
  end
@@ -5,4 +5,4 @@ source "https://rubygems.org"
5
5
  gem "pg"
6
6
  gem "activerecord", "~> 5.0.0"
7
7
 
8
- gemspec :path => "../"
8
+ gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "pg"
6
+ gem "activerecord", "~> 5.1.3"
7
+
8
+ gemspec path: "../"
@@ -1,4 +1,6 @@
1
1
  # coding: utf-8
2
+ # frozen_string_literal: true
3
+
2
4
  lib = File.expand_path("../lib", __FILE__)
3
5
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
6
  require "jsonb_accessor/version"
@@ -7,32 +9,32 @@ Gem::Specification.new do |spec|
7
9
  spec.name = "jsonb_accessor"
8
10
  spec.version = JsonbAccessor::VERSION
9
11
  spec.authors = ["Michael Crismali", "Joe Hirn", "Jason Haruska"]
10
- spec.email = ["michael.crismali@gmail.com", "joe@devmynd.com", "jason@haruska.com"]
12
+ spec.email = ["michael@crismali.com", "joe@devmynd.com", "jason@haruska.com"]
11
13
 
12
- spec.summary = %q{Adds typed jsonb backed fields to your ActiveRecord models.}
13
- spec.description = %q{Adds typed jsonb backed fields to your ActiveRecord models.}
14
+ spec.summary = "Adds typed jsonb backed fields to your ActiveRecord models."
15
+ spec.description = "Adds typed jsonb backed fields to your ActiveRecord models."
14
16
  spec.homepage = "https://github.com/devmynd/jsonb_accessor"
15
17
  spec.license = "MIT"
16
18
  spec.required_ruby_version = "~> 2.0"
17
19
 
18
- spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) || f.match(%r{png\z}) }
20
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) || f.match(/png\z/) }
19
21
  spec.bindir = "exe"
20
22
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
23
  spec.require_paths = ["lib"]
22
24
 
23
25
  spec.add_dependency "activerecord", ">= 5.0"
26
+ spec.add_dependency "activesupport", ">= 5.0"
24
27
  spec.add_dependency "pg", ">= 0.18.1"
25
28
 
26
- spec.add_development_dependency "appraisal"
27
- spec.add_development_dependency "bundler", "~> 1.9"
28
- spec.add_development_dependency "database_cleaner"
29
+ spec.add_development_dependency "appraisal", "~> 2.2.0"
30
+ spec.add_development_dependency "bundler", "~> 2.1.4"
31
+ spec.add_development_dependency "database_cleaner", "~> 1.6.0"
29
32
  spec.add_development_dependency "awesome_print"
30
33
  spec.add_development_dependency "pry"
31
34
  spec.add_development_dependency "pry-doc"
32
35
  spec.add_development_dependency "pry-nav"
33
- spec.add_development_dependency "rake", "~> 10.0"
34
- spec.add_development_dependency "rspec", "~> 3.2"
35
- spec.add_development_dependency "rubocop", "~> 0.31"
36
- spec.add_development_dependency "shoulda-matchers"
37
- spec.add_development_dependency "standalone_migrations"
36
+ spec.add_development_dependency "rake", ">= 12.3.3"
37
+ spec.add_development_dependency "rspec", "~> 3.6.0"
38
+ spec.add_development_dependency "rubocop", "~> 0.48.1"
39
+ spec.add_development_dependency "standalone_migrations", "~> 5.2.0"
38
40
  end
@@ -1,10 +1,12 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require "active_record"
3
4
 
4
5
  require "active_record/connection_adapters/postgresql_adapter"
5
6
 
6
7
  require "jsonb_accessor/version"
7
8
  require "jsonb_accessor/macro"
9
+ require "jsonb_accessor/query_helper"
8
10
  require "jsonb_accessor/query_builder"
9
11
 
10
12
  module JsonbAccessor
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module JsonbAccessor
3
4
  module Macro
4
5
  module ClassMethods
@@ -33,7 +34,7 @@ module JsonbAccessor
33
34
  end
34
35
 
35
36
  # Get store keys to default values mapping
36
- store_keys_and_defaults = ::JsonbAccessor::QueryBuilder.convert_keys_to_store_keys(names_and_defaults, public_send(store_key_mapping_method_name))
37
+ store_keys_and_defaults = ::JsonbAccessor::QueryHelper.convert_keys_to_store_keys(names_and_defaults, public_send(store_key_mapping_method_name))
37
38
 
38
39
  # Define jsonb_defaults_mapping_for_<jsonb_attribute>
39
40
  defaults_mapping_method_name = "jsonb_defaults_mapping_for_#{jsonb_attribute}"
@@ -45,7 +46,14 @@ module JsonbAccessor
45
46
  end
46
47
 
47
48
  all_defaults_mapping = public_send(defaults_mapping_method_name)
48
- attribute jsonb_attribute, :jsonb, default: all_defaults_mapping if all_defaults_mapping.present?
49
+ # Fields may have procs as default value. This means `all_defaults_mapping` may contain procs as values. To make this work
50
+ # with the attributes API, we need to wrap `all_defaults_mapping` with a proc itself, making sure it returns a plain hash
51
+ # each time it is evaluated.
52
+ all_defaults_mapping_proc =
53
+ if all_defaults_mapping.present?
54
+ -> { all_defaults_mapping.map { |key, value| [key, value.respond_to?(:call) ? value.call : value] }.to_h }
55
+ end
56
+ attribute jsonb_attribute, :jsonb, default: all_defaults_mapping_proc if all_defaults_mapping_proc.present?
49
57
 
50
58
  # Setters are in a module to allow users to override them and still be able to use `super`.
51
59
  setters = Module.new do
@@ -66,7 +74,7 @@ module JsonbAccessor
66
74
  empty_store_key_attributes = names_to_store_keys.values.each_with_object({}) { |name, defaults| defaults[name] = nil }
67
75
  empty_named_attributes = names_to_store_keys.keys.each_with_object({}) { |name, defaults| defaults[name] = nil }
68
76
 
69
- store_key_attributes = ::JsonbAccessor::QueryBuilder.convert_keys_to_store_keys(value, names_to_store_keys)
77
+ store_key_attributes = ::JsonbAccessor::QueryHelper.convert_keys_to_store_keys(value, names_to_store_keys)
70
78
  write_attribute(jsonb_attribute, empty_store_key_attributes.merge(store_key_attributes))
71
79
 
72
80
  empty_named_attributes.merge(value).each { |name, attribute_value| write_attribute(name, attribute_value) }
@@ -76,25 +84,42 @@ module JsonbAccessor
76
84
 
77
85
  # Makes sure new objects have the appropriate values in their jsonb fields.
78
86
  after_initialize do
79
- jsonb_values = public_send(jsonb_attribute) || {}
80
- jsonb_values.each do |store_key, value|
81
- name = names_and_store_keys.key(store_key)
82
- write_attribute(name, value) if name
87
+ if has_attribute?(jsonb_attribute)
88
+ jsonb_values = public_send(jsonb_attribute) || {}
89
+ jsonb_values.each do |store_key, value|
90
+ name = names_and_store_keys.key(store_key)
91
+ next unless name
92
+
93
+ write_attribute(name, value)
94
+ clear_attribute_change(name) if persisted?
95
+ end
83
96
  end
84
- clear_changes_information if persisted?
85
97
  end
86
98
 
87
99
  # <jsonb_attribute>_where scope
88
100
  scope("#{jsonb_attribute}_where", lambda do |attributes|
89
- store_key_attributes = ::JsonbAccessor::QueryBuilder.convert_keys_to_store_keys(attributes, all.model.public_send(store_key_mapping_method_name))
101
+ store_key_attributes = ::JsonbAccessor::QueryHelper.convert_keys_to_store_keys(attributes, all.model.public_send(store_key_mapping_method_name))
90
102
  jsonb_where(jsonb_attribute, store_key_attributes)
91
103
  end)
92
104
 
93
105
  # <jsonb_attribute>_where_not scope
94
106
  scope("#{jsonb_attribute}_where_not", lambda do |attributes|
95
- store_key_attributes = ::JsonbAccessor::QueryBuilder.convert_keys_to_store_keys(attributes, all.model.public_send(store_key_mapping_method_name))
107
+ store_key_attributes = ::JsonbAccessor::QueryHelper.convert_keys_to_store_keys(attributes, all.model.public_send(store_key_mapping_method_name))
96
108
  jsonb_where_not(jsonb_attribute, store_key_attributes)
97
109
  end)
110
+
111
+ # <jsonb_attribute>_order scope
112
+ scope("#{jsonb_attribute}_order", lambda do |*args|
113
+ ordering_options = args.extract_options!
114
+ order_by_defaults = args.each_with_object({}) { |attribute, config| config[attribute] = :asc }
115
+ store_key_mapping = all.model.public_send(store_key_mapping_method_name)
116
+
117
+ order_by_defaults.merge(ordering_options).reduce(all) do |query, (name, direction)|
118
+ key = store_key_mapping[name.to_s]
119
+ order_query = jsonb_order(jsonb_attribute, key, direction)
120
+ query.merge(order_query)
121
+ end
122
+ end)
98
123
  end
99
124
  end
100
125
  end
@@ -1,111 +1,41 @@
1
1
  # frozen_string_literal: true
2
- module JsonbAccessor
3
- GREATER_THAN = ">"
4
- GREATER_THAN_OR_EQUAL_TO = ">="
5
- LESS_THAN = "<"
6
- LESS_THAN_OR_EQUAL_TO = "<="
7
-
8
- NUMBER_OPERATORS_MAP = {
9
- GREATER_THAN => GREATER_THAN,
10
- "greater_than" => GREATER_THAN,
11
- "gt" => GREATER_THAN,
12
- GREATER_THAN_OR_EQUAL_TO => GREATER_THAN_OR_EQUAL_TO,
13
- "greater_than_or_equal_to" => GREATER_THAN_OR_EQUAL_TO,
14
- "gte" => GREATER_THAN_OR_EQUAL_TO,
15
- LESS_THAN => LESS_THAN,
16
- "less_than" => LESS_THAN,
17
- "lt" => LESS_THAN,
18
- LESS_THAN_OR_EQUAL_TO => LESS_THAN_OR_EQUAL_TO,
19
- "less_than_or_equal_to" => LESS_THAN_OR_EQUAL_TO,
20
- "lte" => LESS_THAN_OR_EQUAL_TO
21
- }.freeze
22
-
23
- NUMBER_OPERATORS = NUMBER_OPERATORS_MAP.keys.freeze
24
-
25
- TIME_OPERATORS_MAP = {
26
- "after" => GREATER_THAN,
27
- "before" => LESS_THAN
28
- }.freeze
29
-
30
- TIME_OPERATORS = TIME_OPERATORS_MAP.keys.freeze
31
-
32
- IS_NUMBER_QUERY_ARGUMENTS = lambda do |arg|
33
- arg.is_a?(Hash) &&
34
- arg.keys.map(&:to_s).all? { |key| JsonbAccessor::NUMBER_OPERATORS.include?(key) }
35
- end
36
-
37
- IS_TIME_QUERY_ARGUMENTS = lambda do |arg|
38
- arg.is_a?(Hash) &&
39
- arg.keys.map(&:to_s).all? { |key| JsonbAccessor::TIME_OPERATORS.include?(key) }
40
- end
41
-
42
- ORDER_DIRECTIONS = [:asc, :desc, "asc", "desc"].freeze
43
2
 
3
+ module JsonbAccessor
44
4
  module QueryBuilder
45
5
  extend ActiveSupport::Concern
46
- InvalidColumnName = Class.new(StandardError)
47
- InvalidFieldName = Class.new(StandardError)
48
- InvalidDirection = Class.new(StandardError)
49
-
50
- def self.validate_column_name!(query, column_name)
51
- if query.model.columns.none? { |column| column.name == column_name.to_s }
52
- raise InvalidColumnName, "a column named `#{column_name}` does not exist on the `#{query.model.table_name}` table"
53
- end
54
- end
55
-
56
- def self.validate_field_name!(query, column_name, field_name)
57
- store_keys = query.model.public_send("jsonb_store_key_mapping_for_#{column_name}").values
58
- if store_keys.exclude?(field_name.to_s)
59
- valid_field_names = store_keys.map { |key| "`#{key}`" }.join(", ")
60
- raise InvalidFieldName, "`#{field_name}` is not a valid field name, valid field names include: #{valid_field_names}"
61
- end
62
- end
63
-
64
- def self.validate_direction!(option)
65
- if ORDER_DIRECTIONS.exclude?(option)
66
- raise InvalidDirection, "`#{option}` is not a valid direction for ordering, only `asc` and `desc` are accepted"
67
- end
68
- end
69
-
70
- def self.convert_keys_to_store_keys(attributes, store_key_mapping)
71
- attributes.each_with_object({}) do |(name, value), new_attributes|
72
- store_key = store_key_mapping[name.to_s]
73
- new_attributes[store_key] = value
74
- end
75
- end
76
6
 
77
7
  included do
78
8
  scope(:jsonb_contains, lambda do |column_name, attributes|
79
- JsonbAccessor::QueryBuilder.validate_column_name!(all, column_name)
9
+ JsonbAccessor::QueryHelper.validate_column_name!(all, column_name)
80
10
  where("#{table_name}.#{column_name} @> (?)::jsonb", attributes.to_json)
81
11
  end)
82
12
 
83
13
  scope(:jsonb_excludes, lambda do |column_name, attributes|
84
- JsonbAccessor::QueryBuilder.validate_column_name!(all, column_name)
14
+ JsonbAccessor::QueryHelper.validate_column_name!(all, column_name)
85
15
  where.not("#{table_name}.#{column_name} @> (?)::jsonb", attributes.to_json)
86
16
  end)
87
17
 
88
18
  scope(:jsonb_number_where, lambda do |column_name, field_name, given_operator, value|
89
- JsonbAccessor::QueryBuilder.validate_column_name!(all, column_name)
90
- operator = JsonbAccessor::NUMBER_OPERATORS_MAP.fetch(given_operator.to_s)
19
+ JsonbAccessor::QueryHelper.validate_column_name!(all, column_name)
20
+ operator = JsonbAccessor::QueryHelper::NUMBER_OPERATORS_MAP.fetch(given_operator.to_s)
91
21
  where("(#{table_name}.#{column_name} ->> ?)::float #{operator} ?", field_name, value)
92
22
  end)
93
23
 
94
24
  scope(:jsonb_number_where_not, lambda do |column_name, field_name, given_operator, value|
95
- JsonbAccessor::QueryBuilder.validate_column_name!(all, column_name)
96
- operator = JsonbAccessor::NUMBER_OPERATORS_MAP.fetch(given_operator.to_s)
25
+ JsonbAccessor::QueryHelper.validate_column_name!(all, column_name)
26
+ operator = JsonbAccessor::QueryHelper::NUMBER_OPERATORS_MAP.fetch(given_operator.to_s)
97
27
  where.not("(#{table_name}.#{column_name} ->> ?)::float #{operator} ?", field_name, value)
98
28
  end)
99
29
 
100
30
  scope(:jsonb_time_where, lambda do |column_name, field_name, given_operator, value|
101
- JsonbAccessor::QueryBuilder.validate_column_name!(all, column_name)
102
- operator = JsonbAccessor::TIME_OPERATORS_MAP.fetch(given_operator.to_s)
31
+ JsonbAccessor::QueryHelper.validate_column_name!(all, column_name)
32
+ operator = JsonbAccessor::QueryHelper::TIME_OPERATORS_MAP.fetch(given_operator.to_s)
103
33
  where("(#{table_name}.#{column_name} ->> ?)::timestamp #{operator} ?", field_name, value)
104
34
  end)
105
35
 
106
36
  scope(:jsonb_time_where_not, lambda do |column_name, field_name, given_operator, value|
107
- JsonbAccessor::QueryBuilder.validate_column_name!(all, column_name)
108
- operator = JsonbAccessor::TIME_OPERATORS_MAP.fetch(given_operator.to_s)
37
+ JsonbAccessor::QueryHelper.validate_column_name!(all, column_name)
38
+ operator = JsonbAccessor::QueryHelper::TIME_OPERATORS_MAP.fetch(given_operator.to_s)
109
39
  where.not("(#{table_name}.#{column_name} ->> ?)::timestamp #{operator} ?", field_name, value)
110
40
  end)
111
41
 
@@ -113,11 +43,10 @@ module JsonbAccessor
113
43
  query = all
114
44
  contains_attributes = {}
115
45
 
116
- attributes.each do |name, value|
117
- case value
118
- when IS_NUMBER_QUERY_ARGUMENTS
46
+ JsonbAccessor::QueryHelper.convert_ranges(attributes).each do |name, value|
47
+ if JsonbAccessor::QueryHelper.number_query_arguments?(value)
119
48
  value.each { |operator, query_value| query = query.jsonb_number_where(column_name, name, operator, query_value) }
120
- when IS_TIME_QUERY_ARGUMENTS
49
+ elsif JsonbAccessor::QueryHelper.time_query_arguments?(value)
121
50
  value.each { |operator, query_value| query = query.jsonb_time_where(column_name, name, operator, query_value) }
122
51
  else
123
52
  contains_attributes[name] = value
@@ -132,10 +61,13 @@ module JsonbAccessor
132
61
  excludes_attributes = {}
133
62
 
134
63
  attributes.each do |name, value|
135
- case value
136
- when IS_NUMBER_QUERY_ARGUMENTS
64
+ if value.is_a?(Range)
65
+ raise JsonbAccessor::QueryHelper::NotSupported, "`jsonb_where_not` scope does not accept ranges as arguments. Given `#{value}` for `#{name}` field"
66
+ end
67
+
68
+ if JsonbAccessor::QueryHelper.number_query_arguments?(value)
137
69
  value.each { |operator, query_value| query = query.jsonb_number_where_not(column_name, name, operator, query_value) }
138
- when IS_TIME_QUERY_ARGUMENTS
70
+ elsif JsonbAccessor::QueryHelper.time_query_arguments?(value)
139
71
  value.each { |operator, query_value| query = query.jsonb_time_where_not(column_name, name, operator, query_value) }
140
72
  else
141
73
  excludes_attributes[name] = value
@@ -146,10 +78,10 @@ module JsonbAccessor
146
78
  end)
147
79
 
148
80
  scope(:jsonb_order, lambda do |column_name, field_name, direction|
149
- JsonbAccessor::QueryBuilder.validate_column_name!(all, column_name)
150
- JsonbAccessor::QueryBuilder.validate_field_name!(all, column_name, field_name)
151
- JsonbAccessor::QueryBuilder.validate_direction!(direction)
152
- order("(#{table_name}.#{column_name} -> '#{field_name}') #{direction}")
81
+ JsonbAccessor::QueryHelper.validate_column_name!(all, column_name)
82
+ JsonbAccessor::QueryHelper.validate_field_name!(all, column_name, field_name)
83
+ JsonbAccessor::QueryHelper.validate_direction!(direction)
84
+ order(Arel.sql("(#{table_name}.#{column_name} -> '#{field_name}') #{direction}"))
153
85
  end)
154
86
  end
155
87
  end
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JsonbAccessor
4
+ module QueryHelper
5
+ # Errors
6
+ InvalidColumnName = Class.new(StandardError)
7
+ InvalidFieldName = Class.new(StandardError)
8
+ InvalidDirection = Class.new(StandardError)
9
+ NotSupported = Class.new(StandardError)
10
+
11
+ # Constants
12
+ GREATER_THAN = ">"
13
+ GREATER_THAN_OR_EQUAL_TO = ">="
14
+ LESS_THAN = "<"
15
+ LESS_THAN_OR_EQUAL_TO = "<="
16
+
17
+ NUMBER_OPERATORS_MAP = {
18
+ GREATER_THAN => GREATER_THAN,
19
+ "greater_than" => GREATER_THAN,
20
+ "gt" => GREATER_THAN,
21
+ GREATER_THAN_OR_EQUAL_TO => GREATER_THAN_OR_EQUAL_TO,
22
+ "greater_than_or_equal_to" => GREATER_THAN_OR_EQUAL_TO,
23
+ "gte" => GREATER_THAN_OR_EQUAL_TO,
24
+ LESS_THAN => LESS_THAN,
25
+ "less_than" => LESS_THAN,
26
+ "lt" => LESS_THAN,
27
+ LESS_THAN_OR_EQUAL_TO => LESS_THAN_OR_EQUAL_TO,
28
+ "less_than_or_equal_to" => LESS_THAN_OR_EQUAL_TO,
29
+ "lte" => LESS_THAN_OR_EQUAL_TO
30
+ }.freeze
31
+
32
+ NUMBER_OPERATORS = NUMBER_OPERATORS_MAP.keys.freeze
33
+
34
+ TIME_OPERATORS_MAP = {
35
+ "after" => GREATER_THAN,
36
+ "before" => LESS_THAN
37
+ }.freeze
38
+
39
+ TIME_OPERATORS = TIME_OPERATORS_MAP.keys.freeze
40
+
41
+ ORDER_DIRECTIONS = [:asc, :desc, "asc", "desc"].freeze
42
+
43
+ class << self
44
+ def validate_column_name!(query, column_name)
45
+ if query.model.columns.none? { |column| column.name == column_name.to_s }
46
+ raise InvalidColumnName, "a column named `#{column_name}` does not exist on the `#{query.model.table_name}` table"
47
+ end
48
+ end
49
+
50
+ def validate_field_name!(query, column_name, field_name)
51
+ store_keys = query.model.public_send("jsonb_store_key_mapping_for_#{column_name}").values
52
+ if store_keys.exclude?(field_name.to_s)
53
+ valid_field_names = store_keys.map { |key| "`#{key}`" }.join(", ")
54
+ raise InvalidFieldName, "`#{field_name}` is not a valid field name, valid field names include: #{valid_field_names}"
55
+ end
56
+ end
57
+
58
+ def validate_direction!(option)
59
+ if ORDER_DIRECTIONS.exclude?(option)
60
+ raise InvalidDirection, "`#{option}` is not a valid direction for ordering, only `asc` and `desc` are accepted"
61
+ end
62
+ end
63
+
64
+ def convert_keys_to_store_keys(attributes, store_key_mapping)
65
+ attributes.each_with_object({}) do |(name, value), new_attributes|
66
+ store_key = store_key_mapping[name.to_s]
67
+ new_attributes[store_key] = value
68
+ end
69
+ end
70
+
71
+ def number_query_arguments?(arg)
72
+ arg.is_a?(Hash) && arg.keys.map(&:to_s).all? { |key| NUMBER_OPERATORS.include?(key) }
73
+ end
74
+
75
+ def time_query_arguments?(arg)
76
+ arg.is_a?(Hash) && arg.keys.map(&:to_s).all? { |key| TIME_OPERATORS.include?(key) }
77
+ end
78
+
79
+ def convert_number_ranges(attributes)
80
+ attributes.each_with_object({}) do |(name, value), new_attributes|
81
+ is_range = value.is_a?(Range)
82
+
83
+ new_attributes[name] = if is_range && value.first.is_a?(Numeric) && value.exclude_end?
84
+ { greater_than_or_equal_to: value.first, less_than: value.end }
85
+ elsif is_range && value.first.is_a?(Numeric)
86
+ { greater_than_or_equal_to: value.first, less_than_or_equal_to: value.end }
87
+ else
88
+ value
89
+ end
90
+ end
91
+ end
92
+
93
+ def convert_time_ranges(attributes)
94
+ attributes.each_with_object({}) do |(name, value), new_attributes|
95
+ is_range = value.is_a?(Range)
96
+
97
+ if is_range && (value.first.is_a?(Time) || value.first.is_a?(Date))
98
+ start_time = value.first
99
+ end_time = value.end
100
+ new_attributes[name] = { before: end_time, after: start_time }
101
+ else
102
+ new_attributes[name] = value
103
+ end
104
+ end
105
+ end
106
+
107
+ def convert_ranges(attributes)
108
+ %i[convert_number_ranges convert_time_ranges].reduce(attributes) do |new_attributes, converter_method|
109
+ public_send(converter_method, new_attributes)
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module JsonbAccessor
3
- VERSION = "1.0.0.beta.4"
4
+ VERSION = "1.1.0"
4
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jsonb_accessor
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.beta.4
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Crismali
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: exe
12
12
  cert_chain: []
13
- date: 2017-04-28 00:00:00.000000000 Z
13
+ date: 2020-06-29 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activerecord
@@ -26,6 +26,20 @@ dependencies:
26
26
  - - ">="
27
27
  - !ruby/object:Gem::Version
28
28
  version: '5.0'
29
+ - !ruby/object:Gem::Dependency
30
+ name: activesupport
31
+ requirement: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - ">="
34
+ - !ruby/object:Gem::Version
35
+ version: '5.0'
36
+ type: :runtime
37
+ prerelease: false
38
+ version_requirements: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: '5.0'
29
43
  - !ruby/object:Gem::Dependency
30
44
  name: pg
31
45
  requirement: !ruby/object:Gem::Requirement
@@ -44,44 +58,44 @@ dependencies:
44
58
  name: appraisal
45
59
  requirement: !ruby/object:Gem::Requirement
46
60
  requirements:
47
- - - ">="
61
+ - - "~>"
48
62
  - !ruby/object:Gem::Version
49
- version: '0'
63
+ version: 2.2.0
50
64
  type: :development
51
65
  prerelease: false
52
66
  version_requirements: !ruby/object:Gem::Requirement
53
67
  requirements:
54
- - - ">="
68
+ - - "~>"
55
69
  - !ruby/object:Gem::Version
56
- version: '0'
70
+ version: 2.2.0
57
71
  - !ruby/object:Gem::Dependency
58
72
  name: bundler
59
73
  requirement: !ruby/object:Gem::Requirement
60
74
  requirements:
61
75
  - - "~>"
62
76
  - !ruby/object:Gem::Version
63
- version: '1.9'
77
+ version: 2.1.4
64
78
  type: :development
65
79
  prerelease: false
66
80
  version_requirements: !ruby/object:Gem::Requirement
67
81
  requirements:
68
82
  - - "~>"
69
83
  - !ruby/object:Gem::Version
70
- version: '1.9'
84
+ version: 2.1.4
71
85
  - !ruby/object:Gem::Dependency
72
86
  name: database_cleaner
73
87
  requirement: !ruby/object:Gem::Requirement
74
88
  requirements:
75
- - - ">="
89
+ - - "~>"
76
90
  - !ruby/object:Gem::Version
77
- version: '0'
91
+ version: 1.6.0
78
92
  type: :development
79
93
  prerelease: false
80
94
  version_requirements: !ruby/object:Gem::Requirement
81
95
  requirements:
82
- - - ">="
96
+ - - "~>"
83
97
  - !ruby/object:Gem::Version
84
- version: '0'
98
+ version: 1.6.0
85
99
  - !ruby/object:Gem::Dependency
86
100
  name: awesome_print
87
101
  requirement: !ruby/object:Gem::Requirement
@@ -142,75 +156,61 @@ dependencies:
142
156
  name: rake
143
157
  requirement: !ruby/object:Gem::Requirement
144
158
  requirements:
145
- - - "~>"
159
+ - - ">="
146
160
  - !ruby/object:Gem::Version
147
- version: '10.0'
161
+ version: 12.3.3
148
162
  type: :development
149
163
  prerelease: false
150
164
  version_requirements: !ruby/object:Gem::Requirement
151
165
  requirements:
152
- - - "~>"
166
+ - - ">="
153
167
  - !ruby/object:Gem::Version
154
- version: '10.0'
168
+ version: 12.3.3
155
169
  - !ruby/object:Gem::Dependency
156
170
  name: rspec
157
171
  requirement: !ruby/object:Gem::Requirement
158
172
  requirements:
159
173
  - - "~>"
160
174
  - !ruby/object:Gem::Version
161
- version: '3.2'
175
+ version: 3.6.0
162
176
  type: :development
163
177
  prerelease: false
164
178
  version_requirements: !ruby/object:Gem::Requirement
165
179
  requirements:
166
180
  - - "~>"
167
181
  - !ruby/object:Gem::Version
168
- version: '3.2'
182
+ version: 3.6.0
169
183
  - !ruby/object:Gem::Dependency
170
184
  name: rubocop
171
185
  requirement: !ruby/object:Gem::Requirement
172
186
  requirements:
173
187
  - - "~>"
174
188
  - !ruby/object:Gem::Version
175
- version: '0.31'
189
+ version: 0.48.1
176
190
  type: :development
177
191
  prerelease: false
178
192
  version_requirements: !ruby/object:Gem::Requirement
179
193
  requirements:
180
194
  - - "~>"
181
195
  - !ruby/object:Gem::Version
182
- version: '0.31'
183
- - !ruby/object:Gem::Dependency
184
- name: shoulda-matchers
185
- requirement: !ruby/object:Gem::Requirement
186
- requirements:
187
- - - ">="
188
- - !ruby/object:Gem::Version
189
- version: '0'
190
- type: :development
191
- prerelease: false
192
- version_requirements: !ruby/object:Gem::Requirement
193
- requirements:
194
- - - ">="
195
- - !ruby/object:Gem::Version
196
- version: '0'
196
+ version: 0.48.1
197
197
  - !ruby/object:Gem::Dependency
198
198
  name: standalone_migrations
199
199
  requirement: !ruby/object:Gem::Requirement
200
200
  requirements:
201
- - - ">="
201
+ - - "~>"
202
202
  - !ruby/object:Gem::Version
203
- version: '0'
203
+ version: 5.2.0
204
204
  type: :development
205
205
  prerelease: false
206
206
  version_requirements: !ruby/object:Gem::Requirement
207
207
  requirements:
208
- - - ">="
208
+ - - "~>"
209
209
  - !ruby/object:Gem::Version
210
- version: '0'
210
+ version: 5.2.0
211
211
  description: Adds typed jsonb backed fields to your ActiveRecord models.
212
212
  email:
213
- - michael.crismali@gmail.com
213
+ - michael@crismali.com
214
214
  - joe@devmynd.com
215
215
  - jason@haruska.com
216
216
  executables: []
@@ -235,11 +235,12 @@ files:
235
235
  - db/migrate/20150407031737_set_up_testing_db.rb
236
236
  - db/schema.rb
237
237
  - gemfiles/activerecord_5.0.0.gemfile
238
- - gemfiles/activerecord_5.0.0.gemfile.lock
238
+ - gemfiles/activerecord_5.1.0.gemfile
239
239
  - jsonb_accessor.gemspec
240
240
  - lib/jsonb_accessor.rb
241
241
  - lib/jsonb_accessor/macro.rb
242
242
  - lib/jsonb_accessor/query_builder.rb
243
+ - lib/jsonb_accessor/query_helper.rb
243
244
  - lib/jsonb_accessor/version.rb
244
245
  homepage: https://github.com/devmynd/jsonb_accessor
245
246
  licenses:
@@ -256,12 +257,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
256
257
  version: '2.0'
257
258
  required_rubygems_version: !ruby/object:Gem::Requirement
258
259
  requirements:
259
- - - ">"
260
+ - - ">="
260
261
  - !ruby/object:Gem::Version
261
- version: 1.3.1
262
+ version: '0'
262
263
  requirements: []
263
264
  rubyforge_project:
264
- rubygems_version: 2.5.1
265
+ rubygems_version: 2.6.11
265
266
  signing_key:
266
267
  specification_version: 4
267
268
  summary: Adds typed jsonb backed fields to your ActiveRecord models.
@@ -1,140 +0,0 @@
1
- PATH
2
- remote: ..
3
- specs:
4
- jsonb_accessor (1.0.0.beta.4)
5
- activerecord (>= 5.0)
6
- pg (>= 0.18.1)
7
-
8
- GEM
9
- remote: https://rubygems.org/
10
- specs:
11
- actionpack (5.0.2)
12
- actionview (= 5.0.2)
13
- activesupport (= 5.0.2)
14
- rack (~> 2.0)
15
- rack-test (~> 0.6.3)
16
- rails-dom-testing (~> 2.0)
17
- rails-html-sanitizer (~> 1.0, >= 1.0.2)
18
- actionview (5.0.2)
19
- activesupport (= 5.0.2)
20
- builder (~> 3.1)
21
- erubis (~> 2.7.0)
22
- rails-dom-testing (~> 2.0)
23
- rails-html-sanitizer (~> 1.0, >= 1.0.3)
24
- activemodel (5.0.2)
25
- activesupport (= 5.0.2)
26
- activerecord (5.0.2)
27
- activemodel (= 5.0.2)
28
- activesupport (= 5.0.2)
29
- arel (~> 7.0)
30
- activesupport (5.0.2)
31
- concurrent-ruby (~> 1.0, >= 1.0.2)
32
- i18n (~> 0.7)
33
- minitest (~> 5.1)
34
- tzinfo (~> 1.1)
35
- appraisal (2.1.0)
36
- bundler
37
- rake
38
- thor (>= 0.14.0)
39
- arel (7.1.4)
40
- ast (2.3.0)
41
- awesome_print (1.7.0)
42
- builder (3.2.3)
43
- coderay (1.1.1)
44
- concurrent-ruby (1.0.5)
45
- database_cleaner (1.5.3)
46
- diff-lcs (1.3)
47
- erubis (2.7.0)
48
- i18n (0.8.1)
49
- loofah (2.0.3)
50
- nokogiri (>= 1.5.9)
51
- method_source (0.8.2)
52
- mini_portile2 (2.1.0)
53
- minitest (5.10.1)
54
- nokogiri (1.7.0.1)
55
- mini_portile2 (~> 2.1.0)
56
- parser (2.4.0.0)
57
- ast (~> 2.2)
58
- pg (0.20.0)
59
- powerpack (0.1.1)
60
- pry (0.10.4)
61
- coderay (~> 1.1.0)
62
- method_source (~> 0.8.1)
63
- slop (~> 3.4)
64
- pry-doc (0.10.0)
65
- pry (~> 0.9)
66
- yard (~> 0.9)
67
- pry-nav (0.2.4)
68
- pry (>= 0.9.10, < 0.11.0)
69
- rack (2.0.1)
70
- rack-test (0.6.3)
71
- rack (>= 1.0)
72
- rails-dom-testing (2.0.2)
73
- activesupport (>= 4.2.0, < 6.0)
74
- nokogiri (~> 1.6)
75
- rails-html-sanitizer (1.0.3)
76
- loofah (~> 2.0)
77
- railties (5.0.2)
78
- actionpack (= 5.0.2)
79
- activesupport (= 5.0.2)
80
- method_source
81
- rake (>= 0.8.7)
82
- thor (>= 0.18.1, < 2.0)
83
- rainbow (2.2.1)
84
- rake (10.5.0)
85
- rspec (3.5.0)
86
- rspec-core (~> 3.5.0)
87
- rspec-expectations (~> 3.5.0)
88
- rspec-mocks (~> 3.5.0)
89
- rspec-core (3.5.4)
90
- rspec-support (~> 3.5.0)
91
- rspec-expectations (3.5.0)
92
- diff-lcs (>= 1.2.0, < 2.0)
93
- rspec-support (~> 3.5.0)
94
- rspec-mocks (3.5.0)
95
- diff-lcs (>= 1.2.0, < 2.0)
96
- rspec-support (~> 3.5.0)
97
- rspec-support (3.5.0)
98
- rubocop (0.47.1)
99
- parser (>= 2.3.3.1, < 3.0)
100
- powerpack (~> 0.1)
101
- rainbow (>= 1.99.1, < 3.0)
102
- ruby-progressbar (~> 1.7)
103
- unicode-display_width (~> 1.0, >= 1.0.1)
104
- ruby-progressbar (1.8.1)
105
- shoulda-matchers (3.1.1)
106
- activesupport (>= 4.0.0)
107
- slop (3.6.0)
108
- standalone_migrations (5.0.0)
109
- activerecord (>= 4.2.7, < 5.1.0)
110
- railties (>= 4.2.7, < 5.1.0)
111
- rake (~> 10.0)
112
- thor (0.19.4)
113
- thread_safe (0.3.6)
114
- tzinfo (1.2.2)
115
- thread_safe (~> 0.1)
116
- unicode-display_width (1.1.3)
117
- yard (0.9.8)
118
-
119
- PLATFORMS
120
- ruby
121
-
122
- DEPENDENCIES
123
- activerecord (~> 5.0.0)
124
- appraisal
125
- awesome_print
126
- bundler (~> 1.9)
127
- database_cleaner
128
- jsonb_accessor!
129
- pg
130
- pry
131
- pry-doc
132
- pry-nav
133
- rake (~> 10.0)
134
- rspec (~> 3.2)
135
- rubocop (~> 0.31)
136
- shoulda-matchers
137
- standalone_migrations
138
-
139
- BUNDLED WITH
140
- 1.14.6