duck_record 0.0.1 → 0.0.3

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
2
  SHA1:
3
- metadata.gz: 6ddd659e371a6f19e0c6721e0db7c6b39d84d959
4
- data.tar.gz: 2ddcf8b3f50ddc3954db3ce04e83d4dbd356a175
3
+ metadata.gz: edf615681e997320dd43475ac1b65efbf4a6355b
4
+ data.tar.gz: 2790953d7ec98a37866298eb4c445ed62e71f22c
5
5
  SHA512:
6
- metadata.gz: 7bc31bc533d951a904ac72d7de80d7398281ad7d701bf3380ab845fd4da6b33f3f491a2f93d2a5599d8461429f501e9fc1822531686c6568c3ede57700206991
7
- data.tar.gz: cb451b408a562925a2505b3a4282deaedd25d638251d2f49ae5f6d6808911f43a93887735f086a4435b93b177a27b7d74310931d9dd87c6915356857fc0555ba
6
+ metadata.gz: 5bb6d8c090cc556f4edf072de23146850001ebdea769ab6656eb129aec0e07500b2c66aafdca655dc6df3a6efcada0af47f071a48bc3a9d8ee3c1a42545025b5
7
+ data.tar.gz: a7ef53be7401e9612c332d2a2f812f92d1a8ed8145d63855ac2da2ce4de415b3e5ff0afbf49219dd9ec1a787dbf81746219932d373edcaede219990f92a0b0ad
data/README.md CHANGED
@@ -9,10 +9,12 @@ Actually it's extract from Active Record.
9
9
  ```ruby
10
10
  class Book < DuckRecord::Base
11
11
  attribute :title, :string
12
- attribute :tags, :string, array: true
13
12
  attribute :price, :decimal, default: 0
14
- attribute :meta, :json, default: {}
15
13
  attribute :bought_at, :datetime, default: -> { Time.new }
14
+
15
+ # some types that cheated from PG
16
+ attribute :tags, :string, array: true
17
+ attribute :meta, :json, default: {}
16
18
 
17
19
  validates :title, presence: true
18
20
  end
@@ -44,7 +46,9 @@ $ gem install duck_record
44
46
  - `has_one`, `has_many`
45
47
  - refactor that original design for database
46
48
  - update docs
49
+ - add useful methods
47
50
  - add tests
51
+ - let me known..
48
52
 
49
53
  ## Contributing
50
54
 
@@ -1,6 +1,10 @@
1
1
  module DuckRecord
2
2
  class Attribute # :nodoc:
3
3
  class << self
4
+ def from_database(name, value, type)
5
+ FromDatabase.new(name, value, type)
6
+ end
7
+
4
8
  def from_user(name, value, type, original_attribute = nil)
5
9
  FromUser.new(name, value, type, original_attribute)
6
10
  end
@@ -64,16 +68,16 @@ module DuckRecord
64
68
  self.class.from_user(name, value, type, original_attribute || self)
65
69
  end
66
70
 
71
+ def with_value_from_database(value)
72
+ self.class.from_database(name, value, type)
73
+ end
74
+
67
75
  def with_cast_value(value)
68
76
  self.class.with_cast_value(name, value, type)
69
77
  end
70
78
 
71
79
  def with_type(type)
72
- if changed_in_place?
73
- with_value_from_user(value).with_type(type)
74
- else
75
- self.class.new(name, value_before_type_cast, type, original_attribute)
76
- end
80
+ self.class.new(name, value_before_type_cast, type, original_attribute)
77
81
  end
78
82
 
79
83
  def type_cast(*)
@@ -104,38 +108,11 @@ module DuckRecord
104
108
  [self.class, name, value_before_type_cast, type].hash
105
109
  end
106
110
 
107
- def init_with(coder)
108
- @name = coder["name"]
109
- @value_before_type_cast = coder["value_before_type_cast"]
110
- @type = coder["type"]
111
- @original_attribute = coder["original_attribute"]
112
- @value = coder["value"] if coder.map.key?("value")
113
- end
114
-
115
- def encode_with(coder)
116
- coder["name"] = name
117
- coder["value_before_type_cast"] = value_before_type_cast if value_before_type_cast
118
- coder["type"] = type if type
119
- coder["original_attribute"] = original_attribute if original_attribute
120
- coder["value"] = value if defined?(@value)
121
- end
122
-
123
- # TODO Change this to private once we've dropped Ruby 2.2 support.
124
- # Workaround for Ruby 2.2 "private attribute?" warning.
125
111
  protected
126
112
 
127
113
  attr_reader :original_attribute
128
114
  alias_method :assigned?, :original_attribute
129
115
 
130
- def original_value_for_database
131
- if assigned?
132
- original_attribute.original_value_for_database
133
- else
134
- _original_value_for_database
135
- end
136
- end
137
-
138
- private
139
116
  def initialize_dup(other)
140
117
  if defined?(@value) && @value.duplicable?
141
118
  @value = @value.dup
@@ -146,10 +123,28 @@ module DuckRecord
146
123
  assigned? && type.changed?(original_value, value, value_before_type_cast)
147
124
  end
148
125
 
126
+ def original_value_for_database
127
+ if assigned?
128
+ original_attribute.original_value_for_database
129
+ else
130
+ _original_value_for_database
131
+ end
132
+ end
133
+
149
134
  def _original_value_for_database
150
135
  type.serialize(original_value)
151
136
  end
152
137
 
138
+ class FromDatabase < Attribute # :nodoc:
139
+ def type_cast(value)
140
+ type.deserialize(value)
141
+ end
142
+
143
+ def _original_value_for_database
144
+ value_before_type_cast
145
+ end
146
+ end
147
+
153
148
  class FromUser < Attribute # :nodoc:
154
149
  def type_cast(value)
155
150
  type.cast(value)
@@ -172,7 +167,7 @@ module DuckRecord
172
167
 
173
168
  class Null < Attribute # :nodoc:
174
169
  def initialize(name)
175
- super(name, nil, nil)
170
+ super(name, nil, Type::Value.new)
176
171
  end
177
172
 
178
173
  def type_cast(*)
@@ -183,9 +178,10 @@ module DuckRecord
183
178
  self.class.with_cast_value(name, nil, type)
184
179
  end
185
180
 
186
- def with_value_from_user(value)
181
+ def with_value_from_database(value)
187
182
  raise ActiveModel::MissingAttributeError, "can't write unknown attribute `#{name}`"
188
183
  end
184
+ alias_method :with_value_from_user, :with_value_from_database
189
185
  end
190
186
 
191
187
  class Uninitialized < Attribute # :nodoc:
@@ -211,11 +207,7 @@ module DuckRecord
211
207
  def initialized?
212
208
  false
213
209
  end
214
-
215
- def with_type(type)
216
- self.class.new(name, DuckRecord::Type::Value.new)
217
- end
218
210
  end
219
- private_constant :FromUser, :Null, :Uninitialized, :WithCastValue
211
+ private_constant :FromDatabase, :FromUser, :Null, :Uninitialized, :WithCastValue
220
212
  end
221
213
  end
@@ -3,9 +3,9 @@ require 'duck_record/attribute'
3
3
  module DuckRecord
4
4
  class Attribute # :nodoc:
5
5
  class UserProvidedDefault < FromUser # :nodoc:
6
- def initialize(name, value, type, database_default)
6
+ def initialize(name, value, type, default)
7
7
  @user_provided_value = value
8
- super(name, value, type, database_default)
8
+ super(name, value, type, default)
9
9
  end
10
10
 
11
11
  def value_before_type_cast
@@ -61,7 +61,7 @@ module DuckRecord
61
61
  end
62
62
  end
63
63
  unless errors.empty?
64
- error_descriptions = errors.map(&:message).join(",")
64
+ error_descriptions = errors.map(&:message).join(',')
65
65
  raise MultiparameterAssignmentErrors.new(errors), "#{errors.size} error(s) on assignment of multiparameter attributes [#{error_descriptions}]"
66
66
  end
67
67
  end
@@ -70,7 +70,7 @@ module DuckRecord
70
70
  attributes = {}
71
71
 
72
72
  pairs.each do |(multiparameter_name, value)|
73
- attribute_name = multiparameter_name.split("(").first
73
+ attribute_name = multiparameter_name.split('(').first
74
74
  attributes[attribute_name] ||= {}
75
75
 
76
76
  parameter_value = value.empty? ? nil : type_cast_attribute_value(multiparameter_name, value)
@@ -182,14 +182,7 @@ module DuckRecord
182
182
  def respond_to?(name, include_private = false)
183
183
  return false unless super
184
184
 
185
- case name
186
- when :to_partial_path
187
- name = 'to_partial_path'.freeze
188
- when :to_model
189
- name = 'to_model'.freeze
190
- else
191
- name = name.to_s
192
- end
185
+ name = name.to_s
193
186
 
194
187
  # If the result is true then check for the select case.
195
188
  # For queries selecting a subset of columns, return false for unselected columns.
@@ -63,14 +63,14 @@ module DuckRecord
63
63
 
64
64
  private
65
65
 
66
- # Handle *_before_type_cast for method_missing.
67
- def attribute_before_type_cast(attribute_name)
68
- read_attribute_before_type_cast(attribute_name)
69
- end
66
+ # Handle *_before_type_cast for method_missing.
67
+ def attribute_before_type_cast(attribute_name)
68
+ read_attribute_before_type_cast(attribute_name)
69
+ end
70
70
 
71
- def attribute_came_from_user?(attribute_name)
72
- @attributes[attribute_name].came_from_user?
73
- end
71
+ def attribute_came_from_user?(attribute_name)
72
+ @attributes[attribute_name].came_from_user?
73
+ end
74
74
  end
75
75
  end
76
76
  end
@@ -9,11 +9,6 @@ module DuckRecord
9
9
 
10
10
  include ActiveModel::Dirty
11
11
 
12
- included do
13
- class_attribute :partial_writes, instance_writer: false
14
- self.partial_writes = true
15
- end
16
-
17
12
  def initialize_dup(other) # :nodoc:
18
13
  super
19
14
  @attributes = self.class._default_attributes.map do |attr|
@@ -88,18 +83,6 @@ module DuckRecord
88
83
  mutation_tracker.forget_change(attr_name)
89
84
  end
90
85
 
91
- def _update_record(*)
92
- partial_writes? ? super(keys_for_partial_write) : super
93
- end
94
-
95
- def _create_record(*)
96
- partial_writes? ? super(keys_for_partial_write) : super
97
- end
98
-
99
- def keys_for_partial_write
100
- changed & self.class.column_names
101
- end
102
-
103
86
  def store_original_attributes
104
87
  @attributes = @attributes.map(&:forgetting_assignment)
105
88
  @mutation_tracker = nil
@@ -25,7 +25,7 @@ module DuckRecord
25
25
  # Making it frozen means that it doesn't get duped when used to
26
26
  # key the @attributes in read_attribute.
27
27
  def define_method_attribute(name)
28
- safe_name = name.unpack("h*".freeze).first
28
+ safe_name = name.unpack('h*'.freeze).first
29
29
  temp_method = "__temp__#{safe_name}"
30
30
 
31
31
  DuckRecord::AttributeMethods::AttrNames.set_name_cache safe_name, name
@@ -5,7 +5,6 @@ module DuckRecord
5
5
  def initialize(attributes)
6
6
  @attributes = attributes
7
7
  @forced_changes = Set.new
8
- @deprecated_forced_changes = Set.new
9
8
  end
10
9
 
11
10
  def changed_values
@@ -32,7 +31,7 @@ module DuckRecord
32
31
  end
33
32
 
34
33
  def any_changes?
35
- attr_names.any? { |attr| changed?(attr) } || deprecated_forced_changes.any?
34
+ attr_names.any? { |attr| changed?(attr) }
36
35
  end
37
36
 
38
37
  def changed?(attr_name, from: OPTION_NOT_GIVEN, to: OPTION_NOT_GIVEN)
@@ -61,15 +60,11 @@ module DuckRecord
61
60
  forced_changes << attr_name.to_s
62
61
  end
63
62
 
64
- def deprecated_force_change(attr_name)
65
- deprecated_forced_changes << attr_name.to_s
66
- end
67
-
68
63
  # TODO Change this to private once we've dropped Ruby 2.2 support.
69
64
  # Workaround for Ruby 2.2 "private attribute?" warning.
70
65
  protected
71
66
 
72
- attr_reader :attributes, :forced_changes, :deprecated_forced_changes
67
+ attr_reader :attributes, :forced_changes
73
68
 
74
69
  private
75
70
 
@@ -89,7 +84,7 @@ module DuckRecord
89
84
  {}
90
85
  end
91
86
 
92
- def change_to_attribute(attr_name)
87
+ def change_to_attribute(_)
93
88
  end
94
89
 
95
90
  def any_changes?(*)
@@ -1,4 +1,3 @@
1
- require 'duck_record/attribute_set/builder'
2
1
  require 'duck_record/attribute_set/yaml_encoder'
3
2
 
4
3
  module DuckRecord
@@ -88,12 +87,12 @@ module DuckRecord
88
87
  # Workaround for Ruby 2.2 "private attribute?" warning.
89
88
  protected
90
89
 
91
- attr_reader :attributes
90
+ attr_reader :attributes
92
91
 
93
92
  private
94
93
 
95
- def initialized_attributes
96
- attributes.select { |_, attr| attr.initialized? }
97
- end
94
+ def initialized_attributes
95
+ attributes.select { |_, attr| attr.initialized? }
96
+ end
98
97
  end
99
98
  end
@@ -221,8 +221,7 @@ module DuckRecord
221
221
  def define_attribute(
222
222
  name,
223
223
  cast_type,
224
- default: NO_DEFAULT_PROVIDED,
225
- user_provided_default: true
224
+ default: NO_DEFAULT_PROVIDED
226
225
  )
227
226
  attribute_types[name] = cast_type
228
227
  define_default_attribute(name, default, cast_type)
@@ -13,13 +13,7 @@ module DuckRecord
13
13
  super
14
14
  end
15
15
 
16
- def initialize_find_by_cache # :nodoc:
17
- @find_by_statement_cache = { true => {}.extend(Mutex_m), false => {}.extend(Mutex_m) }
18
- end
19
-
20
16
  def inherited(child_class) # :nodoc:
21
- # initialize cache at class definition for thread safety
22
- child_class.initialize_find_by_cache
23
17
  super
24
18
  end
25
19
 
@@ -62,7 +56,10 @@ module DuckRecord
62
56
  init_internals
63
57
  initialize_internals_callback
64
58
 
65
- assign_attributes(attributes) if attributes
59
+ if attributes
60
+ assign_attributes(attributes)
61
+ clear_changes_information
62
+ end
66
63
 
67
64
  yield self if block_given?
68
65
  _run_initialize_callbacks
@@ -83,7 +80,6 @@ module DuckRecord
83
80
  # post.init_with(coder)
84
81
  # post.title # => 'hello world'
85
82
  def init_with(coder)
86
- coder = LegacyYamlAdapter.convert(self.class, coder)
87
83
  @attributes = self.class.yaml_encoder.decode(coder)
88
84
 
89
85
  init_internals
@@ -92,7 +88,6 @@ module DuckRecord
92
88
 
93
89
  yield self if block_given?
94
90
 
95
- _run_find_callbacks
96
91
  _run_initialize_callbacks
97
92
 
98
93
  self
@@ -199,17 +194,17 @@ module DuckRecord
199
194
  if defined?(@attributes) && @attributes
200
195
  pp.seplist(self.class.attribute_names, proc { pp.text ',' }) do |attribute_name|
201
196
  attribute_value = read_attribute(attribute_name)
202
- pp.breakable " "
197
+ pp.breakable ' '
203
198
  pp.group(1) do
204
199
  pp.text attribute_name
205
- pp.text ":"
200
+ pp.text ':'
206
201
  pp.breakable
207
202
  pp.pp attribute_value
208
203
  end
209
204
  end
210
205
  else
211
- pp.breakable " "
212
- pp.text "not initialized"
206
+ pp.breakable ' '
207
+ pp.text 'not initialized'
213
208
  end
214
209
  end
215
210
  end
@@ -234,7 +229,7 @@ module DuckRecord
234
229
  end
235
230
 
236
231
  def init_internals
237
- @readonly = false
232
+ @readonly = false
238
233
  end
239
234
 
240
235
  def initialize_internals_callback
@@ -1,4 +1,4 @@
1
- require "active_support/core_ext/hash/indifferent_access"
1
+ require 'active_support/core_ext/hash/indifferent_access'
2
2
 
3
3
  module DuckRecord
4
4
  # == Single table inheritance
@@ -99,7 +99,7 @@ module DuckRecord
99
99
  # Returns the class type of the record using the current module as a prefix. So descendants of
100
100
  # MyApp::Business::Account would appear as MyApp::Business::AccountSubclass.
101
101
  def compute_type(type_name)
102
- if type_name.start_with?("::".freeze)
102
+ if type_name.start_with?('::'.freeze)
103
103
  # If the type is prefixed with a scope operator then we assume that
104
104
  # the type_name is an absolute reference.
105
105
  ActiveSupport::Dependencies.constantize(type_name)
@@ -8,10 +8,6 @@ module DuckRecord
8
8
 
9
9
  module ClassMethods
10
10
 
11
- def attributes_builder # :nodoc:
12
- @attributes_builder ||= AttributeSet::Builder.new(attribute_types)
13
- end
14
-
15
11
  def attribute_types # :nodoc:
16
12
  load_schema
17
13
  @attribute_types ||= Hash.new
@@ -16,7 +16,7 @@ module DuckRecord
16
16
 
17
17
  # Set the i18n scope to overwrite ActiveModel.
18
18
  def i18n_scope #:nodoc:
19
- :activerecord
19
+ :duck_record
20
20
  end
21
21
  end
22
22
  end
@@ -47,6 +47,7 @@ module DuckRecord
47
47
  DateTime = ActiveModel::Type::DateTime
48
48
  Time = ActiveModel::Type::Time
49
49
  Date = ActiveModel::Type::Date
50
+ Value = ActiveModel::Type::Value
50
51
 
51
52
  register(:big_integer, Type::BigInteger, override: false)
52
53
  register(:binary, Type::Binary, override: false)
@@ -43,21 +43,21 @@ module DuckRecord
43
43
 
44
44
  def assert_valid_value(value)
45
45
  if coder.respond_to?(:assert_valid_value)
46
- coder.assert_valid_value(value, action: "serialize")
46
+ coder.assert_valid_value(value, action: 'serialize')
47
47
  end
48
48
  end
49
49
 
50
50
  private
51
51
 
52
- def default_value?(value)
53
- value == coder.load(nil)
54
- end
52
+ def default_value?(value)
53
+ value == coder.load(nil)
54
+ end
55
55
 
56
- def encoded(value)
57
- unless default_value?(value)
58
- coder.dump(value)
59
- end
56
+ def encoded(value)
57
+ unless default_value?(value)
58
+ coder.dump(value)
60
59
  end
60
+ end
61
61
  end
62
62
  end
63
63
  end
@@ -1,3 +1,3 @@
1
1
  module DuckRecord
2
- VERSION = '0.0.1'
2
+ VERSION = '0.0.3'
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: duck_record
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - jasl
@@ -52,8 +52,10 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '5.0'
55
- description: "It looks like Active Record and quacks like Active Record, it's Duck
56
- Record! \n Actually it's extract from Active Record."
55
+ description: "It looks like Active Record and quacks like Active Record, but it can't
56
+ do persistence or querying,\n it's Duck Record! \n Actually it's extract from
57
+ Active Record.\n Used for creating virtual models like ActiveType or ModelAttribute
58
+ does."
57
59
  email:
58
60
  - jasl9187@hotmail.com
59
61
  executables: []
@@ -74,7 +76,6 @@ files:
74
76
  - lib/duck_record/attribute_methods/write.rb
75
77
  - lib/duck_record/attribute_mutation_tracker.rb
76
78
  - lib/duck_record/attribute_set.rb
77
- - lib/duck_record/attribute_set/builder.rb
78
79
  - lib/duck_record/attribute_set/yaml_encoder.rb
79
80
  - lib/duck_record/attributes.rb
80
81
  - lib/duck_record/base.rb
@@ -122,5 +123,5 @@ rubyforge_project:
122
123
  rubygems_version: 2.6.10
123
124
  signing_key:
124
125
  specification_version: 4
125
- summary: Active Record without persistence
126
+ summary: Used for creating virtual models like ActiveType or ModelAttribute does
126
127
  test_files: []
@@ -1,124 +0,0 @@
1
- require 'duck_record/attribute'
2
-
3
- module DuckRecord
4
- class AttributeSet # :nodoc:
5
- class Builder # :nodoc:
6
- attr_reader :types, :always_initialized, :default
7
-
8
- def initialize(types, always_initialized = nil, &default)
9
- @types = types
10
- @always_initialized = always_initialized
11
- @default = default
12
- end
13
-
14
- def build_from_user(values = {}, additional_types = {})
15
- if always_initialized && !values.key?(always_initialized)
16
- values[always_initialized] = nil
17
- end
18
-
19
- attributes = LazyAttributeHash.new(types, values, additional_types, &default)
20
- AttributeSet.new(attributes)
21
- end
22
- end
23
- end
24
-
25
- class LazyAttributeHash # :nodoc:
26
- delegate :transform_values, :each_key, :each_value, :fetch, to: :materialize
27
-
28
- def initialize(types, values, additional_types, &default)
29
- @types = types
30
- @values = values
31
- @additional_types = additional_types
32
- @materialized = false
33
- @delegate_hash = {}
34
- @default = default || proc {}
35
- end
36
-
37
- def key?(key)
38
- delegate_hash.key?(key) || values.key?(key) || types.key?(key)
39
- end
40
-
41
- def [](key)
42
- delegate_hash[key] || assign_default_value(key)
43
- end
44
-
45
- def []=(key, value)
46
- if frozen?
47
- raise RuntimeError, "Can't modify frozen hash"
48
- end
49
- delegate_hash[key] = value
50
- end
51
-
52
- def deep_dup
53
- dup.tap do |copy|
54
- copy.instance_variable_set(:@delegate_hash, delegate_hash.transform_values(&:dup))
55
- end
56
- end
57
-
58
- def initialize_dup(_)
59
- @delegate_hash = Hash[delegate_hash]
60
- super
61
- end
62
-
63
- def select
64
- keys = types.keys | values.keys | delegate_hash.keys
65
- keys.each_with_object({}) do |key, hash|
66
- attribute = self[key]
67
- if yield(key, attribute)
68
- hash[key] = attribute
69
- end
70
- end
71
- end
72
-
73
- def ==(other)
74
- if other.is_a?(LazyAttributeHash)
75
- materialize == other.materialize
76
- else
77
- materialize == other
78
- end
79
- end
80
-
81
- def marshal_dump
82
- materialize
83
- end
84
-
85
- def marshal_load(delegate_hash)
86
- @delegate_hash = delegate_hash
87
- @types = {}
88
- @values = {}
89
- @additional_types = {}
90
- @materialized = true
91
- end
92
-
93
- # TODO Change this to private once we've dropped Ruby 2.2 support.
94
- # Workaround for Ruby 2.2 "private attribute?" warning.
95
- protected
96
-
97
- attr_reader :types, :values, :additional_types, :delegate_hash, :default
98
-
99
- def materialize
100
- unless @materialized
101
- values.each_key { |key| self[key] }
102
- types.each_key { |key| self[key] }
103
- unless frozen?
104
- @materialized = true
105
- end
106
- end
107
- delegate_hash
108
- end
109
-
110
- private
111
-
112
- def assign_default_value(name)
113
- type = additional_types.fetch(name, types[name])
114
- value_present = true
115
- value = values.fetch(name) { value_present = false }
116
-
117
- if value_present
118
- delegate_hash[name] = Attribute.from_user(name, value, type)
119
- elsif types.key?(name)
120
- delegate_hash[name] = default.call(name) || Attribute.uninitialized(name, type)
121
- end
122
- end
123
- end
124
- end