hyperstack-org / hyperstack-org/hyperstack

Elegant usage of JSON APIs in Hyperstack

未關閉
#274 7 則留言 2 個 reaction 已指派 1 人 已被 @barriehadfield 認領 在 GitHub 檢視
discussion
主要語言
JavaScript
星號
538
分支
41
PR 合併指標
30 天內沒有已合併 PR

描述

In my app, I want to use traditional REST APIs so I have greater control over which data I fetch and when I fetch it. My app is a mobile app used in a gym, so an internet signal cannot be assumed, so I need to fetch all the data the user might want and hold it in memory.

That said, I did not want to work with big complex multi-nested JSON arrays on the front end! I wanted an ORM for a JSON response, so I wrote this wonderful little recursive class which lets me treat a JSON array as a model which supports has_many relationships out the box!

I thought I would share here in case anyone else found it useful.

(Note there is no security or policy covered here to keep the example as simple as possible).

In the Rails controller:

```ruby
class ApiController < ApplicationController
def workout
params.permit(:workout_id)
@workout = Workout.find params[:workout_id]

render "api/workout"
end
end
```

I am using the RABL Gem to compose the JSON response, so I have a view which creates the JOSN which is a `Workout` model which includes two nested has_many associations ( `primary_exercises` and `alternatives`) each of which has their own child collection called `exsets`.

```ruby
object @workout
attributes :id, :user_id, :created_at, :name, :prescribed_duration_mins
child primary_exercises: :primary_exercises do |e|
exercise e
child alternatives: :alternative_exercises do |e|
exercise e
end
end

def exercise(e)
node(:id) { |e| e.id }
node(:name) { |e| e.exercise_type.name }

child :exsets do |es|
node(:id) { |es| es.id }
node(:prescribed_time_in_s) { |es| es.prescribed_time_in_s }
node(:prescribed_weight_in_kg) { |es| es.prescribed_weight_in_kg }
end
end
```

Then on the client, I have my wonderful recursive class:

```ruby
class JsonModel
include Hyperstack::State::Observable

def initialize(path: nil, name: nil, json: nil)
return get_data(path) if path
init name, json
end

def loaded?
observe @loaded
end

def inspect
puts @name
puts @json
end

def method_missing(m)
return nil unless @json[@name][m]
return @json[@name][m] unless @json[@name][m].class == Array

a = []
@json[@name][m].each do |slice|
jm = JsonModel.new
jm.init slice.keys.first, slice
a << jm
end
return a
end

private

def init(name, json)
@name = name
@json = json
end

def get_data(path)
HTTP.get(path).then do |resp|
puts "got #{path}"
init(resp.json.keys.first, resp.json)
mutate @loaded = true
end.fail do |resp|
puts resp.json
alert "failed to get #{path}"
end
end
end
```

To use my `Workout` model in my client code, I load it in the `before_mount` lifecycle method:

```ruby
@workout = JsonModel.new(path: "/api/workout/#{id}.json")
```

Then in my Component I can use the response just like a model:

```ruby
if @workout.loaded?
H1 { @workout.name }
@workout.primary_exercises.each do |pe|
H2 { pe.name }
pe.exsets.each do |set|
H3 { .... }
end
end
else
P { "Loading..." }
end
```

And TaDa! ORM-like wrapping of JSON responses :-)

貢獻指南

開啟貢獻指南

評估

這個 Issue 還沒有評估資料。

把新 issue 寄到你的電子郵件信箱

精選適合新手參與的 GitHub issue 摘要。