pgrel 0.3.0 → 0.3.1

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: 62a79ba274061f7a36972f8817c5ee5b61242af5d92d8d7ca77f776cfb1ee802
4
- data.tar.gz: f8c8da31f5ce40f8c1e3400dd154aac9f1a8309768586d7f1049e733a1e5650b
3
+ metadata.gz: 686f59a1c868b3ee3268ab06137c35dcd2f8e9fed9136bbbec2a27622401d301
4
+ data.tar.gz: a381b46c2a07d0193a5764d099e25f731ac90c17f2f9bef11ca600ac08f10c1c
5
5
  SHA512:
6
- metadata.gz: 34d502c9098aa129c3a64acd5d48d722e31d38b3ececf9d930d9629da226e73aa9077643882341e6a01a9cf1ae95158348347a3b98f2ee92319b553aede53c8e
7
- data.tar.gz: e7b38cbd1eaa94fffbecaa093caa87ebf4ae73aa128139db1efd8d022047d6775dbbf4452cb2e5ff94ccc8fc6dbc1cf0d93b29ee762230b8c2aafc9f6f450549
6
+ metadata.gz: 92b7300c636eb9f985a7a9b5b9467d1480b92ca38997d73cce0105915f1f2d50f7df5724b5b9e0c5c36e32dd5ea3999803145caa0b51a0561ba40d17603606f8
7
+ data.tar.gz: 7b7153c935e88f6084463e58639e9c297fb44dcc1d8c538a8e17e5cb3f6f836837ca7a8dbefd479dbf22ac5f4fd8e9a3400d47985a9c35b30f65238821cd0bc8
@@ -2,6 +2,10 @@
2
2
 
3
3
  ## master
4
4
 
5
+ ## 0.3.1 (2020-12-18)
6
+
7
+ - Update internal query building for Rails 6.1 compatibility. ([@zokioki][])
8
+
5
9
  ## 0.3.0 (2019-28-01)
6
10
 
7
11
  - Rename `#value` method to `#overlap_values`.
@@ -1,4 +1,4 @@
1
- Copyright 2015 palkan
1
+ Copyright 2015-2020 palkan
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -1,4 +1,5 @@
1
- [![Gem Version](https://badge.fury.io/rb/pgrel.svg)](https://rubygems.org/gems/pgrel) [![Build Status](https://travis-ci.org/palkan/pgrel.svg?branch=master)](https://travis-ci.org/palkan/pgrel)
1
+ [![Gem Version](https://badge.fury.io/rb/pgrel.svg)](https://rubygems.org/gems/pgrel)
2
+ ![Build](https://github.com/palkan/pgrel/workflows/Build/badge.svg)
2
3
 
3
4
  ## Pgrel
4
5
 
@@ -11,14 +12,14 @@ Compatible with **Rails** >= 4.2 (including **Rails 6**).
11
12
  In your Gemfile:
12
13
 
13
14
  ```ruby
14
- gem "pgrel", "~> 0.2"
15
+ gem "pgrel", "~> 0.3"
15
16
  ```
16
17
 
17
18
  ### HStore
18
19
 
19
20
  #### Querying
20
21
 
21
- The functionality is based on ActiveRecord `WhereChain`.
22
+ The functionality is based on ActiveRecord `WhereChain`.
22
23
  To start querying call `where(:store_name)` and chain it with store-specific call (see below).
23
24
 
24
25
  Query by key value:
@@ -103,7 +104,7 @@ Hstore.update_store(:tags).delete_pairs(a: 1, b: 2)
103
104
 
104
105
  All queries and updates for Hstore also available for JSONB.
105
106
 
106
- **NOTE**. Querying by array value always resolves to `(... or ...)` statement.
107
+ **NOTE**. Querying by array value always resolves to `(... or ...)` statement.
107
108
  Thus it's impossible to query json array value, e.g.:
108
109
 
109
110
  ```ruby
@@ -1,2 +1,4 @@
1
- require 'pgrel/version'
2
- require 'pgrel/active_record'
1
+ # frozen_string_literal: true
2
+
3
+ require "pgrel/version"
4
+ require "pgrel/active_record"
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'active_record'
4
- require 'pgrel/active_record/relation'
5
- require 'pgrel/active_record/store/flexible_store'
6
- require 'pgrel/active_record/store/flexible_hstore'
7
- require 'pgrel/active_record/store/flexible_jsonb'
8
- require 'pgrel/active_record/querying'
9
- require 'pgrel/active_record/query_methods'
3
+ require "active_record"
4
+ require "pgrel/active_record/relation"
5
+ require "pgrel/active_record/store/flexible_store"
6
+ require "pgrel/active_record/store/flexible_hstore"
7
+ require "pgrel/active_record/store/flexible_jsonb"
8
+ require "pgrel/active_record/querying"
9
+ require "pgrel/active_record/query_methods"
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'active_record/relation'
4
- require 'pgrel/active_record/store_chain'
5
- require 'pgrel/active_record/store_chain/array_chain'
6
- require 'pgrel/active_record/store_chain/hstore_chain'
7
- require 'pgrel/active_record/store_chain/jsonb_chain'
3
+ require "active_record/relation"
4
+ require "pgrel/active_record/store_chain"
5
+ require "pgrel/active_record/store_chain/array_chain"
6
+ require "pgrel/active_record/store_chain/hstore_chain"
7
+ require "pgrel/active_record/store_chain/jsonb_chain"
8
8
 
9
9
  module ActiveRecord
10
10
  module QueryMethods
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module Querying
3
5
  delegate :update_store, to: :all
@@ -5,7 +5,7 @@ module ActiveRecord
5
5
  def update_store(store_name)
6
6
  raise ArgumentError, "Empty store name to update" if store_name.blank?
7
7
  type = type_for_attribute(store_name.to_s).type
8
- raise TypeConflictError, store_type_error_msg(type) if %i(hstore jsonb).exclude?(type)
8
+ raise TypeConflictError, store_type_error_msg(type) if %i[hstore jsonb].exclude?(type)
9
9
  klass = "ActiveRecord::Store::Flexible#{type.capitalize}".constantize
10
10
  klass.new(self, store_name)
11
11
  end
@@ -10,13 +10,13 @@ module ActiveRecord
10
10
 
11
11
  def merge(pairs)
12
12
  relation.update_all(["#{store_name} = hstore(#{store_name}) || hstore(ARRAY[:keys])",
13
- keys: pairs.to_a.flatten.map(&:to_s)])
13
+ keys: pairs.to_a.flatten.map(&:to_s)])
14
14
  end
15
15
 
16
16
  def delete_pairs(pairs)
17
17
  relation.update_all(
18
18
  ["#{store_name} = delete(#{store_name}, hstore(ARRAY[:keys], ARRAY[:values]))",
19
- keys: pairs.keys.map(&:to_s), values: pairs.values.map(&:to_s)]
19
+ keys: pairs.keys.map(&:to_s), values: pairs.values.map(&:to_s)]
20
20
  )
21
21
  end
22
22
  end
@@ -5,8 +5,8 @@ module ActiveRecord
5
5
  class FlexibleJsonb < ActiveRecord::Store::FlexibleStore
6
6
  def delete_keys(*keys)
7
7
  keys.flatten!
8
- query = String.new "#{store_name} = #{store_name}"
9
- keys.length.times { query.concat(' - ?') }
8
+ query = +"#{store_name} = #{store_name}"
9
+ keys.length.times { query.concat(" - ?") }
10
10
  relation.update_all([query, *keys])
11
11
  end
12
12
 
@@ -16,7 +16,7 @@ module ActiveRecord
16
16
 
17
17
  def delete_pairs(pairs)
18
18
  keys = pairs.keys
19
- pairs = pairs.map { |k,v| { k => v } }
19
+ pairs = pairs.map { |k, v| {k => v} }
20
20
  @relation = relation.where.store(store_name, *pairs)
21
21
  delete_keys(keys)
22
22
  end
@@ -83,17 +83,17 @@ module ActiveRecord
83
83
  else
84
84
  contains_clause(k => v)
85
85
  end
86
- end.join(' and ')
87
- end.join(' or ')
86
+ end.join(" and ")
87
+ end.join(" or ")
88
88
  )
89
89
  @scope
90
90
  end
91
91
 
92
- if ActiveRecord.version.release >= Gem::Version.new("5")
93
- protected
92
+ protected
94
93
 
94
+ if ActiveRecord.version.release >= Gem::Version.new("5")
95
95
  def update_scope(*opts)
96
- where_clause = @scope.send(:where_clause_factory).build(opts, {})
96
+ where_clause = build_where_clause(opts)
97
97
  @scope.where_clause += @inverted ? where_clause.invert : where_clause
98
98
  @scope
99
99
  end
@@ -104,8 +104,6 @@ module ActiveRecord
104
104
  )
105
105
  end
106
106
  else
107
- protected
108
-
109
107
  def update_scope(*opts)
110
108
  where_clause = @scope.send(:build_where, opts).map do |rel|
111
109
  @inverted ? invert_arel(rel) : rel
@@ -142,7 +140,15 @@ module ActiveRecord
142
140
  end
143
141
 
144
142
  def build_or_contains(k, vals)
145
- vals.map { |v| contains_clause(k => v) }.join(' or ')
143
+ vals.map { |v| contains_clause(k => v) }.join(" or ")
144
+ end
145
+
146
+ def build_where_clause(opts)
147
+ if ActiveRecord.version.release >= Gem::Version.new("6.1.0")
148
+ @scope.send(:build_where_clause, opts)
149
+ else
150
+ @scope.send(:where_clause_factory).build(opts, [])
151
+ end
146
152
  end
147
153
  end
148
154
 
@@ -198,17 +204,30 @@ module ActiveRecord
198
204
 
199
205
  if ActiveRecord.version.release >= Gem::Version.new("5")
200
206
  def where_with_prefix(prefix, opts)
201
- where_clause = @scope.send(:where_clause_factory).build(opts, [])
202
- predicates = where_clause.ast.children.map do |rel|
203
- rel.left = to_sql_literal(prefix, rel.left)
204
- rel
207
+ where_clause = build_where_clause(opts)
208
+ where_clause_ast = where_clause.ast
209
+
210
+ # Converting `HomogenousIn` node to `In` type allows us to set its `left`
211
+ # to sql literal as with other node types (`HomogenousIn` does not support this).
212
+ if defined?(Arel::Nodes::HomogeneousIn) && where_clause_ast.is_a?(Arel::Nodes::HomogeneousIn)
213
+ where_clause_ast = Arel::Nodes::In.new(where_clause_ast.left, where_clause_ast.right)
214
+ end
215
+
216
+ predicates = if where_clause_ast.is_a?(Arel::Nodes::And)
217
+ where_clause.ast.children.map do |rel|
218
+ rel.left = to_sql_literal(prefix, rel.left)
219
+ rel
220
+ end
221
+ else
222
+ where_clause_ast.left = to_sql_literal(prefix, where_clause_ast.left)
223
+ [where_clause_ast]
205
224
  end
206
225
 
207
226
  params = if ActiveRecord.version.release >= Gem::Version.new("5.2.0")
208
- [predicates]
209
- else
210
- [predicates, where_clause.binds]
211
- end
227
+ [predicates]
228
+ else
229
+ [predicates, where_clause.binds]
230
+ end
212
231
 
213
232
  where_clause = ActiveRecord::Relation::WhereClause.new(*params)
214
233
  @scope.where_clause += @inverted ? where_clause.invert : where_clause
@@ -4,7 +4,7 @@ module ActiveRecord
4
4
  module QueryMethods
5
5
  # Store chain for jsonb columns.
6
6
  class JsonbChain < KeyStoreChain
7
- OPERATORS = { contains: '@>', overlap: '&&' }.freeze
7
+ OPERATORS = {contains: "@>", overlap: "&&"}.freeze
8
8
 
9
9
  # Query by value in path.
10
10
  #
@@ -18,17 +18,17 @@ module ActiveRecord
18
18
  args = flatten_hash(args.first) if args.size == 1
19
19
  val = args.pop
20
20
 
21
- path = "{#{args.join(',')}}"
21
+ path = "{#{args.join(",")}}"
22
22
 
23
23
  case val
24
24
  when Hash
25
- op = '#>'
25
+ op = "#>"
26
26
  val = ::ActiveSupport::JSON.encode(val)
27
27
  when Array
28
- op = '#>>'
28
+ op = "#>>"
29
29
  val = val.map(&:to_s)
30
30
  else
31
- op = '#>>'
31
+ op = "#>>"
32
32
  val = val.to_s
33
33
  end
34
34
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Pgrel # :nodoc:
4
- VERSION = "0.3.0"
4
+ VERSION = "0.3.1"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pgrel
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - palkan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-01-28 00:00:00.000000000 Z
11
+ date: 2020-12-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -28,58 +28,30 @@ dependencies:
28
28
  name: pg
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: '0.18'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - "~>"
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0.18'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rake
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - "~>"
46
- - !ruby/object:Gem::Version
47
- version: '10.1'
48
- type: :development
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - "~>"
53
- - !ruby/object:Gem::Version
54
- version: '10.1'
55
- - !ruby/object:Gem::Dependency
56
- name: simplecov
57
43
  requirement: !ruby/object:Gem::Requirement
58
44
  requirements:
59
45
  - - ">="
60
46
  - !ruby/object:Gem::Version
61
- version: 0.3.8
62
- type: :development
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - ">="
67
- - !ruby/object:Gem::Version
68
- version: 0.3.8
69
- - !ruby/object:Gem::Dependency
70
- name: pry-byebug
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - ">="
74
- - !ruby/object:Gem::Version
75
- version: '0'
47
+ version: '10.1'
76
48
  type: :development
77
49
  prerelease: false
78
50
  version_requirements: !ruby/object:Gem::Requirement
79
51
  requirements:
80
52
  - - ">="
81
53
  - !ruby/object:Gem::Version
82
- version: '0'
54
+ version: '10.1'
83
55
  - !ruby/object:Gem::Dependency
84
56
  name: rspec
85
57
  requirement: !ruby/object:Gem::Requirement
@@ -101,17 +73,9 @@ executables: []
101
73
  extensions: []
102
74
  extra_rdoc_files: []
103
75
  files:
104
- - ".gitignore"
105
- - ".rubocop.yml"
106
- - ".travis.yml"
107
76
  - CHANGELOG.md
108
- - Gemfile
109
- - MIT-LICENSE
77
+ - LICENSE.txt
110
78
  - README.md
111
- - Rakefile
112
- - gemfiles/rails42.gemfile
113
- - gemfiles/rails5.gemfile
114
- - gemfiles/railsmaster.gemfile
115
79
  - lib/pgrel.rb
116
80
  - lib/pgrel/active_record.rb
117
81
  - lib/pgrel/active_record/query_methods.rb
@@ -125,15 +89,6 @@ files:
125
89
  - lib/pgrel/active_record/store_chain/hstore_chain.rb
126
90
  - lib/pgrel/active_record/store_chain/jsonb_chain.rb
127
91
  - lib/pgrel/version.rb
128
- - pgrel.gemspec
129
- - spec/pgrel/array_spec.rb
130
- - spec/pgrel/hstore_spec.rb
131
- - spec/pgrel/jsonb_spec.rb
132
- - spec/spec_helper.rb
133
- - spec/support/array_store.rb
134
- - spec/support/hstore.rb
135
- - spec/support/jsonb.rb
136
- - spec/support/user.rb
137
92
  homepage: http://github.com/palkan/pgrel
138
93
  licenses:
139
94
  - MIT
@@ -153,8 +108,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
153
108
  - !ruby/object:Gem::Version
154
109
  version: '0'
155
110
  requirements: []
156
- rubyforge_project:
157
- rubygems_version: 2.7.6
111
+ rubygems_version: 3.0.6
158
112
  signing_key:
159
113
  specification_version: 4
160
114
  summary: ActiveRecord extension for querying hstore and jsonb.
data/.gitignore DELETED
@@ -1,37 +0,0 @@
1
- # Numerous always-ignore extensions
2
- *.diff
3
- *.err
4
- *.orig
5
- *.log
6
- *.rej
7
- *.swo
8
- *.swp
9
- *.vi
10
- *~
11
- *.sass-cache
12
- *.iml
13
- .idea/
14
-
15
- # Sublime
16
- *.sublime-project
17
- *.sublime-workspace
18
-
19
- # OS or Editor folders
20
- .DS_Store
21
- .cache
22
- .project
23
- .settings
24
- .tmproj
25
- Thumbs.db
26
- coverage/
27
-
28
- .bundle/
29
- *.log
30
- *.gem
31
- pkg/
32
- spec/dummy/log/*.log
33
- spec/dummy/tmp/
34
- spec/dummy/.sass-cache
35
- Gemfile.local
36
- Gemfile.lock
37
- .rspec_status
@@ -1,41 +0,0 @@
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
- Metrics/LineLength:
23
- Max: 100
24
-
25
- Metrics/MethodLength:
26
- Max: 15
27
-
28
- Metrics/AbcSize:
29
- Max: 20
30
-
31
- Style/BarePercentLiterals:
32
- Enabled: false
33
-
34
- Style/CommentAnnotation:
35
- Enabled: false
36
-
37
- Style/RaiseArgs:
38
- Enabled: false
39
-
40
- Style/StringLiterals:
41
- Enabled: false
@@ -1,20 +0,0 @@
1
- language: ruby
2
- cache: bundler
3
-
4
- addons:
5
- postgresql: "9.6"
6
-
7
- before_script:
8
- - createdb pgrel
9
- - psql -U postgres -d pgrel -c 'CREATE EXTENSION IF NOT EXISTS hstore;'
10
-
11
- matrix:
12
- include:
13
- - rvm: 2.6.0
14
- gemfile: gemfiles/railsmaster.gemfile
15
- - rvm: 2.6.0
16
- gemfile: gemfiles/rails5.gemfile
17
- - rvm: 2.4.3
18
- gemfile: gemfiles/rails5.gemfile
19
- - rvm: 2.3.2
20
- gemfile: gemfiles/rails42.gemfile
data/Gemfile DELETED
@@ -1,11 +0,0 @@
1
- source 'https://rubygems.org'
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', '~> 5.1'
11
- end
data/Rakefile DELETED
@@ -1,6 +0,0 @@
1
- require "bundler/gem_tasks"
2
-
3
- require 'rspec/core/rake_task'
4
- RSpec::Core::RakeTask.new(:spec)
5
-
6
- task :default => :spec
@@ -1,5 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- gem 'activerecord', "~>4.2"
4
-
5
- gemspec path: '..'
@@ -1,5 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- gem 'activerecord', '~> 5.1'
4
-
5
- gemspec path: '..'
@@ -1,5 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- gem 'rails', github: 'rails/rails'
4
-
5
- gemspec path: '..'
@@ -1,27 +0,0 @@
1
- $:.push File.expand_path("../lib", __FILE__)
2
-
3
- # Maintain your gem's version:
4
- require "pgrel/version"
5
-
6
- # Describe your gem and declare its dependencies:
7
- Gem::Specification.new do |s|
8
- s.name = "pgrel"
9
- s.version = Pgrel::VERSION
10
- s.authors = ["palkan"]
11
- s.email = ["dementiev.vm@gmail.com"]
12
- s.homepage = "http://github.com/palkan/pgrel"
13
- s.summary = "ActiveRecord extension for querying hstore and jsonb."
14
- s.description = "ActiveRecord extension for querying hstore and jsonb."
15
- s.license = "MIT"
16
-
17
- s.files = `git ls-files`.split($/)
18
- s.require_paths = ["lib"]
19
-
20
- s.add_runtime_dependency "activerecord", ">= 4.0"
21
-
22
- s.add_development_dependency "pg", "~>0.18"
23
- s.add_development_dependency 'rake', '~> 10.1'
24
- s.add_development_dependency "simplecov", ">= 0.3.8"
25
- s.add_development_dependency 'pry-byebug'
26
- s.add_development_dependency "rspec", ">= 3.1"
27
- end
@@ -1,87 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe ArrayStore do
4
- before(:all) 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 { ArrayStore.delete_all }
18
-
19
- after(:all) do
20
- @connection.drop_table 'array_stores', if_exists: true
21
- end
22
-
23
- let!(:setup) do
24
- ArrayStore.create!(name: 'a', tags: [1, 2, 'a', 'b'])
25
- ArrayStore.create!(name: 'b', tags: [2, 'b', 'e'])
26
- ArrayStore.create!(name: 'c', tags: ['b'])
27
- ArrayStore.create!(name: 'd')
28
- ArrayStore.create!(name: 'e', tags: [2, 'x', 'c'])
29
- end
30
-
31
- context '#overlap' do
32
- it "single simple argument" do
33
- records = ArrayStore.where.store(:tags).overlap(:b)
34
- expect(records.size).to eq 3
35
- end
36
-
37
- it "several arguments" do
38
- records = ArrayStore.where.store(:tags).overlap('a', 1)
39
- expect(records.size).to eq 1
40
- expect(records.first.name).to eq 'a'
41
- end
42
-
43
- it "single array argument" do
44
- records = ArrayStore.where.store(:tags).overlap([1, 'x'])
45
- expect(records.size).to eq 2
46
- end
47
- end
48
-
49
- it '#contains' do
50
- records = ArrayStore.where.store(:tags).contains([2, 'b'])
51
- expect(records.size).to eq 2
52
-
53
- records = ArrayStore.where.store(:tags).contains([2, 'x'])
54
- expect(records.size).to eq 1
55
- expect(records.first.name).to eq 'e'
56
- end
57
-
58
- it '#contained' do
59
- records = ArrayStore.where.store(:tags).contained([1, 2, 'a', 'b'])
60
- expect(records.size).to eq 3
61
- expect(records.detect { |r| r.name == 'd' }).not_to be_nil
62
-
63
- records = ArrayStore.where.store(:tags).contained([])
64
- expect(records.size).to eq 1
65
- expect(records.first.name).to eq 'd'
66
- end
67
-
68
- context '#not' do
69
- it '#overlap' do
70
- expect(ArrayStore.where.store(:tags).not.overlap('b', 2).size).to eq 1
71
- end
72
- end
73
-
74
- context "joins" do
75
- before do
76
- User.create!(name: "x", array_store: ArrayStore.find_by!(name: "a"))
77
- User.create!(name: "y", array_store: ArrayStore.find_by!(name: "b"))
78
- User.create!(name: "z", array_store: ArrayStore.find_by!(name: "c"))
79
- end
80
-
81
- it "works" do
82
- users = User.joins(:array_store).merge(ArrayStore.where.store(:tags).overlap(2))
83
- expect(users.size).to eq 2
84
- expect(users.map(&:name)).to match_array(["x", "y"])
85
- end
86
- end
87
- end
@@ -1,237 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Hstore do
4
- before(:all) do
5
- @connection = ActiveRecord::Base.connection
6
-
7
- @connection.transaction do
8
- @connection.create_table('hstores') do |t|
9
- t.hstore 'tags', default: {}, null: false
10
- t.string 'name'
11
- end
12
- end
13
-
14
- Hstore.reset_column_information
15
- end
16
-
17
- after { Hstore.delete_all }
18
-
19
- after(:all) do
20
- @connection.drop_table 'hstores', if_exists: true
21
- end
22
-
23
- let!(:setup) do
24
- Hstore.create!(name: 'a', tags: { a: 1, b: 2, f: true, d: 'a', g: 'b' })
25
- Hstore.create!(name: 'b', tags: { a: 2, d: 'b', g: 'e' })
26
- Hstore.create!(name: 'c', tags: { f: true, d: 'b' })
27
- Hstore.create!(name: 'd', tags: { f: false })
28
- Hstore.create!(name: 'e', tags: { a: 2, c: 'x', d: 'c', g: 'c' })
29
- Hstore.create!(name: 'f', tags: { i: [1, 2, { a: 1 }], j: { a: 1, b: [1], f: false } } )
30
- Hstore.create!(tags: { 1 => 2 })
31
- Hstore.create!(name: 'z', tags: { z: nil })
32
- end
33
-
34
- context '#where' do
35
- it 'simple values' do
36
- expect(Hstore.where.store(:tags, a: 1, b: 2).first.name).to eq 'a'
37
- expect(Hstore.where.store(:tags, a: 2, c: 'x').first.name).to eq 'e'
38
- expect(Hstore.where.store(:tags, f: false).first.name).to eq 'd'
39
- end
40
-
41
- it 'integer keys' do
42
- expect(Hstore.where.store(:tags, 1 => 2).size).to eq 1
43
- end
44
-
45
- it 'arrays' do
46
- expect(Hstore.where.store(:tags, a: [1, 2, 3]).size).to eq 3
47
- end
48
-
49
- it 'many arrays' do
50
- expect(
51
- Hstore.where.store(
52
- :tags,
53
- a: [1, 2, 3],
54
- c: %w(x y z),
55
- d: %w(a c),
56
- g: %w(b c)
57
- ).size
58
- ).to eq 1
59
- expect(
60
- Hstore.where.store(
61
- :tags,
62
- a: [1, 2, 3],
63
- d: %w(a c),
64
- g: %w(b c)
65
- ).size
66
- ).to eq 2
67
- end
68
-
69
- it 'lonely keys' do
70
- records = Hstore.where.store(:tags, [:z])
71
- expect(records.size).to eq 1
72
- expect(records.first.name).to eq 'z'
73
- end
74
-
75
- it 'many hashes' do
76
- expect(Hstore.where.store(:tags, { a: 2 }, { b: 2 }).size).to eq 3
77
- end
78
-
79
- it 'many hashes and lonely keys' do
80
- expect(Hstore.where.store(:tags, { a: 1 }, :z).size).to eq 2
81
- expect(Hstore.where.store(:tags, { a: 1 }, [:z]).size).to eq 2
82
- end
83
- end
84
-
85
- it '#key' do
86
- records = Hstore.where.store(:tags).key(:a)
87
- expect(records.size).to eq 3
88
-
89
- records = Hstore.where.store(:tags).key(:b)
90
- expect(records.size).to eq 1
91
- expect(records.first.name).to eq 'a'
92
- end
93
-
94
- it '#keys' do
95
- records = Hstore.where.store(:tags).keys('a', 'f')
96
- expect(records.size).to eq 1
97
- expect(records.first.name).to eq 'a'
98
-
99
- records = Hstore.where.store(:tags).keys([:a, :c])
100
- expect(records.size).to eq 1
101
- expect(records.first.name).to eq 'e'
102
- end
103
-
104
- describe '#overlap_values' do
105
- let(:records) { Hstore.where.store(:tags).overlap_values(1, false, [1, 2, { a: 1 }]) }
106
-
107
- it 'returns records with overlapping values' do
108
- expect(records.size).to eq 3
109
- end
110
-
111
- it 'calls avals function only once' do
112
- expect(records.to_sql.scan(/avals/).count).to eq 1
113
- end
114
- end
115
-
116
- it '#contains_values' do
117
- records = Hstore.where.store(:tags).contains_values(1)
118
- expect(records.size).to eq 1
119
- expect(records.first.name).to eq 'a'
120
-
121
- records = Hstore.where.store(:tags).contains_values('2', 'b')
122
- expect(records.size).to eq 2
123
-
124
- records = Hstore.where.store(:tags).contains_values(true)
125
- expect(records.size).to eq 2
126
-
127
- records = Hstore.where.store(:tags).contains_values([1, 2, { a: 1 }], { a: 1, b: [1], f: false })
128
- expect(records.size).to eq 1
129
- end
130
-
131
- it '#any' do
132
- records = Hstore.where.store(:tags).any('b', 'f')
133
- expect(records.size).to eq 3
134
-
135
- records = Hstore.where.store(:tags).any([:c, :b])
136
- expect(records.size).to eq 2
137
- end
138
-
139
- it '#contains' do
140
- records = Hstore.where.store(:tags).contains(f: true)
141
- expect(records.size).to eq 2
142
-
143
- records = Hstore.where.store(:tags).contains(a: 2, c: 'x')
144
- expect(records.size).to eq 1
145
- expect(records.first.name).to eq 'e'
146
- end
147
-
148
- it '#contained' do
149
- records = Hstore.where.store(:tags).contained(
150
- a: 2,
151
- b: 2,
152
- f: true,
153
- d: 'b',
154
- g: 'e'
155
- )
156
- expect(records.size).to eq 2
157
-
158
- records = Hstore.where.store(:tags).contained(c: 'x', f: false)
159
- expect(records.size).to eq 1
160
- expect(records.first.name).to eq 'd'
161
- end
162
-
163
- context '#not' do
164
- it '#where' do
165
- expect(Hstore.where.store(:tags).not(a: 2).size).to eq 6
166
- expect(Hstore.where.store(:tags).not(a: 1, g: 'c').size).to eq 8
167
- end
168
-
169
- it '#any' do
170
- expect(Hstore.where.store(:tags).not.any('a', 'f').size).to eq 3
171
- end
172
-
173
- it '#keys' do
174
- expect(Hstore.where.store(:tags).not.keys('a', 'f').size).to eq 7
175
- end
176
- end
177
-
178
- context "#update_store" do
179
- let(:store) { :tags }
180
-
181
- subject { Hstore.update_store(store) }
182
-
183
- it "works on relation" do
184
- Hstore.where.store(store).keys(:a).update_store(store).delete_keys(:a)
185
- expect(Hstore.where.store(store).keys(:a)).to_not exist
186
- end
187
-
188
- it "#delete_keys" do
189
- subject.delete_keys(:i)
190
- expect(Hstore.where.store(store).keys(:i)).to_not exist
191
-
192
- subject.delete_keys(:a, :b)
193
- expect(Hstore.where.store(store).keys(:a)).to_not exist
194
- expect(Hstore.where.store(store).keys(:b)).to_not exist
195
-
196
- subject.delete_keys([:c, :d])
197
- expect(Hstore.where.store(store).keys(:c)).to_not exist
198
- expect(Hstore.where.store(store).keys(:d)).to_not exist
199
- end
200
-
201
- it "#merge" do
202
- subject.merge(new_key: 1)
203
- expect(Hstore.where.store(store).keys(:new_key).count).to be_eql Hstore.count
204
-
205
- subject.merge([['new_key2', 'a'], ['new_key3', 'b']])
206
- expect(Hstore.where.store(store).keys(:new_key2, :new_key3).count).to be_eql Hstore.count
207
- end
208
-
209
- it "#delete_pairs" do
210
- subject.delete_pairs(f: true, a: 1)
211
- expect(Hstore.where.store(store, f: true)).to_not exist
212
- expect(Hstore.where.store(store, a: 1)).to_not exist
213
- expect(Hstore.where.store(store, f: false)).to exist
214
- expect(Hstore.where.store(store, a: 2)).to exist
215
- end
216
- end
217
-
218
- context "joins" do
219
- before do
220
- User.create!(name: "x", hstore: Hstore.find_by!(name: "a"))
221
- User.create!(name: "y", hstore: Hstore.find_by!(name: "b"))
222
- User.create!(name: "z", hstore: Hstore.find_by!(name: "c"))
223
- end
224
-
225
- it "works" do
226
- users = User.joins(:hstore).merge(Hstore.where.store(:tags).key(:a))
227
- expect(users.size).to eq 2
228
- expect(users.map(&:name)).to match_array(["x", "y"])
229
- end
230
-
231
- it "works with #contains_values" do
232
- users = User.joins(:hstore).merge(Hstore.where.store(:tags).contains_values(1))
233
- expect(users.size).to eq 1
234
- expect(users.map(&:name)).to match_array(["x"])
235
- end
236
- end
237
- end
@@ -1,239 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Jsonb do
4
- before(:all) do
5
- @connection = ActiveRecord::Base.connection
6
-
7
- @connection.transaction do
8
- @connection.create_table('jsonbs') do |t|
9
- t.jsonb 'tags', default: {}, null: false
10
- t.string 'name'
11
- end
12
- end
13
-
14
- Jsonb.reset_column_information
15
- end
16
-
17
- after { Jsonb.delete_all }
18
-
19
- after(:all) do
20
- @connection.drop_table 'jsonbs', if_exists: true
21
- end
22
-
23
- let!(:setup) do
24
- Jsonb.create!(name: 'a')
25
- Jsonb.create!(name: 'b', tags: { a: 1, d: { e: 2 } })
26
- Jsonb.create!(name: 'c', tags: { a: 2, b: %w(c d) })
27
- Jsonb.create!(name: 'd', tags: { a: 1, b: { c: 'd', e: true } })
28
- Jsonb.create!(name: 'e', tags: { b: 2, c: 'e' })
29
- Jsonb.create!(name: 'f', tags: { d: { e: 1, f: { h: { k: 'a', s: 2 } } } })
30
- Jsonb.create!(name: 'g', tags: { f: false, g: { a: 1, b: '1' }, h: [1, '1'] })
31
- Jsonb.create!(name: 'z', tags: { z: nil } )
32
- end
33
-
34
- context '#where' do
35
- it 'simple values' do
36
- expect(Jsonb.where.store(:tags, b: 2, c: 'e').first.name).to eq 'e'
37
- end
38
-
39
- it 'nested values' do
40
- expect(
41
- Jsonb.where.store(
42
- :tags,
43
- a: 1,
44
- b: { c: 'd', e: true }
45
- ).first.name).to eq 'd'
46
- end
47
-
48
- it 'arrays (as IN)' do
49
- expect(Jsonb.where.store(:tags, a: [1, 2, 3]).size).to eq 3
50
- end
51
-
52
- it 'lonely keys' do
53
- records = Jsonb.where.store(:tags, [:z])
54
- expect(records.size).to eq 1
55
- expect(records.first.name).to eq 'z'
56
- end
57
-
58
- it 'many hashes' do
59
- expect(Jsonb.where.store(:tags, { a: 2 }, { b: 2 }).size).to eq 2
60
- end
61
-
62
- it 'many hashes and lonely keys' do
63
- expect(Jsonb.where.store(:tags, { a: 2 }, :z).size).to eq 2
64
- expect(Jsonb.where.store(:tags, { a: 2 }, [:z]).size).to eq 2
65
- end
66
- end
67
-
68
- context '#path' do
69
- it 'pass object' do
70
- expect(Jsonb.where.store(:tags).path(b: { c: 'd' }).first.name).to eq 'd'
71
-
72
- expect(
73
- Jsonb.where.store(:tags).path(
74
- d: { f: { h: { s: 2 } } }
75
- ).first.name
76
- ).to eq 'f'
77
- end
78
-
79
- it 'pass array' do
80
- expect(Jsonb.where.store(:tags).path(:b, :c, 'd').first.name).to eq 'd'
81
- end
82
-
83
- it 'match object' do
84
- expect(
85
- Jsonb.where.store(:tags).path(:d, :f, :h, k: 'a', s: 2).first.name
86
- ).to eq 'f'
87
- end
88
-
89
- it 'match array (as IN)' do
90
- expect(Jsonb.where.store(:tags).path(:d, :e, [1, 2]).size).to eq 2
91
- expect(Jsonb.where.store(:tags).path(d: { e: [1, 2] }).size).to eq 2
92
- end
93
- end
94
-
95
- it '#key' do
96
- records = Jsonb.where.store(:tags).key(:a)
97
- expect(records.size).to eq 3
98
-
99
- records = Jsonb.where.store(:tags).key(:c)
100
- expect(records.size).to eq 1
101
- expect(records.first.name).to eq 'e'
102
- end
103
-
104
- it '#keys' do
105
- records = Jsonb.where.store(:tags).keys('a', 'b')
106
- expect(records.size).to eq 2
107
-
108
- records = Jsonb.where.store(:tags).keys([:b, :c])
109
- expect(records.size).to eq 1
110
- expect(records.first.name).to eq 'e'
111
- end
112
-
113
- describe '#overlap_values' do
114
- let(:records) { Jsonb.where.store(:tags).overlap_values(1, false, { e: 2 }) }
115
-
116
- it 'returns records with overlapping values' do
117
- expect(records.size).to eq 3
118
- end
119
-
120
- it 'calls array_agg function only once' do
121
- expect(records.to_sql.scan(/array_agg/).count).to eq 1
122
- end
123
- end
124
-
125
- it '#contains_values' do
126
- records = Jsonb.where.store(:tags).contains_values(1)
127
- expect(records.size).to eq 2
128
-
129
- records = Jsonb.where.store(:tags).contains_values(2, 'e')
130
- expect(records.size).to eq 1
131
- expect(records.first.name).to eq 'e'
132
-
133
- records = Jsonb.where.store(:tags).contains_values(e: 1, f: { h: { k: 'a', s: 2 } })
134
- expect(records.size).to eq 1
135
-
136
- records = Jsonb.where.store(:tags).contains_values(false, { a: 1, b: '1' }, [1, '1'])
137
- expect(records.size).to eq 1
138
- end
139
-
140
- it '#any' do
141
- records = Jsonb.where.store(:tags).any('a', 'b')
142
- expect(records.size).to eq 4
143
-
144
- records = Jsonb.where.store(:tags).any([:c, :b])
145
- expect(records.size).to eq 3
146
- end
147
-
148
- it '#contains' do
149
- records = Jsonb.where.store(:tags).contains(a: 1)
150
- expect(records.size).to eq 2
151
-
152
- records = Jsonb.where.store(:tags).contains(a: 1, b: { c: 'd' })
153
- expect(records.size).to eq 1
154
- expect(records.first.name).to eq 'd'
155
- end
156
-
157
- it '#contained' do
158
- records = Jsonb.where.store(:tags).contained(
159
- a: 2,
160
- b: 2,
161
- f: true,
162
- c: 'e',
163
- g: 'e'
164
- )
165
- expect(records.size).to eq 2
166
-
167
- records =
168
- Jsonb
169
- .where.store(:tags).key(:a)
170
- .where.store(:tags).contained(a: 2, b: %w(a b c d))
171
-
172
- expect(records.size).to eq 1
173
- expect(records.first.name).to eq 'c'
174
- end
175
-
176
- context '#not' do
177
- it '#path' do
178
- expect(
179
- Jsonb.where.store(:tags).not.path(:d, :f, :h, k: 'a', s: 2).size
180
- ).to eq 0
181
- end
182
- end
183
-
184
- context "#update_store" do
185
- let(:store) { :tags }
186
-
187
- subject { Jsonb.update_store(store) }
188
-
189
- it "#delete_keys" do
190
- subject.delete_keys(:e)
191
- expect(Jsonb.where.store(store).keys(:i)).to_not exist
192
-
193
- subject.delete_keys(:a, :b)
194
- expect(Jsonb.where.store(store).keys(:a)).to_not exist
195
- expect(Jsonb.where.store(store).keys(:b)).to_not exist
196
-
197
- subject.delete_keys([:c, :d])
198
- expect(Jsonb.where.store(store).keys(:c)).to_not exist
199
- expect(Jsonb.where.store(store).keys(:d)).to_not exist
200
- end
201
-
202
- it "#merge" do
203
- subject.merge(new_key: 1)
204
- expect(Jsonb.where.store(store).keys(:new_key).count).to be_eql Jsonb.count
205
- end
206
-
207
- it "#delete_pairs" do
208
- subject.delete_pairs(a: 1, d: { e: 2 })
209
- expect(Jsonb.where.store(store, a: 1)).to_not exist
210
- expect(Jsonb.where.store(store, d: { e: 2 })).to_not exist
211
- end
212
- end
213
-
214
- context "joins" do
215
- before do
216
- User.create!(name: "x", jsonb: Jsonb.find_by!(name: "a"))
217
- User.create!(name: "y", jsonb: Jsonb.find_by!(name: "b"))
218
- User.create!(name: "z", jsonb: Jsonb.find_by!(name: "c"))
219
- end
220
-
221
- it "works" do
222
- users = User.joins(:jsonb).merge(Jsonb.where.store(:tags).key(:a))
223
- expect(users.size).to eq 2
224
- expect(users.map(&:name)).to match_array(["y", "z"])
225
- end
226
-
227
- it "works with #path" do
228
- users = User.joins(:jsonb).merge(Jsonb.where.store(:tags).path(:a, 2))
229
- expect(users.size).to eq 1
230
- expect(users.map(&:name)).to match_array(["z"])
231
- end
232
-
233
- it "works with #contains_values" do
234
- users = User.joins(:jsonb).merge(Jsonb.where.store(:tags).contains_values(1))
235
- expect(users.size).to eq 1
236
- expect(users.map(&:name)).to match_array(["y"])
237
- end
238
- end
239
- end
@@ -1,44 +0,0 @@
1
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
- $LOAD_PATH.unshift(File.dirname(__FILE__))
3
-
4
- if ENV['COVER']
5
- require 'simplecov'
6
- SimpleCov.root File.join(File.dirname(__FILE__), '..')
7
- SimpleCov.start
8
- end
9
-
10
- require 'rspec'
11
- require 'pry-byebug'
12
- require 'active_record'
13
- require 'pg'
14
- require 'pgrel'
15
-
16
- ActiveRecord::Base.establish_connection(
17
- adapter: 'postgresql',
18
- host: 'localhost',
19
- database: 'pgrel'
20
- )
21
- connection = ActiveRecord::Base.connection
22
-
23
- unless connection.extension_enabled?('hstore')
24
- connection.enable_extension 'hstore'
25
- connection.commit_db_transaction
26
- end
27
-
28
- connection.reconnect!
29
-
30
- Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
31
-
32
- RSpec.configure do |config|
33
- config.example_status_persistence_file_path = '.rspec_status'
34
- config.filter_run focus: true
35
- config.run_all_when_everything_filtered = true
36
-
37
- config.order = :random
38
-
39
- config.expect_with :rspec do |c|
40
- c.syntax = :expect
41
- end
42
-
43
- config.after(:each) { User.delete_all }
44
- end
@@ -1,2 +0,0 @@
1
- class ArrayStore < ActiveRecord::Base
2
- end
@@ -1,2 +0,0 @@
1
- class Hstore < ActiveRecord::Base
2
- end
@@ -1,2 +0,0 @@
1
- class Jsonb < ActiveRecord::Base
2
- end
@@ -1,16 +0,0 @@
1
- ActiveRecord::Schema.define do
2
- create_table :users, force: true do |t|
3
- t.string :name
4
- t.string :tags
5
- t.integer :jsonb_id
6
- t.integer :array_store_id
7
- t.integer :hstore_id
8
- t.timestamps null: true
9
- end
10
- end
11
-
12
- class User < ActiveRecord::Base
13
- belongs_to :jsonb
14
- belongs_to :array_store
15
- belongs_to :hstore
16
- end