hyper-model 1.0.alpha1.4 → 1.0.alpha1.5

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
  SHA256:
3
- metadata.gz: '0583cd6276013eed8abad6c7e12454455c6915e9773eb195b51977c8d748217f'
4
- data.tar.gz: a3e3f3e0aa5065281103566c6178bd2e4045a09863e1697d7587a5d2321f17dc
3
+ metadata.gz: a2458d824d9c7121f8e628dca72a9df1f6a87ecfc947361046887ea1eca56657
4
+ data.tar.gz: 4e6d8aa0a03d07c0080d73aa8bddbc876c1fa56eaebca3f7615425914f8fee07
5
5
  SHA512:
6
- metadata.gz: 7d21d5c7def5a3217d00757c611042da17efcaf639eba5fd5c021f79ffaa7e9f98334186d51c5c5353188c56b528286b91cc3d48ed4e8ecadf73cdae40cae04e
7
- data.tar.gz: 5aeaf368b37b2f4224ec0d7d3af83f44708f9093e97f7261d49d65e78af6ac0e5078c055d4fb974b4c2041491911087175e12e5988fc6873bec17ed388dee8ff
6
+ metadata.gz: 2e0e89354d96b059f62b51023a568e2b334ac2b47bc5940bc8ac6621940259e0b03ec90a61e5e9e135a07954d0cd75a93c6f765486412f80470aedb5a5dc96d0
7
+ data.tar.gz: 6b1642506f04f6fc00fd6e6be388632e1157e6405c629928a61d5a8d525fac8666b0f6611d4e261060573b2dc3b184ea3fe0c5640f1a955d2193899b6223202e
@@ -255,7 +255,7 @@ module ActiveRecord
255
255
  pre_syncromesh_has_many name, *args, opts.except(:regulate), &block
256
256
  end
257
257
 
258
- %i[belongs_to has_one].each do |macro|
258
+ %i[belongs_to has_one composed_of].each do |macro|
259
259
  alias_method :"pre_syncromesh_#{macro}", macro
260
260
  define_method(macro) do |name, *aargs, &block|
261
261
  define_method(:"__secure_remote_access_to_#{name}") do |this, _acting_user, *args|
data/lib/hyper-model.rb CHANGED
@@ -20,6 +20,7 @@ if RUBY_ENGINE == 'opal'
20
20
  require "reactive_record/active_record/reactive_record/isomorphic_base"
21
21
  require 'reactive_record/active_record/reactive_record/dummy_value'
22
22
  require 'reactive_record/active_record/reactive_record/column_types'
23
+ require 'reactive_record/active_record/reactive_record/dummy_polymorph'
23
24
  require "reactive_record/active_record/aggregations"
24
25
  require "reactive_record/active_record/associations"
25
26
  require "reactive_record/active_record/reactive_record/backing_record_inspector"
@@ -1,3 +1,3 @@
1
1
  module HyperModel
2
- VERSION = '1.0.alpha1.4'
2
+ VERSION = '1.0.alpha1.5'
3
3
  end
@@ -42,7 +42,8 @@ module Hyperstack
42
42
  Hyperstack::Internal::Component::RenderingContext.render(tag, opts) { children.each(&:render) }
43
43
  end
44
44
  end
45
- const_set component, klass
45
+
46
+ Object.const_set component, klass
46
47
  end
47
48
  end
48
49
  end
@@ -38,16 +38,45 @@ module ActiveRecord
38
38
  attr_reader :macro
39
39
  attr_reader :owner_class
40
40
  attr_reader :source
41
+ attr_reader :source_type
42
+ attr_reader :options
43
+ attr_reader :polymorphic_type_attribute
41
44
 
42
45
  def initialize(owner_class, macro, name, options = {})
43
46
  owner_class.reflect_on_all_associations << self
44
47
  @owner_class = owner_class
45
48
  @macro = macro
46
49
  @options = options
47
- @klass_name = options[:class_name] || (collection? && name.camelize.singularize) || name.camelize
48
- @association_foreign_key = options[:foreign_key] || (macro == :belongs_to && "#{name}_id") || "#{@owner_class.name.underscore}_id"
49
- @source = options[:source] || @klass_name.underscore if options[:through]
50
+ unless options[:polymorphic]
51
+ @klass_name = options[:class_name] || (collection? && name.camelize.singularize) || name.camelize
52
+ end
53
+
54
+ if @klass_name < ActiveRecord::Base
55
+ @klass = @klass_name
56
+ @klass_name = @klass_name.name
57
+ end rescue nil
58
+
59
+ @association_foreign_key =
60
+ options[:foreign_key] ||
61
+ (macro == :belongs_to && "#{name}_id") ||
62
+ (options[:as] && "#{options[:as]}_id") ||
63
+ (options[:polymorphic] && "#{name}_id") ||
64
+ "#{@owner_class.name.underscore}_id"
65
+ if options[:through]
66
+ @source = options[:source] || @klass_name.underscore
67
+ @source_type = options[:source_type] || @klass_name
68
+ end
69
+ @polymorphic_type_attribute = "#{name}_type" if options[:polymorphic]
50
70
  @attribute = name
71
+ @through_associations = Hash.new { |_h, k| [] unless k }
72
+ end
73
+
74
+ def collection?
75
+ @macro == :has_many
76
+ end
77
+
78
+ def singular?
79
+ @macro != :has_many
51
80
  end
52
81
 
53
82
  def through_association
@@ -62,63 +91,119 @@ module ActiveRecord
62
91
 
63
92
  alias through_association? through_association
64
93
 
65
- def through_associations
94
+ # class Membership < ActiveRecord::Base
95
+ # belongs_to :uzer
96
+ # belongs_to :memerable, polymorphic: true
97
+ # end
98
+ #
99
+ # class Project < ActiveRecord::Base
100
+ # has_many :memberships, as: :memerable, dependent: :destroy
101
+ # has_many :uzers, through: :memberships
102
+ # end
103
+ #
104
+ # class Group < ActiveRecord::Base
105
+ # has_many :memberships, as: :memerable, dependent: :destroy
106
+ # has_many :uzers, through: :memberships
107
+ # end
108
+ #
109
+ # class Uzer < ActiveRecord::Base
110
+ # has_many :memberships
111
+ # has_many :groups, through: :memberships, source: :memerable, source_type: 'Group'
112
+ # has_many :projects, through: :memberships, source: :memerable, source_type: 'Project'
113
+ # end
114
+
115
+ # so find the belongs_to relationship whose attribute == ta.source
116
+ # now find the inverse of that relationship using source_value as the model
117
+ # now find any has many through relationships that use that relationship as there source.
118
+ # each of those attributes in the source_value have to be updated.
119
+
120
+ # self is the through association
121
+
122
+
123
+ def through_associations(model)
124
+ # given self is a belongs_to association currently pointing to model
66
125
  # find all associations that use the inverse association as the through association
67
126
  # that is find all associations that are using this association in a through relationship
68
- @through_associations ||= klass.reflect_on_all_associations.select do |assoc|
69
- assoc.through_association && assoc.inverse == self
127
+ the_klass = klass(model)
128
+ @through_associations[the_klass] ||= the_klass.reflect_on_all_associations.select do |assoc|
129
+ assoc.through_association&.inverse == self
70
130
  end
71
131
  end
72
132
 
73
- def source_associations
74
- # find all associations that use this association as the source
75
- # that is final all associations that are using this association as the source in a
76
- # through relationship
77
- @source_associations ||= owner_class.reflect_on_all_associations.collect do |sibling|
78
- sibling.klass.reflect_on_all_associations.select do |assoc|
79
- assoc.source == attribute
133
+ def source_belongs_to_association # private
134
+ # given self is a has_many_through association return the corresponding belongs_to association
135
+ # for the source
136
+ @source_belongs_to_association ||=
137
+ through_association.inverse.owner_class.reflect_on_all_associations.detect do |sibling|
138
+ sibling.attribute == source
80
139
  end
81
- end.flatten
82
140
  end
83
141
 
84
- def inverse
85
- @inverse ||=
86
- through_association ? through_association.inverse : find_inverse
142
+ def source_associations(model)
143
+ # given self is a has_many_through association find the source_association for the given model
144
+ source_belongs_to_association.through_associations(model)
87
145
  end
88
146
 
89
- def inverse_of
90
- @inverse_of ||= inverse.attribute
147
+ alias :polymorphic? polymorphic_type_attribute
148
+
149
+ def inverse(model = nil)
150
+ return @inverse if @inverse
151
+ ta = through_association
152
+ found = ta ? ta.inverse : find_inverse(model)
153
+ @inverse = found unless polymorphic?
154
+ found
91
155
  end
92
156
 
93
- def find_inverse
94
- klass.reflect_on_all_associations.each do |association|
157
+ def inverse_of(model = nil)
158
+ inverse(model).attribute
159
+ end
160
+
161
+ def find_inverse(model) # private
162
+ the_klass = klass(model)
163
+ the_klass.reflect_on_all_associations.each do |association|
95
164
  next if association.association_foreign_key != @association_foreign_key
96
- next if association.klass != @owner_class
97
165
  next if association.attribute == attribute
98
- return association if klass == association.owner_class
166
+ return association if association.polymorphic? || association.klass == owner_class
99
167
  end
168
+ raise "could not find inverse of polymorphic belongs_to: #{model.inspect} #{self.inspect}" if options[:polymorphic]
100
169
  # instead of raising an error go ahead and create the inverse relationship if it does not exist.
101
170
  # https://github.com/hyperstack-org/hyperstack/issues/89
102
171
  if macro == :belongs_to
103
- Hyperstack::Component::IsomorphicHelpers.log "**** warning dynamically adding relationship: #{klass}.has_many :#{@owner_class.name.underscore.pluralize}, foreign_key: #{@association_foreign_key}", :warning
104
- klass.has_many @owner_class.name.underscore.pluralize, foreign_key: @association_foreign_key
172
+ Hyperstack::Component::IsomorphicHelpers.log "**** warning dynamically adding relationship: #{the_klass}.has_many :#{@owner_class.name.underscore.pluralize}, foreign_key: #{@association_foreign_key}", :warning
173
+ the_klass.has_many @owner_class.name.underscore.pluralize, foreign_key: @association_foreign_key
174
+ elsif options[:as]
175
+ Hyperstack::Component::IsomorphicHelpers.log "**** warning dynamically adding relationship: #{the_klass}.belongs_to :#{options[:as]}, polymorphic: true", :warning
176
+ the_klass.belongs_to options[:as], polymorphic: true
105
177
  else
106
- Hyperstack::Component::IsomorphicHelpers.log "**** warning dynamically adding relationship: #{klass}.belongs_to :#{@owner_class.name.underscore}, foreign_key: #{@association_foreign_key}", :warning
107
- klass.belongs_to @owner_class.name.underscore, foreign_key: @association_foreign_key
178
+ Hyperstack::Component::IsomorphicHelpers.log "**** warning dynamically adding relationship: #{the_klass}.belongs_to :#{@owner_class.name.underscore}, foreign_key: #{@association_foreign_key}", :warning
179
+ the_klass.belongs_to @owner_class.name.underscore, foreign_key: @association_foreign_key
108
180
  end
109
181
  end
110
182
 
111
- def klass
112
- @klass ||= Object.const_get(@klass_name)
183
+ def klass(model = nil)
184
+ @klass ||= Object.const_get(@klass_name) if @klass_name
185
+ if @klass && model && !(model.class <= @klass || @klass <= model.class)
186
+ # TODO: added || @klass <= model.class can both cases really happen I guess so
187
+ raise "internal error: provided model #{model} is not subclass of #{@klass}"
188
+ end
189
+ raise 'no model supplied for polymorphic relationship' unless @klass || model
190
+ @klass || model.class
113
191
  end
114
192
 
115
193
  def collection?
116
194
  [:has_many].include? @macro
117
195
  end
118
196
 
119
- end
197
+ def remove_member(member, owner)
198
+ collection = owner.attributes[attribute]
199
+ return if collection.nil?
200
+ collection.delete(member)
201
+ end
120
202
 
203
+ def add_member(member, owner)
204
+ owner.attributes[attribute] ||= ReactiveRecord::Collection.new(owner_class, owner, self)
205
+ owner.attributes[attribute]._internal_push member
206
+ end
207
+ end
121
208
  end
122
-
123
-
124
209
  end
@@ -172,7 +172,7 @@ module ActiveRecord
172
172
  :full_table_name_prefix, :full_table_name_suffix, :reset_sequence_name, :sequence_name=, :next_sequence_value, :column_defaults, :content_columns,
173
173
  :readonly_attributes, :attr_readonly, :create, :create!, :instantiate, :find, :type_caster, :arel_table, :find_by, :find_by!, :initialize_find_by_cache,
174
174
  :generated_association_methods, :arel_engine, :arel_attribute, :predicate_builder, :collection_cache_key, :relation_delegate_class,
175
- :initialize_relation_delegate_cache, :enum, :collecting_queries_for_explain, :exec_explain, :i18n_scope, :lookup_ancestors, :human_attribute_name,
175
+ :initialize_relation_delegate_cache, :enum, :collecting_queries_for_explain, :exec_explain, :i18n_scope, :lookup_ancestors,
176
176
  :references, :uniq, :maximum, :none, :exists?, :second, :limit, :order, :eager_load, :update, :delete_all, :destroy, :ids, :many?, :pluck, :third,
177
177
  :delete, :fourth, :fifth, :forty_two, :second_to_last, :third_to_last, :preload, :sum, :take!, :first!, :last!, :second!, :offset, :select, :fourth!,
178
178
  :third!, :third_to_last!, :fifth!, :where, :first_or_create, :second_to_last!, :forty_two!, :first, :having, :any?, :one?, :none?, :find_or_create_by,
@@ -185,7 +185,10 @@ module ActiveRecord
185
185
  ]
186
186
 
187
187
  def method_missing(name, *args, &block)
188
- if args.count == 1 && name.start_with?("find_by_") && !block
188
+ if name == 'human_attribute_name'
189
+ opts = args[1] || {}
190
+ opts[:default] || args[0]
191
+ elsif args.count == 1 && name.start_with?("find_by_") && !block
189
192
  find_by(name.sub(/^find_by_/, '') => args[0])
190
193
  elsif [].respond_to?(name)
191
194
  all.send(name, *args, &block)
@@ -336,14 +339,34 @@ module ActiveRecord
336
339
  end
337
340
  end
338
341
 
342
+ # def define_attribute_methods
343
+ # columns_hash.each do |name, column_hash|
344
+ # next if name == primary_key
345
+ # column_hash[:serialized?] = true if ReactiveRecord::Base.serialized?[self][name]
346
+ #
347
+ # define_method(name) { @backing_record.get_attr_value(name, nil) } unless method_defined?(name)
348
+ # define_method("#{name}!") { @backing_record.get_attr_value(name, true) } unless method_defined?("#{name}!")
349
+ # define_method("#{name}=") { |val| @backing_record.set_attr_value(name, val) } unless method_defined?("#{name}=")
350
+ # define_method("#{name}_changed?") { @backing_record.changed?(name) } unless method_defined?("#{name}_changed?")
351
+ # define_method("#{name}?") { @backing_record.get_attr_value(name, nil).present? } unless method_defined?("#{name}?")
352
+ # end
353
+ # self.inheritance_column = nil if inheritance_column && !columns_hash.key?(inheritance_column)
354
+ # end
355
+
356
+
339
357
  def define_attribute_methods
340
358
  columns_hash.each do |name, column_hash|
341
359
  next if name == primary_key
342
- define_method(name) { @backing_record.get_attr_value(name, nil) }
343
- define_method("#{name}!") { @backing_record.get_attr_value(name, true) }
344
- define_method("#{name}=") { |val| @backing_record.set_attr_value(name, val) }
345
- define_method("#{name}_changed?") { @backing_record.changed?(name) }
346
- define_method("#{name}?") { @backing_record.get_attr_value(name, nil).present? }
360
+ # only add serialized key if its serialized. This just makes testing a bit
361
+ # easier by keeping the columns_hash the same if there are no seralized strings
362
+ # see rspec ./spec/batch1/column_types/column_type_spec.rb:100
363
+ column_hash[:serialized?] = true if ReactiveRecord::Base.serialized?[self][name]
364
+
365
+ define_method(name) { @backing_record.get_attr_value(name, nil) } unless method_defined?(name)
366
+ define_method("#{name}!") { @backing_record.get_attr_value(name, true) } unless method_defined?("#{name}!")
367
+ define_method("#{name}=") { |val| @backing_record.set_attr_value(name, val) } unless method_defined?("#{name}=")
368
+ define_method("#{name}_changed?") { @backing_record.changed?(name) } unless method_defined?("#{name}_changed?")
369
+ define_method("#{name}?") { @backing_record.get_attr_value(name, nil).present? } unless method_defined?("#{name}?")
347
370
  end
348
371
  self.inheritance_column = nil if inheritance_column && !columns_hash.key?(inheritance_column)
349
372
  end
@@ -373,22 +396,53 @@ module ActiveRecord
373
396
 
374
397
  associations = reflect_on_all_associations
375
398
 
399
+ already_processed_keys = Set.new
400
+ old_param = param.dup
401
+
376
402
  param = param.collect do |key, value|
377
- assoc = associations.detect do |association|
378
- association.association_foreign_key == key
403
+ next if already_processed_keys.include? key
404
+
405
+ model_name = model_id = nil
406
+
407
+ assoc = associations.detect do |poly_assoc|
408
+ if key == poly_assoc.polymorphic_type_attribute
409
+ model_name = value
410
+ already_processed_keys << poly_assoc.association_foreign_key
411
+ elsif key == poly_assoc.association_foreign_key
412
+ model_id = value
413
+ already_processed_keys << poly_assoc.polymorphic_type_attribute
414
+ end
379
415
  end
380
416
 
381
417
  if assoc
382
- if value
383
- [assoc.attribute, { id: [value] }]
384
- else
418
+ if !value
385
419
  [assoc.attribute, [nil]]
420
+ elsif assoc.polymorphic?
421
+ model_id ||= param.detect { |k, *| k == assoc.association_foreign_key }&.last
422
+ model_id ||= target.send(assoc.attribute)&.id
423
+ if model_id.nil?
424
+ raise "Error in #{self.name}._react_param_conversion. \n"\
425
+ "Could not determine the id of #{assoc.attribute} of #{target.inspect}.\n"\
426
+ "It was not provided in the conversion data, "\
427
+ "and it is unknown on the client"
428
+ end
429
+ model_name ||= param.detect { |k, *| k == assoc.polymorphic_type_attribute }&.last
430
+ model_name ||= target.send(assoc.polymorphic_type_attribute)
431
+ unless Object.const_defined?(model_name)
432
+ raise "Error in #{self.name}._react_param_conversion. \n"\
433
+ "Could not determine the type of #{assoc.attribute} of #{target.inspect}.\n"\
434
+ "It was not provided in the conversion data, "\
435
+ "and it is unknown on the client"
436
+ end
437
+
438
+ [assoc.attribute, { id: [model_id], model_name: [model_name] }]
439
+ else
440
+ [assoc.attribute, { id: [value]}]
386
441
  end
387
442
  else
388
443
  [key, [value]]
389
444
  end
390
- end
391
- # TODO: verify wrapping with load_data was added so broadcasting works in 1.0.0.lap28
445
+ end.compact
392
446
  ReactiveRecord::Base.load_data do
393
447
  ReactiveRecord::ServerDataCache.load_from_json(Hash[param], target)
394
448
  end
@@ -24,6 +24,8 @@ module ActiveModel
24
24
  @messages.clear
25
25
  end
26
26
 
27
+ alias non_reactive_clear clear
28
+
27
29
  def add(attribute, message = :invalid, _options = {})
28
30
  @messages[attribute] << message unless @messages[attribute].include? message
29
31
  end
@@ -89,7 +89,7 @@ module ActiveModel
89
89
  # person.errors.values # => [["cannot be nil", "must be specified"]]
90
90
  def values
91
91
  messages.select do |key, value|
92
- !value.empty?
92
+ !value&.empty?
93
93
  end.values
94
94
  end
95
95
 
@@ -105,9 +105,9 @@ module ActiveModel
105
105
  attr_name =
106
106
  attribute.to_s.tr('.', '_').tr('_', ' ').gsub(/_id$/, '').capitalize
107
107
  # attr_name = @base.class.human_attribute_name(attribute, default: attr_name)
108
- # if @base.class.respond_to?(:human_attribute_name)
108
+ if @base.class.respond_to?(:human_attribute_name)
109
109
  attr_name = @base.class.human_attribute_name(attribute, default: attr_name)
110
- # end
110
+ end
111
111
  # I18n.t(:"errors.format",
112
112
  # default: "%{attribute} %{message}",
113
113
  # attribute: attr_name,
@@ -219,8 +219,12 @@ module ActiveModel
219
219
  # person.errors.clear
220
220
  # person.errors.full_messages # => []
221
221
  def clear
222
+ non_reactive_clear.tap { reactive_empty! true }
223
+ end
224
+
225
+ def non_reactive_clear
222
226
  messages.clear
223
- details.clear.tap { reactive_empty! true }
227
+ details.clear
224
228
  end
225
229
 
226
230
  # Merges the errors from <tt>other</tt>.
@@ -1,5 +1,22 @@
1
1
  module ActiveRecord
2
2
  module InstanceMethods
3
+
4
+ def method_missing(missing, *args, &block)
5
+ column = self.class.columns_hash.detect { |name, *| missing =~ /^#{name}/ }
6
+ if column
7
+ name = column[0]
8
+ case missing
9
+ when /\!\z/ then @backing_record.get_attr_value(name, true)
10
+ when /\=\z/ then @backing_record.set_attr_value(name, *args)
11
+ when /\_changed\?\z/ then @backing_record.changed?(name)
12
+ when /\?/ then @backing_record.get_attr_value(name, nil).present?
13
+ else @backing_record.get_attr_value(name, nil)
14
+ end
15
+ else
16
+ super
17
+ end
18
+ end
19
+
3
20
  def inspect
4
21
  "<#{model_name}:#{ReactiveRecord::Operations::Base::FORMAT % to_key} "\
5
22
  "(#{ReactiveRecord::Operations::Base::FORMAT % object_id}) "\
@@ -81,7 +98,11 @@ module ActiveRecord
81
98
  end
82
99
 
83
100
  def ==(ar_instance)
84
- @backing_record == ar_instance.instance_eval { @backing_record }
101
+ return true if @backing_record == ar_instance.instance_eval { @backing_record }
102
+ return false unless ar_instance.is_a?(ActiveRecord::Base)
103
+ return false if ar_instance.new_record?
104
+ return false unless self.class.base_class == ar_instance.class.base_class
105
+ id == ar_instance.id
85
106
  end
86
107
 
87
108
  def [](attr)