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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: cea325f7a213deab5fa00bcf32d07ccd14456ba3
4
- data.tar.gz: b5dd83b77f9929ee46b0dc8b9d02e78c526a2bb8
2
+ SHA256:
3
+ metadata.gz: ea1302da9cb295270ba77e8016a66df3555f8842884cff89fb08ad82db14fe6b
4
+ data.tar.gz: 3c722a60612f1e0d69c68791ade21162a0d53dcbb7c2ee63cabb7ec362b3d1df
5
5
  SHA512:
6
- metadata.gz: 1ad5f7947d0570e46585d84882ce1e710973fbe43fa63aed576d0065b86918a77c08f9f9de4e8b27dddf69d3429bab58765b2e0893748eea84539dae91a92c36
7
- data.tar.gz: 11231ea0c41e361b2247f6fe1816aa5a19bc1b48071447d9d78b7c3cbc3af787ab7928ef879b0615e2fe6677e223fec05f855f05135eb8bd9265792a4ee2d237
6
+ metadata.gz: fb7bf3a405a060cd01c8b7782d65879ddcc74cc7404057031005780de1d1c5a6b833687db1c8babe587bb055ed50de651ad0b0e9c464125f8c378ca178f84f33
7
+ data.tar.gz: 87eda8dc8e30afbe4e1c4304a6ae1ed137d8383af275c2506965fbcff04683c4f671852ebaac82af4a4994b245e5379825e2f14b936c75a0f8f67ee72cc5997c
@@ -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
- @_start_transaction_state ||= {}
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(:include, klass::LightRecord)
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(:include, klass.superclass::LightRecord)
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 unless field.is_a?(Symbol)
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 !fields.include?(:id) && !fields.include?("id") && primary_key.present?
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
- result = _light_record_execute_query(connection, sql, options)
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
- result = _light_record_execute_query(conn, sql, options)
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
- need_symbolize_keys = defined?(PG::Result) && result.is_a?(PG::Result)
167
-
168
- result.each do |row|
169
- row.symbolize_keys! if need_symbolize_keys
170
- yield klass.new(row)
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.instance_variable_get(:@connection)
262
+ client = connection.raw_connection
178
263
 
179
264
  if client.class.to_s == 'PG::Connection'
180
- connection.execute(sql, "LightRecord - #{self.klass}")
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
@@ -1,5 +1,5 @@
1
- require_relative './test_helper'
2
- require_relative './prepare_db'
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
- it "should include LightRecord submodule if present" do
58
- record = ARQuestion_wLR.limit(1).light_records.first
59
- assert(record.light_included?)
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
- klass = LightRecord.base_extended(ARQuestion_wLR)
62
- assert_includes(klass.ancestors, ARQuestion_wLR::LightRecord)
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.update_attributes(ARQuestion.column_names.first => "bla bla lba")
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
@@ -82,7 +82,7 @@ module TestDB
82
82
  point_longitude varchar(255) NULL,
83
83
  line varchar(255) DEFAULT NULL,
84
84
  construction varchar(255) NULL,
85
- point_granularity real NULL,
85
+ point_granularity integer NULL,
86
86
  PRIMARY KEY(policy_id)
87
87
  );
88
88
  })
@@ -15,3 +15,5 @@ require 'light_record'
15
15
  require 'looksee'
16
16
 
17
17
  require "minitest/autorun"
18
+
19
+ # ActiveRecord::Base.logger = Logger.new(STDOUT)
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.3'
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
- rubyforge_project:
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