superstore 2.4.4 → 2.5.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 (95) hide show
  1. checksums.yaml +5 -5
  2. data/.travis.yml +4 -6
  3. data/Gemfile +2 -3
  4. data/{Gemfile-rails4.2 → Gemfile.rails6} +2 -3
  5. data/README.md +4 -34
  6. data/lib/superstore.rb +17 -18
  7. data/lib/superstore/adapters/abstract_adapter.rb +1 -27
  8. data/lib/superstore/adapters/jsonb_adapter.rb +4 -132
  9. data/lib/superstore/associations.rb +6 -1
  10. data/lib/superstore/associations/association.rb +6 -0
  11. data/lib/superstore/associations/association_scope.rb +20 -0
  12. data/lib/superstore/associations/belongs_to.rb +3 -1
  13. data/lib/superstore/associations/has_many.rb +15 -2
  14. data/lib/superstore/associations/reflection.rb +8 -2
  15. data/lib/superstore/attribute_assignment.rb +7 -0
  16. data/lib/superstore/attribute_methods.rb +1 -109
  17. data/lib/superstore/attribute_methods/primary_key.rb +20 -11
  18. data/lib/superstore/attributes.rb +13 -0
  19. data/lib/superstore/base.rb +8 -33
  20. data/lib/superstore/core.rb +7 -65
  21. data/lib/superstore/model_schema.rb +35 -0
  22. data/lib/superstore/persistence.rb +31 -115
  23. data/lib/superstore/railtie.rb +3 -11
  24. data/lib/superstore/relation/scrolling.rb +48 -0
  25. data/lib/superstore/timestamp.rb +13 -0
  26. data/lib/superstore/types.rb +11 -9
  27. data/lib/superstore/types/array_type.rb +3 -7
  28. data/lib/superstore/types/boolean_type.rb +7 -12
  29. data/lib/superstore/types/date_range_type.rb +7 -0
  30. data/lib/superstore/types/date_type.rb +7 -10
  31. data/lib/superstore/types/float_type.rb +3 -11
  32. data/lib/superstore/types/geo_point_type.rb +30 -0
  33. data/lib/superstore/types/integer_range_type.rb +19 -0
  34. data/lib/superstore/types/integer_type.rb +8 -14
  35. data/lib/superstore/types/json_type.rb +1 -1
  36. data/lib/superstore/types/range_type.rb +51 -0
  37. data/lib/superstore/types/string_type.rb +4 -4
  38. data/lib/superstore/types/time_type.rb +10 -8
  39. data/superstore.gemspec +4 -3
  40. data/test/support/jsonb.rb +3 -1
  41. data/test/support/models.rb +8 -5
  42. data/test/test_helper.rb +6 -2
  43. data/test/unit/adapters/adapter_test.rb +1 -3
  44. data/test/unit/associations/belongs_to_test.rb +1 -1
  45. data/test/unit/associations/has_many_test.rb +10 -2
  46. data/test/unit/attribute_methods/dirty_test.rb +8 -19
  47. data/test/unit/attribute_methods/primary_key_test.rb +1 -1
  48. data/test/unit/attribute_methods_test.rb +10 -22
  49. data/test/unit/{attribute_methods/typecasting_test.rb → attributes_test.rb} +13 -39
  50. data/test/unit/base_test.rb +4 -0
  51. data/test/unit/caching_test.rb +1 -1
  52. data/test/unit/callbacks_test.rb +4 -4
  53. data/test/unit/core_test.rb +9 -19
  54. data/test/unit/persistence_test.rb +17 -54
  55. data/test/unit/{scope/batches_test.rb → relation/scrolling_test.rb} +9 -5
  56. data/test/unit/serialization_test.rb +10 -2
  57. data/test/unit/{timestamps_test.rb → timestamp_test.rb} +5 -5
  58. data/test/unit/types/array_type_test.rb +3 -18
  59. data/test/unit/types/boolean_type_test.rb +7 -21
  60. data/test/unit/types/date_range_type_test.rb +28 -0
  61. data/test/unit/types/date_type_test.rb +15 -6
  62. data/test/unit/types/float_type_test.rb +4 -19
  63. data/test/unit/types/geo_point_type_test.rb +24 -0
  64. data/test/unit/types/integer_range_type_test.rb +28 -0
  65. data/test/unit/types/integer_type_test.rb +7 -16
  66. data/test/unit/types/string_type_test.rb +9 -13
  67. data/test/unit/types/time_type_test.rb +17 -11
  68. data/test/unit/validations_test.rb +2 -2
  69. metadata +39 -39
  70. data/lib/superstore/attribute_methods/definition.rb +0 -17
  71. data/lib/superstore/attribute_methods/dirty.rb +0 -52
  72. data/lib/superstore/attribute_methods/typecasting.rb +0 -53
  73. data/lib/superstore/caching.rb +0 -13
  74. data/lib/superstore/callbacks.rb +0 -29
  75. data/lib/superstore/connection.rb +0 -24
  76. data/lib/superstore/errors.rb +0 -10
  77. data/lib/superstore/inspect.rb +0 -25
  78. data/lib/superstore/model.rb +0 -38
  79. data/lib/superstore/schema.rb +0 -20
  80. data/lib/superstore/scope.rb +0 -73
  81. data/lib/superstore/scope/batches.rb +0 -27
  82. data/lib/superstore/scope/finder_methods.rb +0 -51
  83. data/lib/superstore/scope/query_methods.rb +0 -52
  84. data/lib/superstore/scoping.rb +0 -30
  85. data/lib/superstore/timestamps.rb +0 -19
  86. data/lib/superstore/type.rb +0 -16
  87. data/lib/superstore/types/base_type.rb +0 -23
  88. data/lib/superstore/validations.rb +0 -44
  89. data/test/unit/attribute_methods/definition_test.rb +0 -16
  90. data/test/unit/inspect_test.rb +0 -26
  91. data/test/unit/schema_test.rb +0 -15
  92. data/test/unit/scope/finder_methods_test.rb +0 -62
  93. data/test/unit/scope/query_methods_test.rb +0 -37
  94. data/test/unit/scoping_test.rb +0 -7
  95. data/test/unit/types/base_type_test.rb +0 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 10f632d358dc01cd4a01250b5834f563fee086f4
4
- data.tar.gz: 34ebd6d23f8d47d320bf002f9ec4b4ac8066a197
2
+ SHA256:
3
+ metadata.gz: 69f3fe9607d8df59438f381a38fa0068f7a7435666bc1a79588cbe5914896b59
4
+ data.tar.gz: 7da54acc35f5126576c35e6574e41b7cffd0eca9221ce5784948471eababe617
5
5
  SHA512:
6
- metadata.gz: aa43c12d476a6d7c2a66f51be01218d00374d91e992a73c019d0fc3a9248fda4a0aa2000d4ef52004b1e382331895f728eeb161a33ada001461e7a365bbfd19c
7
- data.tar.gz: d01463ac21d6f220a773450122b1df2b920d5eae3af50608ef60ec6b95b1b5de79968dc6521af578c2fc04d1322db5e79e2b77643d1e654835522ba66b4f5420
6
+ metadata.gz: c7dcb613c89611211e5610bd991156e6ee4fb5f5e0f87f4f0b820ca1ed6555a26de8d618c7da14efa5b10dce9ae5b43aa732ee51d81ca8603be88bd231e63052
7
+ data.tar.gz: db48bc7c88e0ab91dcc376f212b866556f0189b52841aa6491ff5a71dba83b4eaaae4e9b74c57ff759d42b6a20954571265979ea7d854de118dff589803a59e4
@@ -1,13 +1,11 @@
1
1
  language: ruby
2
- rvm:
3
- - 2.3.1
4
- sudo: required
5
- dist: trusty
2
+ rvm: 2.6.3
3
+ dist: xenial
6
4
  cache: bundler
7
5
  gemfile:
8
6
  - Gemfile
9
- - Gemfile-rails4.2
7
+ - Gemfile.rails6
10
8
  addons:
11
- postgresql: 9.5
9
+ postgresql: 9.6
12
10
  services:
13
11
  - postgresql
data/Gemfile CHANGED
@@ -1,11 +1,10 @@
1
- source "http://rubygems.org"
1
+ source 'https://rubygems.org'
2
2
  gemspec
3
3
 
4
+ gem 'rails', '~> 5.2.3'
4
5
  gem 'rake'
5
6
 
6
7
  group :test do
7
- gem 'rails'
8
8
  gem 'pg'
9
- gem 'activerecord', '~> 5.0.0'
10
9
  gem 'mocha', require: false
11
10
  end
@@ -1,11 +1,10 @@
1
- source "http://rubygems.org"
1
+ source 'https://rubygems.org'
2
2
  gemspec
3
3
 
4
+ gem 'rails', '~> 6.0.0.rc1'
4
5
  gem 'rake'
5
6
 
6
7
  group :test do
7
- gem 'rails'
8
8
  gem 'pg'
9
- gem 'activerecord', '~> 4.2.0'
10
9
  gem 'mocha', require: false
11
10
  end
data/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  # Superstore
2
- [![Build Status](https://secure.travis-ci.org/data-axle/superstore.png?rvm=2.0.0)](http://travis-ci.org/data-axle/superstore)
2
+ [![Build Status](https://travis-ci.org/data-axle/superstore.svg?branch=master)](http://travis-ci.org/data-axle/superstore)
3
3
  [![Code Climate](https://codeclimate.com/github/data-axle/superstore/badges/gpa.svg)](https://codeclimate.com/github/data-axle/superstore)
4
4
  [![Gem](https://img.shields.io/gem/v/superstore.svg?maxAge=2592000)](https://rubygems.org/gems/superstore)
5
5
 
@@ -15,27 +15,18 @@ Superstore requires PostgreSQL 9.5 or above.
15
15
  Add the following to the `Gemfile`:
16
16
 
17
17
  ```ruby
18
- gem 'pg'
19
18
  gem 'superstore'
20
19
  ```
21
20
 
22
- Add a `config/superstore.yml`:
23
-
24
- ```yaml
25
- development:
26
- adapter: jsonb
27
- ```
28
-
29
21
  Superstore will share the existing ActiveRecord database connection.
30
22
 
31
23
  ## Defining Models
32
24
 
33
25
  ```ruby
34
26
  class Widget < Superstore::Base
35
- string :name
36
- string :description
37
- integer :price
38
- array :colors, unique: true
27
+ attribute :name, type: :string
28
+ attribute :price, type: :integer
29
+ attribute :colors, type: :array
39
30
 
40
31
  validates :name, presence: :true
41
32
 
@@ -69,24 +60,3 @@ widget.price_was
69
60
  widget.save
70
61
  widget.save!
71
62
  ```
72
-
73
- ## Finding records
74
-
75
- ```ruby
76
- widget = Widget.find(uuid)
77
- widget = Widget.first
78
- widgets = Widget.all
79
- Widget.find_each do |widget|
80
- # Codez
81
- end
82
- ```
83
-
84
- ## Scoping
85
-
86
- Some lightweight scoping features are available:
87
-
88
- ```ruby
89
- Widget.where('color' => 'red')
90
- Widget.select(['name', 'color'])
91
- Widget.limit(10)
92
- ```
@@ -1,8 +1,7 @@
1
1
  require 'active_support/all'
2
2
  require 'active_model'
3
+ require 'active_record'
3
4
  require 'global_id/identification'
4
- require 'oj'
5
- require 'superstore/errors'
6
5
 
7
6
  module Superstore
8
7
  extend ActiveSupport::Autoload
@@ -10,30 +9,21 @@ module Superstore
10
9
  autoload :AttributeMethods
11
10
  autoload :Base
12
11
  autoload :Associations
13
- autoload :Caching
14
- autoload :Callbacks
12
+ autoload :AttributeAssignment
13
+ autoload :Attributes
15
14
  autoload :Connection
16
15
  autoload :Core
17
16
  autoload :Identity
18
- autoload :Inspect
19
- autoload :Model
17
+ autoload :Inheritance
18
+ autoload :ModelSchema
20
19
  autoload :Persistence
21
- autoload :Schema
22
- autoload :CassandraSchema
23
- autoload :Scope
24
- autoload :Scoping
25
- autoload :Timestamps
26
- autoload :Type
27
- autoload :Validations
20
+ autoload :Timestamp
28
21
 
29
22
  module AttributeMethods
30
23
  extend ActiveSupport::Autoload
31
24
 
32
25
  eager_autoload do
33
- autoload :Definition
34
- autoload :Dirty
35
26
  autoload :PrimaryKey
36
- autoload :Typecasting
37
27
  end
38
28
  end
39
29
 
@@ -42,13 +32,13 @@ module Superstore
42
32
 
43
33
  autoload :AbstractAdapter
44
34
  autoload :JsonbAdapter
45
- autoload :CassandraAdapter
46
35
  end
47
36
 
48
37
  module Associations
49
38
  extend ActiveSupport::Autoload
50
39
 
51
40
  autoload :Association
41
+ autoload :AssociationScope
52
42
  autoload :Reflection
53
43
  autoload :BelongsTo
54
44
  autoload :HasMany
@@ -64,16 +54,25 @@ module Superstore
64
54
  end
65
55
  end
66
56
 
57
+ module Relation
58
+ extend ActiveSupport::Autoload
59
+
60
+ autoload :Scrolling
61
+ end
62
+
67
63
  module Types
68
64
  extend ActiveSupport::Autoload
69
65
 
70
- autoload :BaseType
71
66
  autoload :ArrayType
72
67
  autoload :BooleanType
73
68
  autoload :DateType
69
+ autoload :DateRangeType
74
70
  autoload :FloatType
71
+ autoload :GeoPointType
75
72
  autoload :IntegerType
73
+ autoload :IntegerRangeType
76
74
  autoload :JsonType
75
+ autoload :RangeType
77
76
  autoload :StringType
78
77
  autoload :TimeType
79
78
  end
@@ -1,10 +1,7 @@
1
1
  module Superstore
2
2
  module Adapters
3
3
  class AbstractAdapter
4
- attr_reader :config
5
- def initialize(config)
6
- @config = config
7
- @batch_statements = nil
4
+ def initialize
8
5
  end
9
6
 
10
7
  # Read records from a instance of Superstore::Scope
@@ -22,29 +19,6 @@ module Superstore
22
19
  # Delete rows by an array of ids
23
20
  def delete(table, ids) # abstract
24
21
  end
25
-
26
- def execute_batch(statements) # abstract
27
- end
28
-
29
- def batching?
30
- !@batch_statements.nil?
31
- end
32
-
33
- def batch
34
- @batch_statements = []
35
- yield
36
- execute_batch(@batch_statements) if @batch_statements.any?
37
- ensure
38
- @batch_statements = nil
39
- end
40
-
41
- def execute_batchable(statement)
42
- if @batch_statements
43
- @batch_statements << statement
44
- else
45
- execute statement
46
- end
47
- end
48
22
  end
49
23
  end
50
24
  end
@@ -4,86 +4,6 @@ require 'pg'
4
4
  module Superstore
5
5
  module Adapters
6
6
  class JsonbAdapter < AbstractAdapter
7
- class QueryBuilder
8
- def initialize(adapter, scope)
9
- @adapter = adapter
10
- @scope = scope
11
- end
12
-
13
- def to_query
14
- [
15
- "SELECT #{select_string}",
16
- from_string,
17
- where_string,
18
- order_string,
19
- limit_string
20
- ].delete_if(&:blank?) * ' '
21
- end
22
-
23
- def from_string
24
- "FROM #{@scope.klass.table_name}"
25
- end
26
-
27
- def select_string
28
- if @scope.select_values.empty?
29
- '*'
30
- elsif @scope.select_values == [@adapter.primary_key_column]
31
- @adapter.primary_key_column
32
- else
33
- selects = @scope.select_values.map { |key| "#{@adapter.quote(key)},document->#{@adapter.quote(key)}" }
34
- "#{@adapter.primary_key_column}, json_build_object(#{selects * ','}) as document"
35
- end
36
- end
37
-
38
- def where_string
39
- wheres = where_values_as_strings
40
-
41
- if @scope.id_values.any?
42
- wheres << @adapter.create_ids_where_clause(@scope.id_values)
43
- end
44
-
45
- if wheres.any?
46
- "WHERE #{wheres * ' AND '}"
47
- end
48
- end
49
-
50
- def order_string
51
- if @scope.order_values.any?
52
- orders = @scope.order_values.join(', ')
53
- "ORDER BY #{orders}"
54
- elsif @scope.id_values.many?
55
- id_orders = @scope.id_values.map { |id| "ID=#{@adapter.quote(id)} DESC" }.join(',')
56
- "ORDER BY #{id_orders}"
57
- end
58
- end
59
-
60
- def limit_string
61
- if @scope.limit_value
62
- "LIMIT #{@scope.limit_value}"
63
- end
64
- end
65
-
66
- def where_values_as_strings
67
- @scope.where_values.map do |where_value|
68
- if where_value.is_a?(Hash)
69
- key = where_value.keys.first
70
- value = where_value.values.first
71
-
72
- if value.nil?
73
- "(document->>'#{key}') IS NULL"
74
- elsif value.is_a?(Array)
75
- typecasted_values = value.map { |v| "'#{v}'" }.join(',')
76
- "document->>'#{key}' IN (#{typecasted_values})"
77
- else
78
- "document->>'#{key}' = '#{value}'"
79
- end
80
- else
81
- where_value
82
- end
83
- end
84
- end
85
- end
86
-
87
7
  PRIMARY_KEY_COLUMN = 'id'.freeze
88
8
  def primary_key_column
89
9
  PRIMARY_KEY_COLUMN
@@ -105,39 +25,10 @@ module Superstore
105
25
  connection.execute statement
106
26
  end
107
27
 
108
- def to_ids(scope)
109
- statement = QueryBuilder.new(self, scope.select(primary_key_column)).to_query
110
- connection.select_values(statement)
111
- end
112
-
113
- def select(scope)
114
- statement = QueryBuilder.new(self, scope).to_query
115
-
116
- connection.execute(statement).each do |result|
117
- yield result[primary_key_column], Oj.compat_load(result['document'])
118
- end
119
- end
120
-
121
- def scroll(scope, batch_size)
122
- statement = QueryBuilder.new(self, scope).to_query
123
- cursor_name = "cursor_#{SecureRandom.hex(6)}"
124
- fetch_sql = "FETCH FORWARD #{batch_size} FROM #{cursor_name}"
125
-
126
- connection.transaction do
127
- connection.execute "DECLARE #{cursor_name} NO SCROLL CURSOR FOR (#{statement})"
128
-
129
- while (batch = connection.execute(fetch_sql)).any?
130
- batch.each do |result|
131
- yield result[primary_key_column], Oj.compat_load(result['document'])
132
- end
133
- end
134
- end
135
- end
136
-
137
28
  def insert(table, id, attributes)
138
29
  not_nil_attributes = attributes.reject { |key, value| value.nil? }
139
30
  statement = "INSERT INTO #{table} (#{primary_key_column}, document) VALUES (#{quote(id)}, #{to_quoted_jsonb(not_nil_attributes)})"
140
- execute_batchable statement
31
+ execute statement
141
32
  end
142
33
 
143
34
  def update(table, id, attributes)
@@ -157,31 +48,13 @@ module Superstore
157
48
 
158
49
  statement = "UPDATE #{table} SET document = #{value_update} WHERE #{primary_key_column} = #{quote(id)}"
159
50
 
160
- execute_batchable statement
51
+ execute statement
161
52
  end
162
53
 
163
54
  def delete(table, ids)
164
55
  statement = "DELETE FROM #{table} WHERE #{create_ids_where_clause(ids)}"
165
56
 
166
- execute_batchable statement
167
- end
168
-
169
- def execute_batch(statements)
170
- connection.transaction do
171
- execute(statements * ";\n")
172
- end
173
- end
174
-
175
- def create_table(table_name, options = {})
176
- ActiveRecord::Migration.create_table table_name, id: false do |t|
177
- t.string :id, null: false
178
- t.jsonb :document, null: false
179
- end
180
- connection.execute "ALTER TABLE \"#{table_name}\" ADD CONSTRAINT #{table_name}_pkey PRIMARY KEY (id)"
181
- end
182
-
183
- def drop_table(table_name)
184
- ActiveRecord::Migration.drop_table table_name
57
+ execute statement
185
58
  end
186
59
 
187
60
  def create_ids_where_clause(ids)
@@ -204,9 +77,8 @@ module Superstore
204
77
  "ARRAY[#{quoted_fields}]"
205
78
  end
206
79
 
207
- OJ_OPTIONS = {mode: :compat}
208
80
  def to_quoted_jsonb(data)
209
- "#{quote(Oj.dump(data, OJ_OPTIONS))}::JSONB"
81
+ "#{quote(JSON.generate(data))}::JSONB"
210
82
  end
211
83
  end
212
84
  end
@@ -2,7 +2,12 @@ module Superstore
2
2
  module Associations
3
3
  extend ActiveSupport::Concern
4
4
 
5
- module ClassMethods
5
+ included do
6
+ include ActiveRecord::Associations
7
+ extend ClassOverrides
8
+ end
9
+
10
+ module ClassOverrides
6
11
  # === Options
7
12
  # [:class_name]
8
13
  # Use if the class cannot be inferred from the association
@@ -7,6 +7,7 @@ module Superstore
7
7
  def initialize(owner, reflection)
8
8
  @owner = owner
9
9
  @reflection = reflection
10
+ reset
10
11
  end
11
12
 
12
13
  def association_class
@@ -33,6 +34,11 @@ module Superstore
33
34
  def loaded!
34
35
  @loaded = true
35
36
  end
37
+
38
+ def reset
39
+ @loaded = false
40
+ @target = nil
41
+ end
36
42
  end
37
43
  end
38
44
  end