pgrel 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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