attr_json 0.4.0 → 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
  SHA256:
3
- metadata.gz: 05247aceb96f0df4ce9f36a4f6c56099700a33aeddcc0d2a9596330f0ecb23c9
4
- data.tar.gz: ed213264805ed42866d00c6e03e5504a63c96763ac1f83a3518302dbb5c51954
3
+ metadata.gz: be1175039b6fbd78d5a9c1bcaae7c4ce003d29f403ef1e86d71d3c226e659a0b
4
+ data.tar.gz: 588710deb7c2b6076ba1cf1094f9b4ab67fb7bd74cecfc8c560de5b0ef50eafb
5
5
  SHA512:
6
- metadata.gz: 193073a8b1ac277d2f062bb8fae958ec2be300e53216e29fd7af5cf8e70db96e39da92c23ee2f452907b31e55fca0966dff7dd6e45c270028dd2c400a4f53477
7
- data.tar.gz: d92e6761a2dfab4db80931a5af85bbad112631b12f0c62536fcd5ff532df41ffb475a0bea3cd10ac7d013b7ab4d779a19e2af980ab00fa320168715840e32fcc
6
+ metadata.gz: d71520892d16915465eab7c9e373f5e5ecabb8efee7a295da6532fbedd0c9817890da7e689e31f1bf1c7b3c1cad73f071f3563d7eb6d5637516de40174fce627
7
+ data.tar.gz: ea225c01754abfa5dec0e8749fd91ff68e1a329b54e684a27fee14655de7eb4eddf832538a961b14bbf83e222e0b90bfe554a8fdac001ca95aefb456353ed763
@@ -1,21 +1,64 @@
1
1
  #
2
- dist: trusty
3
2
  sudo: false
4
3
  addons:
5
4
  postgresql: '9.4'
6
5
  chrome: stable
7
6
  language: ruby
8
7
  cache: bundler
9
- rvm:
10
- - 2.4
11
- - 2.5.1
12
- gemfile:
13
- - gemfiles/rails_5_0.gemfile
14
- - gemfiles/rails_5_1.gemfile
15
- - gemfiles/rails_5_2.gemfile
16
- - gemfiles/rails_edge_6.gemfile
8
+
9
+ # rvm:
10
+ # - 2.4.5
11
+ # - 2.5.3
12
+ # - 2.6.5
13
+ # - 2.7.0
14
+ # gemfile:
15
+ # - gemfiles/rails_5_0.gemfile
16
+ # - gemfiles/rails_5_1.gemfile
17
+ # - gemfiles/rails_5_2.gemfile
18
+ # - gemfiles/rails_6_0.gemfile
19
+ # - gemfiles/rails_edge.gemfile
20
+
17
21
  before_install:
18
- - gem install bundler -v 1.14.6
22
+ - gem install bundler -v "~> 2.0"
23
+
19
24
  matrix:
25
+ include:
26
+ - rvm: 2.4.5
27
+ gemfile: gemfiles/rails_5_0.gemfile
28
+
29
+ - rvm: 2.4.5
30
+ gemfile: gemfiles/rails_5_1.gemfile
31
+
32
+ - rvm: 2.4.5
33
+ gemfile: gemfiles/rails_5_2.gemfile
34
+
35
+ - rvm: 2.5.3
36
+ gemfile: gemfiles/rails_5_0.gemfile
37
+
38
+ - rvm: 2.5.3
39
+ gemfile: gemfiles/rails_5_1.gemfile
40
+
41
+ - rvm: 2.5.3
42
+ gemfile: gemfiles/rails_5_2.gemfile
43
+
44
+ - rvm: 2.5.3
45
+ gemfile: gemfiles/rails_6_0.gemfile
46
+
47
+ - rvm: 2.6.5
48
+ gemfile: gemfiles/rails_5_2.gemfile
49
+
50
+ - rvm: 2.6.5
51
+ gemfile: gemfiles/rails_6_0.gemfile
52
+
53
+ - rvm: 2.7.0
54
+ gemfile: gemfiles/rails_6_0.gemfile
55
+
56
+ - rvm: 2.7.0
57
+ gemfile: gemfiles/rails_edge.gemfile
58
+
20
59
  allow_failures:
21
- - gemfile: gemfiles/rails_edge_6.gemfile
60
+ - gemfile: gemfiles/rails_edge.gemfile
61
+ fast_finish: true
62
+
63
+
64
+
data/Appraisals CHANGED
@@ -25,10 +25,17 @@ appraise "rails-5-2" do
25
25
  gem "pg", "~> 1.0"
26
26
  end
27
27
 
28
+ appraise "rails-6-0" do
29
+ gem 'combustion', "~> 1.0"
30
+
31
+ gem "rails", ">= 6.0.0.beta1", "< 6.1"
32
+ gem "pg", "~> 1.0"
33
+ end
34
+
28
35
  appraise "rails-edge-6" do
29
36
  # Edge rails needs unreleased combustion
30
37
  # https://github.com/pat/combustion/issues/92
31
- gem 'combustion', git: "https://github.com/pat/combustion.git"
38
+ gem 'combustion', "~> 1.0"
32
39
 
33
40
  gem "rails", git: "https://github.com/rails/rails.git", branch: "master"
34
41
  gem "pg", "~> 1.0"
@@ -0,0 +1,13 @@
1
+ # Changelog
2
+ Notable changes to this project will be documented in this file.
3
+
4
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [Unreleased](https://github.com/jrochkind/attr_json/compare/v1.1.0...HEAD)
8
+
9
+ ## [1.1.0](https://github.com/jrochkind/attr_json/compare/v1.0.0...v1.1.0)
10
+
11
+ ### Added
12
+
13
+ * not_jsonb_contains query method, like `jsonb_contains` but negated. https://github.com/jrochkind/attr_json/pull/85
data/Gemfile CHANGED
@@ -10,7 +10,7 @@ gemspec
10
10
  # We also have these development dependencies here in the Gemfile instead of the
11
11
  # gemspec so appraisal can override them from our Appraisal file.
12
12
 
13
- gem 'combustion', '~> 0.9.0'
13
+ gem 'combustion', '~> 1.1'
14
14
 
15
15
  # all of rails is NOT a dependency, just activerecord.
16
16
  # But we use it for integration testing with combustion. Hmm, a bit annoying
@@ -24,13 +24,13 @@ gem 'rails'
24
24
  gem 'railties'
25
25
 
26
26
  gem "pg"
27
- gem "rspec-rails", "~> 3.7"
27
+ gem "rspec-rails", "~> 4.0"
28
28
  gem "simple_form", ">= 4.0"
29
29
  gem 'cocoon', ">= 1.2"
30
30
  gem 'jquery-rails'
31
31
 
32
32
  gem 'capybara', "~> 3.0"
33
- gem "chromedriver-helper"
33
+ gem 'webdrivers', '~> 4.0'
34
34
  gem "selenium-webdriver"
35
35
 
36
36
  gem "byebug"
data/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  [![Gem Version](https://badge.fury.io/rb/attr_json.svg)](https://badge.fury.io/rb/attr_json)
4
4
 
5
5
 
6
- ActiveRecord attributes stored serialized in a json column, super smooth. For Rails 5.0, 5.1, or 5.2. Ruby 2.4+.
6
+ ActiveRecord attributes stored serialized in a json column, super smooth. For Rails 5.0, 5.1, 5.2, or 6.0. Ruby 2.4+.
7
7
 
8
8
  Typed and cast like Active Record. Supporting [nested models](#nested), [dirty tracking](#dirty), some [querying](#querying) (with postgres [jsonb](https://www.postgresql.org/docs/9.5/static/datatype-json.html) contains), and [working smoothy with form builders](#forms).
9
9
 
@@ -11,8 +11,6 @@ Typed and cast like Active Record. Supporting [nested models](#nested), [dirty t
11
11
 
12
12
  [Why might you want or not want this?](#why)
13
13
 
14
- AttrJson is pre-1.0. The functionality that is documented here _is_ already implemented (these docs are real, not vaporware) and seems pretty solid. It may still have backwards-incompat changes before 1.0 release. Review and feedback is very welcome.
15
-
16
14
  Developed for postgres, but most features should work with MySQL json columns too, although
17
15
  has not yet been tested with MySQL.
18
16
 
@@ -140,6 +138,9 @@ MyModel.jsonb_contains(my_string: "foo", my_integer: 100).first
140
138
  # Implemented with scopes, this is an ordinary relation, you can
141
139
  # combine it with whatever, just like ordinary `where`.
142
140
 
141
+ MyModel.not_jsonb_contains(my:string: "foo", my_integer: 100).to_sql
142
+ # SELECT "products".* FROM "products" WHERE NOT (products.json_attributes @> ('{"my_string":"foo","my_integer":100}')::jsonb)
143
+
143
144
  # typecasts much like ActiveRecord on query too:
144
145
  MyModel.jsonb_contains(my_string: "foo", my_integer: "100")
145
146
  # no problem
@@ -285,6 +286,20 @@ always mean 'contains' -- the previous query needs a `my_labels.hello`
285
286
  which is a hash that includes the key/value, `lang: en`, it can have
286
287
  other key/values in it too. String values will need to match exactly.
287
288
 
289
+ <a name="arbitrary-json-data"></a>
290
+ ## Storing Arbitrary JSON data
291
+
292
+ Arbitrary JSON data (hashes, arrays, primitives of any depth) can be stored within attributes by using the rails built in `ActiveModel::Type::Value` as the attribute type. This is basically a "no-op" value type -- JSON alone will be used to serialize/deserialize whatever values you put there, because of the json type on the container field.
293
+
294
+ ```ruby
295
+ class MyModel < ActiveRecord::Base
296
+ include AttrJson::Record
297
+
298
+ attr_json :arbitrary_hash, ActiveModel::Type::Value.new
299
+ end
300
+
301
+ ```
302
+
288
303
 
289
304
  <a name="forms"></a>
290
305
  ## Forms and Form Builders
@@ -383,16 +398,16 @@ to prevent overwriting other updates from processes.
383
398
 
384
399
  ## State of Code, and To Be Done
385
400
 
386
- This is a pre-1.0 work in progress. But the functionality that is here seems pretty solid.
401
+ The functionality that is here seems pretty solid, and is being used by jrochkind in a production app.
387
402
 
388
- Backwards incompatible changes are possible before 1.0. Once I tag something 1.0, I'm pretty serious about minimizing backwards incompats.
389
-
390
- I do not yet use this myself in production, and may not for a while. I generally am reluctant to release something as 1.0 with implied suitable for production when I'm not yet using it in production myself, but may with enough feedback. A couple others are already using in production.
403
+ We are committed to [semantic versioning](https://semver.org/) and will endeavor to release no backwards breaking changes without a major version. We are also serious about minimizing backwards incompat releases altogether (ie minimiing major version releases).
391
404
 
392
405
  Feedback of any kind of _very welcome_, please feel free to use the issue tracker.
393
406
 
394
407
  Except for the jsonb_contains stuff using postgres jsonb contains operator, I don't believe any postgres-specific features are used. It ought to work with MySQL, testing and feedback welcome. (Or a PR to test on MySQL?). My own interest is postgres.
395
408
 
409
+ This is still mostly a single-maintainer operation, so has all the sustainability risks of that. Although there are other people using and contributing to it, check out the Github Issues and Pull Request tabs yourself to get a sense.
410
+
396
411
  ### Possible future features:
397
412
 
398
413
  * partial updates for json hashes would be really nice: Using postgres jsonb merge operators to only overwrite what changed. In my initial attempts, AR doesn't make it easy to customize this.
@@ -450,3 +465,5 @@ There is a `./bin/console` that will give you a console in the context of attr_j
450
465
  * Didn't actually notice existing [json_attributes](https://github.com/joel/json_attributes)
451
466
  until I was well on my way here. I think it's not updated for Rails5 or type-aware,
452
467
  haven't looked at it too much.
468
+
469
+ * [store_model](https://github.com/DmitryTsepelev/store_model) was created after `attr_json`, and has some overlapping functionality.
@@ -45,10 +45,10 @@ attributes use as much of the existing ActiveRecord architecture as we can.}
45
45
  # Only to get CI to work on versions of Rails other than we release with,
46
46
  # should never release a gem with RAILS_GEM set!
47
47
  unless ENV['APPRAISAL_INITIALIZED'] || ENV["TRAVIS"]
48
- spec.add_runtime_dependency "activerecord", ">= 5.0.0", "< 5.3"
48
+ spec.add_runtime_dependency "activerecord", ">= 5.0.0", "< 6.1"
49
49
  end
50
50
 
51
- spec.add_development_dependency "bundler", "~> 1.14"
51
+ spec.add_development_dependency "bundler"
52
52
  spec.add_development_dependency "rake", ">= 10.0"
53
53
  spec.add_development_dependency "rspec", "~> 3.7"
54
54
  spec.add_development_dependency "database_cleaner", "~> 1.5"
@@ -6,12 +6,12 @@ gem "combustion", "~> 0.9.0"
6
6
  gem "rails", "~> 5.0.0"
7
7
  gem "railties"
8
8
  gem "pg", "~> 0.18"
9
- gem "rspec-rails", "~> 3.7"
9
+ gem "rspec-rails", "~> 4.0"
10
10
  gem "simple_form", ">= 4.0"
11
11
  gem "cocoon", ">= 1.2"
12
12
  gem "jquery-rails"
13
13
  gem "capybara", "~> 3.0"
14
- gem "chromedriver-helper"
14
+ gem "webdrivers", "~> 4.0"
15
15
  gem "selenium-webdriver"
16
16
  gem "byebug"
17
17
  gem "rails-ujs", require: false
@@ -6,12 +6,12 @@ gem "combustion", "~> 0.9.0"
6
6
  gem "rails", "~> 5.1.0"
7
7
  gem "railties"
8
8
  gem "pg", "~> 1.0"
9
- gem "rspec-rails", "~> 3.7"
9
+ gem "rspec-rails", "~> 4.0"
10
10
  gem "simple_form", ">= 4.0"
11
11
  gem "cocoon", ">= 1.2"
12
12
  gem "jquery-rails"
13
13
  gem "capybara", "~> 3.0"
14
- gem "chromedriver-helper"
14
+ gem "webdrivers", "~> 4.0"
15
15
  gem "selenium-webdriver"
16
16
  gem "byebug"
17
17
 
@@ -6,12 +6,12 @@ gem "combustion", "~> 0.9.0"
6
6
  gem "rails", "~> 5.2.0"
7
7
  gem "railties"
8
8
  gem "pg", "~> 1.0"
9
- gem "rspec-rails", "~> 3.7"
9
+ gem "rspec-rails", "~> 4.0"
10
10
  gem "simple_form", ">= 4.0"
11
11
  gem "cocoon", ">= 1.2"
12
12
  gem "jquery-rails"
13
13
  gem "capybara", "~> 3.0"
14
- gem "chromedriver-helper"
14
+ gem "webdrivers", "~> 4.0"
15
15
  gem "selenium-webdriver"
16
16
  gem "byebug"
17
17
 
@@ -0,0 +1,18 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "combustion", "~> 1.0"
6
+ gem "rails", ">= 6.0.0.beta1", "< 6.1"
7
+ gem "railties"
8
+ gem "pg", "~> 1.0"
9
+ gem "rspec-rails", "~> 4.0"
10
+ gem "simple_form", ">= 4.0"
11
+ gem "cocoon", ">= 1.2"
12
+ gem "jquery-rails"
13
+ gem "capybara", "~> 3.0"
14
+ gem "webdrivers", "~> 4.0"
15
+ gem "selenium-webdriver"
16
+ gem "byebug"
17
+
18
+ gemspec path: "../"
@@ -0,0 +1,19 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "combustion", "~> 1.0"
6
+ gem "rails", git: "https://github.com/rails/rails.git", branch: "master"
7
+ gem "railties"
8
+ gem "pg", "~> 1.0"
9
+ gem "rspec-rails", "~> 3.7"
10
+ gem "simple_form", ">= 4.0"
11
+ gem "cocoon", ">= 1.2"
12
+ gem "jquery-rails"
13
+ gem "capybara", "~> 3.0"
14
+ gem "webdrivers", "~> 4.0"
15
+ gem "selenium-webdriver"
16
+ gem "byebug"
17
+ gem "coffee-rails"
18
+
19
+ gemspec path: "../"
@@ -2,16 +2,16 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "combustion", git: "https://github.com/pat/combustion.git"
5
+ gem "combustion", "~> 1.0"
6
6
  gem "rails", git: "https://github.com/rails/rails.git", branch: "master"
7
7
  gem "railties"
8
8
  gem "pg", "~> 1.0"
9
- gem "rspec-rails", "~> 3.7"
9
+ gem "rspec-rails", "~> 4.0"
10
10
  gem "simple_form", ">= 4.0"
11
11
  gem "cocoon", ">= 1.2"
12
12
  gem "jquery-rails"
13
13
  gem "capybara", "~> 3.0"
14
- gem "chromedriver-helper"
14
+ gem "webdrivers", "~> 4.0"
15
15
  gem "selenium-webdriver"
16
16
  gem "byebug"
17
17
  gem "coffee-rails"
@@ -1,7 +1,6 @@
1
1
  require "attr_json/version"
2
2
 
3
3
  require "active_record"
4
- require "active_record/connection_adapters/postgresql_adapter"
5
4
 
6
5
  require 'attr_json/config'
7
6
  require 'attr_json/record'
@@ -12,7 +12,9 @@
12
12
  attr_reader :name, :type, :original_args, :container_attribute
13
13
 
14
14
  # @param name [Symbol,String]
15
- # @param type [Symbol,ActiveModel::Type::Value]
15
+ # @param type [Symbol,ActiveModel::Type::Value] Symbol is looked up in
16
+ # ActiveRecord::Type.lookup, but with `adapter: nil` for no custom
17
+ # adapter-specific lookup.
16
18
  #
17
19
  # @option options store_key [Symbol,String]
18
20
  # @option options container_attribute [Symbol,ActiveModel::Type::Value]
@@ -40,7 +42,12 @@
40
42
  # ActiveModel::Type.lookup may make more sense, but ActiveModel::Type::Date
41
43
  # seems to have a bug with multi-param assignment. Mostly they return
42
44
  # the same types, but ActiveRecord::Type::Date works with multi-param assignment.
43
- type = ActiveRecord::Type.lookup(type)
45
+ #
46
+ # We pass `adapter: nil` to avoid triggering a db connection.
47
+ # See: https://github.com/jrochkind/attr_json/issues/41
48
+ # This is at the "cost" of not using any adapter-specific types... which
49
+ # maybe preferable anyway?
50
+ type = ActiveRecord::Type.lookup(type, adapter: nil)
44
51
  elsif ! type.is_a? ActiveModel::Type::Value
45
52
  raise ArgumentError, "Second argument (#{type}) must be a symbol or instance of an ActiveModel::Type::Value subclass"
46
53
  end
@@ -54,6 +54,11 @@ module AttrJson
54
54
  @name_to_definition.values
55
55
  end
56
56
 
57
+ # Returns all registered attributes as an array of symbols
58
+ def attribute_names
59
+ @name_to_definition.keys
60
+ end
61
+
57
62
  def container_attributes
58
63
  @store_key_to_definition.keys.collect(&:to_s)
59
64
  end
@@ -18,7 +18,7 @@ module AttrJson
18
18
  extend ActiveSupport::Concern
19
19
 
20
20
  included do
21
- unless self < ActiveRecord::Base
21
+ unless self <= ActiveRecord::Base
22
22
  raise TypeError, "AttrJson::Record can only be used with an ActiveRecord::Base model. #{self} does not appear to be one. Are you looking for ::AttrJson::Model?"
23
23
  end
24
24
 
@@ -26,6 +26,37 @@ module AttrJson
26
26
  self.attr_json_registry = AttrJson::AttributeDefinition::Registry.new
27
27
  end
28
28
 
29
+ protected
30
+
31
+ # adapted from ActiveRecord query_attribute method
32
+ # https://github.com/rails/rails/blob/v5.2.3/activerecord/lib/active_record/attribute_methods/query.rb#L12
33
+ #
34
+ # Sadly we could not re-use Rails code here, becuase the built-in method assumes attribute
35
+ # can be obtained with `self[attr_name]`, which you can not with attr_json (is that bad?), as
36
+ # well as `self.class.columns_hash[attr_name]` which you definitely can not (which is probably not bad),
37
+ # and has no way to use the value-translation semantics independently of that. May be a problem if
38
+ # ActiveRecord changes it's query method semantics in the future, will have to be sync'd here.
39
+ #
40
+ # Used to implement query methods on attr_json attributes, like `attr_json :foo, :string`, method `#foo?`
41
+ def self.attr_json_query_method(record, attribute)
42
+ value = record.send(attribute)
43
+
44
+ case value
45
+ when true
46
+ true
47
+ when false, nil, ActiveModel::Type::Boolean::FALSE_VALUES
48
+ false
49
+ else
50
+ if value.respond_to?(:to_i) && ( Numeric === value || value.to_s !~ /[^0-9]/ )
51
+ !value.to_i.zero?
52
+ elsif value.respond_to?(:zero?)
53
+ !value.zero?
54
+ else
55
+ !value.blank?
56
+ end
57
+ end
58
+ end
59
+
29
60
  class_methods do
30
61
  # Access or set class-wide json_attribute_config. Inherited by sub-classes,
31
62
  # but setting on sub-classes is unique to subclass. Similar to how
@@ -133,30 +164,27 @@ module AttrJson
133
164
  end
134
165
 
135
166
  _attr_jsons_module.module_eval do
167
+ # For getter and setter, we used to use read_store_attribute/write_store_attribute
168
+ # copied from Rails store_accessor implementation.
169
+ # https://github.com/rails/rails/blob/74c3e43fba458b9b863d27f0c45fd2d8dc603cbc/activerecord/lib/active_record/store.rb#L90-L96
170
+ #
171
+ # But in fact just getting/setting in the hash provided to us by ActiveRecord json type
172
+ # container works BETTER for dirty tracking. We had a test that only passed doing it
173
+ # this simple way.
174
+
136
175
  define_method("#{name}=") do |value|
137
176
  attribute_def = self.class.attr_json_registry.fetch(name.to_sym)
138
- # write_store_attribute copied from Rails store_accessor implementation.
139
- # https://github.com/rails/rails/blob/74c3e43fba458b9b863d27f0c45fd2d8dc603cbc/activerecord/lib/active_record/store.rb#L90-L96
140
-
141
- # special handling for nil, sorry, because if name key was previously
142
- # not present, write_store_attribute by default will decide there was
143
- # no change and refuse to make the change. TODO messy.
144
- if value.nil? && !public_send(attribute_def.container_attribute).has_key?(attribute_def.store_key)
145
- public_send :"#{attribute_def.container_attribute}_will_change!"
146
- public_send(attribute_def.container_attribute)[attribute_def.store_key] = nil
147
- else
148
- # use of `write_store_attribute` is copied from Rails store_accessor implementation.
149
- # https://github.com/rails/rails/blob/74c3e43fba458b9b863d27f0c45fd2d8dc603cbc/activerecord/lib/active_record/store.rb#L90-L96
150
- write_store_attribute(attribute_def.container_attribute, attribute_def.store_key, attribute_def.cast(value))
151
- end
177
+ public_send(attribute_def.container_attribute)[attribute_def.store_key] = attribute_def.cast(value)
152
178
  end
153
179
 
154
180
  define_method("#{name}") do
155
181
  attribute_def = self.class.attr_json_registry.fetch(name.to_sym)
182
+ public_send(attribute_def.container_attribute)[attribute_def.store_key]
183
+ end
156
184
 
157
- # use of `read_store_attribute` is copied from Rails store_accessor implementation.
158
- # https://github.com/rails/rails/blob/74c3e43fba458b9b863d27f0c45fd2d8dc603cbc/activerecord/lib/active_record/store.rb#L90-L96
159
- read_store_attribute(attribute_def.container_attribute, attribute_def.store_key)
185
+ define_method("#{name}?") do
186
+ # implementation of `query_store_attribute` is based on Rails `query_attribute` implementation
187
+ AttrJson::Record.attr_json_query_method(self, name)
160
188
  end
161
189
  end
162
190
 
@@ -10,6 +10,20 @@ module AttrJson
10
10
  end
11
11
 
12
12
  def contains_relation
13
+ contains_relation_impl do |relation, query, params|
14
+ relation.where(query, params)
15
+ end
16
+ end
17
+
18
+ def contains_not_relation
19
+ contains_relation_impl do |relation, query, params|
20
+ relation.where.not(query, params)
21
+ end
22
+ end
23
+
24
+ protected
25
+
26
+ def contains_relation_impl
13
27
  result_relation = relation
14
28
 
15
29
  group_attributes_by_container.each do |container_attribute, attributes|
@@ -18,14 +32,12 @@ module AttrJson
18
32
  attributes.each do |key, value|
19
33
  add_to_param_hash!(param_hash, key, value)
20
34
  end
21
- result_relation = result_relation.where("#{relation.table_name}.#{container_attribute} @> (?)::jsonb", param_hash.to_json)
35
+ result_relation = yield(result_relation, "#{relation.table_name}.#{container_attribute} @> (?)::jsonb", param_hash.to_json)
22
36
  end
23
37
 
24
38
  result_relation
25
39
  end
26
40
 
27
- protected
28
-
29
41
  def merge_param_hash!(original, new)
30
42
  original.deep_merge!(new) do |key, old_val, new_val|
31
43
  if old_val.is_a?(Array) && old_val.first.is_a?(Hash) && new_val.is_a?(Array) && new_val.first.is_a?(Hash)
@@ -17,6 +17,8 @@ module AttrJson
17
17
  #
18
18
  # some_model.jsonb_contains(a_string: "foo").first
19
19
  #
20
+ # some_model.not_jsonb_contains(a_string: "bar").first
21
+ #
20
22
  # See more in {file:README} docs.
21
23
  module QueryScopes
22
24
  extend ActiveSupport::Concern
@@ -29,6 +31,10 @@ module AttrJson
29
31
  scope(:jsonb_contains, lambda do |attributes|
30
32
  QueryBuilder.new(self, attributes).contains_relation
31
33
  end)
34
+
35
+ scope(:not_jsonb_contains, lambda do |attributes|
36
+ QueryBuilder.new(self, attributes).contains_not_relation
37
+ end)
32
38
  end
33
39
  end
34
40
  end
@@ -8,6 +8,8 @@ module AttrJson
8
8
  # but normally that's only done in AttrJson::Model.to_type, there isn't
9
9
  # an anticipated need to create from any other place.
10
10
  class Model < ::ActiveModel::Type::Value
11
+ class BadCast < ArgumentError ; end
12
+
11
13
  attr_accessor :model
12
14
  def initialize(model)
13
15
  #TODO type check, it really better be a AttrJson::Model. maybe?
@@ -34,10 +36,11 @@ module AttrJson
34
36
  # TODO Maybe we ought not to do this on #to_h?
35
37
  model.new_from_serializable(v.to_h)
36
38
  else
37
- # Bad input? Most existing ActiveModel::Types seem to decide
38
- # either nil, or a base value like the empty string. They don't
39
- # raise. So we won't either, just nil.
40
- nil
39
+ # Bad input. Originally we were trying to return nil, to be like
40
+ # existing ActiveRecord which kind of silently does a basic value
41
+ # with null input. But that ended up making things confusing, let's
42
+ # just raise.
43
+ raise BadCast.new("Can not cast from #{v.inspect} to #{self.type}")
41
44
  end
42
45
  end
43
46
 
@@ -51,6 +51,8 @@ module AttrJson
51
51
  # MyRecord.jsonb_contains(author: { name: "foo", type: "Corporation"})
52
52
  # MyRecord.jsonb_contains(author: Corporation.new(name: "foo"))
53
53
  #
54
+ # Additionally, there is not_jsonb_contains, which creates the same query terms like jsonb_contains, but negated.
55
+ #
54
56
  class PolymorphicModel < ActiveModel::Type::Value
55
57
  class TypeError < ::TypeError ; end
56
58
 
@@ -1,3 +1,3 @@
1
1
  module AttrJson
2
- VERSION = "0.4.0"
2
+ VERSION = "1.1.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: attr_json
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jonathan Rochkind
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-10-29 00:00:00.000000000 Z
11
+ date: 2020-06-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -19,7 +19,7 @@ dependencies:
19
19
  version: 5.0.0
20
20
  - - "<"
21
21
  - !ruby/object:Gem::Version
22
- version: '5.3'
22
+ version: '6.1'
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
@@ -29,21 +29,21 @@ dependencies:
29
29
  version: 5.0.0
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
- version: '5.3'
32
+ version: '6.1'
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: bundler
35
35
  requirement: !ruby/object:Gem::Requirement
36
36
  requirements:
37
- - - "~>"
37
+ - - ">="
38
38
  - !ruby/object:Gem::Version
39
- version: '1.14'
39
+ version: '0'
40
40
  type: :development
41
41
  prerelease: false
42
42
  version_requirements: !ruby/object:Gem::Requirement
43
43
  requirements:
44
- - - "~>"
44
+ - - ">="
45
45
  - !ruby/object:Gem::Version
46
- version: '1.14'
46
+ version: '0'
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: rake
49
49
  requirement: !ruby/object:Gem::Requirement
@@ -133,6 +133,7 @@ files:
133
133
  - ".travis.yml"
134
134
  - ".yardopts"
135
135
  - Appraisals
136
+ - CHANGELOG.md
136
137
  - Gemfile
137
138
  - LICENSE.txt
138
139
  - README.md
@@ -149,6 +150,8 @@ files:
149
150
  - gemfiles/rails_5_0.gemfile
150
151
  - gemfiles/rails_5_1.gemfile
151
152
  - gemfiles/rails_5_2.gemfile
153
+ - gemfiles/rails_6_0.gemfile
154
+ - gemfiles/rails_edge.gemfile
152
155
  - gemfiles/rails_edge_6.gemfile
153
156
  - lib/attr_json.rb
154
157
  - lib/attr_json/attribute_definition.rb
@@ -192,7 +195,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
192
195
  version: '0'
193
196
  requirements: []
194
197
  rubyforge_project:
195
- rubygems_version: 2.7.7
198
+ rubygems_version: 2.7.6
196
199
  signing_key:
197
200
  specification_version: 4
198
201
  summary: ActiveRecord attributes stored serialized in a json column, super smooth.