hyper-model 0.6.0 → 0.99.0

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.
Files changed (140) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +35 -41
  3. data/.rspec +2 -0
  4. data/.travis.yml +33 -0
  5. data/CHANGELOG.md +34 -0
  6. data/DOCS.md +735 -0
  7. data/Gemfile +7 -0
  8. data/Gemfile.lock +298 -224
  9. data/{LICENSE → LICENSE.txt} +6 -6
  10. data/README.md +51 -2
  11. data/Rakefile +18 -0
  12. data/bin/console +14 -0
  13. data/bin/setup +7 -0
  14. data/codeship.database.yml +18 -0
  15. data/hyper-model.gemspec +62 -36
  16. data/lib/active_model_client_stubs.rb +16 -0
  17. data/lib/active_record_base.rb +331 -0
  18. data/{examples/chat-app/app/assets/images/.keep → lib/acts_as_string.rb} +0 -0
  19. data/lib/enumerable/pluck.rb +6 -0
  20. data/lib/hyper-model.rb +59 -8
  21. data/lib/hyper_model/version.rb +3 -0
  22. data/lib/hyper_react/input_tags.rb +47 -0
  23. data/lib/hyperloop/model/load.rb +1 -1
  24. data/lib/kernel/itself.rb +5 -0
  25. data/lib/object/tap.rb +7 -0
  26. data/lib/opal/equality_patches.rb +15 -0
  27. data/lib/opal/parse_patch.rb +14 -0
  28. data/lib/opal/set_patches.rb +8 -0
  29. data/lib/reactive_record/active_record/aggregations.rb +69 -0
  30. data/lib/reactive_record/active_record/associations.rb +118 -0
  31. data/lib/reactive_record/active_record/base.rb +10 -0
  32. data/lib/reactive_record/active_record/class_methods.rb +406 -0
  33. data/lib/reactive_record/active_record/error.rb +31 -0
  34. data/lib/reactive_record/active_record/errors.rb +374 -0
  35. data/lib/reactive_record/active_record/instance_methods.rb +187 -0
  36. data/lib/reactive_record/active_record/public_columns_hash.rb +44 -0
  37. data/lib/reactive_record/active_record/reactive_record/backing_record_inspector.rb +36 -0
  38. data/lib/reactive_record/active_record/reactive_record/base.rb +416 -0
  39. data/lib/reactive_record/active_record/reactive_record/collection.rb +558 -0
  40. data/lib/reactive_record/active_record/reactive_record/column_types.rb +75 -0
  41. data/lib/reactive_record/active_record/reactive_record/dummy_value.rb +236 -0
  42. data/lib/reactive_record/active_record/reactive_record/getters.rb +133 -0
  43. data/lib/reactive_record/active_record/reactive_record/isomorphic_base.rb +576 -0
  44. data/lib/reactive_record/active_record/reactive_record/lookup_tables.rb +54 -0
  45. data/lib/reactive_record/active_record/reactive_record/operations.rb +107 -0
  46. data/lib/reactive_record/active_record/reactive_record/scoped_collection.rb +62 -0
  47. data/lib/reactive_record/active_record/reactive_record/setters.rb +194 -0
  48. data/lib/reactive_record/active_record/reactive_record/unscoped_collection.rb +16 -0
  49. data/lib/reactive_record/active_record/reactive_record/while_loading.rb +343 -0
  50. data/lib/reactive_record/active_record_error.rb +4 -0
  51. data/lib/reactive_record/broadcast.rb +223 -0
  52. data/lib/reactive_record/engine.rb +11 -0
  53. data/lib/reactive_record/interval.rb +190 -0
  54. data/lib/reactive_record/permissions.rb +117 -0
  55. data/lib/reactive_record/pry.rb +13 -0
  56. data/lib/reactive_record/reactive_scope.rb +18 -0
  57. data/lib/reactive_record/scope_description.rb +121 -0
  58. data/lib/reactive_record/serializers.rb +7 -0
  59. data/lib/reactive_record/server_data_cache.rb +478 -0
  60. data/path_release_steps.md +9 -0
  61. metadata +399 -109
  62. data/CODE_OF_CONDUCT.md +0 -49
  63. data/examples/chat-app/.gitignore +0 -21
  64. data/examples/chat-app/Gemfile +0 -62
  65. data/examples/chat-app/Gemfile.lock +0 -309
  66. data/examples/chat-app/README.md +0 -3
  67. data/examples/chat-app/Rakefile +0 -6
  68. data/examples/chat-app/app/assets/config/manifest.js +0 -3
  69. data/examples/chat-app/app/assets/javascripts/application.js +0 -3
  70. data/examples/chat-app/app/assets/stylesheets/application.scss +0 -33
  71. data/examples/chat-app/app/controllers/application_controller.rb +0 -3
  72. data/examples/chat-app/app/controllers/home_controller.rb +0 -5
  73. data/examples/chat-app/app/hyperloop/components/app.rb +0 -12
  74. data/examples/chat-app/app/hyperloop/components/formatted_div.rb +0 -15
  75. data/examples/chat-app/app/hyperloop/components/input_box.rb +0 -26
  76. data/examples/chat-app/app/hyperloop/components/message.rb +0 -29
  77. data/examples/chat-app/app/hyperloop/components/messages.rb +0 -8
  78. data/examples/chat-app/app/hyperloop/components/nav.rb +0 -30
  79. data/examples/chat-app/app/hyperloop/models/application_record.rb +0 -3
  80. data/examples/chat-app/app/hyperloop/models/message.rb +0 -6
  81. data/examples/chat-app/app/hyperloop/operations/operations.rb +0 -13
  82. data/examples/chat-app/app/hyperloop/stores/message_store.rb +0 -17
  83. data/examples/chat-app/app/policies/application_policy.rb +0 -9
  84. data/examples/chat-app/app/views/layouts/application.html.erb +0 -12
  85. data/examples/chat-app/bin/bundle +0 -3
  86. data/examples/chat-app/bin/rails +0 -9
  87. data/examples/chat-app/bin/rake +0 -9
  88. data/examples/chat-app/bin/setup +0 -34
  89. data/examples/chat-app/bin/spring +0 -17
  90. data/examples/chat-app/bin/update +0 -29
  91. data/examples/chat-app/config.ru +0 -5
  92. data/examples/chat-app/config/application.rb +0 -12
  93. data/examples/chat-app/config/boot.rb +0 -3
  94. data/examples/chat-app/config/cable.yml +0 -9
  95. data/examples/chat-app/config/database.yml +0 -25
  96. data/examples/chat-app/config/environment.rb +0 -5
  97. data/examples/chat-app/config/environments/development.rb +0 -56
  98. data/examples/chat-app/config/environments/production.rb +0 -86
  99. data/examples/chat-app/config/environments/test.rb +0 -42
  100. data/examples/chat-app/config/initializers/application_controller_renderer.rb +0 -6
  101. data/examples/chat-app/config/initializers/assets.rb +0 -11
  102. data/examples/chat-app/config/initializers/backtrace_silencers.rb +0 -7
  103. data/examples/chat-app/config/initializers/cookies_serializer.rb +0 -5
  104. data/examples/chat-app/config/initializers/filter_parameter_logging.rb +0 -4
  105. data/examples/chat-app/config/initializers/hyperloop.rb +0 -6
  106. data/examples/chat-app/config/initializers/inflections.rb +0 -16
  107. data/examples/chat-app/config/initializers/mime_types.rb +0 -4
  108. data/examples/chat-app/config/initializers/new_framework_defaults.rb +0 -24
  109. data/examples/chat-app/config/initializers/session_store.rb +0 -3
  110. data/examples/chat-app/config/initializers/wrap_parameters.rb +0 -14
  111. data/examples/chat-app/config/locales/en.yml +0 -23
  112. data/examples/chat-app/config/puma.rb +0 -47
  113. data/examples/chat-app/config/routes.rb +0 -5
  114. data/examples/chat-app/config/secrets.yml +0 -22
  115. data/examples/chat-app/config/spring.rb +0 -6
  116. data/examples/chat-app/db/migrate/20170319194429_create_message.rb +0 -9
  117. data/examples/chat-app/db/schema.rb +0 -48
  118. data/examples/chat-app/db/seeds.rb +0 -7
  119. data/examples/chat-app/lib/assets/.keep +0 -0
  120. data/examples/chat-app/lib/tasks/.keep +0 -0
  121. data/examples/chat-app/log/.keep +0 -0
  122. data/examples/chat-app/public/404.html +0 -67
  123. data/examples/chat-app/public/422.html +0 -67
  124. data/examples/chat-app/public/500.html +0 -66
  125. data/examples/chat-app/public/apple-touch-icon-precomposed.png +0 -0
  126. data/examples/chat-app/public/apple-touch-icon.png +0 -0
  127. data/examples/chat-app/public/favicon.ico +0 -0
  128. data/examples/chat-app/public/robots.txt +0 -5
  129. data/examples/chat-app/test/controllers/.keep +0 -0
  130. data/examples/chat-app/test/fixtures/.keep +0 -0
  131. data/examples/chat-app/test/fixtures/files/.keep +0 -0
  132. data/examples/chat-app/test/helpers/.keep +0 -0
  133. data/examples/chat-app/test/integration/.keep +0 -0
  134. data/examples/chat-app/test/mailers/.keep +0 -0
  135. data/examples/chat-app/test/models/.keep +0 -0
  136. data/examples/chat-app/test/test_helper.rb +0 -10
  137. data/examples/chat-app/tmp/.keep +0 -0
  138. data/examples/chat-app/vendor/assets/javascripts/.keep +0 -0
  139. data/examples/chat-app/vendor/assets/stylesheets/.keep +0 -0
  140. data/lib/hyperloop/model/version.rb +0 -5
@@ -0,0 +1,6 @@
1
+ # Add pluck to enumerable... its already done for us in rails 5+
2
+ module Enumerable
3
+ def pluck(key)
4
+ map { |element| element[key] }
5
+ end
6
+ end unless Enumerable.method_defined? :pluck
@@ -1,14 +1,65 @@
1
- require "hyperloop/model/version"
1
+ require 'set'
2
2
  require 'hyperloop-config'
3
-
4
- Hyperloop.import 'hyper-component'
5
- Hyperloop.import 'hyper-model'
6
-
3
+ require 'hyper-component'
7
4
  if RUBY_ENGINE == 'opal'
8
- require 'hyper-mesh'
5
+ require 'hyper-operation'
6
+ require 'active_support'
7
+ require 'time'
8
+ require 'date'
9
+ require 'kernel/itself' unless Object.instance_methods.include?(:itself)
10
+ require 'object/tap'
11
+ require 'active_model_client_stubs'
12
+ require "reactive_record/active_record_error"
13
+ require "reactive_record/active_record/errors"
14
+ require "reactive_record/active_record/error"
15
+ require "reactive_record/server_data_cache"
16
+ require "reactive_record/active_record/reactive_record/while_loading"
17
+ require "reactive_record/active_record/reactive_record/operations"
18
+ require 'reactive_record/broadcast'
19
+ require "reactive_record/active_record/reactive_record/isomorphic_base"
20
+ require 'reactive_record/active_record/reactive_record/dummy_value'
21
+ require 'reactive_record/active_record/reactive_record/column_types'
22
+ require "reactive_record/active_record/aggregations"
23
+ require "reactive_record/active_record/associations"
24
+ require "reactive_record/active_record/reactive_record/backing_record_inspector"
25
+ require "reactive_record/active_record/reactive_record/getters"
26
+ require "reactive_record/active_record/reactive_record/setters"
27
+ require "reactive_record/active_record/reactive_record/lookup_tables"
28
+ require "reactive_record/active_record/reactive_record/base"
29
+ require "reactive_record/active_record/reactive_record/collection"
30
+ require "reactive_record/active_record/reactive_record/scoped_collection"
31
+ require "reactive_record/active_record/reactive_record/unscoped_collection"
32
+ require "reactive_record/interval"
33
+ require_relative 'active_record_base'
34
+ require_relative 'reactive_record/scope_description'
35
+ require "reactive_record/active_record/class_methods"
36
+ require "reactive_record/active_record/instance_methods"
37
+ require "reactive_record/active_record/base"
38
+ require 'hyper_react/input_tags'
9
39
  require 'hyperloop/model/load'
40
+ require_relative 'hyper_model/version'
41
+ require_relative 'opal/parse_patch'
42
+ require_relative 'opal/set_patches'
43
+ require_relative 'opal/equality_patches'
44
+ React::IsomorphicHelpers.log(
45
+ "The gem 'hyper-mesh' is deprecated. Use gem 'hyper-model' instead.", :warning
46
+ ) unless defined? Hyperloop::Model
10
47
  else
11
48
  require 'opal'
12
- require 'hyper-mesh'
13
- Opal.append_path(File.expand_path('../', __FILE__).untaint)
49
+ require 'hyper-operation'
50
+ require "reactive_record/permissions"
51
+ require "reactive_record/server_data_cache"
52
+ require "reactive_record/active_record/reactive_record/operations"
53
+ require 'reactive_record/broadcast'
54
+ require "reactive_record/active_record/reactive_record/isomorphic_base"
55
+ require "reactive_record/active_record/public_columns_hash"
56
+ require "reactive_record/serializers"
57
+ require "reactive_record/pry"
58
+ require_relative 'active_record_base'
59
+ require 'hyper_model/version'
60
+
61
+ Opal.append_path File.expand_path('../sources/', __FILE__).untaint
62
+ Opal.append_path File.expand_path('../', __FILE__).untaint
63
+ Opal.append_path File.expand_path('../../vendor', __FILE__).untaint
14
64
  end
65
+ require 'enumerable/pluck'
@@ -0,0 +1,3 @@
1
+ module HyperModel
2
+ VERSION = '0.99.0'
3
+ end
@@ -0,0 +1,47 @@
1
+ # Special handling of input tags so they ignore defaultValue (and defaultChecked) values while loading.
2
+ # This is accomplished by adding a react 'key prop' that tracks whether the default value is loading.
3
+ # When the default value transitions from loading to loaded the key will be updated causing react to
4
+ # remount the component with the new default value.
5
+ # To handle cases where defaultValue (or defaultChecked) is an expression, a proc (or lambda) can be
6
+ # provided for the default value. The proc will be called, and if it raises the waiting_on_resources
7
+ # flag then we know that within that expression there is a value still being loaded, and the react
8
+ # key will be set accordingly.
9
+
10
+ module React
11
+ module Component
12
+ module Tags
13
+ %i[INPUT SELECT TEXTAREA].each do |component|
14
+ remove_method component
15
+ send(:remove_const, component)
16
+ tag = component.downcase
17
+ klass = Class.new(Hyperloop::Component) do
18
+ collect_other_params_as :opts
19
+ render do
20
+ opts = props.dup # should be opts = params.opts.dup but requires next release candiate of hyper-react
21
+ default_value = opts[:defaultValue] || opts[:defaultChecked]
22
+ if default_value.respond_to? :call
23
+ begin
24
+ saved_waiting_on_resources = React::RenderingContext.waiting_on_resources
25
+ React::RenderingContext.waiting_on_resources = false
26
+ default_value = default_value.call
27
+ opts[:key] = React::RenderingContext.waiting_on_resources
28
+ if opts[:defaultValue]
29
+ opts[:defaultValue] = default_value
30
+ else
31
+ opts[:defaultChecked] = default_value
32
+ end
33
+ ensure
34
+ React::RenderingContext.waiting_on_resources = !!saved_waiting_on_resources
35
+ end
36
+ else
37
+ opts[:key] = !!default_value.loading?
38
+ end
39
+ opts[:value] = opts[:value].to_s if opts.key? :value # this may not be needed
40
+ React::RenderingContext.render(tag, opts) { children.each(&:render) }
41
+ end
42
+ end
43
+ const_set component, klass
44
+ end
45
+ end
46
+ end
47
+ end
@@ -1,7 +1,7 @@
1
1
  module Hyperloop
2
2
  module Model
3
3
  def self.load(&block)
4
- ReactiveRecord.load &block
4
+ ReactiveRecord.load(&block)
5
5
  end
6
6
  end
7
7
  end
@@ -0,0 +1,5 @@
1
+ module Kernel
2
+ def itself
3
+ self
4
+ end
5
+ end
@@ -0,0 +1,7 @@
1
+ class Object
2
+ def tap
3
+ val = `self.$$is_boolean` ? self==true : self
4
+ yield val
5
+ val
6
+ end
7
+ end
@@ -0,0 +1,15 @@
1
+ class Date
2
+ alias broken_equals ==
3
+ def ==(other)
4
+ return false unless other.is_a?(Date)
5
+ broken_equals(other)
6
+ end
7
+ end
8
+
9
+ class Time
10
+ alias broken_equals ==
11
+ def ==(other)
12
+ return false unless other.is_a?(Time)
13
+ broken_equals(other)
14
+ end
15
+ end
@@ -0,0 +1,14 @@
1
+ begin
2
+ JSON.parse("test")
3
+ rescue Exception => e
4
+ JSON.class_eval do
5
+ class << self
6
+ alias old_parse parse
7
+ end
8
+ def self.parse(*args, &block)
9
+ old_parse(*args, &block)
10
+ rescue Exception => e
11
+ raise StandardError.new e.message
12
+ end
13
+ end unless e.is_a? StandardError
14
+ end
@@ -0,0 +1,8 @@
1
+ class Set
2
+ def &(enum)
3
+ n = self.class.new
4
+ enum.each { |o| n.add(o) if include?(o) }
5
+ n
6
+ end
7
+ alias intersection &
8
+ end unless Set.method_defined? :intersection
@@ -0,0 +1,69 @@
1
+ module ActiveRecord
2
+
3
+ class Base
4
+
5
+ def self.reflect_on_all_aggregations
6
+ base_class.instance_eval { @aggregations ||= [] }
7
+ end
8
+
9
+ def self.reflect_on_aggregation(attribute)
10
+ reflect_on_all_aggregations.detect { |aggregation| aggregation.attribute == attribute }
11
+ end
12
+
13
+ end
14
+
15
+ module Aggregations
16
+
17
+ class AggregationReflection
18
+
19
+ attr_reader :klass_name
20
+ attr_reader :attribute
21
+ attr_reader :mapped_attributes
22
+ attr_reader :constructor
23
+
24
+ def construct(args)
25
+
26
+ end
27
+
28
+ def initialize(owner_class, macro, name, options = {})
29
+ owner_class.reflect_on_all_aggregations << self
30
+ @owner_class = owner_class
31
+ @constructor = options[:constructor] || :new
32
+ @klass_name = options[:class_name] || name.camelize
33
+ @attribute = name
34
+ if options[:mapping].respond_to? :collect
35
+ @mapped_attributes = options[:mapping].collect(&:last)
36
+ else
37
+ ReactiveRecord::Base.log("improper aggregate definition #{@owner_class}, :#{name}, class_name: #{@klass_name} - missing mapping", :error)
38
+ @mapped_attributes = []
39
+ end
40
+ end
41
+
42
+ def klass
43
+ @klass ||= Object.const_get(@klass_name)
44
+ end
45
+
46
+ def serialize(object)
47
+ if object.nil?
48
+ object # return dummy value if that is what we got
49
+ else
50
+ @mapped_attributes.collect { |attr| object.send(attr) }
51
+ end
52
+ end
53
+
54
+ def deserialize(array)
55
+ if array.nil?
56
+ array # return dummy value if that is what we got
57
+ elsif @constructor.respond_to?(:call)
58
+ @constructor.call(*array)
59
+ else
60
+ klass.send(@constructor, *array)
61
+ end
62
+ end
63
+
64
+ end
65
+
66
+ end
67
+
68
+
69
+ end
@@ -0,0 +1,118 @@
1
+ module ActiveRecord
2
+
3
+ class Base
4
+
5
+ def self.reflect_on_all_associations
6
+ base_class.instance_eval { @associations ||= superclass.instance_eval { (@associations && @associations.dup) || [] } }
7
+ end
8
+
9
+ def self.reflect_on_association(attr)
10
+ reflection_finder { |assoc| assoc.attribute == attr }
11
+ end
12
+
13
+ def self.reflect_on_association_by_foreign_key(key)
14
+ reflection_finder { |assoc| assoc.association_foreign_key == key }
15
+ end
16
+
17
+ def self.reflection_finder(&block)
18
+ found = reflect_on_all_associations.detect do |assoc|
19
+ assoc.owner_class == self && yield(assoc)
20
+ end
21
+ if found
22
+ found
23
+ elsif superclass == Base
24
+ nil
25
+ else
26
+ superclass.reflection_finder(&block)
27
+ end
28
+ end
29
+
30
+ end
31
+
32
+ module Associations
33
+
34
+ class AssociationReflection
35
+
36
+ attr_reader :association_foreign_key
37
+ attr_reader :attribute
38
+ attr_reader :macro
39
+ attr_reader :owner_class
40
+ attr_reader :source
41
+
42
+ def initialize(owner_class, macro, name, options = {})
43
+ owner_class.reflect_on_all_associations << self
44
+ @owner_class = owner_class
45
+ @macro = macro
46
+ @options = options
47
+ @klass_name = options[:class_name] || (collection? && name.camelize.sub(/s$/, '')) || 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
+ @attribute = name
51
+ end
52
+
53
+ def through_association
54
+ return unless @options[:through]
55
+ @through_association ||= @owner_class.reflect_on_all_associations.detect do |association|
56
+ association.attribute == @options[:through]
57
+ end
58
+ raise "Through association #{@options[:through]} for "\
59
+ "#{@owner_class}.#{attribute} not found." unless @through_association
60
+ @through_association
61
+ end
62
+
63
+ alias through_association? through_association
64
+
65
+ def through_associations
66
+ # find all associations that use the inverse association as the through association
67
+ # 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
70
+ end
71
+ end
72
+
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
80
+ end
81
+ end.flatten
82
+ end
83
+
84
+ def inverse
85
+ @inverse ||=
86
+ through_association ? through_association.inverse : find_inverse
87
+ end
88
+
89
+ def inverse_of
90
+ @inverse_of ||= inverse.attribute
91
+ end
92
+
93
+ def find_inverse
94
+ klass.reflect_on_all_associations.each do |association|
95
+ next if association.association_foreign_key != @association_foreign_key
96
+ next if association.klass != @owner_class
97
+ next if association.attribute == attribute
98
+ return association if klass == association.owner_class
99
+ end
100
+ raise "Association #{@owner_class}.#{attribute} "\
101
+ "(foreign_key: #{@association_foreign_key}) "\
102
+ "has no inverse in #{@klass_name}"
103
+ end
104
+
105
+ def klass
106
+ @klass ||= Object.const_get(@klass_name)
107
+ end
108
+
109
+ def collection?
110
+ [:has_many].include? @macro
111
+ end
112
+
113
+ end
114
+
115
+ end
116
+
117
+
118
+ end
@@ -0,0 +1,10 @@
1
+ module ActiveRecord
2
+ # client side ActiveRecord::Base proxy
3
+ class Base
4
+ include InstanceMethods
5
+ extend ClassMethods
6
+
7
+ scope :limit, ->() {}
8
+ scope :offset, ->() {}
9
+ end
10
+ end
@@ -0,0 +1,406 @@
1
+ module ActiveRecord
2
+
3
+ module ClassMethods
4
+
5
+ alias _new_without_sti_type_cast new
6
+
7
+ def new(*args, &block)
8
+ _new_without_sti_type_cast(*args, &block).cast_to_current_sti_type
9
+ end
10
+
11
+ def base_class
12
+ unless self < Base
13
+ raise ActiveRecordError, "#{name} doesn't descend from ActiveRecord"
14
+ end
15
+
16
+ if superclass == Base || superclass.abstract_class?
17
+ self
18
+ else
19
+ superclass.base_class
20
+ end
21
+ end
22
+
23
+ def abstract_class?
24
+ defined?(@abstract_class) && @abstract_class == true
25
+ end
26
+
27
+ def primary_key
28
+ @primary_key_value ||= (self == base_class) ? :id : base_class.primary_key
29
+ end
30
+
31
+ def primary_key=(val)
32
+ @primary_key_value = val.to_s
33
+ end
34
+
35
+ def inheritance_column
36
+ return nil if @no_inheritance_column
37
+ @inheritance_column_value ||=
38
+ if self == base_class
39
+ @inheritance_column_value || 'type'
40
+ else
41
+ superclass.inheritance_column.tap { |v| @no_inheritance_column = !v }
42
+ end
43
+ end
44
+
45
+ def inheritance_column=(name)
46
+ @no_inheritance_column = !name
47
+ @inheritance_column_value = name
48
+ end
49
+
50
+ def model_name
51
+ @model_name ||= ActiveModel::Name.new(self)
52
+ end
53
+
54
+ def find(id)
55
+ ReactiveRecord::Base.find(self, primary_key => id)
56
+ end
57
+
58
+ def find_by(opts = {})
59
+ dealiased_opts = {}
60
+ opts.each { |attr, value| dealiased_opts[_dealias_attribute(attr)] = value }
61
+ ReactiveRecord::Base.find(self, dealiased_opts)
62
+ end
63
+
64
+ def enum(*args)
65
+ # when we implement schema validation we should also implement value checking
66
+ end
67
+
68
+ def serialize(attr, *args)
69
+ ReactiveRecord::Base.serialized?[self][attr] = true
70
+ end
71
+
72
+ def _dealias_attribute(new)
73
+ if self == base_class
74
+ _attribute_aliases[new] || new
75
+ else
76
+ _attribute_aliases[new] ||= superclass._dealias_attribute(new)
77
+ end
78
+ end
79
+
80
+ def _attribute_aliases
81
+ @_attribute_aliases ||= {}
82
+ end
83
+
84
+ def alias_attribute(new_name, old_name)
85
+ ['', '=', '_changed?'].each do |variant|
86
+ define_method("#{new_name}#{variant}") { |*args, &block| send("#{old_name}#{variant}", *args, &block) }
87
+ end
88
+ _attribute_aliases[new_name] = old_name
89
+ end
90
+
91
+ # ignore any of these methods if they get called on the client. This list should be trimmed down to include only
92
+ # methods to be called as "macros" such as :after_create, etc...
93
+ SERVER_METHODS = [
94
+ :regulate_relationship, :regulate_scope,
95
+ :attribute_type_decorations, :defined_enums, :_validators, :timestamped_migrations, :lock_optimistically, :lock_optimistically=,
96
+ :local_stored_attributes=, :lock_optimistically?, :attribute_aliases?, :attribute_method_matchers?, :defined_enums?,
97
+ :has_many_without_reactive_record_add_changed_method, :has_many_with_reactive_record_add_changed_method,
98
+ :belongs_to_without_reactive_record_add_changed_method, :belongs_to_with_reactive_record_add_changed_method,
99
+ :cache_timestamp_format, :composed_of_with_reactive_record_add_changed_method, :schema_format, :schema_format=,
100
+ :error_on_ignored_order_or_limit, :error_on_ignored_order_or_limit=, :timestamped_migrations=, :dump_schema_after_migration,
101
+ :dump_schema_after_migration=, :dump_schemas, :dump_schemas=, :warn_on_records_fetched_greater_than=,
102
+ :belongs_to_required_by_default, :default_connection_handler, :connection_handler=, :default_connection_handler=,
103
+ :skip_time_zone_conversion_for_attributes, :skip_time_zone_conversion_for_attributes=, :time_zone_aware_types,
104
+ :time_zone_aware_types=, :protected_environments, :skip_time_zone_conversion_for_attributes?, :time_zone_aware_types?,
105
+ :partial_writes, :partial_writes=, :composed_of_without_reactive_record_add_changed_method, :logger, :partial_writes?,
106
+ :after_initialize, :record_timestamps, :record_timestamps=, :after_find, :after_touch, :before_save, :around_save,
107
+ :belongs_to_required_by_default=, :default_connection_handler?, :before_create, :around_create, :before_update, :around_update,
108
+ :after_save, :before_destroy, :around_destroy, :after_create, :after_destroy, :after_update, :_validation_callbacks,
109
+ :_validation_callbacks?, :_validation_callbacks=, :_initialize_callbacks, :_initialize_callbacks?, :_initialize_callbacks=,
110
+ :_find_callbacks, :_find_callbacks?, :_find_callbacks=, :_touch_callbacks, :_touch_callbacks?, :_touch_callbacks=, :_save_callbacks,
111
+ :_save_callbacks?, :_save_callbacks=, :_create_callbacks, :_create_callbacks?, :_create_callbacks=, :_update_callbacks,
112
+ :_update_callbacks?, :_update_callbacks=, :_destroy_callbacks, :_destroy_callbacks?, :_destroy_callbacks=, :record_timestamps?,
113
+ :pre_synchromesh_scope, :pre_synchromesh_default_scope, :do_not_synchronize, :do_not_synchronize?,
114
+ :logger=, :maintain_test_schema, :maintain_test_schema=, :scope, :time_zone_aware_attributes, :time_zone_aware_attributes=,
115
+ :default_timezone, :default_timezone=, :_attr_readonly, :warn_on_records_fetched_greater_than, :configurations, :configurations=,
116
+ :_attr_readonly?, :table_name_prefix=, :table_name_suffix=, :schema_migrations_table_name=, :internal_metadata_table_name,
117
+ :internal_metadata_table_name=, :primary_key_prefix_type, :_attr_readonly=, :pluralize_table_names=, :protected_environments=,
118
+ :ignored_columns=, :ignored_columns, :index_nested_attribute_errors, :index_nested_attribute_errors=, :primary_key_prefix_type=,
119
+ :table_name_prefix?, :table_name_suffix?, :schema_migrations_table_name?, :internal_metadata_table_name?, :protected_environments?,
120
+ :pluralize_table_names?, :ignored_columns?, :store_full_sti_class, :store_full_sti_class=, :nested_attributes_options,
121
+ :nested_attributes_options=, :store_full_sti_class?, :default_scopes, :default_scope_override, :default_scopes=, :default_scope_override=,
122
+ :nested_attributes_options?, :cache_timestamp_format=, :cache_timestamp_format?, :reactive_record_association_keys, :_validators=,
123
+ :has_many, :belongs_to, :composed_of, :belongs_to_without_reactive_record_add_is_method, :_rollback_callbacks, :_commit_callbacks,
124
+ :_before_commit_callbacks, :attribute_type_decorations=, :_commit_callbacks=, :_commit_callbacks?, :_before_commit_callbacks?,
125
+ :_before_commit_callbacks=, :_rollback_callbacks=, :_before_commit_without_transaction_enrollment_callbacks?,
126
+ :_before_commit_without_transaction_enrollment_callbacks=, :_commit_without_transaction_enrollment_callbacks,
127
+ :_commit_without_transaction_enrollment_callbacks?, :_commit_without_transaction_enrollment_callbacks=, :_rollback_callbacks?,
128
+ :_rollback_without_transaction_enrollment_callbacks?, :_rollback_without_transaction_enrollment_callbacks=,
129
+ :_rollback_without_transaction_enrollment_callbacks, :_before_commit_without_transaction_enrollment_callbacks, :aggregate_reflections,
130
+ :_reflections=, :aggregate_reflections=, :pluralize_table_names, :public_columns_hash, :attributes_to_define_after_schema_loads,
131
+ :attributes_to_define_after_schema_loads=, :table_name_suffix, :schema_migrations_table_name, :attribute_aliases,
132
+ :attribute_method_matchers, :connection_handler, :attribute_aliases=, :attribute_method_matchers=, :_validate_callbacks,
133
+ :_validate_callbacks?, :_validate_callbacks=, :_validators?, :_reflections?, :aggregate_reflections?, :include_root_in_json,
134
+ :_reflections, :include_root_in_json=, :include_root_in_json?, :local_stored_attributes, :default_scope, :table_name_prefix,
135
+ :attributes_to_define_after_schema_loads?, :attribute_type_decorations?, :defined_enums=, :suppress, :has_secure_token,
136
+ :generate_unique_secure_token, :store, :store_accessor, :_store_accessors_module, :stored_attributes, :reflect_on_aggregation,
137
+ :reflect_on_all_aggregations, :_reflect_on_association, :reflect_on_all_associations, :clear_reflections_cache, :reflections,
138
+ :reflect_on_association, :reflect_on_all_autosave_associations, :no_touching, :transaction, :after_commit, :after_rollback, :before_commit,
139
+ :before_commit_without_transaction_enrollment, :after_create_commit, :after_update_commit, :after_destroy_commit,
140
+ :after_commit_without_transaction_enrollment, :after_rollback_without_transaction_enrollment, :raise_in_transactional_callbacks,
141
+ :raise_in_transactional_callbacks=, :accepts_nested_attributes_for, :has_secure_password, :has_one, :has_and_belongs_to_many,
142
+ :before_validation, :after_validation, :serialize, :primary_key, :dangerous_attribute_method?, :get_primary_key, :quoted_primary_key,
143
+ :define_method_attribute, :reset_primary_key, :primary_key=, :define_method_attribute=, :attribute_names, :initialize_generated_modules,
144
+ :column_for_attribute, :define_attribute_methods, :undefine_attribute_methods, :instance_method_already_implemented?, :method_defined_within?,
145
+ :dangerous_class_method?, :class_method_defined_within?, :attribute_method?, :has_attribute?, :generated_attribute_methods,
146
+ :attribute_method_prefix, :attribute_method_suffix, :attribute_method_affix, :attribute_alias?, :attribute_alias, :define_attribute_method,
147
+ :update_counters, :locking_enabled?, :locking_column, :locking_column=, :reset_locking_column, :decorate_attribute_type,
148
+ :decorate_matching_attribute_types, :attribute, :define_attribute, :reset_counters, :increment_counter, :decrement_counter,
149
+ :validates_absence_of, :validates_length_of, :validates_size_of, :validates_presence_of, :validates_associated, :validates_uniqueness_of,
150
+ :validates_acceptance_of, :validates_confirmation_of, :validates_exclusion_of, :validates_format_of, :validates_inclusion_of,
151
+ :validates_numericality_of, :define_callbacks, :normalize_callback_params, :__update_callbacks, :get_callbacks, :set_callback,
152
+ :set_callbacks, :skip_callback, :reset_callbacks, :deprecated_false_terminator, :define_model_callbacks, :validate, :validators,
153
+ :validates_each, :validates_with, :clear_validators!, :validators_on, :validates, :_validates_default_keys, :_parse_validates_options,
154
+ :validates!, :_to_partial_path, :sanitize, :sanitize_sql, :sanitize_conditions, :quote_value, :sanitize_sql_for_conditions, :sanitize_sql_array,
155
+ :sanitize_sql_for_assignment, :sanitize_sql_hash_for_assignment, :sanitize_sql_for_order, :expand_hash_conditions_for_aggregates, :sanitize_sql_like,
156
+ :replace_named_bind_variables, :replace_bind_variables, :raise_if_bind_arity_mismatch, :replace_bind_variable, :quote_bound_value, :all,
157
+ :default_scoped, :valid_scope_name?, :scope_attributes?, :before_remove_const, :ignore_default_scope?, :unscoped, :build_default_scope,
158
+ :evaluate_default_scope, :ignore_default_scope=, :current_scope, :current_scope=, :scope_attributes, :base_class, :abstract_class?,
159
+ :finder_needs_type_condition?, :sti_name, :descends_from_active_record?, :abstract_class, :compute_type, :abstract_class=, :table_name, :columns,
160
+ :table_exists?, :columns_hash, :column_names, :attribute_types, :prefetch_primary_key?, :sequence_name, :quoted_table_name, :_default_attributes,
161
+ :type_for_attribute, :inheritance_column, :attributes_builder, :inheritance_column=, :reset_table_name, :table_name=, :reset_column_information,
162
+ :full_table_name_prefix, :full_table_name_suffix, :reset_sequence_name, :sequence_name=, :next_sequence_value, :column_defaults, :content_columns,
163
+ :readonly_attributes, :attr_readonly, :create, :create!, :instantiate, :find, :type_caster, :arel_table, :find_by, :find_by!, :initialize_find_by_cache,
164
+ :generated_association_methods, :arel_engine, :arel_attribute, :predicate_builder, :collection_cache_key, :relation_delegate_class,
165
+ :initialize_relation_delegate_cache, :enum, :collecting_queries_for_explain, :exec_explain, :i18n_scope, :lookup_ancestors, :human_attribute_name,
166
+ :references, :uniq, :maximum, :none, :exists?, :second, :limit, :order, :eager_load, :update, :delete_all, :destroy, :ids, :many?, :pluck, :third,
167
+ :delete, :fourth, :fifth, :forty_two, :second_to_last, :third_to_last, :preload, :sum, :take!, :first!, :last!, :second!, :offset, :select, :fourth!,
168
+ :third!, :third_to_last!, :fifth!, :where, :first_or_create, :second_to_last!, :forty_two!, :first, :having, :any?, :one?, :none?, :find_or_create_by,
169
+ :from, :first_or_create!, :first_or_initialize, :except, :find_or_create_by!, :find_or_initialize_by, :includes, :destroy_all, :update_all, :or,
170
+ :find_in_batches, :take, :joins, :find_each, :last, :in_batches, :reorder, :group, :left_joins, :left_outer_joins, :rewhere, :readonly, :create_with,
171
+ :distinct, :unscope, :calculate, :average, :count_by_sql, :minimum, :lock, :find_by_sql, :count, :cache, :uncached, :connection, :connection_pool,
172
+ :establish_connection, :connected?, :clear_cache!, :clear_reloadable_connections!, :connection_id, :connection_config, :clear_all_connections!,
173
+ :remove_connection, :connection_specification_name, :connection_specification_name=, :retrieve_connection, :connection_id=, :clear_active_connections!,
174
+ :sqlite3_connection, :direct_descendants, :benchmark, :model_name, :with_options, :attr_protected, :attr_accessible
175
+ ]
176
+
177
+ def method_missing(name, *args, &block)
178
+ if args.count == 1 && name.start_with?("find_by_") && !block
179
+ find_by(_dealias_attribute(name.sub(/^find_by_/, "")) => args[0])
180
+ elsif [].respond_to?(name)
181
+ all.send(name, *args, &block)
182
+ elsif name.end_with?('!')
183
+ send(name.chop, *args, &block).send(:reload_from_db) rescue nil
184
+ elsif !SERVER_METHODS.include?(name)
185
+ raise "#{self.name}.#{name}(#{args}) (called class method missing)"
186
+ end
187
+ end
188
+
189
+ # client side AR
190
+
191
+ # Any method that can be applied to an array will be applied to the result
192
+ # of all instead.
193
+ # Any method ending with ! just means apply the method after forcing a reload
194
+ # from the DB.
195
+
196
+ # alias pre_synchromesh_method_missing method_missing
197
+ #
198
+ # def method_missing(name, *args, &block)
199
+ # return all.send(name, *args, &block) if [].respond_to?(name)
200
+ # if name.end_with?('!')
201
+ # return send(name.chop, *args, &block).send(:reload_from_db) rescue nil
202
+ # end
203
+ # pre_synchromesh_method_missing(name, *args, &block)
204
+ # end
205
+
206
+ def create(*args, &block)
207
+ new(*args).save(&block)
208
+ end
209
+
210
+ def scope(name, *args)
211
+ opts = _synchromesh_scope_args_check(args)
212
+ scope_description = ReactiveRecord::ScopeDescription.new(self, name, opts)
213
+ singleton_class.send(:define_method, name) do |*vargs|
214
+ all.build_child_scope(scope_description, *name, *vargs)
215
+ end
216
+ # singleton_class.send(:define_method, "#{name}=") do |_collection|
217
+ # raise 'NO LONGER IMPLEMENTED - DOESNT PLAY WELL WITH SYNCHROMESH'
218
+ # end
219
+ end
220
+
221
+ def default_scope(*args, &block)
222
+ opts = _synchromesh_scope_args_check([*block, *args])
223
+ @_default_scopes ||= []
224
+ @_default_scopes << opts
225
+ end
226
+
227
+ def all
228
+ ReactiveRecord::Base.default_scope[self] ||=
229
+ begin
230
+ root = ReactiveRecord::Collection
231
+ .new(self, nil, nil, self, 'all')
232
+ .extend(ReactiveRecord::UnscopedCollection)
233
+ (@_default_scopes || [{ client: _all_filter }]).inject(root) do |scope, opts|
234
+ scope.build_child_scope(ReactiveRecord::ScopeDescription.new(self, :all, opts))
235
+ end
236
+ end
237
+ end
238
+
239
+ def _all_filter
240
+ # provides a filter for the all scopes taking into account STI subclasses
241
+ # note: within the lambda `self` will be the model instance
242
+ defining_class_is_base_class = base_class == self
243
+ defining_model_name = model_name.to_s
244
+ lambda do
245
+ # have to delay computation of inheritance column since it might
246
+ # not be defined when class is first defined
247
+ ic = self.class.inheritance_column
248
+ defining_class_is_base_class || !ic || self[ic] == defining_model_name
249
+ end
250
+ end
251
+
252
+ # def all=(_collection)
253
+ # raise "NO LONGER IMPLEMENTED DOESNT PLAY WELL WITH SYNCHROMESH"
254
+ # end
255
+
256
+ def unscoped
257
+ ReactiveRecord::Base.unscoped[self] ||=
258
+ ReactiveRecord::Collection
259
+ .new(self, nil, nil, self, 'unscoped')
260
+ .extend(ReactiveRecord::UnscopedCollection)
261
+ end
262
+
263
+ def finder_method(name)
264
+ ReactiveRecord::ScopeDescription.new(self, "_#{name}", {})
265
+ [name, "#{name}!"].each do |method|
266
+ singleton_class.send(:define_method, method) do |*vargs|
267
+ all.apply_scope("_#{method}", *vargs).first
268
+ end
269
+ end
270
+ end
271
+
272
+ def abstract_class=(val)
273
+ @abstract_class = val
274
+ end
275
+
276
+ # def scope(name, body)
277
+ # singleton_class.send(:define_method, name) do | *args |
278
+ # args = (args.count == 0) ? name : [name, *args]
279
+ # ReactiveRecord::Base.class_scopes(self)[args] ||= ReactiveRecord::Collection.new(self, nil, nil, self, args)
280
+ # end
281
+ # singleton_class.send(:define_method, "#{name}=") do |collection|
282
+ # ReactiveRecord::Base.class_scopes(self)[name] = collection
283
+ # end
284
+ # end
285
+
286
+ # def all
287
+ # ReactiveRecord::Base.class_scopes(self)[:all] ||= ReactiveRecord::Collection.new(self, nil, nil, self, "all")
288
+ # end
289
+ #
290
+ # def all=(collection)
291
+ # ReactiveRecord::Base.class_scopes(self)[:all] = collection
292
+ # end
293
+
294
+ [:belongs_to, :has_many, :has_one].each do |macro|
295
+ define_method(macro) do |*args| # is this a bug in opal? saying name, scope=nil, opts={} does not work!
296
+ name = args.first
297
+ opts = (args.count > 1 and args.last.is_a? Hash) ? args.last : {}
298
+ assoc = Associations::AssociationReflection.new(self, macro, name, opts)
299
+ if macro == :has_many
300
+ define_method(name) { @backing_record.get_has_many(assoc, nil) }
301
+ define_method("#{name}=") { |val| @backing_record.set_has_many(assoc, val) }
302
+ else
303
+ define_method(name) { @backing_record.get_belongs_to(assoc, nil) }
304
+ define_method("#{name}=") { |val| @backing_record.set_belongs_to(assoc, val) }
305
+ end
306
+ assoc
307
+ end
308
+ end
309
+
310
+ def composed_of(name, opts = {})
311
+ reflection = Aggregations::AggregationReflection.new(base_class, :composed_of, name, opts)
312
+ if reflection.klass < ActiveRecord::Base
313
+ define_method(name) { @backing_record.get_ar_aggregate(reflection, nil) }
314
+ define_method("#{name}=") { |val| @backing_record.set_ar_aggregate(reflection, val) }
315
+ else
316
+ define_method(name) { @backing_record.get_non_ar_aggregate(name, nil) }
317
+ define_method("#{name}=") { |val| @backing_record.set_non_ar_aggregate(reflection, val) }
318
+ end
319
+ end
320
+
321
+ def column_names
322
+ ReactiveRecord::Base.public_columns_hash.keys
323
+ end
324
+
325
+ def columns_hash
326
+ ReactiveRecord::Base.public_columns_hash[name] || {}
327
+ end
328
+
329
+ def server_methods
330
+ @server_methods ||= {}
331
+ end
332
+
333
+ def server_method(name, default: nil)
334
+ server_methods[name] = { default: default }
335
+ define_method(name) do |*args|
336
+ vector = args.count.zero? ? name : [[name] + args]
337
+ @backing_record.get_server_method(vector, nil)
338
+ end
339
+ define_method("#{name}!") do |*args|
340
+ vector = args.count.zero? ? name : [[name] + args]
341
+ @backing_record.get_server_method(vector, true)
342
+ end
343
+ end
344
+
345
+ def define_attribute_methods
346
+ columns_hash.each do |name, column_hash|
347
+ next if name == primary_key
348
+ define_method(name) { @backing_record.get_attr_value(name, nil) }
349
+ define_method("#{name}!") { @backing_record.get_attr_value(name, true) }
350
+ define_method("#{name}=") { |val| @backing_record.set_attr_value(name, val) }
351
+ define_method("#{name}_changed?") { @backing_record.changed?(name) }
352
+ define_method("#{name}?") { @backing_record.get_attr_value(name, nil).present? }
353
+ end
354
+ self.inheritance_column = nil if inheritance_column && !columns_hash.key?(inheritance_column)
355
+ end
356
+
357
+ def _react_param_conversion(param, opt = nil)
358
+ param = Native(param)
359
+ param = JSON.from_object(param.to_n) if param.is_a? Native::Object
360
+ result =
361
+ if param.is_a? self
362
+ param
363
+ elsif param.is_a? Hash
364
+ if opt == :validate_only
365
+ klass = ReactiveRecord::Base.infer_type_from_hash(self, param)
366
+ klass == self || klass < self
367
+ else
368
+ # TODO: investigate saving .changes here and then replacing the
369
+ # TODO: changes after the load is complete. In other words preserve the
370
+ # TODO: changed values as changes while just updating the synced values.
371
+ target =
372
+ if param[primary_key]
373
+ find(param[primary_key])
374
+ else
375
+ new
376
+ end
377
+
378
+ associations = reflect_on_all_associations
379
+
380
+ param = param.collect do |key, value|
381
+ assoc = associations.detect do |association|
382
+ association.association_foreign_key == key
383
+ end
384
+
385
+ if assoc
386
+ if value
387
+ [assoc.attribute, { id: [value] }]
388
+ else
389
+ [assoc.attribute, [nil]]
390
+ end
391
+ else
392
+ [key, [value]]
393
+ end
394
+ end
395
+ # TODO: verify wrapping with load_data was added so broadcasting works in 1.0.0.lap28
396
+ ReactiveRecord::Base.load_data do
397
+ ReactiveRecord::ServerDataCache.load_from_json(Hash[param], target)
398
+ end
399
+ target.cast_to_current_sti_type
400
+ end
401
+ end
402
+
403
+ result
404
+ end
405
+ end
406
+ end