roda-contrib 0.2.0 → 0.3.0

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
  SHA1:
3
- metadata.gz: 02c8c0cc04c5707343034068120ae9b8ed70cf90
4
- data.tar.gz: af47f15ba933ef6a3c0dc06219a6e1a0a1f2d7dd
3
+ metadata.gz: 98460898008a6d9fa6213b1d5b77f524a9eac86c
4
+ data.tar.gz: d58b7165a2d2ba77371768f928ac5acfea445f26
5
5
  SHA512:
6
- metadata.gz: 309ecb8592c05a838d39658cfad67c1d121646cc64ad0d7c54ade11ce5fadfacfb395743ddffe38d76a4de02287a401488d40c48abb965182b7cff2a59caeaa8
7
- data.tar.gz: 5b9127a06f6c68c9cb3050713c923263e4cae9fdb01477b05ad1cc2238c4fc0a046a2ecfea7f2a1ef97ce090bb16f02d6a75023240b0071baf8acc8a1ba183a2
6
+ metadata.gz: 3c879c14dc8f0856ab6b088fd61bae9bd57e51a8704e052f1c8123e1f580b8d934462e3542cfd4ad2641fccff8c187d49701d0040264761312c07e366fb501da
7
+ data.tar.gz: ec7b4e96e1e8fecf2ad83e4916033083d71d61f98c28fb71ee46ce194169e000efdbbcef898c38d0caf819f4ff79bb157b3bf275ef83ae71fe268de94cc84c93
data/CHANGELOG CHANGED
@@ -1,3 +1,6 @@
1
+ = 0.3.0(2017-6-7)
2
+ * add json_api plugin
3
+
1
4
  = 0.2.0(2017-6-6)
2
5
  * add load_all plugin
3
6
  * add multi_dispatch plugin
data/README.md CHANGED
@@ -21,11 +21,12 @@ Or install it yourself as:
21
21
 
22
22
  ## Usage
23
23
 
24
- Currently, the roda-contrib gem ships 3 plugins:
24
+ Currently, the roda-contrib gem ships 4 plugins:
25
25
 
26
26
  * load\_all
27
27
  * multi\_dispatch
28
28
  * csrf
29
+ * json\_api
29
30
 
30
31
  When loading plugins from this gem, you should append the 'contrib' to the
31
32
  symbol to load it. For example, if you want to use the multi\_dispatch plugin:
@@ -0,0 +1,6 @@
1
+ = 0.3.0 release note
2
+
3
+ == New Feature
4
+
5
+ * add _contrib_json_api_ plugin.
6
+ This plugin offers intergration with the jsonapi-rb gem
@@ -0,0 +1,177 @@
1
+ require 'rack/parser'
2
+ require 'jsonapi/serializable'
3
+ require 'jsonapi/deserializable'
4
+
5
+ module RodaContrib
6
+ module Plugins
7
+ # This plugin intergrate Roda with the jsonapi-rb gem. It offers a simple
8
+ # interface to users, but a bit opinioned.
9
+ #
10
+ # === Prerequest
11
+ # To use this plugin, you should install the +rack-parser+ and the
12
+ # +jsonapi-rb+ gem first.
13
+ #
14
+ # === Configuration
15
+ # The +contrib_json_api+ plugin will not complain if you offer nothing to
16
+ # configure the boring middleware.
17
+ #
18
+ # App.plugin :contrib_json_api
19
+ #
20
+ # You can use options pass to this plugin to configure the +Rack::Parser+.
21
+ #
22
+ # App.plugin :contrib_json_api, {
23
+ # :parser => { 'application/json' => proc { |data| MultiJSON.parse(data) } },
24
+ # :handler => { 'application/json' => proc { |e, t| [400, {'Content-Type' => t}, ["broke"]] }
25
+ # }
26
+ #
27
+ # Check the rack-parser documentation for more information
28
+ #
29
+ # Optionally, if you already have the rack-parser middleware and don't want
30
+ # to setup the middleware again, you can use +:skip_middleware+ option to
31
+ # do so:
32
+ #
33
+ # App.plugin :contrib_json_api, skip_middleware: true
34
+ #
35
+ # === Usage
36
+ # The +contrib_json_api+ plugin only expose three methods to use.
37
+ #
38
+ # the +represent+ method is used for rendering the resources
39
+ # represent rc, with: SerializableRc
40
+ #
41
+ # the +represent_err+ method is used for rendering the errors, if you don't
42
+ # offer a title, 'model field validation error' will be used as title. the
43
+ # status is default to 400.
44
+ #
45
+ # represent rc.errors, title: 'gateway not reachable', status: 500
46
+ #
47
+ # the +json_params+ method is used to get the parsed data from the request
48
+ # body.
49
+ #
50
+ # a lot of options could be applied to customize the +represent+ and
51
+ # +represent_err+ method. Checkout the official jsonapi-rb documentation:
52
+ # https://jsonapi-rb.org/guides
53
+ #
54
+ # === Exmaple
55
+ # class App < Roda
56
+ # plugin :contrib_json_api
57
+ #
58
+ # route do |r|
59
+ # r.on 'product' do
60
+ # r.is :id do |id|
61
+ # @rc = Product.with_pk(id.to_i)
62
+ #
63
+ # r.get do
64
+ # represent @rc, with: SerializableProduct
65
+ # end
66
+ #
67
+ # r.post do
68
+ # @rc.set_fields(json_params)
69
+ # if @rc.valid?
70
+ # @rc.save_changes
71
+ # represent @rc, with: SerializableProduct
72
+ # else
73
+ # represent_err @rc.errors
74
+ # end
75
+ # end
76
+ # end
77
+ # end
78
+ # end
79
+ # end
80
+ module JsonApi
81
+ CONTENT_TYPE = 'Content-Type'.freeze
82
+
83
+ JSONAPI_MEDIA_TYPE = 'application/vnd.api+json'.freeze
84
+
85
+ RACK_PASER_PARAMS = 'rack.parser.result'.freeze
86
+
87
+ DEFAULT_JSON_PARSER = proc { |data| JSON.parse(data) }
88
+
89
+ DEFAULT_ERROR_HANDLER = proc { |err, type|
90
+ e = [SerializableError.new(title: 'invalid JSON doc', err: err, status: 400)]
91
+ msg = JSONAPI::Serializable::ErrorRenderer.render(e, {})
92
+ [400, { CONTENT_TYPE => JSONAPI_MEDIA_TYPE }, [msg]]
93
+ }
94
+
95
+ DEFAULT_RACK_PARSER_OPTS = {
96
+ parsers: { JSONAPI_MEDIA_TYPE => DEFAULT_JSON_PARSER },
97
+ handlers: { JSONAPI_MEDIA_TYPE => DEFAULT_ERROR_HANDLER }
98
+ }.freeze
99
+
100
+ class SerializableError < JSONAPI::Serializable::Error
101
+ title { @title }
102
+ status { @status }
103
+ detail { @err.is_a?(Exception) ? @err.message : @err }
104
+ end
105
+
106
+ def self.configure(app, opts={})
107
+ return if opts[:skip_middleware]
108
+
109
+ app.instance_exec do
110
+ @middleware.each do |(mid, *rest), _|
111
+ if mid.equal?(::Rack::Parser)
112
+ rest[0].merge!(opts)
113
+ build_rack_app
114
+ return
115
+ end
116
+ end
117
+
118
+ if opts.empty?
119
+ use ::Rack::Parser, DEFAULT_RACK_PARSER_OPTS.dup
120
+ return
121
+ elsif opts[:parsers].nil? || opts[:pasers][JSONAPI_MEDIA_TYPE].nil?
122
+ opts[:parsers] = { JSONAPI_MEDIA_TYPE => DEFAULT_JSON_PARSER }
123
+ use ::Rack::Parser, opts
124
+ end
125
+ end
126
+ end
127
+
128
+ module InstanceMethods
129
+ def represent(rc, options={})
130
+ options[:class] = options.delete(:with) unless options[:class]
131
+ rendered = JSONAPI::Serializable::Renderer.render(rc, options)
132
+
133
+ response[CONTENT_TYPE] = JSONAPI_MEDIA_TYPE
134
+ response.write(rendered)
135
+ request.halt
136
+ end
137
+
138
+ def represent_err(errs, options={})
139
+ t = options.delete(:title) || 'model field validation failed'
140
+ s = options.delete[:status] || 400
141
+ e = if errs.resond_to? :full_messages
142
+ errs.full_messages.map { |err| SerializableError.new(title: t, err: err, status: s) }
143
+ else
144
+ [SerializableError.new(title: t, err: errs, status: s)]
145
+ end
146
+ rendered = JSONAPI::Serializable::ErrorRenderer.render(e, options)
147
+
148
+ response.status = s
149
+ response[CONTENT_TYPE] = JSONAPI_MEDIA_TYPE
150
+ response.write(rendered)
151
+ request.halt
152
+ end
153
+
154
+ def json_params
155
+ parse_jsonapi env[RACK_PASER_PARAMS]
156
+ end
157
+
158
+ private
159
+ def parse_jsonapi(rc, options={})
160
+ klass = options[:with] || JSONAPI::Deserializable::Resource
161
+ begin
162
+ result = klass.call(rc)
163
+ rescue JSONAPI::Parser::InvalidDocument => e
164
+ errs = [SerializableError.new(title: 'Invalid JSONAPI doc', error: e, status: 400)]
165
+ rendered = JSONAPI::Serializable::ErrorRenderer.render(errs, {})
166
+
167
+ response.status = 400
168
+ response[CONTENT_TYPE] = JSONAPI_MEDIA_TYPE
169
+ response.write(rendered)
170
+ request.halt
171
+ end
172
+ result
173
+ end
174
+ end
175
+ end
176
+ end
177
+ end
@@ -1,3 +1,3 @@
1
1
  module RodaContrib
2
- VERSION = '0.2.0'.freeze
2
+ VERSION = '0.3.0'.freeze
3
3
  end
@@ -0,0 +1,3 @@
1
+ require 'roda/contrib/plugins/json_api'
2
+
3
+ Roda::RodaPlugins.register_plugin(:contrib_json_api, RodaContrib::Plugins::JsonApi)
data/roda-contrib.gemspec CHANGED
@@ -29,6 +29,8 @@ Gem::Specification.new do |spec|
29
29
  spec.add_development_dependency 'yard'
30
30
  spec.add_development_dependency 'rack-test'
31
31
  spec.add_development_dependency 'rack_csrf'
32
+ spec.add_development_dependency 'rack-parser'
33
+ spec.add_development_dependency 'jsonapi-rb'
32
34
 
33
35
  spec.add_dependency 'roda', '~> 2.0'
34
36
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: roda-contrib
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - luciusgone
@@ -94,6 +94,34 @@ dependencies:
94
94
  - - ">="
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rack-parser
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: jsonapi-rb
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
97
125
  - !ruby/object:Gem::Dependency
98
126
  name: roda
99
127
  requirement: !ruby/object:Gem::Requirement
@@ -126,14 +154,17 @@ files:
126
154
  - Rakefile
127
155
  - doc/release_notes/0_1_0.rdoc
128
156
  - doc/release_notes/0_2_0.rdoc
157
+ - doc/release_notes/0_3_0.rdoc
129
158
  - lib/roda/contrib.rb
130
159
  - lib/roda/contrib/action.rb
131
160
  - lib/roda/contrib/action/dispatchable.rb
132
161
  - lib/roda/contrib/plugins/csrf.rb
162
+ - lib/roda/contrib/plugins/json_api.rb
133
163
  - lib/roda/contrib/plugins/load_all.rb
134
164
  - lib/roda/contrib/plugins/multi_dispatch.rb
135
165
  - lib/roda/contrib/version.rb
136
166
  - lib/roda/plugins/contrib_csrf.rb
167
+ - lib/roda/plugins/contrib_json_api.rb
137
168
  - lib/roda/plugins/contrib_load_all.rb
138
169
  - lib/roda/plugins/contrib_multi_dispatch.rb
139
170
  - roda-contrib.gemspec