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.
- checksums.yaml +5 -5
- data/.travis.yml +4 -6
- data/Gemfile +2 -3
- data/{Gemfile-rails4.2 → Gemfile.rails6} +2 -3
- data/README.md +4 -34
- data/lib/superstore.rb +17 -18
- data/lib/superstore/adapters/abstract_adapter.rb +1 -27
- data/lib/superstore/adapters/jsonb_adapter.rb +4 -132
- data/lib/superstore/associations.rb +6 -1
- 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/has_many.rb +15 -2
- data/lib/superstore/associations/reflection.rb +8 -2
- data/lib/superstore/attribute_assignment.rb +7 -0
- data/lib/superstore/attribute_methods.rb +1 -109
- data/lib/superstore/attribute_methods/primary_key.rb +20 -11
- data/lib/superstore/attributes.rb +13 -0
- data/lib/superstore/base.rb +8 -33
- data/lib/superstore/core.rb +7 -65
- data/lib/superstore/model_schema.rb +35 -0
- data/lib/superstore/persistence.rb +31 -115
- data/lib/superstore/railtie.rb +3 -11
- data/lib/superstore/relation/scrolling.rb +48 -0
- data/lib/superstore/timestamp.rb +13 -0
- data/lib/superstore/types.rb +11 -9
- data/lib/superstore/types/array_type.rb +3 -7
- 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 +19 -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 +51 -0
- data/lib/superstore/types/string_type.rb +4 -4
- data/lib/superstore/types/time_type.rb +10 -8
- data/superstore.gemspec +4 -3
- data/test/support/jsonb.rb +3 -1
- data/test/support/models.rb +8 -5
- data/test/test_helper.rb +6 -2
- 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 +9 -19
- data/test/unit/persistence_test.rb +17 -54
- 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 +28 -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 +24 -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 -39
- 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.rb +0 -73
- 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/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 -37
- data/test/unit/scoping_test.rb +0 -7
- data/test/unit/types/base_type_test.rb +0 -11
@@ -2,140 +2,56 @@ module Superstore
|
|
2
2
|
module Persistence
|
3
3
|
extend ActiveSupport::Concern
|
4
4
|
|
5
|
-
included do
|
6
|
-
class_attribute :batch_statements
|
7
|
-
end
|
8
|
-
|
9
5
|
module ClassMethods
|
10
|
-
def
|
11
|
-
|
12
|
-
end
|
13
|
-
|
14
|
-
def delete_all
|
15
|
-
adapter.execute "TRUNCATE #{table_name}"
|
6
|
+
def find_by_id(id)
|
7
|
+
find_by(id: id)
|
16
8
|
end
|
17
9
|
|
18
|
-
def
|
19
|
-
|
20
|
-
end
|
10
|
+
def _insert_record(attributes)
|
11
|
+
id = attributes.fetch(primary_key)
|
21
12
|
|
22
|
-
|
23
|
-
adapter.update table_name, id, encode_attributes(attributes)
|
13
|
+
adapter.insert table_name, id, serialize_attributes(attributes)
|
24
14
|
end
|
25
15
|
|
26
|
-
def
|
27
|
-
|
28
|
-
end
|
16
|
+
def _update_record(attributes, constraints)
|
17
|
+
id = constraints.fetch(primary_key)
|
29
18
|
|
30
|
-
|
31
|
-
adapter.batch(&block)
|
19
|
+
adapter.update table_name, id, serialize_attributes(attributes)
|
32
20
|
end
|
33
21
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
object.instance_variable_set("@destroyed", false)
|
39
|
-
object.instance_variable_set("@attributes", typecast_persisted_attributes(attributes))
|
40
|
-
object.instance_variable_set("@association_cache", {})
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
def encode_attributes(attributes)
|
45
|
-
encoded = {}
|
46
|
-
attributes.each do |column_name, value|
|
47
|
-
if value.nil?
|
48
|
-
encoded[column_name] = nil
|
49
|
-
else
|
50
|
-
encoded[column_name] = attribute_definitions[column_name].type.encode(value)
|
22
|
+
if Rails.version >= '6.0'
|
23
|
+
def instantiate_instance_of(klass, attributes, column_types = {}, &block)
|
24
|
+
if attributes[superstore_column].is_a?(String)
|
25
|
+
attributes = JSON.parse(attributes[superstore_column]).merge('id' => attributes['id'])
|
51
26
|
end
|
52
|
-
end
|
53
|
-
encoded
|
54
|
-
end
|
55
27
|
|
56
|
-
|
57
|
-
|
58
|
-
def quote_columns(column_names)
|
59
|
-
column_names.map { |name| "'#{name}'" }
|
28
|
+
super(klass, attributes, column_types, &block)
|
60
29
|
end
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
if definition = attribute_definitions[key]
|
67
|
-
result[key] = definition.instantiate(value)
|
68
|
-
end
|
30
|
+
private :instantiate_instance_of
|
31
|
+
else
|
32
|
+
def instantiate(attributes, column_types = {}, &block)
|
33
|
+
if attributes[superstore_column].is_a?(String)
|
34
|
+
attributes = JSON.parse(attributes[superstore_column]).merge('id' => attributes['id'])
|
69
35
|
end
|
70
36
|
|
71
|
-
|
37
|
+
super(attributes, column_types, &block)
|
72
38
|
end
|
73
|
-
end
|
74
|
-
|
75
|
-
def new_record?
|
76
|
-
@new_record
|
77
|
-
end
|
78
|
-
|
79
|
-
def destroyed?
|
80
|
-
@destroyed
|
81
|
-
end
|
82
|
-
|
83
|
-
def persisted?
|
84
|
-
!(new_record? || destroyed?)
|
85
|
-
end
|
86
|
-
|
87
|
-
def save(*)
|
88
|
-
create_or_update
|
89
|
-
end
|
90
|
-
|
91
|
-
def destroy
|
92
|
-
self.class.delete(id)
|
93
|
-
@destroyed = true
|
94
|
-
end
|
95
|
-
|
96
|
-
def update_attribute(name, value)
|
97
|
-
name = name.to_s
|
98
|
-
send("#{name}=", value)
|
99
|
-
save(validate: false)
|
100
|
-
end
|
101
|
-
|
102
|
-
def update(attributes)
|
103
|
-
self.attributes = attributes
|
104
|
-
save
|
105
|
-
end
|
106
|
-
|
107
|
-
alias update_attributes update
|
108
|
-
|
109
|
-
def update!(attributes)
|
110
|
-
self.attributes = attributes
|
111
|
-
save!
|
112
|
-
end
|
113
|
-
|
114
|
-
alias update_attributes! update!
|
115
|
-
|
116
|
-
def reload
|
117
|
-
clear_association_cache
|
118
|
-
@attributes = self.class.find(id).instance_variable_get('@attributes')
|
119
|
-
self
|
120
|
-
end
|
121
|
-
|
122
|
-
private
|
123
|
-
|
124
|
-
def create_or_update
|
125
|
-
new_record? ? create_self : update_self
|
126
39
|
end
|
127
40
|
|
128
|
-
def
|
129
|
-
|
41
|
+
def serialize_attributes(attributes)
|
42
|
+
serialized = {}
|
43
|
+
attributes.each do |attr_name, value|
|
44
|
+
next if attr_name == primary_key
|
45
|
+
serialized[attr_name] = attribute_types[attr_name].serialize(value)
|
46
|
+
end
|
47
|
+
serialized
|
130
48
|
end
|
131
49
|
|
132
|
-
|
133
|
-
write :update_record
|
134
|
-
end
|
50
|
+
private
|
135
51
|
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
52
|
+
def adapter
|
53
|
+
@adapter ||= Superstore::Adapters::JsonbAdapter.new
|
54
|
+
end
|
55
|
+
end
|
140
56
|
end
|
141
57
|
end
|
data/lib/superstore/railtie.rb
CHANGED
@@ -1,17 +1,9 @@
|
|
1
1
|
module Superstore
|
2
2
|
class Railtie < Rails::Railtie
|
3
3
|
initializer "superstore.config" do |app|
|
4
|
-
ActiveSupport.on_load :
|
5
|
-
|
6
|
-
|
7
|
-
config = ERB.new(pathname.read).result
|
8
|
-
config = YAML.load(config)
|
9
|
-
|
10
|
-
if config = config[Rails.env]
|
11
|
-
self.config = config.symbolize_keys!
|
12
|
-
else
|
13
|
-
raise "Missing environment #{Rails.env} in superstore.yml"
|
14
|
-
end
|
4
|
+
ActiveSupport.on_load :active_record do
|
5
|
+
ActiveRecord::Relation.class_eval do
|
6
|
+
include Superstore::Relation::Scrolling
|
15
7
|
end
|
16
8
|
end
|
17
9
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Superstore
|
2
|
+
module Relation
|
3
|
+
module Scrolling
|
4
|
+
def scroll_each(options = {})
|
5
|
+
batch_size = options[:batch_size] || 1000
|
6
|
+
|
7
|
+
scroll_results(batch_size) do |attributes|
|
8
|
+
yield klass.instantiate(attributes)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def scroll_in_batches(options = {})
|
13
|
+
batch_size = options[:batch_size] || 1000
|
14
|
+
batch = []
|
15
|
+
|
16
|
+
scroll_each(options) do |record|
|
17
|
+
batch << record
|
18
|
+
|
19
|
+
if batch.size == batch_size
|
20
|
+
yield batch
|
21
|
+
batch = []
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
yield(batch) if batch.any?
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def scroll_results(batch_size)
|
31
|
+
statement = to_sql
|
32
|
+
cursor_name = "cursor_#{SecureRandom.hex(6)}"
|
33
|
+
fetch_sql = "FETCH FORWARD #{batch_size} FROM #{cursor_name}"
|
34
|
+
|
35
|
+
connection.transaction do
|
36
|
+
connection.execute "DECLARE #{cursor_name} NO SCROLL CURSOR FOR (#{statement})"
|
37
|
+
|
38
|
+
while (batch = connection.execute(fetch_sql)).any?
|
39
|
+
batch.each do |result|
|
40
|
+
yield result
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/superstore/types.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
1
|
+
ActiveRecord::Type.register(:superstore_array, Superstore::Types::ArrayType)
|
2
|
+
ActiveRecord::Type.register(:superstore_boolean, Superstore::Types::BooleanType)
|
3
|
+
ActiveRecord::Type.register(:superstore_date, Superstore::Types::DateType)
|
4
|
+
ActiveRecord::Type.register(:superstore_date_range, Superstore::Types::DateRangeType)
|
5
|
+
ActiveRecord::Type.register(:superstore_float, Superstore::Types::FloatType)
|
6
|
+
ActiveRecord::Type.register(:superstore_geo_point, Superstore::Types::GeoPointType)
|
7
|
+
ActiveRecord::Type.register(:superstore_integer, Superstore::Types::IntegerType)
|
8
|
+
ActiveRecord::Type.register(:superstore_integer_range, Superstore::Types::IntegerRangeType)
|
9
|
+
ActiveRecord::Type.register(:superstore_json, Superstore::Types::JsonType)
|
10
|
+
ActiveRecord::Type.register(:superstore_time, Superstore::Types::TimeType)
|
11
|
+
ActiveRecord::Type.register(:superstore_string, Superstore::Types::StringType)
|
@@ -1,12 +1,8 @@
|
|
1
1
|
module Superstore
|
2
2
|
module Types
|
3
|
-
class ArrayType <
|
4
|
-
def
|
5
|
-
|
6
|
-
end
|
7
|
-
|
8
|
-
def typecast(value)
|
9
|
-
value.to_a
|
3
|
+
class ArrayType < ActiveModel::Type::Value
|
4
|
+
def cast_value(value)
|
5
|
+
Array(value)
|
10
6
|
end
|
11
7
|
end
|
12
8
|
end
|
@@ -1,20 +1,15 @@
|
|
1
1
|
module Superstore
|
2
2
|
module Types
|
3
|
-
class BooleanType <
|
3
|
+
class BooleanType < ActiveModel::Type::Value
|
4
4
|
TRUE_VALS = [true, 'true', '1']
|
5
|
-
FALSE_VALS = [false, 'false', '0'
|
6
|
-
VALID_VALS = TRUE_VALS + FALSE_VALS
|
5
|
+
FALSE_VALS = [false, 'false', '0']
|
7
6
|
|
8
|
-
def
|
9
|
-
|
10
|
-
|
7
|
+
def cast_value(value)
|
8
|
+
if TRUE_VALS.include?(value)
|
9
|
+
true
|
10
|
+
elsif FALSE_VALS.include?(value)
|
11
|
+
false
|
11
12
|
end
|
12
|
-
|
13
|
-
TRUE_VALS.include?(bool)
|
14
|
-
end
|
15
|
-
|
16
|
-
def decode(str)
|
17
|
-
TRUE_VALS.include?(str)
|
18
13
|
end
|
19
14
|
end
|
20
15
|
end
|
@@ -1,21 +1,18 @@
|
|
1
1
|
module Superstore
|
2
2
|
module Types
|
3
|
-
class DateType <
|
3
|
+
class DateType < ActiveModel::Type::Value
|
4
4
|
FORMAT = '%Y-%m-%d'
|
5
|
-
REGEX = /\A\d{4}-\d{2}-\d{2}\Z/
|
6
5
|
|
7
|
-
def
|
8
|
-
|
9
|
-
value.strftime(FORMAT)
|
6
|
+
def serialize(value)
|
7
|
+
value.strftime(FORMAT) if value
|
10
8
|
end
|
11
9
|
|
12
|
-
def
|
13
|
-
|
14
|
-
Date.parse(str)
|
10
|
+
def deserialize(str)
|
11
|
+
Date.strptime(str, FORMAT) if str
|
15
12
|
end
|
16
13
|
|
17
|
-
def
|
18
|
-
value.to_date
|
14
|
+
def cast_value(value)
|
15
|
+
value.to_date rescue nil
|
19
16
|
end
|
20
17
|
end
|
21
18
|
end
|
@@ -1,16 +1,8 @@
|
|
1
1
|
module Superstore
|
2
2
|
module Types
|
3
|
-
class FloatType <
|
4
|
-
def
|
5
|
-
|
6
|
-
end
|
7
|
-
|
8
|
-
def decode(str)
|
9
|
-
str.to_f unless str.empty?
|
10
|
-
end
|
11
|
-
|
12
|
-
def typecast(value)
|
13
|
-
value.to_f
|
3
|
+
class FloatType < ActiveModel::Type::Value
|
4
|
+
def cast_value(value)
|
5
|
+
Float(value) rescue nil
|
14
6
|
end
|
15
7
|
end
|
16
8
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Superstore
|
4
|
+
module Types
|
5
|
+
class GeoPointType < ActiveModel::Type::Value
|
6
|
+
def deserialize(value)
|
7
|
+
{lat: value[:lat] || value['lat'], lon: value[:lon] || value['lon']} if value
|
8
|
+
end
|
9
|
+
|
10
|
+
def cast_value(value)
|
11
|
+
case value
|
12
|
+
when String
|
13
|
+
cast_value value.split(/[,\s]+/)
|
14
|
+
when Array
|
15
|
+
to_float_or_nil(lat: value[0], lon: value[1])
|
16
|
+
when Hash
|
17
|
+
to_float_or_nil(lat: value[:lat] || value['lat'], lon: value[:lon] || value['lon'])
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def to_float_or_nil(coords)
|
24
|
+
if coords[:lat] && coords[:lon]
|
25
|
+
coords.transform_values!(&:to_f)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Superstore
|
2
|
+
module Types
|
3
|
+
class IntegerRangeType < RangeType
|
4
|
+
self.subtype = IntegerType.new
|
5
|
+
|
6
|
+
def serialize_for_open_ended(value)
|
7
|
+
value.abs == Float::INFINITY ? nil : super
|
8
|
+
end
|
9
|
+
|
10
|
+
def convert_min(method, value)
|
11
|
+
value.nil? ? -Float::INFINITY : super
|
12
|
+
end
|
13
|
+
|
14
|
+
def convert_max(method, value)
|
15
|
+
value.nil? ? Float::INFINITY : super
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -1,19 +1,13 @@
|
|
1
1
|
module Superstore
|
2
2
|
module Types
|
3
|
-
class IntegerType <
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
def decode(str)
|
12
|
-
str.to_i unless str.empty?
|
13
|
-
end
|
14
|
-
|
15
|
-
def typecast(value)
|
16
|
-
value.to_i
|
3
|
+
class IntegerType < ActiveModel::Type::Value
|
4
|
+
def cast_value(value)
|
5
|
+
if value.is_a?(String)
|
6
|
+
Integer(value, 10)
|
7
|
+
else
|
8
|
+
Integer(value)
|
9
|
+
end
|
10
|
+
rescue
|
17
11
|
end
|
18
12
|
end
|
19
13
|
end
|