pgrel 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/.rubocop.yml +45 -0
  4. data/.travis.yml +14 -14
  5. data/Gemfile +9 -0
  6. data/README.md +2 -0
  7. data/gemfiles/rails40.gemfile +1 -1
  8. data/gemfiles/rails41.gemfile +1 -1
  9. data/gemfiles/rails42.gemfile +1 -1
  10. data/gemfiles/rails5.gemfile +1 -0
  11. data/lib/pgrel/active_record/query_methods.rb +31 -0
  12. data/lib/pgrel/active_record/store_chain/array_chain.rb +18 -0
  13. data/lib/pgrel/active_record/store_chain/hstore_chain.rb +36 -0
  14. data/lib/pgrel/active_record/store_chain/jsonb_chain.rb +76 -0
  15. data/lib/pgrel/active_record/store_chain.rb +176 -0
  16. data/lib/pgrel/active_record.rb +2 -0
  17. data/lib/pgrel/version.rb +1 -1
  18. data/lib/pgrel.rb +2 -2
  19. data/pgrel.gemspec +3 -4
  20. data/spec/pgrel/array_spec.rb +71 -0
  21. data/spec/pgrel/hstore_spec.rb +125 -0
  22. data/spec/pgrel/jsonb_spec.rb +137 -0
  23. data/spec/spec_helper.rb +19 -8
  24. data/spec/support/array_store.rb +2 -0
  25. data/spec/support/hstore.rb +2 -0
  26. data/spec/support/jsonb.rb +2 -0
  27. metadata +36 -65
  28. data/Gemfile.lock +0 -112
  29. data/spec/dummy/README.rdoc +0 -28
  30. data/spec/dummy/Rakefile +0 -6
  31. data/spec/dummy/app/assets/images/.keep +0 -0
  32. data/spec/dummy/app/assets/javascripts/application.js +0 -13
  33. data/spec/dummy/app/assets/stylesheets/application.css +0 -15
  34. data/spec/dummy/app/controllers/application_controller.rb +0 -5
  35. data/spec/dummy/app/controllers/concerns/.keep +0 -0
  36. data/spec/dummy/app/helpers/application_helper.rb +0 -2
  37. data/spec/dummy/app/mailers/.keep +0 -0
  38. data/spec/dummy/app/models/.keep +0 -0
  39. data/spec/dummy/app/models/concerns/.keep +0 -0
  40. data/spec/dummy/app/views/layouts/application.html.erb +0 -14
  41. data/spec/dummy/bin/bundle +0 -3
  42. data/spec/dummy/bin/rails +0 -4
  43. data/spec/dummy/bin/rake +0 -4
  44. data/spec/dummy/bin/setup +0 -29
  45. data/spec/dummy/config/application.rb +0 -26
  46. data/spec/dummy/config/boot.rb +0 -5
  47. data/spec/dummy/config/database.yml +0 -29
  48. data/spec/dummy/config/environment.rb +0 -5
  49. data/spec/dummy/config/environments/development.rb +0 -41
  50. data/spec/dummy/config/environments/production.rb +0 -79
  51. data/spec/dummy/config/environments/test.rb +0 -42
  52. data/spec/dummy/config/initializers/assets.rb +0 -11
  53. data/spec/dummy/config/initializers/backtrace_silencers.rb +0 -7
  54. data/spec/dummy/config/initializers/cookies_serializer.rb +0 -3
  55. data/spec/dummy/config/initializers/filter_parameter_logging.rb +0 -4
  56. data/spec/dummy/config/initializers/inflections.rb +0 -16
  57. data/spec/dummy/config/initializers/mime_types.rb +0 -4
  58. data/spec/dummy/config/initializers/session_store.rb +0 -3
  59. data/spec/dummy/config/initializers/wrap_parameters.rb +0 -14
  60. data/spec/dummy/config/locales/en.yml +0 -23
  61. data/spec/dummy/config/routes.rb +0 -56
  62. data/spec/dummy/config/secrets.yml +0 -22
  63. data/spec/dummy/config.ru +0 -4
  64. data/spec/dummy/lib/assets/.keep +0 -0
  65. data/spec/dummy/log/.keep +0 -0
  66. data/spec/dummy/public/404.html +0 -67
  67. data/spec/dummy/public/422.html +0 -67
  68. data/spec/dummy/public/500.html +0 -66
  69. data/spec/dummy/public/favicon.ico +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7879b5fb4b3bac896964ab6e0e5827ce6f088a37
4
- data.tar.gz: 22bd6d7ad1f87a9c9044eb845cf6140e93e27c88
3
+ metadata.gz: 642a7d4b7a7b19f5acc220e6f74d41d6d0e00524
4
+ data.tar.gz: 007ba40877cfc0c6bdbb260502b98b1627ceaaa6
5
5
  SHA512:
6
- metadata.gz: 821537ca351a39f7e4707ab5d7c482b4f417ba12c0eaac8a7b5a164d3df971ff4b23a2d941d1504c7748f00611ef3ed9c5d7c64bc222606d125679c0dd5dd14a
7
- data.tar.gz: d6f51444606d5f574b427aad9c932f7186ee2f54d712dbc509e2c573eeefcf1172445e8777278934835d4881d7e000af6a7eab00cb8908eac9106d51655de41e
6
+ metadata.gz: 82cf0b3c4ed6c5ec72a55e6572ee0a197cb15079c70f48d61499acaac15d4d98bcb2262f5965a55c5c669bd4a98e04edd0e18da9b0ff7e018ae94ca82b2363e5
7
+ data.tar.gz: 130ed3ac3d71c6c98f72a33a881877f23f147653a1735a68a4bc8016996345cca77721713874d9f0a8d9b2991f2ccc4758b026224b4aa450e9b2cf0e0c8c8ce9
data/.gitignore CHANGED
@@ -23,6 +23,7 @@
23
23
  .settings
24
24
  .tmproj
25
25
  Thumbs.db
26
+ coverage/
26
27
 
27
28
  .bundle/
28
29
  log/*.log
@@ -30,3 +31,5 @@ pkg/
30
31
  spec/dummy/log/*.log
31
32
  spec/dummy/tmp/
32
33
  spec/dummy/.sass-cache
34
+ Gemfile.local
35
+ Gemfile.lock
data/.rubocop.yml ADDED
@@ -0,0 +1,45 @@
1
+ # This strict rubocop config should be used before submiting PR and should pass
2
+ # Doesn't include:
3
+ # - specs
4
+
5
+
6
+ AllCops:
7
+ # Include gemspec and Rakefile
8
+ Include:
9
+ - 'app/**/*.rb'
10
+ - 'config/**/*.rb'
11
+ - 'lib/**/*.rb'
12
+ - 'lib/**/*.rake'
13
+ Exclude:
14
+ - 'vendor/**/*'
15
+ - 'spec/**/*'
16
+ - 'db/*.rb'
17
+ - 'bin/**/*'
18
+ RunRailsCops: false
19
+ DisplayCopNames: true
20
+ StyleGuideCopsOnly: false
21
+
22
+ Style/Documentation:
23
+ Exclude:
24
+ - 'lib/**/version.rb'
25
+
26
+ Metrics/LineLength:
27
+ Max: 100
28
+
29
+ Metrics/MethodLength:
30
+ Max: 15
31
+
32
+ Metrics/AbcSize:
33
+ Max: 20
34
+
35
+ Style/BarePercentLiterals:
36
+ Enabled: false
37
+
38
+ Style/CommentAnnotation:
39
+ Enabled: false
40
+
41
+ Style/RaiseArgs:
42
+ Enabled: false
43
+
44
+ Style/StringLiterals:
45
+ Enabled: false
data/.travis.yml CHANGED
@@ -1,21 +1,21 @@
1
1
  language: ruby
2
2
  cache: bundler
3
- rvm:
4
- - 2.2
5
3
 
6
- gemfile:
7
- - Gemfile
4
+ addons:
5
+ postgresql: "9.4"
6
+
7
+ before_script:
8
+ - createuser pgrel -d
9
+ - createdb -U pgrel pgrel
10
+ - psql -U postgres -d pgrel -c 'CREATE EXTENSION IF NOT EXISTS hstore;'
8
11
 
9
12
  matrix:
10
13
  include:
11
- - rvm: 2.2
12
- gemfile: gemfiles/rails40.gemfile
13
-
14
- - rvm: 2.2
15
- gemfile: gemfiles/rails41.gemfile
16
-
17
- - rvm: 2.2
14
+ - rvm: 2.2.1
15
+ gemfile: gemfiles/rails5.gemfile
16
+ - rvm: 2.2.1
18
17
  gemfile: gemfiles/rails42.gemfile
19
-
20
- - rvm: 2.2
21
- gemfile: gemfiles/rails5.gemfile
18
+ - rvm: 2.1
19
+ gemfile: gemfiles/rails41.gemfile
20
+ - rvm: 2.1
21
+ gemfile: gemfiles/rails40.gemfile
data/Gemfile CHANGED
@@ -1,2 +1,11 @@
1
1
  source 'https://rubygems.org'
2
+
2
3
  gemspec
4
+
5
+ local_gemfile = 'Gemfile.local'
6
+
7
+ if File.exist?(local_gemfile)
8
+ eval(File.read(local_gemfile)) # rubocop:disable Lint/Eval
9
+ else
10
+ gem 'activerecord', '~>4.0'
11
+ end
data/README.md CHANGED
@@ -2,4 +2,6 @@
2
2
 
3
3
  ## Pgrel
4
4
 
5
+ **Rails 5**
6
+
5
7
  ActiveRecord extension for querying hstore and jsonb.
@@ -1,5 +1,5 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- gem 'rails', '~> 4.0.1'
3
+ gem 'activerecord', "~>4.0"
4
4
 
5
5
  gemspec path: '..'
@@ -1,5 +1,5 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- gem 'rails', '~> 4.1.0'
3
+ gem 'activerecord', "~>4.1"
4
4
 
5
5
  gemspec path: '..'
@@ -1,5 +1,5 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- gem 'rails', '~> 4.2.0'
3
+ gem 'activerecord', "~>4.2"
4
4
 
5
5
  gemspec path: '..'
@@ -1,5 +1,6 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
+ gem 'arel', github: 'rails/arel'
3
4
  gem 'rails', github: 'rails/rails'
4
5
 
5
6
  gemspec path: '..'
@@ -0,0 +1,31 @@
1
+ require 'active_record/relation'
2
+ require 'pgrel/active_record/store_chain'
3
+ require 'pgrel/active_record/store_chain/array_chain'
4
+ require 'pgrel/active_record/store_chain/hstore_chain'
5
+ require 'pgrel/active_record/store_chain/jsonb_chain'
6
+
7
+ module ActiveRecord
8
+ module QueryMethods
9
+ # Extend WhereChain with 'store' method.
10
+ class WhereChain
11
+ def store(store_name, *opts)
12
+ store_name = store_name.to_s
13
+ column = @scope.klass.columns_hash[store_name]
14
+
15
+ # Rails 4 column has method 'array'
16
+ # but Rails 5 has 'array?'.
17
+ #
18
+ # So, check both(
19
+ if (arr = column.try(:array)) || (arr.nil? && column.array?)
20
+ klass = ArrayChain
21
+ else
22
+ column_klass = column.type.capitalize
23
+ klass = "ActiveRecord::QueryMethods::#{column_klass}Chain".constantize
24
+ end
25
+ chain = klass.new(@scope, store_name)
26
+ return chain.where(*opts) unless opts.empty?
27
+ chain
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,18 @@
1
+ module ActiveRecord
2
+ module QueryMethods
3
+ # Store chain for array columns.
4
+ class ArrayChain < StoreChain
5
+ # Whether the array overlaps provided array.
6
+ #
7
+ # Example
8
+ # Model.create!(name: 'first', store: ['b', 'c'])
9
+ # Model.create!(name: 'second', store: ['a', 'b'])
10
+ #
11
+ # Model.store(:store).overlap('c').all #=> [Model(name: 'first', ...)]
12
+ # Model.store(:store).overlap(['b']).size #=> 2
13
+ def overlap(*vals)
14
+ update_scope "#{@store_name} && #{type_cast(vals.flatten)}"
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,36 @@
1
+ module ActiveRecord
2
+ module QueryMethods
3
+ # Store chain for hstore columns.
4
+ class HstoreChain < KeyStoreChain
5
+ # Query by store values.
6
+ #
7
+ # Supports array values.
8
+ #
9
+ # Example
10
+ # Model.create!(name: 'first', store: {b: 1, c: 2})
11
+ # Model.create!(name: 'second', store: {b: 2, c: 3})
12
+ #
13
+ # Model.store(:store, c: 2).all #=> [Model(name: 'first', ...)]
14
+ # Model.store(:store, b: [1, 2]).size #=> 2
15
+ def where(opts)
16
+ opts = stringify(opts)
17
+ where_with_prefix "#{@store_name}->", opts
18
+ end
19
+
20
+ private
21
+
22
+ def stringify(val)
23
+ case val
24
+ when String
25
+ val
26
+ when Array
27
+ val.map { |v| stringify(v) }
28
+ when Hash
29
+ Hash[val.map { |k, v| [k, stringify(v)] }]
30
+ else
31
+ val.to_s
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,76 @@
1
+ module ActiveRecord
2
+ module QueryMethods
3
+ # Store chain for jsonb columns.
4
+ class JsonbChain < KeyStoreChain
5
+ # Query by store values.
6
+ # Supports array values (convert to IN statement).
7
+ #
8
+ # Example
9
+ # Model.create!(name: 'first', store: {b: 1, c: 2})
10
+ # Model.create!(name: 'second', store: {b: 2, c: 3})
11
+ #
12
+ # Model.store(:store, c: 2).all #=> [Model(name: 'first', ...)]
13
+ # Model.store(:store, b: [1, 2]).size #=> 2
14
+ def where(opts)
15
+ opts = flatten_json(opts)
16
+ where_with_prefix "#{@store_name}->", opts
17
+ end
18
+
19
+ # Query by quality in path.
20
+ #
21
+ # Path can be set as object or as args.
22
+ #
23
+ # Example:
24
+ # Model.create!(name: 'first', store: {b: 1, c: { d: 3 } })
25
+ # Model.create!(name: 'second', store: {b: 2, c: { d: 1 }})
26
+ #
27
+ # Model.store(:store).path(c: {d: 3}).all #=> [Model(name: 'first', ...)]
28
+ # Model.store(:store).path('c', 'd', [1, 3]).size #=> 2
29
+ def path(*args)
30
+ args = flatten_hash(args.first) if args.size == 1
31
+ val = args.pop
32
+
33
+ path = "{#{args.join(',')}}"
34
+
35
+ case val
36
+ when Hash
37
+ op = '#>'
38
+ val = ::ActiveSupport::JSON.encode(val)
39
+ when Array
40
+ op = '#>>'
41
+ val = val.map(&:to_s)
42
+ else
43
+ op = '#>>'
44
+ val = val.to_s
45
+ end
46
+
47
+ where_with_prefix "#{@store_name}#{op}", path => val
48
+ end
49
+
50
+ private
51
+
52
+ def flatten_json(val)
53
+ Hash[
54
+ val.map do |k, v|
55
+ if v.is_a?(Array)
56
+ [k, v.map { |i| ::ActiveSupport::JSON.encode(i) }]
57
+ else
58
+ [k, ::ActiveSupport::JSON.encode(v)]
59
+ end
60
+ end
61
+ ]
62
+ end
63
+
64
+ def flatten_hash(hash)
65
+ case hash
66
+ when Hash
67
+ hash.flat_map { |k, v| [k, *flatten_hash(v)] }
68
+ when Array
69
+ [hash]
70
+ else
71
+ hash
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,176 @@
1
+ # Check rails version
2
+ RAILS_5 = ActiveRecord.version.release >= Gem::Version.new("5")
3
+
4
+ module ActiveRecord
5
+ module QueryMethods
6
+ # Base class for different store chains (hstore, jsonb, array).
7
+ # Provides _containment_ queries methods.
8
+ # Provides basic methods.
9
+ class StoreChain
10
+ def initialize(scope, store_name)
11
+ @scope = scope
12
+ @store_name = store_name
13
+ @inverted = false
14
+ end
15
+
16
+ # Whether the store contains provided store
17
+ #
18
+ # Example
19
+ # Model.create!(name: 'first', store: {a: 1, b: 2})
20
+ # Model.create!(name: 'second', store: {b: 1, c: 3})
21
+ #
22
+ # data = {a: 1}
23
+ # Model.store(:store).contains(data).all #=> [Model(name: 'first', ...)]
24
+ def contains(opts)
25
+ update_scope "#{@store_name} @> #{type_cast(opts)}"
26
+ end
27
+
28
+ # Whether the store is contained within provided store
29
+ #
30
+ # Example
31
+ # Model.create!(name: 'first', store: {b: 1})
32
+ # Model.create!(name: 'second', store: {b: 1, c: 3})
33
+ #
34
+ # data = {b: 1, c: 2}
35
+ # Model.store(:store).contains(data).all #=> [Model(name: 'first', ...)]
36
+ def contained(opts)
37
+ update_scope "#{@store_name} <@ #{type_cast(opts)}"
38
+ end
39
+
40
+ # Add negation to condition.
41
+ #
42
+ # Example
43
+ # Model.create!(name: 'first', store: {b: 2})
44
+ # Model.create!(name: 'second', store: {b: 1, c: 3})
45
+ #
46
+ # Model.store(:store).not.contains({c: 3}).all #=> [Model(name: 'first')]
47
+ #
48
+ # Model.store(:store).not(b: 2).all #=> [Model(name: 'second')]
49
+ def not(opts = :chain)
50
+ @inverted = true
51
+ return where(opts) unless opts == :chain
52
+ self
53
+ end
54
+
55
+ if RAILS_5
56
+ protected
57
+
58
+ def update_scope(*opts)
59
+ where_clause = @scope.send(:where_clause_factory).build(opts, {})
60
+ @scope.where_clause += @inverted ? where_clause.invert : where_clause
61
+ @scope
62
+ end
63
+
64
+ def type_cast(value)
65
+ ActiveRecord::Base.connection.quote(
66
+ @scope.table.type_cast_for_database(@store_name, value)
67
+ )
68
+ end
69
+
70
+ def where_with_prefix(prefix, opts)
71
+ where_clause = @scope.send(:where_clause_factory).build(opts, {})
72
+ predicates = where_clause.ast.children.map do |rel|
73
+ rel.left = to_sql_literal(prefix, rel.left)
74
+ rel
75
+ end
76
+ where_clause = ActiveRecord::Relation::WhereClause.new(
77
+ predicates,
78
+ where_clause.binds
79
+ )
80
+ @scope.where_clause += @inverted ? where_clause.invert : where_clause
81
+ @scope
82
+ end
83
+ else
84
+ protected
85
+
86
+ def update_scope(*opts)
87
+ where_clause = @scope.send(:build_where, opts).map do |rel|
88
+ @inverted ? invert_arel(rel) : rel
89
+ end
90
+ @scope.where_values += where_clause
91
+ @scope
92
+ end
93
+
94
+ def type_cast(value)
95
+ ActiveRecord::Base.connection.quote(
96
+ value,
97
+ @scope.klass.columns_hash[@store_name]
98
+ )
99
+ end
100
+
101
+ def where_with_prefix(prefix, opts)
102
+ where_value = @scope.send(:build_where, opts).map do |rel|
103
+ rel.left = to_sql_literal(prefix, rel.left)
104
+ @inverted ? invert_arel(rel) : rel
105
+ end
106
+ @scope.where_values += where_value
107
+ @scope
108
+ end
109
+
110
+ def invert_arel(rel)
111
+ case rel
112
+ when Arel::Nodes::In
113
+ Arel::Nodes::NotIn.new(rel.left, rel.right)
114
+ when Arel::Nodes::Equality
115
+ Arel::Nodes::NotEqual.new(rel.left, rel.right)
116
+ when String
117
+ Arel::Nodes::Not.new(Arel::Nodes::SqlLiteral.new(rel))
118
+ else
119
+ Arel::Nodes::Not.new(rel)
120
+ end
121
+ end
122
+ end
123
+ end
124
+
125
+ # Base class for key-value types of stores (hstore, jsonb)
126
+ class KeyStoreChain < StoreChain
127
+ # Single key existence
128
+ #
129
+ # Example
130
+ # Model.create!(name: 'first', store: {a: 1})
131
+ # Model.create!(name: 'second', store: {b: 1})
132
+ #
133
+ # # Get all records which have key 'a' in store 'store'
134
+ # Model.store(:store).key('a').all #=> [Model(name: 'first', ...)]
135
+ def key(key)
136
+ update_scope "#{@store_name} ? :key", key: key.to_s
137
+ end
138
+
139
+ # Several keys existence
140
+ #
141
+ # Example
142
+ # Model.create!(name: 'first', store: {a: 1, b: 2})
143
+ # Model.create!(name: 'second', store: {b: 1, c: 3})
144
+ #
145
+ # Model.store(:store).keys('a','b').all #=> [Model(name: 'first', ...)]
146
+ def keys(*keys)
147
+ update_scope(
148
+ "#{@store_name} ?& ARRAY[:keys]",
149
+ keys: keys.flatten.map(&:to_s)
150
+ )
151
+ end
152
+
153
+ # Any of the keys existence
154
+ #
155
+ # Example
156
+ # Model.create!(name: 'first', store: {a: 1, b: 2})
157
+ # Model.create!(name: 'second', store: {b: 1, c: 3})
158
+ #
159
+ # Model.store(:store).keys('a','b').count #=> 2
160
+ def any(*keys)
161
+ update_scope(
162
+ "#{@store_name} ?| ARRAY[:keys]",
163
+ keys: keys.flatten.map(&:to_s)
164
+ )
165
+ end
166
+
167
+ protected
168
+
169
+ def to_sql_literal(prefix, node)
170
+ Arel::Nodes::SqlLiteral.new(
171
+ "#{prefix}'#{node.name}'"
172
+ )
173
+ end
174
+ end
175
+ end
176
+ end
@@ -0,0 +1,2 @@
1
+ require 'active_record'
2
+ require 'pgrel/active_record/query_methods'
data/lib/pgrel/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Pgrel
2
- VERSION = "0.0.1"
2
+ VERSION = "0.1.0"
3
3
  end
data/lib/pgrel.rb CHANGED
@@ -1,2 +1,2 @@
1
- module Pgrel
2
- end
1
+ require 'pgrel/version'
2
+ require 'pgrel/active_record'
data/pgrel.gemspec CHANGED
@@ -17,12 +17,11 @@ Gem::Specification.new do |s|
17
17
  s.files = `git ls-files`.split($/)
18
18
  s.require_paths = ["lib"]
19
19
 
20
- s.add_runtime_dependency "rails", '>= 4.0'
21
-
22
- s.add_development_dependency "pg"
20
+ s.add_runtime_dependency "activerecord", ">=4.0.0"
23
21
 
22
+ s.add_development_dependency "pg", "~>0.18"
23
+ s.add_development_dependency('rake', '~> 10.1')
24
24
  s.add_development_dependency "simplecov", ">= 0.3.8"
25
25
  s.add_development_dependency 'pry-byebug'
26
26
  s.add_development_dependency "rspec", "~> 3.1.0"
27
- s.add_development_dependency "rspec-rails", "~> 3.1.0"
28
27
  end
@@ -0,0 +1,71 @@
1
+ require 'spec_helper'
2
+
3
+ describe ArrayStore do
4
+ before do
5
+ @connection = ActiveRecord::Base.connection
6
+
7
+ @connection.transaction do
8
+ @connection.create_table('array_stores') do |t|
9
+ t.string 'tags', default: [], null: false, array: true
10
+ t.string 'name'
11
+ end
12
+ end
13
+
14
+ ArrayStore.reset_column_information
15
+ end
16
+
17
+ after do
18
+ @connection.drop_table 'array_stores', if_exists: true
19
+ end
20
+
21
+ let!(:setup) do
22
+ ArrayStore.create!(name: 'a', tags: [1, 2, 'a', 'b'])
23
+ ArrayStore.create!(name: 'b', tags: [2, 'b', 'e'])
24
+ ArrayStore.create!(name: 'c', tags: ['b'])
25
+ ArrayStore.create!(name: 'd')
26
+ ArrayStore.create!(name: 'e', tags: [2, 'x', 'c'])
27
+ end
28
+
29
+ context '#overlap' do
30
+ it "single simple argument" do
31
+ records = ArrayStore.where.store(:tags).overlap(:b)
32
+ expect(records.size).to eq 3
33
+ end
34
+
35
+ it "several arguments" do
36
+ records = ArrayStore.where.store(:tags).overlap('a', 1)
37
+ expect(records.size).to eq 1
38
+ expect(records.first.name).to eq 'a'
39
+ end
40
+
41
+ it "single array argument" do
42
+ records = ArrayStore.where.store(:tags).overlap([1, 'x'])
43
+ expect(records.size).to eq 2
44
+ end
45
+ end
46
+
47
+ it '#contains' do
48
+ records = ArrayStore.where.store(:tags).contains([2, 'b'])
49
+ expect(records.size).to eq 2
50
+
51
+ records = ArrayStore.where.store(:tags).contains([2, 'x'])
52
+ expect(records.size).to eq 1
53
+ expect(records.first.name).to eq 'e'
54
+ end
55
+
56
+ it '#contained' do
57
+ records = ArrayStore.where.store(:tags).contained([1, 2, 'a', 'b'])
58
+ expect(records.size).to eq 3
59
+ expect(records.detect { |r| r.name == 'd' }).not_to be_nil
60
+
61
+ records = ArrayStore.where.store(:tags).contained([])
62
+ expect(records.size).to eq 1
63
+ expect(records.first.name).to eq 'd'
64
+ end
65
+
66
+ context '#not' do
67
+ it '#overlap' do
68
+ expect(ArrayStore.where.store(:tags).not.overlap('b', 2).size).to eq 1
69
+ end
70
+ end
71
+ end