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 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