duck_record 0.0.1 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
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