superstore 2.4.3 → 3.0.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.
- checksums.yaml +5 -5
- data/.github/workflows/ruby.yml +40 -0
- data/.gitignore +1 -0
- data/.simplecov +3 -0
- data/Gemfile +2 -3
- data/LICENSE +1 -1
- data/MIT-LICENSE +1 -1
- data/README.md +4 -34
- data/lib/superstore/adapters/abstract_adapter.rb +1 -27
- data/lib/superstore/adapters/jsonb_adapter.rb +4 -132
- data/lib/superstore/associations/association.rb +6 -0
- data/lib/superstore/associations/association_scope.rb +20 -0
- data/lib/superstore/associations/belongs_to.rb +3 -1
- data/lib/superstore/associations/builder/association.rb +1 -1
- data/lib/superstore/associations/has_many.rb +15 -2
- data/lib/superstore/associations/reflection.rb +8 -2
- data/lib/superstore/associations.rb +9 -4
- data/lib/superstore/attribute_assignment.rb +7 -0
- data/lib/superstore/attribute_methods/primary_key.rb +20 -11
- data/lib/superstore/attribute_methods.rb +1 -109
- data/lib/superstore/attributes.rb +13 -0
- data/lib/superstore/base.rb +6 -34
- data/lib/superstore/core.rb +7 -65
- data/lib/superstore/model_schema.rb +35 -0
- data/lib/superstore/persistence.rb +26 -115
- data/lib/superstore/railtie.rb +3 -11
- data/lib/superstore/relation/scrolling.rb +48 -0
- data/lib/superstore/types/array_type.rb +3 -7
- data/lib/superstore/types/base.rb +9 -0
- data/lib/superstore/types/boolean_type.rb +7 -12
- data/lib/superstore/types/date_range_type.rb +7 -0
- data/lib/superstore/types/date_type.rb +7 -10
- data/lib/superstore/types/float_type.rb +3 -11
- data/lib/superstore/types/geo_point_type.rb +30 -0
- data/lib/superstore/types/integer_range_type.rb +15 -0
- data/lib/superstore/types/integer_type.rb +8 -14
- data/lib/superstore/types/json_type.rb +1 -1
- data/lib/superstore/types/range_type.rb +58 -0
- data/lib/superstore/types/string_type.rb +4 -4
- data/lib/superstore/types/time_type.rb +10 -8
- data/lib/superstore/types.rb +11 -9
- data/lib/superstore.rb +17 -19
- data/superstore.gemspec +8 -10
- data/test/support/jsonb.rb +3 -1
- data/test/support/models.rb +10 -4
- data/test/test_helper.rb +7 -3
- data/test/unit/adapters/adapter_test.rb +1 -3
- data/test/unit/associations/belongs_to_test.rb +1 -1
- data/test/unit/associations/has_many_test.rb +10 -2
- data/test/unit/attribute_methods/dirty_test.rb +8 -19
- data/test/unit/attribute_methods/primary_key_test.rb +1 -1
- data/test/unit/attribute_methods_test.rb +10 -22
- data/test/unit/{attribute_methods/typecasting_test.rb → attributes_test.rb} +13 -39
- data/test/unit/base_test.rb +4 -0
- data/test/unit/caching_test.rb +1 -1
- data/test/unit/callbacks_test.rb +4 -4
- data/test/unit/core_test.rb +15 -20
- data/test/unit/persistence_test.rb +36 -44
- data/test/unit/{scope/batches_test.rb → relation/scrolling_test.rb} +9 -5
- data/test/unit/serialization_test.rb +10 -2
- data/test/unit/{timestamps_test.rb → timestamp_test.rb} +5 -5
- data/test/unit/types/array_type_test.rb +3 -18
- data/test/unit/types/boolean_type_test.rb +7 -21
- data/test/unit/types/date_range_type_test.rb +29 -0
- data/test/unit/types/date_type_test.rb +15 -6
- data/test/unit/types/float_type_test.rb +4 -19
- data/test/unit/types/geo_point_type_test.rb +28 -0
- data/test/unit/types/integer_range_type_test.rb +28 -0
- data/test/unit/types/integer_type_test.rb +7 -16
- data/test/unit/types/string_type_test.rb +9 -13
- data/test/unit/types/time_type_test.rb +17 -11
- data/test/unit/validations_test.rb +2 -2
- metadata +39 -53
- data/.travis.yml +0 -13
- data/Gemfile-rails4.2 +0 -11
- data/lib/superstore/attribute_methods/definition.rb +0 -17
- data/lib/superstore/attribute_methods/dirty.rb +0 -52
- data/lib/superstore/attribute_methods/typecasting.rb +0 -53
- data/lib/superstore/caching.rb +0 -13
- data/lib/superstore/callbacks.rb +0 -29
- data/lib/superstore/connection.rb +0 -24
- data/lib/superstore/errors.rb +0 -10
- data/lib/superstore/inspect.rb +0 -25
- data/lib/superstore/model.rb +0 -38
- data/lib/superstore/schema.rb +0 -20
- data/lib/superstore/scope/batches.rb +0 -27
- data/lib/superstore/scope/finder_methods.rb +0 -51
- data/lib/superstore/scope/query_methods.rb +0 -52
- data/lib/superstore/scope.rb +0 -73
- data/lib/superstore/scoping.rb +0 -30
- data/lib/superstore/timestamps.rb +0 -19
- data/lib/superstore/type.rb +0 -16
- data/lib/superstore/types/base_type.rb +0 -23
- data/lib/superstore/validations.rb +0 -44
- data/test/unit/attribute_methods/definition_test.rb +0 -16
- data/test/unit/inspect_test.rb +0 -26
- data/test/unit/schema_test.rb +0 -15
- data/test/unit/scope/finder_methods_test.rb +0 -62
- data/test/unit/scope/query_methods_test.rb +0 -36
- data/test/unit/scoping_test.rb +0 -7
- data/test/unit/types/base_type_test.rb +0 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 4b50e7d651ea05c7214a0407aca4c530eee09f8ba6e8d867d636400f1a22bb42
|
4
|
+
data.tar.gz: 00d665bc855ad9cfcdf77ceaaefb6d674a5964611eb082ea0e0e0d0da880ac84
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d819bb08f1c070d18bdccd76caf941718947ea0c85191813f8896bc86d6c61d49f4306e3cea816514e05e635299d28bd663bc4515bb1ca90ae01bd7454db49bc
|
7
|
+
data.tar.gz: 014e964e51adb9c2791af3598435e6f7764b9e6bc80edab030f804c84944213eb68e423d7e9daf527f42c92ed98fa7da4421be5f55e4f1a315fb13904dfe3921
|
@@ -0,0 +1,40 @@
|
|
1
|
+
name: Ruby
|
2
|
+
on:
|
3
|
+
push:
|
4
|
+
branches: [ master ]
|
5
|
+
pull_request:
|
6
|
+
branches: [ master ]
|
7
|
+
jobs:
|
8
|
+
test:
|
9
|
+
runs-on: ubuntu-20.04
|
10
|
+
strategy:
|
11
|
+
matrix:
|
12
|
+
ruby: ['3.1']
|
13
|
+
env:
|
14
|
+
CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}
|
15
|
+
services:
|
16
|
+
postgres:
|
17
|
+
image: postgres:12.8
|
18
|
+
options: >-
|
19
|
+
--health-cmd pg_isready
|
20
|
+
--health-interval 10s
|
21
|
+
--health-timeout 5s
|
22
|
+
--health-retries 5
|
23
|
+
ports:
|
24
|
+
- 5432:5432
|
25
|
+
env:
|
26
|
+
POSTGRES_USER: runner
|
27
|
+
POSTGRES_HOST_AUTH_METHOD: trust
|
28
|
+
steps:
|
29
|
+
- name: Checkout the repo
|
30
|
+
uses: actions/checkout@v2
|
31
|
+
- name: Install Ruby, bundler and the bundle
|
32
|
+
uses: ruby/setup-ruby@v1
|
33
|
+
with:
|
34
|
+
ruby-version: ${{ matrix.ruby }}
|
35
|
+
bundler-cache: true
|
36
|
+
- name: Run tests
|
37
|
+
run: bundle exec rake
|
38
|
+
- name: Publish code coverage
|
39
|
+
if: ${{ github.actor != 'dependabot[bot]' }}
|
40
|
+
uses: paambaati/codeclimate-action@v3.0.0
|
data/.gitignore
CHANGED
data/.simplecov
ADDED
data/Gemfile
CHANGED
data/LICENSE
CHANGED
data/MIT-LICENSE
CHANGED
data/README.md
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# Superstore
|
2
|
-
[](https://github.com/data-axle/superstore/actions/workflows/ruby.yml)
|
3
3
|
[](https://codeclimate.com/github/data-axle/superstore)
|
4
4
|
[](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
|
-
|
36
|
-
|
37
|
-
|
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,10 +1,7 @@
|
|
1
1
|
module Superstore
|
2
2
|
module Adapters
|
3
3
|
class AbstractAdapter
|
4
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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(
|
81
|
+
"#{quote(JSON.generate(data))}::JSONB"
|
210
82
|
end
|
211
83
|
end
|
212
84
|
end
|
@@ -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
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Superstore
|
2
|
+
module Associations
|
3
|
+
class AssociationScope < ActiveRecord::Relation
|
4
|
+
def initialize(klass, association)
|
5
|
+
super(klass)
|
6
|
+
@association = association
|
7
|
+
end
|
8
|
+
|
9
|
+
def exec_queries
|
10
|
+
super.each { |r| @association.set_inverse_instance r }
|
11
|
+
end
|
12
|
+
|
13
|
+
def <<(*records)
|
14
|
+
if loaded?
|
15
|
+
@records = @records + records
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -19,12 +19,25 @@ module Superstore
|
|
19
19
|
self.target = relation
|
20
20
|
end
|
21
21
|
|
22
|
+
def set_inverse_instance(record)
|
23
|
+
return unless reflection.inverse_name
|
24
|
+
|
25
|
+
inverse = record.association(reflection.inverse_name)
|
26
|
+
inverse.target = owner
|
27
|
+
end
|
28
|
+
|
22
29
|
private
|
23
30
|
|
31
|
+
def inverse_of
|
32
|
+
return unless reflection.inverse_name
|
33
|
+
|
34
|
+
@inverse_of ||= association_class.reflect_on_association reflection.inverse_name
|
35
|
+
end
|
36
|
+
|
24
37
|
def load_collection
|
25
|
-
association_class.where(reflection.foreign_key
|
38
|
+
AssociationScope.new(association_class, self).where("document ->> '#{reflection.foreign_key}' = '#{owner.try(reflection.primary_key)}'")
|
26
39
|
end
|
27
40
|
|
28
41
|
end
|
29
42
|
end
|
30
|
-
end
|
43
|
+
end
|
@@ -18,7 +18,7 @@ module Superstore
|
|
18
18
|
when :has_one
|
19
19
|
Superstore::Associations::HasOne
|
20
20
|
end
|
21
|
-
|
21
|
+
|
22
22
|
end
|
23
23
|
|
24
24
|
def instance_variable_name
|
@@ -45,12 +45,18 @@ module Superstore
|
|
45
45
|
options[:polymorphic]
|
46
46
|
end
|
47
47
|
|
48
|
+
def belongs_to?; false; end
|
49
|
+
|
48
50
|
def class_name
|
49
51
|
@class_name ||= (options[:class_name] || name.to_s.classify)
|
50
52
|
end
|
51
53
|
|
54
|
+
def inverse_name
|
55
|
+
options[:inverse_of]
|
56
|
+
end
|
57
|
+
|
52
58
|
private
|
53
|
-
|
59
|
+
|
54
60
|
def derive_foreign_key
|
55
61
|
case macro
|
56
62
|
when :has_many, :has_one
|
@@ -2,7 +2,12 @@ module Superstore
|
|
2
2
|
module Associations
|
3
3
|
extend ActiveSupport::Concern
|
4
4
|
|
5
|
-
|
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
|
@@ -13,7 +18,7 @@ module Superstore
|
|
13
18
|
# end
|
14
19
|
# class Truck < Superstore::Base
|
15
20
|
# end
|
16
|
-
def belongs_to(name, options
|
21
|
+
def belongs_to(name, **options)
|
17
22
|
if options.delete(:superstore)
|
18
23
|
Superstore::Associations::Builder::BelongsTo.build(self, name, options)
|
19
24
|
else
|
@@ -21,7 +26,7 @@ module Superstore
|
|
21
26
|
end
|
22
27
|
end
|
23
28
|
|
24
|
-
def has_many(name, options
|
29
|
+
def has_many(name, **options)
|
25
30
|
if options.delete(:superstore)
|
26
31
|
Superstore::Associations::Builder::HasMany.build(self, name, options)
|
27
32
|
else
|
@@ -29,7 +34,7 @@ module Superstore
|
|
29
34
|
end
|
30
35
|
end
|
31
36
|
|
32
|
-
def has_one(name, options
|
37
|
+
def has_one(name, **options)
|
33
38
|
if options.delete(:superstore)
|
34
39
|
Superstore::Associations::Builder::HasOne.build(self, name, options)
|
35
40
|
else
|
@@ -1,25 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Superstore
|
2
4
|
module AttributeMethods
|
3
5
|
module PrimaryKey
|
4
6
|
extend ActiveSupport::Concern
|
5
7
|
|
8
|
+
included do
|
9
|
+
attribute :id, type: :string
|
10
|
+
include AttributeOverrides
|
11
|
+
end
|
12
|
+
|
6
13
|
module ClassMethods
|
7
|
-
PRIMARY_KEY = 'id'
|
8
14
|
def primary_key
|
9
|
-
|
15
|
+
'id'
|
10
16
|
end
|
11
17
|
end
|
12
18
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
19
|
+
module AttributeOverrides
|
20
|
+
def id
|
21
|
+
value = super
|
22
|
+
if value.nil?
|
23
|
+
value = self.class._generate_key(self)
|
24
|
+
@attributes.write_from_user(self.class.primary_key, value)
|
25
|
+
end
|
26
|
+
value
|
27
|
+
end
|
20
28
|
|
21
|
-
|
22
|
-
|
29
|
+
def attributes
|
30
|
+
super.update(self.class.primary_key => id)
|
31
|
+
end
|
23
32
|
end
|
24
33
|
end
|
25
34
|
end
|