hyper-resource 1.0.0.lap86 → 1.0.0.lap87
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.
- checksums.yaml +4 -4
- data/README.md +206 -1
- data/lib/hyper_record/class_methods.rb +53 -1
- data/lib/hyper_record/server_class_methods.rb +21 -0
- data/lib/hyperloop/resource/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4d276e459e66303c270a3fd19ad817000f7591604673852f2084c1b591b1bf4a
|
4
|
+
data.tar.gz: 14cfa291e0a603badf2f8e4cb9484e3996e5708a2cc059ad1dc5b8ef111784f3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d4d5e94abd9001519c6455257887eec88937c9dadfa1f41527fc5e1516e1d18432e9c6ea87497d041ed2d599985a80d47ae6fc9b4b20ced7d5613c75fb7aebd8
|
7
|
+
data.tar.gz: 72a52d2bc6dce43d4c1c2d2c6827494629786782b58dcb5e9ee7d6bffb822a83830bbd957a15ed9e2d4a56fad9459e69b63f9b107f77822a98a18aaabe40c8af
|
data/README.md
CHANGED
@@ -112,4 +112,209 @@ EXAMPLE
|
|
112
112
|
|
113
113
|
## Implementation
|
114
114
|
|
115
|
-
|
115
|
+
## Implementation
|
116
|
+
|
117
|
+
Hyperloop needs to be installed and working before you install HyperResource. These instructions are likely to change/be simplified as this Gem matures.
|
118
|
+
|
119
|
+
+ Add the gems (make sure its the latest version)
|
120
|
+
|
121
|
+
`gem 'hyper-resource', '1.0.0.lap86'`
|
122
|
+
`gem 'opal-jquery', github: 'janbiedermann/opal-jquery', branch: 'why_to_n'`
|
123
|
+
`gem 'opal-activesupport', github: 'opal/opal-activesupport', branch: 'master'`
|
124
|
+
|
125
|
+
+ Require HyperResource in your `hyperloop_webpack_loader.rb` file
|
126
|
+
|
127
|
+
`require 'hyper-resource'`
|
128
|
+
`require 'opal-jquery'`
|
129
|
+
|
130
|
+
+ Update your `application_record.rb` file and move it to the `hyperloop/models` folder
|
131
|
+
|
132
|
+
```
|
133
|
+
# application_record.rb
|
134
|
+
if RUBY_ENGINE == 'opal'
|
135
|
+
class ApplicationRecord
|
136
|
+
def self.inherited(base)
|
137
|
+
base.include(HyperRecord)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
else
|
141
|
+
class ApplicationRecord < ActiveRecord::Base
|
142
|
+
# when updating this part, also update the ApplicationRecord in app/models/application_record.rb
|
143
|
+
# for rails eager loading in production, so production doesn't fail
|
144
|
+
self.abstract_class = true
|
145
|
+
extend HyperRecord::ServerClassMethods
|
146
|
+
include HyperRecord::PubSub
|
147
|
+
end
|
148
|
+
end
|
149
|
+
```
|
150
|
+
|
151
|
+
+ Move the models you want on the client to the `hyperloop/models` folder
|
152
|
+
|
153
|
+
+ Make sure you guard anything in your model which you do not want on the client:
|
154
|
+
|
155
|
+
```
|
156
|
+
unless RUBY_ENGINE == 'opal'
|
157
|
+
# herein stuff that you do not want on the client (Devise, etc)
|
158
|
+
end
|
159
|
+
```
|
160
|
+
|
161
|
+
+ Add Pusher to your gemfile
|
162
|
+
|
163
|
+
`gem pusher`
|
164
|
+
|
165
|
+
+ Add it with Yarn
|
166
|
+
|
167
|
+
`yarn add pusher-js`
|
168
|
+
|
169
|
+
+ Then import in your `app.js`
|
170
|
+
|
171
|
+
`import Pusher from 'pusher-js';`
|
172
|
+
`global.Pusher = Pusher;`
|
173
|
+
|
174
|
+
+ Add your api endpoint to `hyperloop.rb`
|
175
|
+
|
176
|
+
`config.resource_api_base_path = '/api'`
|
177
|
+
|
178
|
+
+ Create you API controllers as normal - ensure they return JSON in this format
|
179
|
+
|
180
|
+
```
|
181
|
+
{
|
182
|
+
"members":[
|
183
|
+
{"member":{"id":1,"email":"a@b.com","first_name":"John","last_name":"Smith"}},
|
184
|
+
{"member":{"id":2,"email":"b@c.com","first_name":null,"last_name":null}}
|
185
|
+
]
|
186
|
+
}
|
187
|
+
```
|
188
|
+
|
189
|
+
Rabl gem example view:
|
190
|
+
|
191
|
+
```
|
192
|
+
collection @members, root: :members
|
193
|
+
attributes :id,
|
194
|
+
:email,
|
195
|
+
:first_name,
|
196
|
+
:last_name
|
197
|
+
```
|
198
|
+
|
199
|
+
+ Create your API controller and make sure to implement `show` as this is called by HyperResource. Please see the example controller below for details on pub_sub
|
200
|
+
|
201
|
+
```ruby
|
202
|
+
module Api
|
203
|
+
class PersonasController < ApplicationController
|
204
|
+
# GET /api/personas.json
|
205
|
+
def index
|
206
|
+
authorize(Persona)
|
207
|
+
|
208
|
+
@personas = Persona.all
|
209
|
+
subscribe_scope(@personas, Persona, :all)
|
210
|
+
respond_to do |format|
|
211
|
+
format.json {}
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
# GET /api/personas/123.json
|
216
|
+
def show
|
217
|
+
@persona = Persona.find(params[:id])
|
218
|
+
|
219
|
+
authorize(@persona)
|
220
|
+
|
221
|
+
subscribe_record(@persona)
|
222
|
+
respond_to do |format|
|
223
|
+
format.json {}
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
# POST /api/plans/1/personas.json
|
228
|
+
def create
|
229
|
+
authorize(Persona)
|
230
|
+
|
231
|
+
@persona = Persona.new(persona_params)
|
232
|
+
|
233
|
+
subscribe_record(@persona)
|
234
|
+
respond_to do |format|
|
235
|
+
if @persona.save
|
236
|
+
subscribe_record(@persona)
|
237
|
+
publish_scope(Persona, :all)
|
238
|
+
format.json { render :show, status: :created }
|
239
|
+
else
|
240
|
+
format.json { render json: @persona.errors, status: :unprocessable_entity }
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
# PATCH/PUT /api/personas/1.json
|
246
|
+
def update
|
247
|
+
@persona = Persona.find(params[:id])
|
248
|
+
@persona.assign_attributes(persona_params)
|
249
|
+
|
250
|
+
authorize(@persona)
|
251
|
+
|
252
|
+
respond_to do |format|
|
253
|
+
if @persona.update(persona_params)
|
254
|
+
pub_sub_record(@persona)
|
255
|
+
format.json { render :show, status: :ok }
|
256
|
+
else
|
257
|
+
format.json { render json: @persona.errors, status: :unprocessable_entity }
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
# DELETE /personas/1.json
|
263
|
+
def destroy
|
264
|
+
@persona = Persona.find(params[:id])
|
265
|
+
# authorize @persona
|
266
|
+
|
267
|
+
@persona.destroy
|
268
|
+
publish_record(@persona)
|
269
|
+
respond_to do |format|
|
270
|
+
format.json { head :no_content }
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
private
|
275
|
+
|
276
|
+
def persona_params
|
277
|
+
permitted_keys = Persona.attribute_names.map(&:to_sym)
|
278
|
+
%i[id created_at updated_at].each do |key|
|
279
|
+
permitted_keys.delete(key)
|
280
|
+
end
|
281
|
+
params.require(:data).permit(permitted_keys)
|
282
|
+
end
|
283
|
+
end
|
284
|
+
end
|
285
|
+
```
|
286
|
+
|
287
|
+
+ Install Redis and add the following to your `hyperloop.rb`
|
288
|
+
|
289
|
+
```ruby
|
290
|
+
config.redis_instance = if ENV['REDIS_URL']
|
291
|
+
Redis.new(url: ENV['REDIS_URL'])
|
292
|
+
else
|
293
|
+
Redis.new
|
294
|
+
end
|
295
|
+
```
|
296
|
+
|
297
|
+
+ Add the following to your `ApplicationController`
|
298
|
+
|
299
|
+
```
|
300
|
+
include Hyperloop::Resource::PubSub
|
301
|
+
```
|
302
|
+
|
303
|
+
+ Add these routes:
|
304
|
+
|
305
|
+
```
|
306
|
+
namespace :api, defaults: { format: :json } do
|
307
|
+
|
308
|
+
# introspection
|
309
|
+
# get '/:model_klass/relations', to: 'relations#index'
|
310
|
+
# get '/:model_klass/methods', to: 'methods#index'
|
311
|
+
# get '/:model_klass/methods/:id', to: 'methods#show'
|
312
|
+
# patch '/:model_klass/methods/:id', to: 'methods#update'
|
313
|
+
# get '/:model_klass/properties', to: 'properties#index'
|
314
|
+
get '/:model_klass/scopes', to: 'scopes#index'
|
315
|
+
get '/:model_klass/scopes/:id', to: 'scopes#show'
|
316
|
+
patch '/:model_klass/scopes/:id', to: 'scopes#update'
|
317
|
+
|
318
|
+
```
|
319
|
+
|
320
|
+
+ Add the `ScopesController` as per the example in this Gem
|
@@ -122,6 +122,58 @@ module HyperRecord
|
|
122
122
|
# end
|
123
123
|
end
|
124
124
|
|
125
|
+
# macro define collection_query_method, RPC on instance level of a record of current HyperRecord class
|
126
|
+
# The supplied block must return a Array of Records!
|
127
|
+
#
|
128
|
+
# @param name [Symbol] name of method
|
129
|
+
# @param options [Hash] with known keys:
|
130
|
+
# default_result: result to present during render during method call in progress, is a Array by default, should be a Enumerable in any case
|
131
|
+
#
|
132
|
+
# This macro defines additional methods:
|
133
|
+
def collection_query_method(name, options = { default_result: []})
|
134
|
+
# @!method promise_[name]
|
135
|
+
# @return [Promise] on success the .then block will receive the result of the RPC call as arg
|
136
|
+
# on failure the .fail block will receive the HTTP response object as arg
|
137
|
+
define_method("promise_#{name}") do
|
138
|
+
@fetch_states[name] = 'i'
|
139
|
+
unless @rest_methods.has_key?(name)
|
140
|
+
@rest_methods[name] = options
|
141
|
+
@rest_methods[name] = { result: options[:default_result] }
|
142
|
+
end
|
143
|
+
raise "#{self.class.to_s}[_no_id_].#{name}, can't execute instance collection_query_method without id!" unless self.id
|
144
|
+
self.class._promise_get_or_patch("#{resource_base_uri}/#{self.id}/methods/#{name}.json?timestamp=#{`Date.now() + Math.random()`}").then do |response_json|
|
145
|
+
collection = self.class._convert_array_to_collection(response_json[:result], self)
|
146
|
+
@rest_methods[name][:result] = collection
|
147
|
+
@fetch_states[name] = 'f'
|
148
|
+
_notify_observers
|
149
|
+
@rest_methods[name][:result]
|
150
|
+
end.fail do |response|
|
151
|
+
error_message = "#{self.class.to_s}[#{self.id}].#{name}, a collection_query_method, failed to execute!"
|
152
|
+
`console.error(error_message)`
|
153
|
+
response
|
154
|
+
end
|
155
|
+
end
|
156
|
+
# @!method [name]
|
157
|
+
# @return result either the default_result ass specified in the options or the real result if the RPC call already finished
|
158
|
+
define_method(name) do
|
159
|
+
_register_observer
|
160
|
+
unless @rest_methods.has_key?(name)
|
161
|
+
@rest_methods[name] = options
|
162
|
+
@rest_methods[name] = { result: options[:default_result] }
|
163
|
+
end
|
164
|
+
unless @fetch_states.has_key?(name) && 'fi'.include?(@fetch_states[name])
|
165
|
+
self.send("promise_#{name}")
|
166
|
+
end
|
167
|
+
@rest_methods[name][:result]
|
168
|
+
end
|
169
|
+
# @!method update_[name] mark internal structures so that the method is called again once it is requested again
|
170
|
+
# @return nil
|
171
|
+
define_method("update_#{name}") do
|
172
|
+
@fetch_states[name] = 'u'
|
173
|
+
nil
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
125
177
|
# create a new instance of current HyperRecord class and save it to the db
|
126
178
|
#
|
127
179
|
# @param record_hash [Hash] optional data for the record
|
@@ -507,7 +559,7 @@ module HyperRecord
|
|
507
559
|
end
|
508
560
|
end
|
509
561
|
|
510
|
-
# macro define
|
562
|
+
# macro define rest_methods, RPC on instance level of a record of current HyperRecord class
|
511
563
|
#
|
512
564
|
# @param name [Symbol] name of method
|
513
565
|
# @param options [Hash] with known keys:
|
@@ -1,5 +1,26 @@
|
|
1
1
|
module HyperRecord
|
2
2
|
module ServerClassMethods
|
3
|
+
# DSL for defining collection_query_methods
|
4
|
+
# @param name [Symbol] name of the method
|
5
|
+
# @param options [Hash] known key: default_result, used client side
|
6
|
+
def collection_query_method(name, options = { default_result: [] }, &block)
|
7
|
+
rest_methods[name] = options
|
8
|
+
rest_methods[name][:params] = block.arity
|
9
|
+
define_method(name) do
|
10
|
+
array_of_records = instance_exec(&block)
|
11
|
+
array_of_records.map do |record|
|
12
|
+
subscribe_record(record)
|
13
|
+
record_json = record.as_json
|
14
|
+
record_model = record.class.to_s.underscore
|
15
|
+
if record_json.has_key?(record_model)
|
16
|
+
record_json
|
17
|
+
else
|
18
|
+
{ record_model => record_json }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
3
24
|
# DSL for defining rest_class_methods
|
4
25
|
# @param name [Symbol] name of the method
|
5
26
|
# @param options [Hash] known key: default_result, used client side
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hyper-resource
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0.
|
4
|
+
version: 1.0.0.lap87
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jan Biedermann
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-08-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: opal
|