hyper-mesh 0.5.1 → 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (26) hide show
  1. checksums.yaml +4 -4
  2. data/docs/activerecord_api.md +191 -11
  3. data/lib/active_record_base.rb +5 -1
  4. data/lib/hypermesh/version.rb +1 -1
  5. data/lib/reactive_record/active_record/class_methods.rb +37 -1
  6. data/lib/reactive_record/active_record/instance_methods.rb +13 -0
  7. data/lib/reactive_record/active_record/reactive_record/dummy_value.rb +1 -1
  8. data/lib/reactive_record/active_record/reactive_record/isomorphic_base.rb +22 -1
  9. data/lib/reactive_record/active_record/reactive_record/while_loading.rb +6 -0
  10. data/lib/reactive_record/server_data_cache.rb +1 -0
  11. data/lib/synchromesh/client_drivers.rb +19 -9
  12. data/reactive_record_test_app/Gemfile.lock +1 -1
  13. data/reactive_record_test_app/app/models/models.rb +1 -0
  14. data/reactive_record_test_app/app/{views/models → models/public}/address.rb +0 -0
  15. data/reactive_record_test_app/app/{views/models → models/public}/comment.rb +0 -0
  16. data/reactive_record_test_app/app/{views/models → models/public}/todo_item.rb +0 -0
  17. data/reactive_record_test_app/app/{views/models → models/public}/user.rb +0 -0
  18. data/reactive_record_test_app/config/application.rb +2 -1
  19. data/spec/reactive_record/edge_cases_spec.rb +1 -1
  20. data/spec/synchromesh/{unit_tests → aaa-unit_tests}/connection_spec.rb +0 -0
  21. data/spec/synchromesh/{unit_tests → aaa-unit_tests}/dummy_value_spec.rb +0 -0
  22. data/spec/synchromesh/column_types/column_type_spec.rb +16 -0
  23. data/spec/synchromesh/integration/saving_during_commit_spec.rb +64 -0
  24. metadata +13 -12
  25. data/reactive_record_test_app/app/models/.gitkeep +0 -0
  26. data/reactive_record_test_app/app/views/models.rb +0 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 06482f0ee34e8ee178f10efe5c5425a8fa0268fe
4
- data.tar.gz: f8588709cf6970fae4b300418925533757421286
3
+ metadata.gz: 48e04a10e1ad295fd3b0d59e4bf55f376c4b7776
4
+ data.tar.gz: eef9862a88351836cc5a0bccfec7e3d7899d0315
5
5
  SHA512:
6
- metadata.gz: 4e34b870273c9e7dbaba3bc8788bd45e27cee418f272ec65957292656bfa76e51933d0e8bcf49ab178d93f5ba3dee8de36ead41229b3806f774494e8b9223eb9
7
- data.tar.gz: ea01368f7c042bde2b004b17fa462829b2cfd6cd39d83c8542b50c3555a0f0eaaa62e4f47dd3f2c776848dfdf0e3f22020adc032ac71cbf6374a205a8c836484
6
+ metadata.gz: c7458271b5d897075e1769db7017ce01e4cbe87d2c756605051f42e9131ddf8ca5816d81538b12c8f04e852902e4890b86cd3fabc944f6c1c2a484ddff91572e
7
+ data.tar.gz: b636a901cfe8eb0434c54cd3baa61534c34f758243531a2946b2d9dd3504e21d93f3154defbdf2542a6b31e78b7044377d05fe4b6a5c2ef82415f931ba655f91
@@ -1,9 +1,49 @@
1
1
  ## ActiveRecord API
2
2
 
3
- HyperMesh uses a subset of the standard ActiveRecord API to give your client side HyperReact components access to your server side models.
3
+ HyperMesh uses a subset of the standard ActiveRecord API to give your client side HyperReact components access to your server side models. As much as possible HyperMesh follows the syntax as semantics of ActiveRecord.
4
+
5
+ ### Interfacing to React
6
+
7
+ HyperMesh uses the React to deliver your model data to the client without you having to create extra APIs or specialized controllers. The key idea of React is that when state (or params) change, the portions of the display effected by this data will be updated.
8
+
9
+ HyperMesh automatically creates state objects that will be updated as server side data is loaded, or changes.
10
+
11
+ A brief overview of how this works will help you understand the how HyperMesh gets the job done.
12
+
13
+ #### Rendering Cycle
14
+
15
+ On the UI you will be reading models in order to display data.
16
+
17
+ If during the rendering of the display the model data is not yet loaded placeholder values (the default values from the `columns_hash`) will be returned by HyperMesh.
18
+
19
+ HyperMesh then keeps track of where these placeholders (or `DummyValue`s) are displayed, and when they do get loaded, those parts of the display will re-render.
20
+
21
+ If later the data changes (either due to local user actions, or receiving push updates) then again any parts of the display that were dependent on the current values will be re-rendered.
22
+
23
+ You normally do not have to be aware of this. Just access your models using the normal scopes and finders, to compute values and display attributes as you would on the server. Initially the display will show the placeholder values and then will be replaced with the real values.
24
+
25
+
26
+ #### Prerendering
27
+
28
+ During server-side pre-rendering, we do know all the values immediately so on initial page load all the values will be loaded and present.
29
+
30
+ #### Load Cycle Methods
31
+
32
+ There are a number of methods that allow you to interact with this load cycle when needed. These are documented [below].
33
+
4
34
 
5
35
  ### Class Methods
6
36
 
37
+ #### New and Create
38
+
39
+ `new`: Takes a hash of attributes and initializes a new unsaved record. The values of any attributes not specified in the hash will be taken from the models default values specified in the `columns_hash`.
40
+
41
+ If new is passed a native javascript object it will be treated as a hash and converted accordingly.
42
+
43
+ `create`: Short hand for `new(...).save`. See the `save` instance method for details on how saving is done.
44
+
45
+ #### Scoping and Finding
46
+
7
47
  `scope` and `default_scope`: HyperMesh adds four new options to these methods: `joins`, `client`, `select` and `server`. The `joins` option provides information on how the scope will be joined with other models. The `client` and `select` options allow scoping to be done on the client side to offload this from the server, and the `server` option is there just for symmetry with the othe options. See the [Client Side Scoping](/docs/client_side_scoping.md) page for more details.
8
48
 
9
49
  ```ruby
@@ -30,6 +70,24 @@ scope :completed,
30
70
  Word.all.each { |word| LI { word.text }}
31
71
  ```
32
72
 
73
+ `limit` and `offset`: These builtin scopes behave as they do on the server:
74
+
75
+ ```ruby
76
+ Word.offset(500).limit(20) # get words 500-519
77
+ ```
78
+
79
+ `find`: takes an id and returns the corresponding record.
80
+
81
+ `find_by`: takes single item hash indicating an attribute value pair to find.
82
+
83
+ `find_by_...`: i.e. `find_by_first_name` these will find the first record with a matching attribute.
84
+
85
+ ```ruby
86
+ Word.find_by_text('hello') # short for Word.find_by(text: 'hello')
87
+ ```
88
+
89
+ #### Relationships and Aggregations
90
+
33
91
  `belongs_to, has_many, has_one`: These all work as on the server. However it is important that you fully specify both sides of the relationship. This is not always necessary on the server because ActiveRecord uses the table schema to work things out.
34
92
 
35
93
  ```ruby
@@ -44,30 +102,152 @@ end
44
102
 
45
103
  `composed_of`: You can create aggregate models like ActiveRecord.
46
104
 
105
+ #### Defining server methods
106
+
107
+ Normally an application defined instance method will run on the client and the server:
108
+
109
+ ```ruby
110
+ class User < ActiveRecord::Base
111
+ def full_name
112
+ "#{first_name} #{last_name}"
113
+ end
114
+ end
115
+ ```
116
+
117
+ Sometimes it is desirable to only run method on the server. This can be done using the `server_method` macro:
118
+
119
+ ```ruby
120
+ class User < ActiveRecord::Base
121
+ server_method :full_name, default: '' do
122
+ "#{first_name} #{last_name}"
123
+ end
124
+ end
125
+ ```
126
+
127
+ When the method is first called on the client the default value will be returned, and there will be a reactive update when the true value is returned from the server.
128
+
129
+ To force the value to be recomputed at the server append a `!` to the end of the name, otherwise the last value returned from the server will continue to be returned.
130
+
131
+ #### Model Information
132
+
47
133
  `column_names`: returns a list of the database columns.
48
134
 
49
135
  `columns_hash`: returns the details of the columns specification. Note that on the server `columns_hash` returns a hash of objects specifying column information. On the client the entire structure is just one big hash of hashes.
50
136
 
51
- `abstract_class=`, `abstract_class?`, `primary_key`, `primary_key=`, `inheritance_column`, `inheritance_column=`, `model_name`: All work as on the server. See ActiveRecord documentation for more info.
137
+ `abstract_class=`, `abstract_class?`, `primary_key`, `primary_key=`, `inheritance_column`, `inheritance_column=`, `model_name`: All work as on the server. See ActiveRecord documentation for more info.
52
138
 
53
- `new`: Takes a hash of attributes and initializes a new unsaved record. The values of any attributes not specified in the hash will be taken from the models default values specified in the `columns_hash`.
139
+ ### Instance Methods
54
140
 
55
- If new is passed a native javascript object it will be treated as a hash and converted accordingly.
141
+ #### Attribute and Relationship Getter and Setters
56
142
 
57
- `create`: Short hand for `new(...).save`. See the `save` instance method for details on how saving is done.
143
+ All attributes have an associated getter and setter. All relationships have a getter. All belongs_to relationships have a setter. `has_many` relationships can be updated using the push (`<<`) operator or using the `delete` method.
58
144
 
59
- `limit` and `offset`: These builtin scopes behave as they do on the server:
145
+ ```ruby
146
+ puts my_todo.name
147
+ my_todo.name = "neuname"
148
+ my_todo.comments << a_new_comment
149
+ a_new_comment.todo == my_todo # true!
150
+ ```
151
+
152
+ In addition if the attribute getter ends with a bang (!) then this will force a fetch of the attribute from the server. This is typically not necessary if push updates are configured.
153
+
154
+ #### Saving
155
+
156
+ The `save` method works like ActiveRecord save, *except* it returns a promise that is resolved when the save completes (or fails.)
60
157
 
61
158
  ```ruby
62
- Word.offset(500).limit(20) # get words 500-519
159
+ my_todo.save(validate: false).then do |result|
160
+ # result is a hash with {success: ..., message: , models: ....}
161
+ end
63
162
  ```
64
163
 
65
- `find`: takes an id and returns the corresponding record.
164
+ After saving the models will have an `errors` hash with any validation problems.
66
165
 
67
- `find_by`: takes single item hash indicating an attribute value pair to find.
166
+ During the save operation the method `saving?` will return `true`. This can be used to instead of (or with) the promise to update the screen:
68
167
 
69
- `find_by_...`: i.e. `find_by_first_name` these will find the first record with a matching attribute.
168
+ ```ruby
169
+ render do
170
+ ...
171
+ if some_model.saving?
172
+ ... display please wait ...
173
+ elsif some_model.errors.any?
174
+ ... highlight the errors ...
175
+ else
176
+ ... display data ...
177
+ end
178
+ ...
179
+ end
180
+ ```
181
+
182
+ #### Destroy
183
+
184
+ Like `save` destroy returns a promise that is resolved when the destroy completes.
185
+
186
+ After the destroy completes the records `destroyed?` method will return true.
187
+
188
+ #### Other Instance Methods
189
+
190
+ `new?` returns true if the model is new and not yet saved.
191
+
192
+ `primary_key` returns the primary key for the model
193
+
194
+ `id` returns the value of the primary key for this instance
195
+
196
+ `model_name` returns the model_name.
197
+
198
+ `revert` Undoes any unsaved changes to the instance.
199
+
200
+ `changed?` returns true if any attributes have changed (always true for a new model)
201
+
202
+ `dup` duplicate the instance.
203
+
204
+ `==` two instances are the same if they reference the same underlying table row.
205
+
206
+ `..._changed?` (i.e. name_changed?) returns true if the specific attribute has changed.
207
+
208
+ `itself` returns the record, but will also force a load of at least the model's id.
209
+
210
+ #### Other Methods for Interacting with the Load & Render Cycle
211
+
212
+ #### `loading?` and `loaded?`
213
+
214
+ All Ruby objects will respond to these methods. If you want to put up a "Please Wait" message, spinner, etc, you can use the `loaded?` or `loading?` method to determine if the object represents a real loaded value or not.
215
+
216
+ #### The `HyperMesh.load` Method
217
+
218
+ Sometimes it is necessary to insure values are loaded outside of the rendering cycle. For this you can use the `HyperMesh.load` method:
70
219
 
71
220
  ```ruby
72
- Word.find_by_text('hello') # short for Word.find_by(text: 'hello')
221
+ HyperMesh.load do
222
+ x = my_model.some_attribute
223
+ OtherModel.find(x+12).other_attribute
224
+ # code in here can be arbitrarily complex and load
225
+ # will re-execute it until all values are loaded
226
+ # the final expression is passed to the promise
227
+ end.then |result|
228
+ puts result
229
+ end
73
230
  ```
231
+
232
+
233
+
234
+ #### Force Loading Attributes
235
+
236
+ Like
237
+
238
+ Normally you will simply display attributes as part of the render method, and when the values are loaded from the server the component will re-render.
239
+
240
+ Sometimes outside of the render method you may need to insure an attribute (or a server side method) is loaded before proceeding. This is typically when you are building some kind of higher level store.
241
+
242
+ The `load` takes a list of attributes (symbols) and will insure these are loaded. Load returns a promise that is resolved when the load completes, or can be passed a block that will execute when the load completes.
243
+
244
+ ```ruby
245
+ before_mount do
246
+ Todo.find(1).load(:name).then do |name|
247
+ @name = name;
248
+ state.loaded! true
249
+ end
250
+ end
251
+ ```
252
+
253
+ Think hard about how you are using this, as HyperMesh already acts as flux store, and is managing state for you.
@@ -42,6 +42,10 @@ module ActiveRecord
42
42
  pre_synchromesh_default_scope(opts[:server], &block)
43
43
  end
44
44
 
45
+ def server_method(name, opts = {}, &block)
46
+ define_method name, &block
47
+ end
48
+
45
49
  else
46
50
 
47
51
  alias pre_synchromesh_method_missing method_missing
@@ -113,7 +117,7 @@ module ActiveRecord
113
117
  after_commit :synchromesh_after_destroy, on: [:destroy]
114
118
 
115
119
  def synchromesh_after_create
116
- return if do_not_synchronize? || previous_changes.empty?
120
+ return if do_not_synchronize? #|| previous_changes.empty?
117
121
  HyperMesh.after_commit :create, self
118
122
  end
119
123
 
@@ -1,3 +1,3 @@
1
1
  module Hypermesh
2
- VERSION = "0.5.1"
2
+ VERSION = '0.5.2'
3
3
  end
@@ -107,6 +107,10 @@ module ActiveRecord
107
107
  [:belongs_to, :has_many, :has_one].each do |macro|
108
108
  define_method(macro) do |*args| # is this a bug in opal? saying name, scope=nil, opts={} does not work!
109
109
  name = args.first
110
+ define_method(name) { @backing_record.reactive_get!(name, nil) }
111
+ define_method("#{name}=") do |val|
112
+ @backing_record.reactive_set!(name, backing_record.convert(name, val))
113
+ end
110
114
  opts = (args.count > 1 and args.last.is_a? Hash) ? args.last : {}
111
115
  Associations::AssociationReflection.new(self, macro, name, opts)
112
116
  end
@@ -114,16 +118,48 @@ module ActiveRecord
114
118
 
115
119
  def composed_of(name, opts = {})
116
120
  Aggregations::AggregationReflection.new(base_class, :composed_of, name, opts)
121
+ define_method(name) { @backing_record.reactive_get!(name, nil) }
122
+ define_method("#{name}=") do |val|
123
+ @backing_record.reactive_set!(name, backing_record.convert(name, val))
124
+ end
117
125
  end
118
126
 
119
127
  def column_names
120
- [] # it would be great to figure out how to get this information on the client! For now we just return an empty array
128
+ HyperMesh::ClientDrivers.public_columns_hash.keys
121
129
  end
122
130
 
123
131
  def columns_hash
124
132
  HyperMesh::ClientDrivers.public_columns_hash[name] || {}
125
133
  end
126
134
 
135
+ def server_methods
136
+ @server_methods ||= {}
137
+ end
138
+
139
+ def server_method(name, default: nil)
140
+ server_methods[name] = { default: default }
141
+ define_method(name) do |*args|
142
+ vector = args.count.zero? ? name : [[name]+args]
143
+ @backing_record.reactive_get!(vector, nil)
144
+ end
145
+ define_method("#{name}!") do |*args|
146
+ vector = args.count.zero? ? name : [[name]+args]
147
+ @backing_record.reactive_get!(vector, true)
148
+ end
149
+ end
150
+
151
+ def define_attribute_methods
152
+ columns_hash.keys.each do |name|
153
+ next if name == :id
154
+ define_method(name) { @backing_record.reactive_get!(name, nil) }
155
+ define_method("#{name}!") { @backing_record.reactive_get!(name, true) }
156
+ define_method("#{name}=") do |val|
157
+ @backing_record.reactive_set!(name, backing_record.convert(name, val))
158
+ end
159
+ define_method("#{name}_changed?") { @backing_record.changed?(name) }
160
+ end
161
+ end
162
+
127
163
  [
128
164
  "table_name=", "before_validation", "with_options", "validates_presence_of", "validates_format_of",
129
165
  "accepts_nested_attributes_for", "before_create", "after_create", "before_save", "after_save", "before_destroy", "where", "validate",
@@ -34,6 +34,14 @@ module ActiveRecord
34
34
  self.class.primary_key
35
35
  end
36
36
 
37
+ def type
38
+ @backing_record.reactive_get!(:type, nil)
39
+ end
40
+
41
+ def type=(val)
42
+ @backing_record.reactive_set!(:type, backing_record.convert(:type, val))
43
+ end
44
+
37
45
  def id
38
46
  @backing_record.reactive_get!(primary_key)
39
47
  end
@@ -63,7 +71,12 @@ module ActiveRecord
63
71
  @backing_record == ar_instance.instance_eval { @backing_record }
64
72
  end
65
73
 
74
+ def method_missing_warning(name)
75
+ @backing_record.deprecation_warning("Server side method #{name} must be defined using the 'server_method' macro.")
76
+ end
77
+
66
78
  def method_missing(name, *args, &block)
79
+ method_missing_warning("#{name}(#{args})")
67
80
  if name =~ /\!$/
68
81
  name = name.gsub(/\!$/,"")
69
82
  force_update = true
@@ -36,7 +36,7 @@ module ReactiveRecord
36
36
  end
37
37
 
38
38
  def build_default_value_for_nil
39
- nil
39
+ @column_hash[:default] || nil
40
40
  end
41
41
 
42
42
  def build_default_value_for_datetime
@@ -7,6 +7,7 @@ module ReactiveRecord
7
7
  include React::IsomorphicHelpers
8
8
 
9
9
  before_first_mount do |context|
10
+ HyperMesh::ClientDrivers.on_first_mount
10
11
  if RUBY_ENGINE != 'opal'
11
12
  @server_data_cache = ReactiveRecord::ServerDataCache.new(context.controller.acting_user, {})
12
13
  else
@@ -28,6 +29,19 @@ module ReactiveRecord
28
29
  end
29
30
  end
30
31
 
32
+ def self.deprecation_warning(model, message)
33
+ @deprecation_messages ||= []
34
+ message = "Warning: Deprecated feature used in #{model}. #{message}"
35
+ unless @deprecation_messages.include? message
36
+ @deprecation_messages << message
37
+ log message, :warning
38
+ end
39
+ end
40
+
41
+ def deprecation_warning(message)
42
+ self.class.deprecation_warning(model, message)
43
+ end
44
+
31
45
  def records
32
46
  self.class.instance_variable_get(:@records)
33
47
  end
@@ -87,9 +101,16 @@ module ReactiveRecord
87
101
  @pending_records << record if record
88
102
  schedule_fetch
89
103
  end
90
- DummyValue.new(record && record.model.columns_hash[vector.last])
104
+ DummyValue.new(record && record.get_columns_info_for_vector(vector))
105
+ end
106
+
107
+ def get_columns_info_for_vector(vector)
108
+ method_name = vector.last
109
+ method_name = method_name.first if method_name.is_a? Array
110
+ model.columns_hash[method_name] || model.server_methods[method_name]
91
111
  end
92
112
 
113
+
93
114
  class << self
94
115
 
95
116
  attr_reader :pending_fetches
@@ -1,3 +1,9 @@
1
+ module HyperMesh
2
+ def self.load(&block)
3
+ ReactiveRecord.load &block
4
+ end
5
+ end
6
+
1
7
  module ReactiveRecord
2
8
 
3
9
  # will repeatedly execute the block until it is loaded
@@ -106,6 +106,7 @@ module ReactiveRecord
106
106
  end
107
107
 
108
108
  def self.[](models, associations, vectors, acting_user)
109
+ ActiveRecord::Base.public_columns_hash
109
110
  result = nil
110
111
  ActiveRecord::Base.transaction do
111
112
  cache = new(acting_user, ReactiveRecord::Base.save_records(models, associations, acting_user, false, false))
@@ -272,8 +272,9 @@ module HyperMesh
272
272
  "</script>\n"
273
273
  end if RUBY_ENGINE != 'opal'
274
274
 
275
- def self.opts
276
- @opts ||= Hash.new(`window.HyperMeshOpts`)
275
+ class << self
276
+ attr_reader :opts
277
+ attr_reader :public_columns_hash
277
278
  end
278
279
 
279
280
  isomorphic_method(:get_public_columns_hash) do |f|
@@ -282,10 +283,6 @@ module HyperMesh
282
283
  f.when_on_server { ActiveRecord::Base.public_columns_hash }
283
284
  end
284
285
 
285
- def self.public_columns_hash
286
- @public_columns_hash ||= get_public_columns_hash
287
- end
288
-
289
286
  def self.get_queued_data(operation, channel = nil, opts = {})
290
287
  HTTP.get(polling_path(operation, channel), opts).then do |response|
291
288
  response.json.each do |update|
@@ -294,10 +291,23 @@ module HyperMesh
294
291
  end
295
292
  end
296
293
 
297
- # Before first mount, hook up callbacks depending on what kind of transport
298
- # we are using.
294
+ def self.define_attribute_methods
295
+ public_columns_hash.keys.each do |model|
296
+ Object.const_get(model).define_attribute_methods rescue nil
297
+ end
298
+ end
299
+
300
+ # called from ReactiveRecord::Base before_first_mount hook
301
+ # to insure this is done first.
302
+
303
+ def self.on_first_mount
304
+
305
+ if RUBY_ENGINE == 'opal'
306
+ @opts = Hash.new(`window.HyperMeshOpts`)
307
+ @public_columns_hash = get_public_columns_hash
308
+ define_attribute_methods
309
+ end
299
310
 
300
- before_first_mount do
301
311
  if on_opal_client?
302
312
 
303
313
  if opts[:transport] == :pusher
@@ -10,7 +10,7 @@ GIT
10
10
  PATH
11
11
  remote: ../
12
12
  specs:
13
- hyper-mesh (0.5.0)
13
+ hyper-mesh (0.5.2)
14
14
  activerecord (>= 0.3.0)
15
15
  hyper-react (>= 0.10.0)
16
16
 
@@ -0,0 +1 @@
1
+ require_tree './public' if RUBY_ENGINE == 'opal'
@@ -50,7 +50,8 @@ module Dummy
50
50
  # Enable the asset pipeline
51
51
  config.assets.enabled = true
52
52
 
53
- config.autoload_paths += %W(#{config.root}/app/views/models)
53
+ #config.autoload_paths += %W(#{config.root}/app/models/public)
54
+ config.assets.paths << ::Rails.root.join('app', 'models').to_s
54
55
 
55
56
  # Version of your assets, change this if you want to expire all your assets
56
57
  config.assets.version = '1.0'
@@ -21,7 +21,7 @@ describe "reactive-record edge cases", js: true do
21
21
  FactoryGirl.create(:todo, title: "User #{i}'s todo", owner: user)
22
22
  end
23
23
  expect_promise do
24
- ReactiveRecord.load do
24
+ HyperMesh.load do
25
25
  Todo.all.collect do |todo|
26
26
  todo.owner && todo.owner.first_name
27
27
  end.compact
@@ -96,6 +96,9 @@ describe "column types on client", js: true do
96
96
 
97
97
  isomorphic do
98
98
  class TypeTest < ActiveRecord::Base
99
+ server_method :a_server_method, default: "hello" do |s|
100
+ s.reverse
101
+ end
99
102
  end
100
103
  class DefaultTest < ActiveRecord::Base
101
104
  end
@@ -125,6 +128,19 @@ describe "column types on client", js: true do
125
128
  end.to eq(TypeTest.columns_hash.as_json)
126
129
  end
127
130
 
131
+ it 'defines the server method with a default value' do
132
+ expect_evaluate_ruby do
133
+ TypeTest.new.a_server_method('foo')
134
+ end.to eq('hello')
135
+ end
136
+
137
+ it 'loads the server method' do
138
+ TypeTest.create
139
+ expect_promise do
140
+ ReactiveRecord.load { TypeTest.find(1).a_server_method('hello') }
141
+ end.to eq('olleh')
142
+ end
143
+
128
144
  it 'creates a dummy value of the appropriate type' do
129
145
  t = Time.now
130
146
  TypeTest.create(
@@ -0,0 +1,64 @@
1
+ require 'spec_helper'
2
+ require 'synchromesh/integration/test_components'
3
+ require 'rspec-steps'
4
+
5
+ describe "saving during commit", js: true do
6
+
7
+ before(:each) do
8
+ require 'pusher'
9
+ require 'pusher-fake'
10
+ Pusher.app_id = "MY_TEST_ID"
11
+ Pusher.key = "MY_TEST_KEY"
12
+ Pusher.secret = "MY_TEST_SECRET"
13
+ require "pusher-fake/support/base"
14
+
15
+ HyperMesh.configuration do |config|
16
+ config.transport = :pusher
17
+ config.channel_prefix = "synchromesh"
18
+ config.opts = {app_id: Pusher.app_id, key: Pusher.key, secret: Pusher.secret}.merge(PusherFake.configuration.web_options)
19
+ end
20
+
21
+ class CommitIssue < ActiveRecord::Base
22
+ def self.build_tables
23
+ connection.create_table :commit_issues, force: true do |t|
24
+ t.string :name
25
+ t.timestamps
26
+ end
27
+ end
28
+ end
29
+
30
+ isomorphic do
31
+ class CommitIssue < ActiveRecord::Base
32
+ after_create :save_again
33
+ def save_again
34
+ save
35
+ end
36
+ end
37
+ end
38
+
39
+ CommitIssue.build_tables rescue nil
40
+
41
+ stub_const 'ApplicationPolicy', Class.new
42
+ ApplicationPolicy.class_eval do
43
+ always_allow_connection
44
+ regulate_all_broadcasts { |policy| policy.send_all }
45
+ allow_change(to: :all, on: [:create, :update, :destroy]) { true }
46
+ end
47
+ size_window(:small, :portrait)
48
+ end
49
+
50
+ it "broadcast even if saving during after_save" do
51
+ CommitIssue.create(name: 1)
52
+ mount "CommitIssueTest" do
53
+ class CommitIssueTest < React::Component::Base
54
+ render do
55
+ "all: [#{CommitIssue.all.pluck(:name)}]"
56
+ end
57
+ end
58
+ end
59
+ page.should have_content('all: [1]')
60
+ CommitIssue.create(name: 2)
61
+ page.should have_content('all: [1,2]')
62
+ end
63
+
64
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hyper-mesh
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.5.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mitch VanDuyn
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-11-15 00:00:00.000000000 Z
11
+ date: 2016-11-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -1045,17 +1045,16 @@ files:
1045
1045
  - reactive_record_test_app/app/controllers/test_controller.rb
1046
1046
  - reactive_record_test_app/app/helpers/application_helper.rb
1047
1047
  - reactive_record_test_app/app/mailers/.gitkeep
1048
- - reactive_record_test_app/app/models/.gitkeep
1048
+ - reactive_record_test_app/app/models/models.rb
1049
+ - reactive_record_test_app/app/models/public/address.rb
1050
+ - reactive_record_test_app/app/models/public/comment.rb
1051
+ - reactive_record_test_app/app/models/public/todo_item.rb
1052
+ - reactive_record_test_app/app/models/public/user.rb
1049
1053
  - reactive_record_test_app/app/policies/application_policy.rb
1050
1054
  - reactive_record_test_app/app/views/components.rb
1051
1055
  - reactive_record_test_app/app/views/components/test.rb
1052
1056
  - reactive_record_test_app/app/views/home/index.html.erb
1053
1057
  - reactive_record_test_app/app/views/layouts/application.html.erb
1054
- - reactive_record_test_app/app/views/models.rb
1055
- - reactive_record_test_app/app/views/models/address.rb
1056
- - reactive_record_test_app/app/views/models/comment.rb
1057
- - reactive_record_test_app/app/views/models/todo_item.rb
1058
- - reactive_record_test_app/app/views/models/user.rb
1059
1058
  - reactive_record_test_app/config.ru
1060
1059
  - reactive_record_test_app/config/application.rb
1061
1060
  - reactive_record_test_app/config/boot.rb
@@ -1132,6 +1131,8 @@ files:
1132
1131
  - spec/reactive_record/update_scopes_spec.rb
1133
1132
  - spec/spec_helper.rb
1134
1133
  - spec/support/component_helpers.rb
1134
+ - spec/synchromesh/aaa-unit_tests/connection_spec.rb
1135
+ - spec/synchromesh/aaa-unit_tests/dummy_value_spec.rb
1135
1136
  - spec/synchromesh/column_types/column_type_spec.rb
1136
1137
  - spec/synchromesh/crud_access_regulation/broadcast_controls_access_spec.rb
1137
1138
  - spec/synchromesh/crud_access_regulation/model_policies_spec.rb
@@ -1143,6 +1144,7 @@ files:
1143
1144
  - spec/synchromesh/integration/default_scope_spec.rb
1144
1145
  - spec/synchromesh/integration/has_many_through_spec.rb
1145
1146
  - spec/synchromesh/integration/relationships_spec.rb
1147
+ - spec/synchromesh/integration/saving_during_commit_spec.rb
1146
1148
  - spec/synchromesh/integration/scope_spec.rb
1147
1149
  - spec/synchromesh/integration/synchromesh_spec.rb
1148
1150
  - spec/synchromesh/integration/test_components.rb
@@ -1154,8 +1156,6 @@ files:
1154
1156
  - spec/synchromesh/policies/regulate_broadcast_spec.rb
1155
1157
  - spec/synchromesh/policies/regulate_class_connection_spec.rb
1156
1158
  - spec/synchromesh/policies/regulate_instance_connection_spec.rb
1157
- - spec/synchromesh/unit_tests/connection_spec.rb
1158
- - spec/synchromesh/unit_tests/dummy_value_spec.rb
1159
1159
  - spec/test_app/Gemfile
1160
1160
  - spec/test_app/Gemfile.lock
1161
1161
  - spec/test_app/Rakefile
@@ -1259,6 +1259,8 @@ test_files:
1259
1259
  - spec/reactive_record/update_scopes_spec.rb
1260
1260
  - spec/spec_helper.rb
1261
1261
  - spec/support/component_helpers.rb
1262
+ - spec/synchromesh/aaa-unit_tests/connection_spec.rb
1263
+ - spec/synchromesh/aaa-unit_tests/dummy_value_spec.rb
1262
1264
  - spec/synchromesh/column_types/column_type_spec.rb
1263
1265
  - spec/synchromesh/crud_access_regulation/broadcast_controls_access_spec.rb
1264
1266
  - spec/synchromesh/crud_access_regulation/model_policies_spec.rb
@@ -1270,6 +1272,7 @@ test_files:
1270
1272
  - spec/synchromesh/integration/default_scope_spec.rb
1271
1273
  - spec/synchromesh/integration/has_many_through_spec.rb
1272
1274
  - spec/synchromesh/integration/relationships_spec.rb
1275
+ - spec/synchromesh/integration/saving_during_commit_spec.rb
1273
1276
  - spec/synchromesh/integration/scope_spec.rb
1274
1277
  - spec/synchromesh/integration/synchromesh_spec.rb
1275
1278
  - spec/synchromesh/integration/test_components.rb
@@ -1281,8 +1284,6 @@ test_files:
1281
1284
  - spec/synchromesh/policies/regulate_broadcast_spec.rb
1282
1285
  - spec/synchromesh/policies/regulate_class_connection_spec.rb
1283
1286
  - spec/synchromesh/policies/regulate_instance_connection_spec.rb
1284
- - spec/synchromesh/unit_tests/connection_spec.rb
1285
- - spec/synchromesh/unit_tests/dummy_value_spec.rb
1286
1287
  - spec/test_app/Gemfile
1287
1288
  - spec/test_app/Gemfile.lock
1288
1289
  - spec/test_app/Rakefile
File without changes
@@ -1 +0,0 @@
1
- require_tree './models'