jsonb_accessor 1.0.0.beta.5 → 1.2.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: a66f5adf90e5c66c0cacb04698102c37fbd69b27
4
- data.tar.gz: dcee9f04338256069b16e0078bbd21ee34264b7a
3
+ metadata.gz: 80ed7aa0b3f529122a97b2a00b4f81011ef18014
4
+ data.tar.gz: 407d572cea056870ce66d7b92c89d1b3e15bdb2d
5
5
  SHA512:
6
- metadata.gz: eb1b146937ec6624866dc4f8594d25889eb9fd7a92de1610242048e6c5488050b0be7cbe8e9f2f887a07ed45eeb97308071c3889023eb2a3074f8e01928c91a6
7
- data.tar.gz: f8d8fad1f5adea8b6d71b8512991a838f4a8dde52c4b9ca83b03facfab2d00f946d3a2a9e402c18d0f3f44bc2de53fa624e57e2531e2fe1b095c2766561c2ed6
6
+ metadata.gz: be4ffe5fc2318edc84b98ffee5296d30d2a9d03817540c4622f04875d0a41c19022c19c0c998357f6730cad37103ce79c43b46c5aa08a46dce8078b77f03752d
7
+ data.tar.gz: c02e7189b7354d5d112aa9414e9cb4e5329d57b97b75b4d07a7e64da1abab04c63a0419509335fd557728eb48df1183d2a2d00c9c84ed7216ac4461205ec1c4d
data/.gitignore CHANGED
@@ -8,3 +8,4 @@
8
8
  /spec/reports/
9
9
  /tmp/
10
10
  gemfiles/.bundle
11
+ gemfiles/*.gemfile.lock
data/.rubocop.yml CHANGED
@@ -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
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 2.3.1
1
+ 2.4.1
data/.travis.yml CHANGED
@@ -1,16 +1,19 @@
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
- postgresql: '9.4'
8
+ postgresql: "9.4"
7
9
  before_install:
8
10
  - gem update --system
9
11
  - gem --version
10
- - gem install bundler
12
+ - gem install bundler:2.1.4
11
13
  before_script:
12
14
  - bundle exec rake db:create
13
15
  - bundle exec rake db:migrate
16
+ install: bundle _2.1.4_ install --jobs=3 --retry=3 --path=${BUNDLE_PATH:-vendor/bundle}
14
17
  cache: bundler
15
18
  gemfile:
16
19
  - gemfiles/activerecord_5.0.0.gemfile
data/Appraisals CHANGED
@@ -1,11 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  appraise "activerecord-5.0.0" do
4
- gemspec
5
4
  gem "activerecord", "~> 5.0.0"
6
5
  end
7
6
 
8
7
  appraise "activerecord-5.1.0" do
9
- gemspec
10
- gem "activerecord", "~> 5.1.0"
8
+ gem "activerecord", "~> 5.1.3"
11
9
  end
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.5"
28
+ gem "jsonb_accessor", "~> 1"
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'`)
@@ -6,4 +6,3 @@ gem "pg"
6
6
  gem "activerecord", "~> 5.0.0"
7
7
 
8
8
  gemspec path: "../"
9
- gemspec path: "../"
@@ -3,7 +3,6 @@
3
3
  source "https://rubygems.org"
4
4
 
5
5
  gem "pg"
6
- gem "activerecord", "~> 5.1.0"
6
+ gem "activerecord", "~> 5.1.3"
7
7
 
8
8
  gemspec path: "../"
9
- gemspec path: "../"
@@ -23,18 +23,18 @@ Gem::Specification.new do |spec|
23
23
  spec.require_paths = ["lib"]
24
24
 
25
25
  spec.add_dependency "activerecord", ">= 5.0"
26
+ spec.add_dependency "activesupport", ">= 5.0"
26
27
  spec.add_dependency "pg", ">= 0.18.1"
27
28
 
28
- spec.add_development_dependency "appraisal"
29
- spec.add_development_dependency "bundler", "~> 1.9"
30
- spec.add_development_dependency "database_cleaner", "~> 1.5.3"
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"
31
32
  spec.add_development_dependency "awesome_print"
32
33
  spec.add_development_dependency "pry"
33
34
  spec.add_development_dependency "pry-doc"
34
35
  spec.add_development_dependency "pry-nav"
35
- spec.add_development_dependency "rake", "~> 10.0"
36
- spec.add_development_dependency "rspec", "~> 3.2"
36
+ spec.add_development_dependency "rake", ">= 12.3.3"
37
+ spec.add_development_dependency "rspec", "~> 3.6.0"
37
38
  spec.add_development_dependency "rubocop", "~> 0.48.1"
38
- spec.add_development_dependency "shoulda-matchers"
39
- spec.add_development_dependency "standalone_migrations"
39
+ spec.add_development_dependency "standalone_migrations", "~> 5.2.0"
40
40
  end
@@ -6,6 +6,7 @@ require "active_record/connection_adapters/postgresql_adapter"
6
6
 
7
7
  require "jsonb_accessor/version"
8
8
  require "jsonb_accessor/macro"
9
+ require "jsonb_accessor/query_helper"
9
10
  require "jsonb_accessor/query_builder"
10
11
 
11
12
  module JsonbAccessor
@@ -11,7 +11,11 @@ module JsonbAccessor
11
11
 
12
12
  # Defines virtual attributes for each jsonb field.
13
13
  field_types.each do |name, type|
14
- attribute name, *type
14
+ next attribute name, type unless type.is_a?(Array)
15
+ next attribute name, *type unless type.last.is_a?(Hash)
16
+
17
+ *args, keyword_args = type
18
+ attribute name, *args, **keyword_args
15
19
  end
16
20
 
17
21
  store_key_mapping_method_name = "jsonb_store_key_mapping_for_#{jsonb_attribute}"
@@ -34,7 +38,7 @@ module JsonbAccessor
34
38
  end
35
39
 
36
40
  # Get store keys to default values mapping
37
- store_keys_and_defaults = ::JsonbAccessor::QueryBuilder.convert_keys_to_store_keys(names_and_defaults, public_send(store_key_mapping_method_name))
41
+ store_keys_and_defaults = ::JsonbAccessor::QueryHelper.convert_keys_to_store_keys(names_and_defaults, public_send(store_key_mapping_method_name))
38
42
 
39
43
  # Define jsonb_defaults_mapping_for_<jsonb_attribute>
40
44
  defaults_mapping_method_name = "jsonb_defaults_mapping_for_#{jsonb_attribute}"
@@ -46,7 +50,14 @@ module JsonbAccessor
46
50
  end
47
51
 
48
52
  all_defaults_mapping = public_send(defaults_mapping_method_name)
49
- attribute jsonb_attribute, :jsonb, default: all_defaults_mapping if all_defaults_mapping.present?
53
+ # Fields may have procs as default value. This means `all_defaults_mapping` may contain procs as values. To make this work
54
+ # with the attributes API, we need to wrap `all_defaults_mapping` with a proc itself, making sure it returns a plain hash
55
+ # each time it is evaluated.
56
+ all_defaults_mapping_proc =
57
+ if all_defaults_mapping.present?
58
+ -> { all_defaults_mapping.map { |key, value| [key, value.respond_to?(:call) ? value.call : value] }.to_h }
59
+ end
60
+ attribute jsonb_attribute, :jsonb, default: all_defaults_mapping_proc if all_defaults_mapping_proc.present?
50
61
 
51
62
  # Setters are in a module to allow users to override them and still be able to use `super`.
52
63
  setters = Module.new do
@@ -67,7 +78,7 @@ module JsonbAccessor
67
78
  empty_store_key_attributes = names_to_store_keys.values.each_with_object({}) { |name, defaults| defaults[name] = nil }
68
79
  empty_named_attributes = names_to_store_keys.keys.each_with_object({}) { |name, defaults| defaults[name] = nil }
69
80
 
70
- store_key_attributes = ::JsonbAccessor::QueryBuilder.convert_keys_to_store_keys(value, names_to_store_keys)
81
+ store_key_attributes = ::JsonbAccessor::QueryHelper.convert_keys_to_store_keys(value, names_to_store_keys)
71
82
  write_attribute(jsonb_attribute, empty_store_key_attributes.merge(store_key_attributes))
72
83
 
73
84
  empty_named_attributes.merge(value).each { |name, attribute_value| write_attribute(name, attribute_value) }
@@ -81,28 +92,37 @@ module JsonbAccessor
81
92
  jsonb_values = public_send(jsonb_attribute) || {}
82
93
  jsonb_values.each do |store_key, value|
83
94
  name = names_and_store_keys.key(store_key)
84
- write_attribute(name, value) if name
95
+ next unless name
96
+
97
+ write_attribute(name, value)
98
+ clear_attribute_change(name) if persisted?
85
99
  end
86
- clear_changes_information if persisted?
87
100
  end
88
101
  end
89
102
 
90
103
  # <jsonb_attribute>_where scope
91
104
  scope("#{jsonb_attribute}_where", lambda do |attributes|
92
- store_key_attributes = ::JsonbAccessor::QueryBuilder.convert_keys_to_store_keys(attributes, all.model.public_send(store_key_mapping_method_name))
105
+ store_key_attributes = ::JsonbAccessor::QueryHelper.convert_keys_to_store_keys(attributes, all.model.public_send(store_key_mapping_method_name))
93
106
  jsonb_where(jsonb_attribute, store_key_attributes)
94
107
  end)
95
108
 
96
109
  # <jsonb_attribute>_where_not scope
97
110
  scope("#{jsonb_attribute}_where_not", lambda do |attributes|
98
- store_key_attributes = ::JsonbAccessor::QueryBuilder.convert_keys_to_store_keys(attributes, all.model.public_send(store_key_mapping_method_name))
111
+ store_key_attributes = ::JsonbAccessor::QueryHelper.convert_keys_to_store_keys(attributes, all.model.public_send(store_key_mapping_method_name))
99
112
  jsonb_where_not(jsonb_attribute, store_key_attributes)
100
113
  end)
101
114
 
102
115
  # <jsonb_attribute>_order scope
103
- scope("#{jsonb_attribute}_order", lambda do |attribute, direction = nil|
104
- key = all.model.public_send(store_key_mapping_method_name)[attribute.to_s]
105
- jsonb_order(jsonb_attribute, key, direction || :asc)
116
+ scope("#{jsonb_attribute}_order", lambda do |*args|
117
+ ordering_options = args.extract_options!
118
+ order_by_defaults = args.each_with_object({}) { |attribute, config| config[attribute] = :asc }
119
+ store_key_mapping = all.model.public_send(store_key_mapping_method_name)
120
+
121
+ order_by_defaults.merge(ordering_options).reduce(all) do |query, (name, direction)|
122
+ key = store_key_mapping[name.to_s]
123
+ order_query = jsonb_order(jsonb_attribute, key, direction)
124
+ query.merge(order_query)
125
+ end
106
126
  end)
107
127
  end
108
128
  end
@@ -1,112 +1,41 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JsonbAccessor
4
- GREATER_THAN = ">"
5
- GREATER_THAN_OR_EQUAL_TO = ">="
6
- LESS_THAN = "<"
7
- LESS_THAN_OR_EQUAL_TO = "<="
8
-
9
- NUMBER_OPERATORS_MAP = {
10
- GREATER_THAN => GREATER_THAN,
11
- "greater_than" => GREATER_THAN,
12
- "gt" => GREATER_THAN,
13
- GREATER_THAN_OR_EQUAL_TO => GREATER_THAN_OR_EQUAL_TO,
14
- "greater_than_or_equal_to" => GREATER_THAN_OR_EQUAL_TO,
15
- "gte" => GREATER_THAN_OR_EQUAL_TO,
16
- LESS_THAN => LESS_THAN,
17
- "less_than" => LESS_THAN,
18
- "lt" => LESS_THAN,
19
- LESS_THAN_OR_EQUAL_TO => LESS_THAN_OR_EQUAL_TO,
20
- "less_than_or_equal_to" => LESS_THAN_OR_EQUAL_TO,
21
- "lte" => LESS_THAN_OR_EQUAL_TO
22
- }.freeze
23
-
24
- NUMBER_OPERATORS = NUMBER_OPERATORS_MAP.keys.freeze
25
-
26
- TIME_OPERATORS_MAP = {
27
- "after" => GREATER_THAN,
28
- "before" => LESS_THAN
29
- }.freeze
30
-
31
- TIME_OPERATORS = TIME_OPERATORS_MAP.keys.freeze
32
-
33
- IS_NUMBER_QUERY_ARGUMENTS = lambda do |arg|
34
- arg.is_a?(Hash) &&
35
- arg.keys.map(&:to_s).all? { |key| JsonbAccessor::NUMBER_OPERATORS.include?(key) }
36
- end
37
-
38
- IS_TIME_QUERY_ARGUMENTS = lambda do |arg|
39
- arg.is_a?(Hash) &&
40
- arg.keys.map(&:to_s).all? { |key| JsonbAccessor::TIME_OPERATORS.include?(key) }
41
- end
42
-
43
- ORDER_DIRECTIONS = [:asc, :desc, "asc", "desc"].freeze
44
-
45
4
  module QueryBuilder
46
5
  extend ActiveSupport::Concern
47
- InvalidColumnName = Class.new(StandardError)
48
- InvalidFieldName = Class.new(StandardError)
49
- InvalidDirection = Class.new(StandardError)
50
-
51
- def self.validate_column_name!(query, column_name)
52
- if query.model.columns.none? { |column| column.name == column_name.to_s }
53
- raise InvalidColumnName, "a column named `#{column_name}` does not exist on the `#{query.model.table_name}` table"
54
- end
55
- end
56
-
57
- def self.validate_field_name!(query, column_name, field_name)
58
- store_keys = query.model.public_send("jsonb_store_key_mapping_for_#{column_name}").values
59
- if store_keys.exclude?(field_name.to_s)
60
- valid_field_names = store_keys.map { |key| "`#{key}`" }.join(", ")
61
- raise InvalidFieldName, "`#{field_name}` is not a valid field name, valid field names include: #{valid_field_names}"
62
- end
63
- end
64
-
65
- def self.validate_direction!(option)
66
- if ORDER_DIRECTIONS.exclude?(option)
67
- raise InvalidDirection, "`#{option}` is not a valid direction for ordering, only `asc` and `desc` are accepted"
68
- end
69
- end
70
-
71
- def self.convert_keys_to_store_keys(attributes, store_key_mapping)
72
- attributes.each_with_object({}) do |(name, value), new_attributes|
73
- store_key = store_key_mapping[name.to_s]
74
- new_attributes[store_key] = value
75
- end
76
- end
77
6
 
78
7
  included do
79
8
  scope(:jsonb_contains, lambda do |column_name, attributes|
80
- JsonbAccessor::QueryBuilder.validate_column_name!(all, column_name)
9
+ JsonbAccessor::QueryHelper.validate_column_name!(all, column_name)
81
10
  where("#{table_name}.#{column_name} @> (?)::jsonb", attributes.to_json)
82
11
  end)
83
12
 
84
13
  scope(:jsonb_excludes, lambda do |column_name, attributes|
85
- JsonbAccessor::QueryBuilder.validate_column_name!(all, column_name)
14
+ JsonbAccessor::QueryHelper.validate_column_name!(all, column_name)
86
15
  where.not("#{table_name}.#{column_name} @> (?)::jsonb", attributes.to_json)
87
16
  end)
88
17
 
89
18
  scope(:jsonb_number_where, lambda do |column_name, field_name, given_operator, value|
90
- JsonbAccessor::QueryBuilder.validate_column_name!(all, column_name)
91
- 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)
92
21
  where("(#{table_name}.#{column_name} ->> ?)::float #{operator} ?", field_name, value)
93
22
  end)
94
23
 
95
24
  scope(:jsonb_number_where_not, lambda do |column_name, field_name, given_operator, value|
96
- JsonbAccessor::QueryBuilder.validate_column_name!(all, column_name)
97
- 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)
98
27
  where.not("(#{table_name}.#{column_name} ->> ?)::float #{operator} ?", field_name, value)
99
28
  end)
100
29
 
101
30
  scope(:jsonb_time_where, lambda do |column_name, field_name, given_operator, value|
102
- JsonbAccessor::QueryBuilder.validate_column_name!(all, column_name)
103
- 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)
104
33
  where("(#{table_name}.#{column_name} ->> ?)::timestamp #{operator} ?", field_name, value)
105
34
  end)
106
35
 
107
36
  scope(:jsonb_time_where_not, lambda do |column_name, field_name, given_operator, value|
108
- JsonbAccessor::QueryBuilder.validate_column_name!(all, column_name)
109
- 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)
110
39
  where.not("(#{table_name}.#{column_name} ->> ?)::timestamp #{operator} ?", field_name, value)
111
40
  end)
112
41
 
@@ -114,11 +43,10 @@ module JsonbAccessor
114
43
  query = all
115
44
  contains_attributes = {}
116
45
 
117
- attributes.each do |name, value|
118
- case value
119
- when IS_NUMBER_QUERY_ARGUMENTS
46
+ JsonbAccessor::QueryHelper.convert_ranges(attributes).each do |name, value|
47
+ if JsonbAccessor::QueryHelper.number_query_arguments?(value)
120
48
  value.each { |operator, query_value| query = query.jsonb_number_where(column_name, name, operator, query_value) }
121
- when IS_TIME_QUERY_ARGUMENTS
49
+ elsif JsonbAccessor::QueryHelper.time_query_arguments?(value)
122
50
  value.each { |operator, query_value| query = query.jsonb_time_where(column_name, name, operator, query_value) }
123
51
  else
124
52
  contains_attributes[name] = value
@@ -133,10 +61,13 @@ module JsonbAccessor
133
61
  excludes_attributes = {}
134
62
 
135
63
  attributes.each do |name, value|
136
- case value
137
- 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)
138
69
  value.each { |operator, query_value| query = query.jsonb_number_where_not(column_name, name, operator, query_value) }
139
- when IS_TIME_QUERY_ARGUMENTS
70
+ elsif JsonbAccessor::QueryHelper.time_query_arguments?(value)
140
71
  value.each { |operator, query_value| query = query.jsonb_time_where_not(column_name, name, operator, query_value) }
141
72
  else
142
73
  excludes_attributes[name] = value
@@ -147,10 +78,10 @@ module JsonbAccessor
147
78
  end)
148
79
 
149
80
  scope(:jsonb_order, lambda do |column_name, field_name, direction|
150
- JsonbAccessor::QueryBuilder.validate_column_name!(all, column_name)
151
- JsonbAccessor::QueryBuilder.validate_field_name!(all, column_name, field_name)
152
- JsonbAccessor::QueryBuilder.validate_direction!(direction)
153
- 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}"))
154
85
  end)
155
86
  end
156
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,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JsonbAccessor
4
- VERSION = "1.0.0.beta.5"
4
+ VERSION = "1.2.0"
5
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.5
4
+ version: 1.2.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-05-03 00:00:00.000000000 Z
13
+ date: 2021-05-03 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: 1.5.3
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: 1.5.3
98
+ version: 1.6.0
85
99
  - !ruby/object:Gem::Dependency
86
100
  name: awesome_print
87
101
  requirement: !ruby/object:Gem::Requirement
@@ -142,30 +156,30 @@ 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
@@ -180,34 +194,20 @@ dependencies:
180
194
  - - "~>"
181
195
  - !ruby/object:Gem::Version
182
196
  version: 0.48.1
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'
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
213
  - michael@crismali.com
@@ -235,13 +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
239
238
  - gemfiles/activerecord_5.1.0.gemfile
240
- - gemfiles/activerecord_5.1.0.gemfile.lock
241
239
  - jsonb_accessor.gemspec
242
240
  - lib/jsonb_accessor.rb
243
241
  - lib/jsonb_accessor/macro.rb
244
242
  - lib/jsonb_accessor/query_builder.rb
243
+ - lib/jsonb_accessor/query_helper.rb
245
244
  - lib/jsonb_accessor/version.rb
246
245
  homepage: https://github.com/devmynd/jsonb_accessor
247
246
  licenses:
@@ -258,12 +257,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
258
257
  version: '2.0'
259
258
  required_rubygems_version: !ruby/object:Gem::Requirement
260
259
  requirements:
261
- - - ">"
260
+ - - ">="
262
261
  - !ruby/object:Gem::Version
263
- version: 1.3.1
262
+ version: '0'
264
263
  requirements: []
265
264
  rubyforge_project:
266
- rubygems_version: 2.5.1
265
+ rubygems_version: 2.6.11
267
266
  signing_key:
268
267
  specification_version: 4
269
268
  summary: Adds typed jsonb backed fields to your ActiveRecord models.
@@ -1,141 +0,0 @@
1
- PATH
2
- remote: ..
3
- specs:
4
- jsonb_accessor (1.0.0.beta.5)
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.2)
84
- rake
85
- rake (10.5.0)
86
- rspec (3.5.0)
87
- rspec-core (~> 3.5.0)
88
- rspec-expectations (~> 3.5.0)
89
- rspec-mocks (~> 3.5.0)
90
- rspec-core (3.5.4)
91
- rspec-support (~> 3.5.0)
92
- rspec-expectations (3.5.0)
93
- diff-lcs (>= 1.2.0, < 2.0)
94
- rspec-support (~> 3.5.0)
95
- rspec-mocks (3.5.0)
96
- diff-lcs (>= 1.2.0, < 2.0)
97
- rspec-support (~> 3.5.0)
98
- rspec-support (3.5.0)
99
- rubocop (0.48.1)
100
- parser (>= 2.3.3.1, < 3.0)
101
- powerpack (~> 0.1)
102
- rainbow (>= 1.99.1, < 3.0)
103
- ruby-progressbar (~> 1.7)
104
- unicode-display_width (~> 1.0, >= 1.0.1)
105
- ruby-progressbar (1.8.1)
106
- shoulda-matchers (3.1.1)
107
- activesupport (>= 4.0.0)
108
- slop (3.6.0)
109
- standalone_migrations (5.0.0)
110
- activerecord (>= 4.2.7, < 5.1.0)
111
- railties (>= 4.2.7, < 5.1.0)
112
- rake (~> 10.0)
113
- thor (0.19.4)
114
- thread_safe (0.3.6)
115
- tzinfo (1.2.2)
116
- thread_safe (~> 0.1)
117
- unicode-display_width (1.2.1)
118
- yard (0.9.8)
119
-
120
- PLATFORMS
121
- ruby
122
-
123
- DEPENDENCIES
124
- activerecord (~> 5.0.0)
125
- appraisal
126
- awesome_print
127
- bundler (~> 1.9)
128
- database_cleaner (~> 1.5.3)
129
- jsonb_accessor!
130
- pg
131
- pry
132
- pry-doc
133
- pry-nav
134
- rake (~> 10.0)
135
- rspec (~> 3.2)
136
- rubocop (~> 0.48.1)
137
- shoulda-matchers
138
- standalone_migrations
139
-
140
- BUNDLED WITH
141
- 1.14.6
@@ -1,141 +0,0 @@
1
- PATH
2
- remote: ..
3
- specs:
4
- jsonb_accessor (1.0.0.beta.5)
5
- activerecord (>= 5.0)
6
- pg (>= 0.18.1)
7
-
8
- GEM
9
- remote: https://rubygems.org/
10
- specs:
11
- actionpack (5.1.0)
12
- actionview (= 5.1.0)
13
- activesupport (= 5.1.0)
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.1.0)
19
- activesupport (= 5.1.0)
20
- builder (~> 3.1)
21
- erubi (~> 1.4)
22
- rails-dom-testing (~> 2.0)
23
- rails-html-sanitizer (~> 1.0, >= 1.0.3)
24
- activemodel (5.1.0)
25
- activesupport (= 5.1.0)
26
- activerecord (5.1.0)
27
- activemodel (= 5.1.0)
28
- activesupport (= 5.1.0)
29
- arel (~> 8.0)
30
- activesupport (5.1.0)
31
- concurrent-ruby (~> 1.0, >= 1.0.2)
32
- i18n (~> 0.7)
33
- minitest (~> 5.1)
34
- tzinfo (~> 1.1)
35
- appraisal (2.2.0)
36
- bundler
37
- rake
38
- thor (>= 0.14.0)
39
- arel (8.0.0)
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
- erubi (1.6.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.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.1.0)
78
- actionpack (= 5.1.0)
79
- activesupport (= 5.1.0)
80
- method_source
81
- rake (>= 0.8.7)
82
- thor (>= 0.18.1, < 2.0)
83
- rainbow (2.2.2)
84
- rake
85
- rake (10.5.0)
86
- rspec (3.5.0)
87
- rspec-core (~> 3.5.0)
88
- rspec-expectations (~> 3.5.0)
89
- rspec-mocks (~> 3.5.0)
90
- rspec-core (3.5.4)
91
- rspec-support (~> 3.5.0)
92
- rspec-expectations (3.5.0)
93
- diff-lcs (>= 1.2.0, < 2.0)
94
- rspec-support (~> 3.5.0)
95
- rspec-mocks (3.5.0)
96
- diff-lcs (>= 1.2.0, < 2.0)
97
- rspec-support (~> 3.5.0)
98
- rspec-support (3.5.0)
99
- rubocop (0.48.1)
100
- parser (>= 2.3.3.1, < 3.0)
101
- powerpack (~> 0.1)
102
- rainbow (>= 1.99.1, < 3.0)
103
- ruby-progressbar (~> 1.7)
104
- unicode-display_width (~> 1.0, >= 1.0.1)
105
- ruby-progressbar (1.8.1)
106
- shoulda-matchers (3.1.1)
107
- activesupport (>= 4.0.0)
108
- slop (3.6.0)
109
- standalone_migrations (5.2.1)
110
- activerecord (>= 4.2.7, < 5.2.0)
111
- railties (>= 4.2.7, < 5.2.0)
112
- rake (~> 10.0)
113
- thor (0.19.4)
114
- thread_safe (0.3.6)
115
- tzinfo (1.2.3)
116
- thread_safe (~> 0.1)
117
- unicode-display_width (1.2.1)
118
- yard (0.9.9)
119
-
120
- PLATFORMS
121
- ruby
122
-
123
- DEPENDENCIES
124
- activerecord (~> 5.1.0)
125
- appraisal
126
- awesome_print
127
- bundler (~> 1.9)
128
- database_cleaner (~> 1.5.3)
129
- jsonb_accessor!
130
- pg
131
- pry
132
- pry-doc
133
- pry-nav
134
- rake (~> 10.0)
135
- rspec (~> 3.2)
136
- rubocop (~> 0.48.1)
137
- shoulda-matchers
138
- standalone_migrations
139
-
140
- BUNDLED WITH
141
- 1.14.6