hyper-resource 1.0.0.lap86 → 1.0.0.lap87

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: aea164bac45355bd1005264f1a9ce5f273454b2c97ddd63db204fc17542d374d
4
- data.tar.gz: 7f67b54de3ff4f449c93a59904a33f6889323a0d907acc329a01d0fee2622efd
3
+ metadata.gz: 4d276e459e66303c270a3fd19ad817000f7591604673852f2084c1b591b1bf4a
4
+ data.tar.gz: 14cfa291e0a603badf2f8e4cb9484e3996e5708a2cc059ad1dc5b8ef111784f3
5
5
  SHA512:
6
- metadata.gz: 6f304149b7ed89dd3a1963e75a1fda39ec562b8d7c1c1e7b7fb112815cdae463defbf138f66a9500bee056cd054369f3a57a945c259c5ab9557a68c7fc7f4243
7
- data.tar.gz: c27ff1a053421ee0450a133e07e1d45e02faba561d31a328e095d2adf3221d62109ba2916fef25a15bd8b856089e1c6fcd667042d6f1dd85a3ff8fb6ade48964
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
- How to install....
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 rest_class_methods, RPC on instance level of a record of current HyperRecord class
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
@@ -1,5 +1,5 @@
1
1
  module Hyperloop
2
2
  module Resource
3
- VERSION = '1.0.0.lap86'
3
+ VERSION = '1.0.0.lap87'
4
4
  end
5
5
  end
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.lap86
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-06-18 00:00:00.000000000 Z
11
+ date: 2018-08-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: opal