pg_tags_on 0.1.1 → 0.3.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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +22 -2
  3. data/LICENSE.txt +1 -1
  4. data/README.md +30 -22
  5. data/lib/pg_tags_on/active_record/base.rb +1 -1
  6. data/lib/pg_tags_on/repositories/array_jsonb_repository.rb +47 -24
  7. data/lib/pg_tags_on/repositories/array_repository.rb +31 -18
  8. data/lib/pg_tags_on/repositories/base_repository.rb +39 -4
  9. data/lib/pg_tags_on/validations/validator.rb +4 -4
  10. data/lib/pg_tags_on/version.rb +1 -1
  11. metadata +21 -137
  12. data/.gitignore +0 -11
  13. data/.rspec +0 -3
  14. data/.rubocop.yml +0 -9
  15. data/CODE_OF_CONDUCT.md +0 -74
  16. data/Gemfile +0 -6
  17. data/Gemfile.lock +0 -91
  18. data/Rakefile +0 -16
  19. data/bin/console +0 -16
  20. data/bin/setup +0 -8
  21. data/pg_tags_on.gemspec +0 -38
  22. data/spec/array_integers/records_spec.rb +0 -47
  23. data/spec/array_integers/tag_ops_spec.rb +0 -65
  24. data/spec/array_integers/taggings_spec.rb +0 -27
  25. data/spec/array_integers/tags_spec.rb +0 -53
  26. data/spec/array_jsonb/records_spec.rb +0 -89
  27. data/spec/array_jsonb/tag_ops_spec.rb +0 -115
  28. data/spec/array_jsonb/taggings_spec.rb +0 -27
  29. data/spec/array_jsonb/tags_spec.rb +0 -41
  30. data/spec/array_strings/records_spec.rb +0 -61
  31. data/spec/array_strings/tag_ops_spec.rb +0 -65
  32. data/spec/array_strings/taggings_spec.rb +0 -27
  33. data/spec/array_strings/tags_spec.rb +0 -54
  34. data/spec/config/database.yml +0 -6
  35. data/spec/configuration_spec.rb +0 -48
  36. data/spec/helpers/database_helpers.rb +0 -46
  37. data/spec/spec_helper.rb +0 -39
  38. data/spec/support/factory.rb +0 -47
  39. data/spec/tags_query_spec.rb +0 -31
  40. data/spec/validator_spec.rb +0 -40
  41. data/tasks/benchmark.rake +0 -58
@@ -1,27 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe 'ArrayJsonb::Taggings' do
4
- before(:all) do
5
- @column = :tags_jsonb
6
- Entity.pg_tags_on @column, key: :name
7
- truncate && Factory.array_jsonb
8
- end
9
-
10
- let(:column) { @column }
11
- let(:ref) { %("#{Entity.table_name}"."#{column}") }
12
- let(:relation) { Entity.send(column) }
13
-
14
- it 'find all taggings' do
15
- taggings = relation.taggings.order('name')
16
-
17
- expect(taggings.size).to be_eql(6)
18
- expect(taggings.map(&:name)).to be_eql(%w[a b b c c d])
19
- end
20
-
21
- it 'find all taggings for filtered records' do
22
- taggings = Entity.where(attr: 'test2').send(column).taggings.order('name')
23
-
24
- expect(taggings.size).to be_eql(2)
25
- expect(taggings.map(&:name)).to be_eql(%w[b c])
26
- end
27
- end
@@ -1,41 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe 'ArrayJsonb::Tags' do
4
- before(:all) do
5
- @column = :tags_jsonb
6
- Entity.pg_tags_on @column, key: :name
7
- truncate && Factory.array_jsonb
8
- end
9
-
10
- let(:column) { @column }
11
- let(:ref) { %("#{Entity.table_name}"."#{column}") }
12
- let(:relation) { Entity.send(column) }
13
-
14
- it 'find all tags' do
15
- tags = relation.all.order('name')
16
-
17
- expect(tags.size).to be_eql(4)
18
- expect(tags.map(&:name)).to be_eql(%w[a b c d])
19
- end
20
-
21
- it 'find all tags for filtered records' do
22
- tags = Entity.where(attr: 'test2').send(column).all
23
-
24
- expect(tags.size).to be_eql(2)
25
- expect(tags.map(&:name)).to be_eql(%w[b c])
26
- end
27
-
28
- it 'find all tags with counts' do
29
- tag = relation.all_with_counts.first
30
-
31
- expect(tag.count).to be_eql(1)
32
- end
33
-
34
- it 'count tags' do
35
- expect(relation.count).to be_eql(4)
36
- end
37
-
38
- it 'count tags for filtered records' do
39
- expect(Entity.where(attr: 'test2').send(column).count).to be_eql(2)
40
- end
41
- end
@@ -1,61 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe 'ArrayStrings::Records' do
4
- before(:all) do
5
- @column = :tags_str # defined as instance var as it is used in before_all callback
6
- Entity.pg_tags_on @column
7
- truncate && Factory.array_strings
8
- end
9
-
10
- let(:column) { @column }
11
- let(:ref) { %("#{Entity.table_name}"."#{column}") }
12
- let(:relation) { Entity.send(column) }
13
-
14
- it 'find records by tag' do
15
- rel = Entity.where(column => Tags.one('b'))
16
-
17
- expect(rel.to_sql).to include(%(#{ref} @> '{b}'))
18
- expect(rel.count).to be_eql(2)
19
- end
20
-
21
- it 'find records without tag' do
22
- rel = Entity.where.not(column => Tags.one('b'))
23
-
24
- expect(rel.to_sql).to include(%(NOT (#{ref} @> '{b}')))
25
- expect(rel.count).to be_eql(1)
26
- end
27
-
28
- it 'find records with exact same tags' do
29
- rel = Entity.where(column => Tags.eq(%w[c b]))
30
-
31
- expect(rel.count).to be_eql(1)
32
- end
33
-
34
- it 'find records with all tags' do
35
- rel = Entity.where(column => Tags.all(%w[a b c]))
36
-
37
- expect(rel.to_sql).to include(%(#{ref} @> '{a,b,c}'))
38
- expect(rel.count).to be_eql(1)
39
- end
40
-
41
- it 'find records without tags' do
42
- rel = Entity.where.not(column => Tags.all(%w[a b]))
43
-
44
- expect(rel.to_sql).to include(%(NOT (#{ref} @> '{a,b}')))
45
- expect(rel.count).to be_eql(2)
46
- end
47
-
48
- it 'find records with any tag' do
49
- rel = Entity.where(column => Tags.any(%w[a b c]))
50
-
51
- expect(rel.to_sql).to include(%(#{ref} && '{a,b,c}'))
52
- expect(rel.count).to be_eql(2)
53
- end
54
-
55
- it 'find records with tags included in tag list' do
56
- rel = Entity.where(column => Tags.in(%w[a b c]))
57
-
58
- expect(rel.to_sql).to include(%(#{ref} <@ '{a,b,c}'))
59
- expect(rel.count).to be_eql(2)
60
- end
61
- end
@@ -1,65 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe 'ArrayStrings::TagOps' do
4
- before(:all) do
5
- @column = :tags_str # defined as instance var as it is used in before_all callback
6
- Entity.pg_tags_on @column
7
- end
8
-
9
- before do
10
- truncate && Factory.array_strings
11
- end
12
-
13
- let(:column) { @column }
14
- let(:ref) { %("#{Entity.table_name}"."#{column}") }
15
- let(:relation) { Entity.send(column) }
16
-
17
- context 'create' do
18
- it 'create tag' do
19
- relation.create('new-tag1')
20
-
21
- Entity.all.each do |entity|
22
- expect(entity.send(column)).to include('new-tag1')
23
- end
24
- end
25
-
26
- it 'create tag for filtered records' do
27
- Entity.where(attr: 'test2').send(column).create('new-tag2')
28
-
29
- expect(Entity.find_by_attr('test1').send(column)).not_to include('new-tag2')
30
- expect(Entity.find_by_attr('test2').send(column)).to include('new-tag2')
31
- end
32
- end
33
-
34
- context 'update' do
35
- it 'update tag' do
36
- relation.update('b', 'updated-b')
37
-
38
- count = Entity.where(column => Tags.all('updated-b')).count
39
- expect(count).to be_eql(2)
40
- end
41
-
42
- it 'update tag for filtered records' do
43
- Entity.where(attr: 'test2').send(column).update('c', 'updated-c')
44
-
45
- count = Entity.where(column => Tags.all('updated-c')).count
46
- expect(count).to be_eql(1)
47
- end
48
- end
49
-
50
- context 'delete' do
51
- it 'delete tag' do
52
- relation.delete('b')
53
-
54
- tags = relation.all.pluck(:name)
55
- expect(tags).not_to include('b')
56
- end
57
-
58
- it 'delete tag for filtered records' do
59
- Entity.where(attr: 'test2').send(column).delete('c')
60
-
61
- expect(Entity.find_by_attr('test1').send(column)).to include('c')
62
- expect(Entity.find_by_attr('test2').send(column)).not_to include('c')
63
- end
64
- end
65
- end
@@ -1,27 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe 'ArrayStrings::Taggings' do
4
- before(:all) do
5
- @column = :tags_str # defined as instance var as it is used in before_all callback
6
- Entity.pg_tags_on @column
7
- truncate && Factory.array_strings
8
- end
9
-
10
- let(:column) { @column }
11
- let(:ref) { %("#{Entity.table_name}"."#{column}") }
12
- let(:relation) { Entity.send(column) }
13
-
14
- it 'find all taggings' do
15
- taggings = relation.taggings.order('name')
16
-
17
- expect(taggings.size).to be_eql(6)
18
- expect(taggings.map(&:name)).to be_eql(%w[a b b c c d])
19
- end
20
-
21
- it 'find all taggings for filtered records' do
22
- taggings = Entity.where(attr: 'test2').send(column).taggings.order('name')
23
-
24
- expect(taggings.size).to be_eql(2)
25
- expect(taggings.map(&:name)).to be_eql(%w[b c])
26
- end
27
- end
@@ -1,54 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe 'ArrayStrings::Tags' do
4
- before(:all) do
5
- @column = :tags_str # defined as instance var as it is used in before_all callback
6
- Entity.pg_tags_on @column
7
- truncate && Factory.array_strings
8
- end
9
-
10
- let(:column) { @column }
11
- let(:ref) { %("#{Entity.table_name}"."#{column}") }
12
- let(:relation) { Entity.send(column) }
13
-
14
- it 'find all tags' do
15
- tags = relation.all.order('name')
16
-
17
- expect(tags.size).to be_eql(4)
18
- expect(tags.map(&:name)).to be_eql(%w[a b c d])
19
- end
20
-
21
- it 'find all tags for filtered records' do
22
- tags = Entity.where(attr: 'test2').send(column).all
23
-
24
- expect(tags.size).to be_eql(2)
25
- expect(tags.map(&:name)).to be_eql(%w[b c])
26
- end
27
-
28
- it 'find all tags with counts' do
29
- tag = relation.all_with_counts.first
30
-
31
- expect(tag.count).to be_eql(1)
32
- end
33
-
34
- it 'count tags' do
35
- expect(relation.count).to be_eql(4)
36
- end
37
-
38
- it 'count tags for filtered records' do
39
- expect(Entity.where(attr: 'test2').send(column).count).to be_eql(2)
40
- end
41
-
42
- context 'cast integers to strings' do
43
- before do
44
- Entity.create(attr: 'int1', column => [1])
45
- end
46
-
47
- it 'find tags in numeric format' do
48
- tags = relation.all.where(name: 1)
49
-
50
- expect(tags.size).to be_eql(1)
51
- expect(tags.map(&:name)).to be_eql(['1'])
52
- end
53
- end
54
- end
@@ -1,6 +0,0 @@
1
- adapter: postgresql
2
- host: <%= ENV.fetch("PG_HOST") { "localhost" } %>
3
- port: <%= ENV.fetch("PG_PORT") { 5432 } %>
4
- username: <%= ENV.fetch("PG_USER") { raise "PG_USER env. not set" } %>
5
- password: <%= ENV.fetch("PG_PSWD") { raise "PG_PSWD env. not set" } %>
6
- database: <%= ENV.fetch("PG_TAGS_ON_DB") { raise "PG_TAGS_ON_DB env. not set" } %>
@@ -1,48 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe 'configuration' do
4
- before do
5
- Entity.pg_tags_on_reset
6
- end
7
-
8
- it 'should set query class' do
9
- PgTagsOn.configure do |c|
10
- c.query_class = 'Tagz'
11
- end
12
- Entity.pg_tags_on :tags_int
13
-
14
- expect(Kernel.const_defined?('Tagz')).to be_truthy
15
- end
16
-
17
- it 'should set columns for multiple models' do
18
- class Entity1 < ActiveRecord::Base
19
- self.table_name = 'entities'
20
- pg_tags_on :tags_int
21
- end
22
-
23
- class Entity2 < ActiveRecord::Base
24
- self.table_name = 'entities'
25
- pg_tags_on :tags_str
26
- end
27
-
28
- expect(Entity1.pg_tags_on_settings).to be_eql('tags_int' => {})
29
- expect(Entity2.pg_tags_on_settings).to be_eql('tags_str' => {})
30
- end
31
-
32
- it 'should set multiple columns for the same model' do
33
- Entity.pg_tags_on :tags_int
34
- Entity.pg_tags_on :tags_str
35
-
36
- expect(Entity.pg_tags_on_settings).to be_eql('tags_int' => {}, 'tags_str' => {})
37
- end
38
-
39
- it 'should raise error if column does not exists' do
40
- expect { Entity.pg_tags_on(:dummy) }.to raise_error(PgTagsOn::ColumnNotFoundError)
41
- end
42
-
43
- it 'should create named scopes' do
44
- Entity.pg_tags_on :tags_int
45
-
46
- expect(Entity).to respond_to(:tags_int)
47
- end
48
- end
@@ -1,46 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module DatabaseHelpers
4
- def establish_connection
5
- ActiveRecord::Base.establish_connection(config)
6
- end
7
-
8
- def config
9
- @config ||= begin
10
- file = File.expand_path('../config/database.yml', File.dirname(__FILE__))
11
- YAML.safe_load(ERB.new(File.read(file)).result)
12
- end
13
- end
14
-
15
- def load_schema
16
- @connection = ActiveRecord::Base.connection
17
- @connection.drop_table :entities
18
- @connection.transaction do
19
- @connection.create_table :entities, force: true do |t|
20
- t.integer :tags_int, array: true
21
- t.string :tags_str, array: true
22
- t.text :tags_text, array: true
23
- t.jsonb :tags_jsonb, array: true
24
- t.string :attr
25
- end
26
- @connection.add_index :entities, :tags_str, using: 'gin'
27
- end
28
- end
29
-
30
- def truncate
31
- ActiveRecord::Base.connection.truncate(Entity.table_name)
32
- end
33
-
34
- def encode_json(json)
35
- case json
36
- when String
37
- ActiveSupport::JSON.encode(json)
38
- when Hash
39
- ActiveSupport::JSON.encode(json.to_json)
40
- when Array
41
- "'{" + json.map { |item| encode_json(item) }.join(',') + "}'"
42
- end
43
- end
44
-
45
- module_function :config, :establish_connection, :load_schema, :truncate
46
- end
data/spec/spec_helper.rb DELETED
@@ -1,39 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- WITH_COVERAGE = ENV['COVERAGE']
4
-
5
- if WITH_COVERAGE
6
- require 'simplecov'
7
- SimpleCov.start do
8
- add_filter %r{/benchmark/}
9
- end
10
- end
11
-
12
- require 'pg'
13
- require 'active_record'
14
- require 'bundler/setup'
15
- require 'pg_tags_on'
16
- require 'helpers/database_helpers'
17
- require 'support/factory'
18
- require 'pry'
19
-
20
- RSpec.configure do |config|
21
- # Enable flags like --only-failures and --next-failure
22
- config.example_status_persistence_file_path = '.rspec_status'
23
-
24
- # Disable RSpec exposing methods globally on `Module` and `main`
25
- config.disable_monkey_patching!
26
-
27
- config.expect_with :rspec do |c|
28
- c.syntax = :expect
29
- end
30
-
31
- config.include DatabaseHelpers
32
-
33
- config.before(:all) do
34
- DatabaseHelpers.establish_connection
35
- DatabaseHelpers.load_schema
36
- end
37
- end
38
-
39
- Entity = Class.new(::ActiveRecord::Base)
@@ -1,47 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class Factory
4
- def self.array_strings
5
- Entity.insert_all([
6
- { tags_str: %w[a b c], attr: 'test1' },
7
- { tags_str: %w[b c], attr: 'test2' },
8
- { tags_str: ['d'], attr: 'test3' }
9
- ])
10
- end
11
-
12
- def self.array_integers
13
- Entity.insert_all([
14
- { tags_int: [1, 2, 3], attr: 'test1' },
15
- { tags_int: [2, 3], attr: 'test2' },
16
- { tags_int: [4], attr: 'test3' }
17
- ])
18
- end
19
-
20
- def self.array_jsonb
21
- Entity.insert_all([
22
- { tags_jsonb: [{ name: 'a' },
23
- { name: 'b' },
24
- { name: 'c' }],
25
- attr: 'test1' },
26
- { tags_jsonb: [{ name: 'b' },
27
- { name: 'c' }],
28
- attr: 'test2' },
29
- { tags_jsonb: [{ name: 'd' }],
30
- attr: 'test3' }
31
- ])
32
- end
33
-
34
- def self.array_jsonb_with_attrs
35
- Entity.insert_all([
36
- { tags_jsonb: [{ name: 'a', meta: 'a' },
37
- { name: 'b', meta: 'b' },
38
- { name: 'c', meta: 'c' }],
39
- attr: 'test1' },
40
- { tags_jsonb: [{ name: 'b', meta: 'b' },
41
- { name: 'c', meta: 'c' }],
42
- attr: 'test2' },
43
- { tags_jsonb: [{ name: 'd', meta: 'd' }],
44
- attr: 'test3' }
45
- ])
46
- end
47
- end