Skip to content

Commit edc7916

Browse files
committed
Merge branch 'edge' into issue-275
2 parents 47ee071 + c589af1 commit edc7916

16 files changed

Lines changed: 285 additions & 95 deletions

File tree

docs/dsl-isomorphic/hyper-model.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ For complete examples with *push* updates, see any of the apps in the `examples`
6464

6565
Depending on the architecture of your application, you may decide that some of your models should be Isomorphic and some should remain server-only. The consideration will be that your Isomorphic models will be compiled by Opal to JavaScript and accessible on he client (without the need for a boilerplate API) - Hyperstack takes care of the communication between your server-side models and their client-side compiled versions and you can use Policy to govern access to the models.
6666

67-
In order for Hyperstack to see your Models (and his make them Isomorphic) you need to move them to the `hyperstack/models` folder. Only models in this folder will be seen by Hyperstack and compiled to Javascript. Once a Model is on this folder it ill be accessable to both your client and server code.
67+
In order for Hyperstack to see your Models (and make them Isomorphic) you need to move them to the `hyperstack/models` folder. Only models in this folder will be seen by Hyperstack and compiled to Javascript. Once a Model is on this folder it ill be accessable to both your client and server code.
6868

6969
| **Location of Models** | **Scope** |
7070
| ------------------------- |---------------|

ruby/hyper-component/hyper-component.gemspec

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ Gem::Specification.new do |spec|
2323

2424
spec.add_dependency 'hyper-state', Hyperstack::Component::VERSION
2525
spec.add_dependency 'hyperstack-config', Hyperstack::Component::VERSION
26-
spec.add_dependency 'libv8', '~> 6.7.0'
27-
spec.add_dependency 'mini_racer', '~> 0.2.4'
26+
spec.add_dependency 'libv8', '~> 7.3.492.27.1'
27+
spec.add_dependency 'mini_racer', '~> 0.2.6'
2828
spec.add_dependency 'opal', '>= 0.11.0', '< 0.12.0'
2929
spec.add_dependency 'opal-activesupport', '~> 0.3.1'
3030
spec.add_dependency 'react-rails', '>= 2.4.0', '< 2.5.0'

ruby/hyper-model/hyper-model.gemspec

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ Gem::Specification.new do |spec|
3232
spec.add_development_dependency 'bundler', ['>= 1.17.3', '< 2.1']
3333
spec.add_development_dependency 'capybara'
3434
spec.add_development_dependency 'chromedriver-helper', '1.2.0'
35-
spec.add_development_dependency 'libv8'
36-
spec.add_development_dependency 'mini_racer', '~> 0.2.4'
35+
spec.add_development_dependency 'libv8', '~> 7.3.492.27.1'
36+
spec.add_development_dependency 'mini_racer', '~> 0.2.6'
3737
spec.add_development_dependency 'selenium-webdriver'
3838
spec.add_development_dependency 'database_cleaner'
3939
spec.add_development_dependency 'factory_bot_rails'

ruby/hyper-model/lib/reactive_record/active_record/associations.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ def self.reflect_on_association(attr)
1111
end
1212

1313
def self.reflect_on_association_by_foreign_key(key)
14-
reflection_finder { |assoc| assoc.association_foreign_key == key }
14+
reflection_finder { |assoc| assoc.association_foreign_key == key && assoc.macro != :has_many }
1515
end
1616

1717
def self.reflection_finder(&block)
@@ -161,6 +161,7 @@ def inverse_of(model = nil)
161161
def find_inverse(model) # private
162162
the_klass = klass(model)
163163
the_klass.reflect_on_all_associations.each do |association|
164+
next if association == self
164165
next if association.association_foreign_key != @association_foreign_key
165166
next if association.attribute == attribute
166167
return association if association.polymorphic? || association.klass == owner_class

ruby/hyper-model/lib/reactive_record/active_record/class_methods.rb

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ def __hyperstack_preprocess_attrs(attrs)
5757
end
5858
dealiased_attrs = {}
5959
attrs.each { |attr, value| dealiased_attrs[_dealias_attribute(attr)] = value }
60+
dealiased_attrs
6061
end
6162

6263
def find(*args)
@@ -297,11 +298,12 @@ def abstract_class=(val)
297298
assoc = Associations::AssociationReflection.new(self, macro, name, opts)
298299
if macro == :has_many
299300
define_method(name) { @backing_record.get_has_many(assoc, nil) }
300-
define_method("#{name}=") { |val| @backing_record.set_has_many(assoc, val) }
301+
define_method("_hyperstack_internal_setter_#{name}") { |val| @backing_record.set_has_many(assoc, val) }
301302
else
302303
define_method(name) { @backing_record.get_belongs_to(assoc, nil) }
303-
define_method("#{name}=") { |val| @backing_record.set_belongs_to(assoc, val) }
304+
define_method("_hyperstack_internal_setter_#{name}") { |val| @backing_record.set_belongs_to(assoc, val) }
304305
end
306+
alias_method "#{name}=", "_hyperstack_internal_setter_#{name}"
305307
assoc
306308
end
307309
end
@@ -310,11 +312,12 @@ def composed_of(name, opts = {})
310312
reflection = Aggregations::AggregationReflection.new(base_class, :composed_of, name, opts)
311313
if reflection.klass < ActiveRecord::Base
312314
define_method(name) { @backing_record.get_ar_aggregate(reflection, nil) }
313-
define_method("#{name}=") { |val| @backing_record.set_ar_aggregate(reflection, val) }
315+
define_method("_hyperstack_internal_setter_#{name}") { |val| @backing_record.set_ar_aggregate(reflection, val) }
314316
else
315317
define_method(name) { @backing_record.get_non_ar_aggregate(name, nil) }
316-
define_method("#{name}=") { |val| @backing_record.set_non_ar_aggregate(reflection, val) }
318+
define_method("_hyperstack_internal_setter_#{name}") { |val| @backing_record.set_non_ar_aggregate(reflection, val) }
317319
end
320+
alias_method "#{name}=", "_hyperstack_internal_setter_#{name}"
318321
end
319322

320323
def column_names
@@ -339,22 +342,16 @@ def server_method(name, default: nil)
339342
vector = args.count.zero? ? name : [[name] + args]
340343
@backing_record.get_server_method(vector, true)
341344
end
345+
define_method("_hyperstack_internal_setter_#{name}") do |val|
346+
backing_record.set_attr_value(name, val)
347+
end
342348
end
343349

344-
# def define_attribute_methods
345-
# columns_hash.each do |name, column_hash|
346-
# next if name == primary_key
347-
# column_hash[:serialized?] = true if ReactiveRecord::Base.serialized?[self][name]
348-
#
349-
# define_method(name) { @backing_record.get_attr_value(name, nil) } unless method_defined?(name)
350-
# define_method("#{name}!") { @backing_record.get_attr_value(name, true) } unless method_defined?("#{name}!")
351-
# define_method("#{name}=") { |val| @backing_record.set_attr_value(name, val) } unless method_defined?("#{name}=")
352-
# define_method("#{name}_changed?") { @backing_record.changed?(name) } unless method_defined?("#{name}_changed?")
353-
# define_method("#{name}?") { @backing_record.get_attr_value(name, nil).present? } unless method_defined?("#{name}?")
354-
# end
355-
# self.inheritance_column = nil if inheritance_column && !columns_hash.key?(inheritance_column)
356-
# end
357-
350+
# define all the methods for each column. To allow overriding the methods they will NOT
351+
# be defined if already defined (i.e. by the model) See the instance_methods module for how
352+
# super calls are handled in this case. The _hyperstack_internal_setter_... methods
353+
# are used by the load_from_json method when bringing in data from the server, and so therefore
354+
# does not want to be overriden.
358355

359356
def define_attribute_methods
360357
columns_hash.each do |name, column_hash|
@@ -363,10 +360,11 @@ def define_attribute_methods
363360
# easier by keeping the columns_hash the same if there are no seralized strings
364361
# see rspec ./spec/batch1/column_types/column_type_spec.rb:100
365362
column_hash[:serialized?] = true if ReactiveRecord::Base.serialized?[self][name]
366-
363+
367364
define_method(name) { @backing_record.get_attr_value(name, nil) } unless method_defined?(name)
368365
define_method("#{name}!") { @backing_record.get_attr_value(name, true) } unless method_defined?("#{name}!")
369-
define_method("#{name}=") { |val| @backing_record.set_attr_value(name, val) } unless method_defined?("#{name}=")
366+
define_method("_hyperstack_internal_setter_#{name}") { |val| @backing_record.set_attr_value(name, val) }
367+
alias_method "#{name}=", "_hyperstack_internal_setter_#{name}" unless method_defined?("#{name}=")
370368
define_method("#{name}_changed?") { @backing_record.changed?(name) } unless method_defined?("#{name}_changed?")
371369
define_method("#{name}?") { @backing_record.get_attr_value(name, nil).present? } unless method_defined?("#{name}?")
372370
end
@@ -399,18 +397,23 @@ def _react_param_conversion(param, opt = nil)
399397
associations = reflect_on_all_associations
400398

401399
already_processed_keys = Set.new
402-
old_param = param.dup
403400

404401
param = param.collect do |key, value|
405402
next if already_processed_keys.include? key
406403

407404
model_name = model_id = nil
408405

406+
# polymorphic association is where the belongs_to side holds the
407+
# id, and the type of the model the id points to
408+
409+
# belongs_to :duplicate_of, class_name: 'Report', required: false
410+
# has_many :duplicates, class_name: 'Report', foreign_key: 'duplicate_of_id'
411+
409412
assoc = associations.detect do |poly_assoc|
410413
if key == poly_assoc.polymorphic_type_attribute
411414
model_name = value
412415
already_processed_keys << poly_assoc.association_foreign_key
413-
elsif key == poly_assoc.association_foreign_key
416+
elsif key == poly_assoc.association_foreign_key && (poly_assoc.polymorphic_type_attribute || poly_assoc.macro == :belongs_to)
414417
model_id = value
415418
already_processed_keys << poly_assoc.polymorphic_type_attribute
416419
end

ruby/hyper-model/lib/reactive_record/active_record/instance_methods.rb

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,19 @@
11
module ActiveRecord
22
module InstanceMethods
33

4+
# if methods are missing, then they must be a column, which we look up
5+
# in the columns_hash.
6+
7+
# For effeciency all attributes will by default have all the methods defined,
8+
# when the class is loaded. See define_attribute_methods class method.
9+
# However a model may override the attribute methods definition, but then call
10+
# super. Which will result in the method missing call.
11+
12+
# When loading data from the server we do NOT want to call overridden methods
13+
# so we also define a _hyperstack_internal_setter_... method for each attribute
14+
# as well as for belongs_to relationships, server_methods, and the special
15+
# type and model_name methods. See the ClassMethods module for details.
16+
417
def method_missing(missing, *args, &block)
518
column = self.class.columns_hash.detect { |name, *| missing =~ /^#{name}/ }
619
if column
@@ -17,6 +30,19 @@ def method_missing(missing, *args, &block)
1730
end
1831
end
1932

33+
# ignore load_from_json when it calls _hyperstack_internal_setter_id
34+
def _hyperstack_internal_setter_id(*); end
35+
36+
# the system assumes that there is "virtual" model_name and type attribute so
37+
# we define the internal setter here. If the user defines some other attributes
38+
# or uses these names no harm is done since the exact same method would have been
39+
# defined by the define_attribute_methods class method anyway.
40+
%i[model_name type].each do |attr|
41+
define_method("_hyperstack_internal_setter_#{attr}") do |val|
42+
@backing_record.set_attr_value(:model_name, val)
43+
end
44+
end
45+
2046
def inspect
2147
"<#{model_name}:#{ReactiveRecord::Operations::Base::FORMAT % to_key} "\
2248
"(#{ReactiveRecord::Operations::Base::FORMAT % object_id}) "\

ruby/hyper-model/lib/reactive_record/active_record/reactive_record/collection.rb

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -558,8 +558,8 @@ def internal_replace(new_array)
558558
end
559559

560560
def delete(item)
561-
unsaved_children.delete(item)
562-
notify_of_change(
561+
Hyperstack::Internal::State::Mapper.bulk_update do
562+
unsaved_children.delete(item)
563563
if @owner && @association
564564
inverse_of = @association.inverse_of
565565
if (backing_record = item.backing_record) && item.attributes[inverse_of] == @owner && !@association.through_association?
@@ -569,8 +569,8 @@ def delete(item)
569569
delete_internal(item) { @owner.backing_record.sync_has_many(@association.attribute) }
570570
else
571571
delete_internal(item)
572-
end
573-
)
572+
end.tap { Hyperstack::Internal::State::Variable.set(self, :collection, collection) }
573+
end
574574
end
575575

576576
def delete_internal(item)
@@ -618,8 +618,22 @@ def empty?
618618
count.zero?
619619
end
620620

621-
def any?
622-
!count.zero?
621+
def any?(*args, &block)
622+
# If there are any args passed in, then the collection is being used in the condition
623+
# and we must load it all into memory.
624+
return all.any?(*args, &block) if args&.length&.positive? || block.present?
625+
626+
# Otherwise we can just check the count for efficiency
627+
!empty?
628+
end
629+
630+
def none?(*args, &block)
631+
# If there are any args passed in, then the collection is being used in the condition
632+
# and we must load it all into memory.
633+
return all.none?(*args, &block) if args&.length&.positive? || block.present?
634+
635+
# Otherwise we can just check the count for efficiency
636+
empty?
623637
end
624638

625639
def method_missing(method, *args, &block)

0 commit comments

Comments
 (0)