light_record 0.3 → 0.4
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/lib/light_record.rb +116 -19
- data/spec/attribute_spec.rb +49 -0
- data/spec/light_record_spec.rb +45 -8
- data/spec/prepare_db.rb +1 -1
- data/spec/test_helper.rb +2 -0
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: ea1302da9cb295270ba77e8016a66df3555f8842884cff89fb08ad82db14fe6b
|
4
|
+
data.tar.gz: 3c722a60612f1e0d69c68791ade21162a0d53dcbb7c2ee63cabb7ec362b3d1df
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fb7bf3a405a060cd01c8b7782d65879ddcc74cc7404057031005780de1d1c5a6b833687db1c8babe587bb055ed50de651ad0b0e9c464125f8c378ca178f84f33
|
7
|
+
data.tar.gz: 87eda8dc8e30afbe4e1c4304a6ae1ed137d8383af275c2506965fbcff04683c4f671852ebaac82af4a4994b245e5379825e2f14b936c75a0f8f67ee72cc5997c
|
data/lib/light_record.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module LightRecord
|
2
4
|
extend self
|
3
5
|
|
@@ -20,6 +22,13 @@ module LightRecord
|
|
20
22
|
def initialize(data)
|
21
23
|
@attributes = data
|
22
24
|
@readonly = true
|
25
|
+
@association_cache = {}
|
26
|
+
@primary_key = self.class::LIGHT_RECORD_PRIMARY_KEY_CACHE
|
27
|
+
#init_internals
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.model_name
|
31
|
+
self.superclass.model_name
|
23
32
|
end
|
24
33
|
|
25
34
|
def read_attribute_before_type_cast(attr_name)
|
@@ -45,22 +54,86 @@ module LightRecord
|
|
45
54
|
def [](attr_name)
|
46
55
|
@attributes[attr_name.to_sym]
|
47
56
|
end
|
48
|
-
|
57
|
+
|
49
58
|
def attributes
|
50
59
|
@attributes
|
51
60
|
end
|
52
61
|
|
53
62
|
# to avoid errors when try saving data
|
54
63
|
def remember_transaction_record_state
|
55
|
-
@
|
64
|
+
@new_record ||= false
|
65
|
+
@destroyed ||= false
|
66
|
+
@_start_transaction_state ||= {level: 0}
|
56
67
|
super
|
57
68
|
end
|
69
|
+
|
70
|
+
def sync_with_transaction_state
|
71
|
+
nil
|
72
|
+
end
|
73
|
+
|
74
|
+
def new_record?
|
75
|
+
false
|
76
|
+
end
|
77
|
+
|
78
|
+
# For Rails < 5.1
|
79
|
+
# Assign without type casting, no support for alias, sorry
|
80
|
+
def write_attribute_with_type_cast(attr_name, value, should_type_cast)
|
81
|
+
@attributes[attr_name.to_sym] = value
|
82
|
+
end
|
83
|
+
|
84
|
+
# For Rails >= 5.1
|
85
|
+
# Assign without type casting, no support for alias, sorry
|
86
|
+
if ActiveRecord.version >= Gem::Version.new("5.1.0")
|
87
|
+
def write_attribute(attr_name, value)
|
88
|
+
attr_name = attr_name.to_sym
|
89
|
+
attr_name = self.class.primary_key if attr_name == :id && self.class.primary_key
|
90
|
+
@attributes[attr_name] = value
|
91
|
+
value
|
92
|
+
end
|
93
|
+
|
94
|
+
def raw_write_attribute(attr_name, value) # :nodoc:
|
95
|
+
@attributes[attr_name.to_sym] = value
|
96
|
+
value
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
## For Rails 5.2
|
101
|
+
def id_in_database
|
102
|
+
attributes[self.class.primary_key.to_sym]
|
103
|
+
end
|
104
|
+
|
105
|
+
def reload
|
106
|
+
super
|
107
|
+
@attributes = @attributes.to_h.symbolize_keys
|
108
|
+
|
109
|
+
self
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
def write_attribute_without_type_cast(attr_name, value)
|
114
|
+
@attributes[attr_name.to_sym] = value
|
115
|
+
end
|
116
|
+
|
117
|
+
def clear_aggregation_cache
|
118
|
+
if defined?(@aggregation_cache) && @aggregation_cache
|
119
|
+
super
|
120
|
+
end
|
121
|
+
end
|
58
122
|
end
|
59
123
|
|
124
|
+
# for rails 6
|
125
|
+
new_klass.const_set(:LIGHT_RECORD_PRIMARY_KEY_CACHE, klass.primary_key)
|
126
|
+
|
60
127
|
if klass.const_defined?(:LightRecord, false)
|
61
|
-
new_klass.send(:
|
128
|
+
new_klass.send(:prepend, klass::LightRecord)
|
129
|
+
if klass::LightRecord.respond_to?(:included)
|
130
|
+
klass::LightRecord.included(new_klass)
|
131
|
+
end
|
62
132
|
elsif klass.superclass.const_defined?(:LightRecord, false)
|
63
|
-
new_klass.send(:
|
133
|
+
new_klass.send(:prepend, klass.superclass::LightRecord)
|
134
|
+
if klass.superclass::LightRecord.respond_to?(:included)
|
135
|
+
klass.superclass::LightRecord.included(new_klass)
|
136
|
+
end
|
64
137
|
end
|
65
138
|
|
66
139
|
new_klass
|
@@ -74,7 +147,7 @@ module LightRecord
|
|
74
147
|
@fields ||= []
|
75
148
|
|
76
149
|
fields.each do |field|
|
77
|
-
field = field.to_sym
|
150
|
+
field = field.to_sym
|
78
151
|
@fields << field
|
79
152
|
define_method(field) do
|
80
153
|
@attributes[field]
|
@@ -87,7 +160,7 @@ module LightRecord
|
|
87
160
|
end
|
88
161
|
|
89
162
|
# ActiveRecord make method :id refers to primary key, even there is no column "id"
|
90
|
-
if
|
163
|
+
if !@fields.include?(:id) && primary_key.present?
|
91
164
|
define_method(:id) do
|
92
165
|
@attributes[self.class.primary_key.to_sym]
|
93
166
|
end
|
@@ -100,7 +173,7 @@ module LightRecord
|
|
100
173
|
|
101
174
|
# Active record keep it as strings, but I keep it as symbols
|
102
175
|
def column_names
|
103
|
-
@fields.map(&:to_s)
|
176
|
+
@column_names ||= @fields.map(&:to_s)
|
104
177
|
end
|
105
178
|
end
|
106
179
|
|
@@ -118,14 +191,14 @@ module LightRecord
|
|
118
191
|
|
119
192
|
# Executes query and return array of light object (model class extended by LightRecord)
|
120
193
|
def light_records(options = {})
|
121
|
-
client = connection.instance_variable_get(:@connection)
|
122
194
|
sql = self.to_sql
|
123
195
|
|
124
196
|
options = {
|
125
197
|
stream: false, symbolize_keys: true, cache_rows: false, as: :hash,
|
126
198
|
database_timezone: ActiveRecord::Base.default_timezone
|
127
|
-
}
|
128
|
-
|
199
|
+
}.merge(options)
|
200
|
+
|
201
|
+
result = _light_record_execute_query(connection, sql, options.except(:set_const))
|
129
202
|
|
130
203
|
klass = LightRecord.build_for_class(self.klass, result.fields)
|
131
204
|
|
@@ -154,8 +227,9 @@ module LightRecord
|
|
154
227
|
options = {
|
155
228
|
stream: true, symbolize_keys: true, cache_rows: false, as: :hash,
|
156
229
|
database_timezone: ActiveRecord::Base.default_timezone
|
157
|
-
}
|
158
|
-
|
230
|
+
}.merge(options)
|
231
|
+
|
232
|
+
result = _light_record_execute_query(conn, sql, options.except(:set_const))
|
159
233
|
|
160
234
|
klass = LightRecord.build_for_class(self.klass, result.fields)
|
161
235
|
|
@@ -163,21 +237,44 @@ module LightRecord
|
|
163
237
|
self.klass.const_set(:"LR_#{Time.now.to_i}", klass)
|
164
238
|
end
|
165
239
|
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
240
|
+
if defined?(PG::Result) && result.is_a?(PG::Result)
|
241
|
+
begin
|
242
|
+
result.stream_each do |row|
|
243
|
+
row.symbolize_keys!
|
244
|
+
yield klass.new(row)
|
245
|
+
end
|
246
|
+
rescue => error
|
247
|
+
conn.raw_connection.get_last_result
|
248
|
+
throw error
|
249
|
+
ensure
|
250
|
+
result.clear
|
251
|
+
end
|
252
|
+
else
|
253
|
+
result.each do |row|
|
254
|
+
yield klass.new(row)
|
255
|
+
end
|
171
256
|
end
|
172
257
|
ensure
|
173
258
|
ActiveRecord::Base.connection_pool.checkin(conn)
|
174
259
|
end
|
175
260
|
|
176
261
|
private def _light_record_execute_query(connection, sql, options)
|
177
|
-
client = connection.
|
262
|
+
client = connection.raw_connection
|
178
263
|
|
179
264
|
if client.class.to_s == 'PG::Connection'
|
180
|
-
|
265
|
+
if options[:stream]
|
266
|
+
connection.send(:log, sql, "LightRecord - #{self.klass}") do
|
267
|
+
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
268
|
+
client.send_query(sql)
|
269
|
+
client.set_single_row_mode
|
270
|
+
result = client.get_result
|
271
|
+
result.check
|
272
|
+
result
|
273
|
+
end
|
274
|
+
end
|
275
|
+
else
|
276
|
+
connection.execute(sql, "LightRecord - #{self.klass}")
|
277
|
+
end
|
181
278
|
else
|
182
279
|
connection.send(:log, sql, "LightRecord - #{self.klass}") do
|
183
280
|
client.query(sql, options)
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
require_relative 'prepare_db'
|
3
|
+
|
4
|
+
#ActiveRecord::Base.logger = Logger.new(STDOUT)
|
5
|
+
|
6
|
+
class ARQuestion < ActiveRecord::Base
|
7
|
+
self.table_name = "sample"
|
8
|
+
self.primary_key = "policy_id"
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "LightRecord attributes" do
|
12
|
+
|
13
|
+
it "should assign attributes as method" do
|
14
|
+
record = ARQuestion.limit(10).light_records.first
|
15
|
+
record.statecode = "AAA"
|
16
|
+
assert_equal(record.statecode, "AAA")
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should assign attributes ignoring type casting" do
|
20
|
+
record = ARQuestion.limit(10).light_records.first
|
21
|
+
record.statecode = 123
|
22
|
+
assert_equal(record.statecode, 123)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should assign attributes as hash" do
|
26
|
+
record = ARQuestion.limit(10).light_records.first
|
27
|
+
record[:statecode] = "AAA"
|
28
|
+
assert_equal(record[:statecode], "AAA")
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should have correct primary_key" do
|
32
|
+
ActiveRecord::Base.logger = Logger.new(STDOUT)
|
33
|
+
record = ARQuestion.limit(10).light_records.first
|
34
|
+
assert_equal(record.class.primary_key, "policy_id")
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should allow updating attributes" do
|
38
|
+
ActiveRecord::Base.logger = Logger.new(STDOUT)
|
39
|
+
record = ARQuestion.limit(10).light_records.first
|
40
|
+
ARQuestion.transaction do
|
41
|
+
record.update_columns(statecode: "AAA")
|
42
|
+
assert_equal(record.statecode, "AAA")
|
43
|
+
raise ActiveRecord::Rollback
|
44
|
+
end
|
45
|
+
# TODO
|
46
|
+
# assert_equal(record.statecode, "FL")
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
data/spec/light_record_spec.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
require_relative '
|
2
|
-
require_relative '
|
1
|
+
require_relative 'test_helper'
|
2
|
+
require_relative 'prepare_db'
|
3
3
|
|
4
4
|
#ActiveRecord::Base.logger = Logger.new(STDOUT)
|
5
5
|
|
@@ -16,6 +16,19 @@ class ARQuestion_wLR < ActiveRecord::Base
|
|
16
16
|
def light_included?
|
17
17
|
true
|
18
18
|
end
|
19
|
+
|
20
|
+
def point_granularity
|
21
|
+
"Extended #{super}"
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.included(base)
|
25
|
+
base.extend(ClassMethods)
|
26
|
+
end
|
27
|
+
module ClassMethods
|
28
|
+
def im_a_class_method
|
29
|
+
:pam_param_pam_pam
|
30
|
+
end
|
31
|
+
end
|
19
32
|
end
|
20
33
|
end
|
21
34
|
|
@@ -54,12 +67,20 @@ describe "LightRecord" do
|
|
54
67
|
assert_equal(light.attributes, dark.attributes.symbolize_keys)
|
55
68
|
end
|
56
69
|
|
57
|
-
|
58
|
-
|
59
|
-
|
70
|
+
describe "LightRecord submodule" do
|
71
|
+
it "should include LightRecord submodule if present" do
|
72
|
+
record = ARQuestion_wLR.limit(1).light_records.first
|
73
|
+
assert(record.light_included?)
|
74
|
+
|
75
|
+
klass = LightRecord.base_extended(ARQuestion_wLR)
|
76
|
+
assert_includes(klass.ancestors, ARQuestion_wLR::LightRecord)
|
77
|
+
assert_equal(:pam_param_pam_pam, klass.im_a_class_method)
|
78
|
+
end
|
60
79
|
|
61
|
-
|
62
|
-
|
80
|
+
it "should override attribute methods" do
|
81
|
+
record = ARQuestion_wLR.order(:policy_id).limit(1).light_records.first
|
82
|
+
assert_equal(record.point_granularity, "Extended 3")
|
83
|
+
end
|
63
84
|
end
|
64
85
|
|
65
86
|
it "should work with #respond_to?" do
|
@@ -86,7 +107,7 @@ describe "LightRecord" do
|
|
86
107
|
assert(record.readonly?)
|
87
108
|
|
88
109
|
assert_raises(ActiveRecord::ReadOnlyRecord) do
|
89
|
-
record.
|
110
|
+
record.update(ARQuestion.column_names.first => "bla bla lba")
|
90
111
|
end
|
91
112
|
end
|
92
113
|
|
@@ -114,4 +135,20 @@ describe "LightRecord" do
|
|
114
135
|
assert(record.time.gmt?, "Time is not UTC")
|
115
136
|
end
|
116
137
|
end
|
138
|
+
|
139
|
+
it "should return #model_name of original class" do
|
140
|
+
record = ARQuestion.where(policy_id: 119736).light_records.first
|
141
|
+
assert_equal(ARQuestion.model_name, record.class.model_name)
|
142
|
+
end
|
143
|
+
|
144
|
+
it "should reload record" do
|
145
|
+
record = ARQuestion.offset(3).limit(1).light_records.first
|
146
|
+
|
147
|
+
new_granularity = Time.now.to_i
|
148
|
+
ARQuestion.find_by(policy_id: record.policy_id).update_columns(point_granularity: new_granularity)
|
149
|
+
|
150
|
+
refute_equal(record.point_granularity, new_granularity)
|
151
|
+
record.reload
|
152
|
+
assert_equal(record.point_granularity, new_granularity)
|
153
|
+
end
|
117
154
|
end
|
data/spec/prepare_db.rb
CHANGED
data/spec/test_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: light_record
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '0.
|
4
|
+
version: '0.4'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Pavel Evstigneev
|
@@ -18,6 +18,7 @@ extensions: []
|
|
18
18
|
extra_rdoc_files: []
|
19
19
|
files:
|
20
20
|
- lib/light_record.rb
|
21
|
+
- spec/attribute_spec.rb
|
21
22
|
- spec/light_record_spec.rb
|
22
23
|
- spec/prepare_db.rb
|
23
24
|
- spec/test_helper.rb
|
@@ -40,12 +41,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
40
41
|
- !ruby/object:Gem::Version
|
41
42
|
version: '0'
|
42
43
|
requirements: []
|
43
|
-
|
44
|
-
rubygems_version: 2.5.1
|
44
|
+
rubygems_version: 3.0.2
|
45
45
|
signing_key:
|
46
46
|
specification_version: 3
|
47
47
|
summary: ActiveRecord extension to speedup ActiveRecord object allocation
|
48
48
|
test_files:
|
49
|
-
- spec/light_record_spec.rb
|
50
49
|
- spec/prepare_db.rb
|
50
|
+
- spec/attribute_spec.rb
|
51
|
+
- spec/light_record_spec.rb
|
51
52
|
- spec/test_helper.rb
|