superstore 2.4.4 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
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