pgrel 0.3.0 → 0.3.1

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.
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