weasel_diesel 1.2.2 → 1.3.0

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
  SHA1:
3
- metadata.gz: 76695321f4c49f270788ffc3743ed2ca1668c774
4
- data.tar.gz: 567a24d4bac5544bb3d7d89df0fea271afa273b0
3
+ metadata.gz: 66a37bea5e3454ee526eb9281ae4e98ce75945db
4
+ data.tar.gz: 29e993efacc6873959b347b4141e93f1cbb85738
5
5
  SHA512:
6
- metadata.gz: 9fc1388dc4e52f7ec91b518f26319d85e4b14f92bf586f036cc53e920ee9017cbffeda361b4b0c786aa06d5fde37f79ba01df0fabbc2ae4e68c4be4688277377
7
- data.tar.gz: be2eb8217e849bb8b4bdd9b2fe818d4c1ca7928750da35271f707f797636ec8a18722e51c2c83962e10660cb55c45aa6ef7603fa443d89488024c77875af0af5
6
+ metadata.gz: a04322e780856bf69f40e3311cfd3fdd00c4bbabb492b30b8822e99537dac179a7d1929040c3710313df8aa753803415a5465387fe58789a3287f3187477f31d
7
+ data.tar.gz: 473aa3828ff0474ecafad534d986353d67b415fbdcbb9b358ddc461146538ae396e4523882db54ef9705954dd2c605c40db7385976c9ac3ca9235b417eb96e16
@@ -1,6 +1,5 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 1.8.7
4
3
  - 1.9.3
5
4
  - 2.0.0
6
5
  - jruby
@@ -3,10 +3,16 @@
3
3
  All changes can be seen on GitHub and git tags are used to isolate each
4
4
  release.
5
5
 
6
- 1.2.2:
6
+ ## 1.3.0
7
+ * Move documentation generation from wd_sinatra into Weasel-Diesel.
8
+ * Drop support for Ruby 1.8.7.
9
+ * Fix rspec deprecation: `expect { }.not_to raise_error(SpecificErrorClass)`
10
+ * DSL now only extends the top level main object.
11
+
12
+ ## 1.2.2:
7
13
  * Added support for anonymous top level arrays.
8
14
 
9
- *1.2.1*:
15
+ ## 1.2.1:
10
16
 
11
17
  * Modified the way an empty string param is cast/verified. If a param is
12
18
  passed as an empty string but the param isn't specified as a string, the
@@ -15,8 +21,7 @@ an integer param, the cast params will look like that: `{'id' => nil}`,
15
21
  however if `name` is a string param and `{'name' => ''}` is passed, the
16
22
  value won't be nullified.
17
23
 
18
-
19
- **1.2.0**:
24
+ ## 1.2.0:
20
25
 
21
26
  * All service urls are now stored with a prepended slash (if not defined
22
27
  with one). `WDList.find(<verb>, <url>)` will automatically find the
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
- source "http://rubygems.org"
1
+ source "https://rubygems.org"
2
2
 
3
3
  # Specify your gem's dependencies in weasel_diesel.gemspec
4
4
  gemspec
data/LICENSE CHANGED
@@ -1,4 +1,6 @@
1
- Copyright (c) 2012 Matt Aimonetti
1
+ MIT: http://mattaimonetti.mit-license.org
2
+
3
+ Copyright (c) 2013 Matt Aimonetti
2
4
 
3
5
  Permission is hereby granted, free of charge, to any person obtaining
4
6
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![CI Build Status](https://secure.travis-ci.org/mattetti/Weasel-Diesel.png?branch=master)](http://travis-ci.org/mattetti/Weasel-Diesel)
4
4
 
5
- Weasel Diesel is a DSL to describe and document your web API.
5
+ Weasel Diesel is a DSL to describe and document your web API.
6
6
 
7
7
  To get you going quickly, see the [generator for sinatra apps](https://github.com/mattetti/wd-sinatra).
8
8
  The wd_sinatra gem allows you to generate the structure for a sinatra app using Weasel Diesel and with lots of goodies.
@@ -34,8 +34,8 @@ describe_service "/hello_world" do |service|
34
34
 
35
35
  # DOCUMENTATION
36
36
  service.documentation do |doc|
37
- doc.overall "This service provides a simple hello world implementation example."
38
- doc.example "<code>curl -I 'http://localhost:9292/hello_world?name=Matt'</code>"
37
+ doc.overall "This service provides a simple hello world implementation example."
38
+ doc.example "<code>curl -I 'http://localhost:9292/hello_world?name=Matt'</code>"
39
39
  end
40
40
 
41
41
  # ACTION/IMPLEMENTATION (specific to the sinatra app example, can
@@ -55,34 +55,34 @@ Or a more complex example using XML:
55
55
  describe_service "/wsdsl/test.xml" do |service|
56
56
  service.formats :xml, :json
57
57
  service.http_verb :get
58
-
58
+
59
59
  # INPUT
60
60
  service.params do |p|
61
61
  p.string :framework, :in => SpecOptions, :null => false, :required => true
62
-
63
- p.datetime :timestamp,
64
- :default => Time.now,
62
+
63
+ p.datetime :timestamp,
64
+ :default => Time.now,
65
65
  :doc => "The test framework used, could be one of the two following: #{SpecOptions.join(", ")}."
66
66
 
67
67
  p.string :alpha, :in => ['a', 'b', 'c']
68
- p.string :version,
68
+ p.string :version,
69
69
  :null => false,
70
70
  :doc => "The version of the framework to use."
71
-
71
+
72
72
  p.integer :num, :minvalue => 42
73
73
  p.namespace :user do |user|
74
74
  user.integer :id, :required => :true
75
75
  end
76
76
  end
77
-
77
+
78
78
  # OUTPUT
79
- # the response contains a list of player creation ratings each object in the list
79
+ # the response contains a list of player creation ratings each object in the list
80
80
  service.response do |response|
81
81
  response.element(:name => "player_creation_ratings") do |e|
82
82
  e.attribute :id => :integer, :doc => "id doc"
83
83
  e.attribute :is_accepted => :boolean, :doc => "is accepted doc"
84
84
  e.attribute :name => :string, :doc => "name doc"
85
-
85
+
86
86
  e.array :name => 'player_creation_rating', :type => 'PlayerCreationRating' do |a|
87
87
  a.attribute :comments => :string, :doc => "comments doc"
88
88
  a.attribute :player_id => :integer, :doc => "player_id doc"
@@ -91,14 +91,14 @@ Or a more complex example using XML:
91
91
  end
92
92
  end
93
93
  end
94
-
94
+
95
95
  # DOCUMENTATION
96
96
  service.documentation do |doc|
97
97
  # doc.overall <markdown description text>
98
98
  doc.overall <<-DOC
99
99
  This is a test service used to test the framework.
100
100
  DOC
101
-
101
+
102
102
  # doc.example <markdown text>
103
103
  doc.example <<-DOC
104
104
  The most common way to use this service looks like that:
@@ -178,7 +178,7 @@ This is particuliarly frequent when using Rails for instance.
178
178
 
179
179
  ```ruby
180
180
  service.params do |param|
181
- param.string :framework,
181
+ param.string :framework,
182
182
  :in => ['RSpec', 'Bacon'],
183
183
  :required => true,
184
184
  :doc => "The test framework used, could be one of the two following: #{WeaselDieselSpecOptions.join(", ")}."
@@ -240,26 +240,26 @@ end
240
240
 
241
241
  Consider the following JSON response:
242
242
 
243
- ```
244
- { people: [
245
- {
246
- id : 1,
243
+ ```
244
+ { people: [
245
+ {
246
+ id : 1,
247
247
  online : false,
248
- created_at : 123123123123,
248
+ created_at : 123123123123,
249
249
  team : {
250
250
  id : 1231,
251
251
  score : 123.32
252
252
  }
253
- },
254
- {
255
- id : 2,
253
+ },
254
+ {
255
+ id : 2,
256
256
  online : true,
257
- created_at : 123123123123,
257
+ created_at : 123123123123,
258
258
  team: {
259
259
  id : 1233,
260
260
  score : 1.32
261
261
  }
262
- },
262
+ },
263
263
  ] }
264
264
  ```
265
265
 
@@ -287,7 +287,7 @@ Nodes/elements can also use some meta-attributes including:
287
287
  * `key` : refers to an attribute name that is key to this object
288
288
  * `type` : refers to the type of object described, valuable when using JSON across OO based apps.
289
289
 
290
- JSON response validation can be done using an optional module as shown in
290
+ JSON response validation can be done using an optional module as shown in
291
291
  (spec/json_response_verification_spec.rb)[https://github.com/mattetti/Weasel-Diesel/blob/master/spec/json_response_verification_spec.rb].
292
292
  The goal of this module is to help automate API testing by
293
293
  validating the data structure of the returned object.
@@ -312,7 +312,7 @@ end
312
312
  ```
313
313
 
314
314
  Actual output:
315
- ```
315
+ ```
316
316
  {"name": "Example"}
317
317
  ```
318
318
 
@@ -328,42 +328,20 @@ describe_service "example" do |service|
328
328
  end
329
329
  ```
330
330
 
331
+ ## Documentation generation
331
332
 
332
- ## Test Suite & Dependencies
333
-
334
- The test suite requires Ruby 1.9.* along with `RSpec`, `Rack`, and `Sinatra` gems.
335
-
336
- ## Usage with Ruby 1.8
337
-
338
- This library prioritizes Ruby 1.9, but 1.8 support was added
339
- via the backports library and some tweaks.
340
-
341
- However, because Ruby 1.8 hashes do not preserve insert order, the following syntax
342
- **will not work**:
343
-
344
- ``` ruby
345
- service.response do |response|
346
- response.element(:name => "player_creation_ratings") do |e|
347
- e.attribute :id => :integer, :doc => "id doc"
348
- e.attribute :is_accepted => :boolean, :doc => "is accepted doc"
349
- e.attribute :name => :string, :doc => "name doc"
350
- end
351
- end
333
+ ```bash
334
+ $ weasel_diesel generate_doc <SOURCE_PATH> <DESTINATION_PATH>
352
335
  ```
353
336
 
354
- Instead, this alternate syntax must be used:
337
+ To generate documentation for the APIs you created in the api folder. The
338
+ source path is the location of your ruby files. The destination is optional,
339
+ 'doc' is the default.
355
340
 
356
- ``` ruby
357
- service.response do |response|
358
- response.element(:name => "player_creation_ratings") do |e|
359
- e.integer :id, :doc => "id doc"
360
- e.boolean :is_accepted, :doc => "is accepted doc"
361
- e.string :name, :doc => "name doc"
362
- end
363
- end
364
- ```
365
341
 
366
- The end results are identical.
342
+ ## Test Suite & Dependencies
343
+
344
+ The test suite requires `rspec`, `rack`, and `sinatra` gems.
367
345
 
368
346
  ## Copyright
369
347
 
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ require "rubygems"
3
+ require_relative "../lib/weasel_diesel/cli"
4
+
5
+ WeaselDiesel::CLI.start
@@ -1,5 +1,5 @@
1
1
  require 'forwardable'
2
- require File.expand_path('./../params_verification', File.dirname(__FILE__))
2
+ require_relative '../params_verification'
3
3
 
4
4
  # Base code shared by all service controllers
5
5
  # This allows us to share code between controllers
@@ -10,44 +10,44 @@ require File.expand_path('./../params_verification', File.dirname(__FILE__))
10
10
  # @author Matt Aimonetti
11
11
  class SinatraServiceController
12
12
  extend Forwardable
13
-
13
+
14
14
  # The service controller might be loaded outside of a Sinatra App
15
15
  # in this case, we don't need to load the helpers
16
16
  if Object.const_defined?(:Sinatra)
17
- include Sinatra::Helpers
17
+ include Sinatra::Helpers
18
18
  end
19
-
19
+
20
20
  class AuthenticationFailed < StandardError; end
21
-
21
+
22
22
  # @return [WeaselDiesel] The service served by this controller
23
23
  # @api public
24
24
  attr_reader :service
25
-
25
+
26
26
  # @return [Sinatra::Application]
27
27
  # @api public
28
28
  attr_reader :app
29
-
29
+
30
30
  # @return [Hash]
31
31
  # @api public
32
32
  attr_reader :env
33
-
33
+
34
34
  # @return [Sinatra::Request]
35
35
  # @see http://rubydoc.info/github/sinatra/sinatra/Sinatra/Request
36
36
  # @api public
37
37
  attr_reader :request
38
-
38
+
39
39
  # @return [Sinatra::Response]
40
40
  # @see http://rubydoc.info/github/sinatra/sinatra/Sinatra/Response
41
41
  # @api public
42
42
  attr_reader :response
43
-
43
+
44
44
  # @return [Hash]
45
45
  # @api public
46
46
  attr_accessor :params
47
-
47
+
48
48
  # @param [Sinatra::Application] app The Sinatra app used as a reference and to access request params
49
49
  # @param [WeaselDiesel] service The service served by this controller
50
- # @raise [ParamError, NoParamsDefined, MissingParam, UnexpectedParam, InvalidParamType, InvalidParamValue]
50
+ # @raise [ParamError, NoParamsDefined, MissingParam, UnexpectedParam, InvalidParamType, InvalidParamValue]
51
51
  # If the params don't validate one of the {ParamsVerification} errors will be raised.
52
52
  # @api public
53
53
  def initialize(app, service)
@@ -56,10 +56,10 @@ class SinatraServiceController
56
56
  @request = app.request
57
57
  @response = app.response
58
58
  @service = service
59
-
59
+
60
60
  # raises an exception if the params are not valid
61
- # otherwise update the app params with potentially new params (using default values)
62
- # note that if a type if mentioned for a params, the object will be cast to this object type
61
+ # otherwise update the app params with potentially new params (using default values)
62
+ # note that if a type if mentioned for a params, the object will be cast to this object type
63
63
  @params = app.params = ParamsVerification.validate!(app.params, service.defined_params)
64
64
 
65
65
  # Authentication check
@@ -67,7 +67,7 @@ class SinatraServiceController
67
67
  raise AuthenticationFailed unless logged_in?
68
68
  end
69
69
  end
70
-
70
+
71
71
 
72
72
  # Forwarding some methods to the underlying app object
73
73
  def_delegators :app, :settings, :halt, :compile_template, :session
@@ -76,5 +76,5 @@ class SinatraServiceController
76
76
  def logged_in?
77
77
  !session[:player_id].nil?
78
78
  end
79
-
79
+
80
80
  end
@@ -24,7 +24,7 @@ class WeaselDiesel
24
24
  # Lists all top level simple elements and array elements.
25
25
  #
26
26
  # @return [Array<WeaselDiesel::Response::Element, WeaselDiesel::Response::Array>]
27
- def nodes
27
+ def nodes
28
28
  elements + arrays
29
29
  end
30
30
 
@@ -153,6 +153,7 @@ class WeaselDiesel
153
153
  @type = type
154
154
  @attributes = []
155
155
  @meta_attributes = []
156
+ @elements = []
156
157
  @vectors = []
157
158
  @key = nil
158
159
  # we don't need to initialize the nested elements, by default they should be nil
@@ -164,7 +165,7 @@ class WeaselDiesel
164
165
  # @option opts [String, Symbol] attribute_name The name of the attribute, the value being the type
165
166
  # @option opts [String, Symbol] :doc The attribute documentation
166
167
  # @option opts [String, Symbol] :mock An optional mock value used by service related tools
167
- #
168
+ #
168
169
  # @example Creation of a response attribute called 'best_lap_time'
169
170
  # service.response do |response|
170
171
  # response.element(:name => "my_stats", :type => 'Leaderboard') do |e|
@@ -174,13 +175,13 @@ class WeaselDiesel
174
175
  #
175
176
  # @return [Array<WeaselDiesel::Response::Attribute>]
176
177
  # @api public
177
- def attribute(opts, extra_opts={})
178
- raise ArgumentError unless opts.is_a?(Hash) && extra_opts.is_a?(Hash)
179
- new_attribute = Attribute.new(opts, extra_opts)
178
+ def attribute(opts)
179
+ raise ArgumentError unless opts.is_a?(Hash)
180
+ new_attribute = Attribute.new(opts)
180
181
  @attributes << new_attribute
181
182
  # document the attribute if description available
182
183
  # we might want to have a placeholder message when a response attribute isn't defined
183
- if opts.merge!(extra_opts).has_key?(:doc)
184
+ if opts.has_key?(:doc)
184
185
  @doc.attribute(new_attribute.name, opts[:doc])
185
186
  end
186
187
  @attributes
@@ -191,7 +192,7 @@ class WeaselDiesel
191
192
  # @param [Hash] opts An element's attribute options
192
193
  # @option opts [String, Symbol] attribute_name The name of the attribute, the value being the type
193
194
  # @option opts [String, Symbol] :mock An optional mock value used by service related tools
194
- #
195
+ #
195
196
  # @example Creation of a response attribute called 'best_lap_time'
196
197
  # service.response do |response|
197
198
  # response.element(:name => "my_stats", :type => 'Leaderboard') do |e|
@@ -212,12 +213,12 @@ class WeaselDiesel
212
213
  # Defines an array aka vector of elements.
213
214
  #
214
215
  # @param [String, Symbol] name The name of the array element.
215
- # @param [String, Symbol] type Optional type information, useful to store the represented
216
+ # @param [String, Symbol] type Optional type information, useful to store the represented
216
217
  # object types for instance.
217
218
  #
218
219
  # @param [Proc] &block
219
220
  # A block to execute against the newly created array.
220
- #
221
+ #
221
222
  # @example Defining an element array called 'player_creation_rating'
222
223
  # element.array 'player_creation_rating', 'PlayerCreationRating' do |a|
223
224
  # a.attribute :comments => :string
@@ -227,7 +228,7 @@ class WeaselDiesel
227
228
  # end
228
229
  # @yield [Vector] the newly created array/vector instance
229
230
  # @see Element#initialize
230
- #
231
+ #
231
232
  # @return [Array<WeaselDiesel::Response::Vector>]
232
233
  # @api public
233
234
  def array(name, type=nil)
@@ -299,7 +300,7 @@ class WeaselDiesel
299
300
  # @param [Symbol, String] name the name of the attribute.
300
301
  # @param [Hash] opts the attribute options.
301
302
  def string(name=nil, opts={})
302
- attribute({name => :string}, opts)
303
+ attribute({name => :string}.merge(opts))
303
304
  end
304
305
 
305
306
  # Shortcut to create a string attribute
@@ -307,7 +308,7 @@ class WeaselDiesel
307
308
  # @param [Symbol, String] name the name of the attribute.
308
309
  # @param [Hash] opts the attribute options.
309
310
  def integer(name=nil, opts={})
310
- attribute({name => :integer}, opts)
311
+ attribute({name => :integer}.merge(opts))
311
312
  end
312
313
 
313
314
  # Shortcut to create a string attribute
@@ -315,7 +316,7 @@ class WeaselDiesel
315
316
  # @param [Symbol, String] name the name of the attribute.
316
317
  # @param [Hash] opts the attribute options.
317
318
  def float(name=nil, opts={})
318
- attribute({name => :float}, opts)
319
+ attribute({name => :float}.merge(opts))
319
320
  end
320
321
 
321
322
  # Shortcut to create a string attribute
@@ -323,7 +324,7 @@ class WeaselDiesel
323
324
  # @param [Symbol, String] name the name of the attribute.
324
325
  # @param [Hash] opts the attribute options.
325
326
  def boolean(name=nil, opts={})
326
- attribute({name => :boolean}, opts)
327
+ attribute({name => :boolean}.merge(opts))
327
328
  end
328
329
 
329
330
  # Shortcut to create a string attribute
@@ -331,20 +332,21 @@ class WeaselDiesel
331
332
  # @param [Symbol, String] name the name of the attribute.
332
333
  # @param [Hash] opts the attribute options.
333
334
  def datetime(name=nil, opts={})
334
- attribute({name => :datetime}, opts)
335
+ attribute({name => :datetime}.merge(opts))
335
336
  end
336
337
 
337
338
  # Converts an element into a hash representation
338
339
  #
340
+ # @param [Boolean] root_node true if this node has no parents.
339
341
  # @return [Hash] the element attributes formated in a hash
340
- def to_hash
342
+ def to_hash(root_node=true)
341
343
  attrs = {}
342
344
  attributes.each{ |attr| attrs[attr.name] = attr.type }
343
- elements.each{ |el| attrs[el.name] = el.to_hash } if elements
345
+ (vectors + elements).each{ |el| attrs[el.name] = el.to_hash(false) }
344
346
  if self.class == Vector
345
- name ? {name => [attrs]} : [attrs]
347
+ (root_node && name) ? {name => [attrs]} : [attrs]
346
348
  else
347
- name ? {name => attrs} : attrs
349
+ (root_node && name) ? {name => attrs} : attrs
348
350
  end
349
351
  end
350
352
 
@@ -429,17 +431,14 @@ class WeaselDiesel
429
431
  # name, type, doc, type
430
432
  #
431
433
  # @param [Hash, Array] o_params
432
- # @param [Hash] o_extra_params A hash with extra params passed, needed to support Ruby 1.8 :(
433
434
  #
434
435
  # @api public
435
- def initialize(o_params, o_extra_params={})
436
+ def initialize(o_params)
436
437
  params = o_params.dup
437
- extra_params = o_extra_params.dup
438
438
  if params.is_a?(Hash)
439
439
  @name, @type = params.shift
440
440
  @doc = params.delete(:doc) if params.has_key?(:doc)
441
- @doc ||= extra_params.delete(:doc) if extra_params.has_key?(:doc)
442
- @opts = params.merge!(extra_params)
441
+ @opts = params
443
442
  elsif params.is_a?(Array)
444
443
  @name = params.shift
445
444
  @type = params.shift
@@ -1,9 +1,9 @@
1
- require File.expand_path('inflection', File.dirname(__FILE__))
2
- require File.expand_path('params', File.dirname(__FILE__))
3
- require File.expand_path('response', File.dirname(__FILE__))
4
- require File.expand_path('documentation', File.dirname(__FILE__))
5
- require File.expand_path('ws_list', File.dirname(__FILE__))
6
- require File.expand_path('kernel_ext', File.dirname(__FILE__))
1
+ require_relative 'inflection'
2
+ require_relative 'params'
3
+ require_relative 'response'
4
+ require_relative 'documentation'
5
+ require_relative 'ws_list'
6
+ require 'weasel_diesel/dsl'
7
7
 
8
8
  # WeaselDiesel offers a web service DSL to define web services,
9
9
  # their params, http verbs, formats expected as well as the documentation
@@ -386,6 +386,12 @@ class WeaselDiesel
386
386
  end
387
387
  end
388
388
 
389
+ # Left for generators to implement. It's empty because WD itself isn't concerned
390
+ # with implementation, but needs it defined so doc generation can read WD web
391
+ # service definitions.
392
+ def implementation(&block)
393
+ end
394
+
389
395
  SERVICE_ROOT_REGEXP = /(.*?)[\/\(\.]/
390
396
  SERVICE_ACTION_REGEXP = /[\/\(\.]([a-z0-9_]+)[\/\(\.\?]/i
391
397
  SERVICE_RESTFUL_SHOW_REGEXP = /\/:[a-z0-9_]+\.\w{3}$/
@@ -422,6 +428,4 @@ class WeaselDiesel
422
428
  end
423
429
  end
424
430
 
425
-
426
-
427
431
  end
@@ -0,0 +1,60 @@
1
+ require "thor"
2
+ require_relative "../weasel_diesel"
3
+
4
+ class WeaselDiesel
5
+ class CLI < Thor
6
+ include Thor::Actions
7
+ namespace :weasel_diesel
8
+
9
+ desc "generate_doc SOURCE_PATH DESTINATION_PATH", "Generate HTML documentation for WeaselDiesel web services"
10
+ def generate_doc(source_path, destination_path="doc")
11
+ api_files = Dir.glob(File.join(destination_root, source_path, "**", "*.rb"))
12
+ if api_files.empty?
13
+ puts "No ruby files in source_path: #{File.join(destination_root, source_path)}"
14
+ return
15
+ end
16
+ api_files.each do |api|
17
+ require api
18
+ end
19
+
20
+ require 'fileutils'
21
+ destination = File.join(destination_root, destination_path)
22
+ FileUtils.mkdir_p(destination) unless File.exist?(destination)
23
+ File.open("#{destination}/index.html", "w"){|f| f << doc_template.result(binding)}
24
+ puts "Documentation available there: #{destination}/index.html"
25
+ `open #{destination}/index.html` if RUBY_PLATFORM =~ /darwin/ && !ENV['DONT_OPEN']
26
+ end
27
+
28
+ private
29
+
30
+ def response_element_html(el)
31
+ response_element_template.result(binding)
32
+ end
33
+
34
+ def input_params_html(required, optional)
35
+ input_params_template.result(binding)
36
+ end
37
+
38
+ def input_params_template
39
+ file = resources.join '_input_params.erb'
40
+ ERB.new File.read(file)
41
+ end
42
+
43
+ def response_element_template
44
+ file = resources.join '_response_element.erb'
45
+ ERB.new File.read(file)
46
+ end
47
+
48
+ def doc_template
49
+ file = resources.join 'template.erb'
50
+ ERB.new File.read(file)
51
+ end
52
+
53
+ def resources
54
+ require 'pathname'
55
+ @resources ||= Pathname.new(File.join(File.dirname(__FILE__), 'doc_generator'))
56
+ end
57
+
58
+ end
59
+
60
+ end
@@ -0,0 +1,36 @@
1
+ <table class="table table-striped table-responsive">
2
+ <tdbody>
3
+ <% [["required", required], ["optional", optional]].each do |label, rules| %>
4
+ <% unless rules.empty? %>
5
+ <% rules.each do |rule| %>
6
+ <tr>
7
+ <td>
8
+ <strong><%= rule.name %></strong>
9
+ <br />
10
+ <em><%= label %></em>
11
+ </td>
12
+ <td>
13
+ <p>
14
+ <strong><%= rule.options[:type] || 'string' %></strong>
15
+
16
+ <% if desc = rule.doc %>
17
+ - <%= desc %>
18
+ <% end %>
19
+ </p>
20
+
21
+ <% if options = rule.options[:options] %>
22
+ <strong>Options:</strong> <%= options.join(', ') %>
23
+ <br />
24
+ <% end %>
25
+
26
+ <% if default = rule.options[:default] %>
27
+ <strong>Default:</strong> <%= default %>
28
+ <br />
29
+ <% end %>
30
+ </td>
31
+ </tr>
32
+ <% end %>
33
+ <% end %>
34
+ <% end %>
35
+ </tdbody>
36
+ </table>
@@ -0,0 +1,49 @@
1
+ <table class="table table-striped table-responsive">
2
+ <tdbody>
3
+ <% if el.name %>
4
+ <tr>
5
+ <td colspan="2">
6
+ <strong><%= el.name %></strong>
7
+ <% if false && el.is_a?(WeaselDiesel::Response::Vector) %>
8
+ <span class="response-array">(Array, these are the properties of each array item)</span>
9
+ <% end %>
10
+ </td>
11
+ </tr>
12
+ <% end %>
13
+ <% el.properties.each do |prop| %>
14
+ <tr>
15
+ <td>
16
+ <strong><%= prop.name %></strong>
17
+ <br />
18
+ <em>
19
+ <% if false && prop.opts && prop.opts.respond_to?(:[]) && prop.opts[:null] %>
20
+ required
21
+ <% else %>
22
+ optional
23
+ <% end %>
24
+ </em>
25
+ </td>
26
+ <td>
27
+ <p>
28
+ <strong><%= prop.type %></strong>
29
+ <% unless true && prop.doc.nil? or prop.doc.empty? %>
30
+ - <%= prop.doc %>
31
+ <% end %>
32
+ </p>
33
+ </td>
34
+ </tr>
35
+ <% end %>
36
+
37
+ <% if false && el.arrays %>
38
+ <% el.arrays.each do |e| %>
39
+ <%= response_element_html(e) %>
40
+ <% end %>
41
+ <% end %>
42
+
43
+ <% if false && el.elements %>
44
+ <% el.elements.each do |e| %>
45
+ <%= response_element_html(e) %>
46
+ <% end %>
47
+ <% end %>
48
+ </tdbody>
49
+ </table>
@@ -0,0 +1,120 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>API Documentation</title>
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <!-- Bootstrap -->
7
+ <link rel="stylesheet" href="https://netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css">
8
+ <!-- Optional theme -->
9
+ <link rel="stylesheet" href="https://netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap-theme.min.css">
10
+
11
+ <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
12
+ <!--[if lt IE 9]>
13
+ <script src="../../assets/js/html5shiv.js"></script>
14
+ <script src="../../assets/js/respond.min.js"></script>
15
+ <![endif]-->
16
+ </head>
17
+ <body>
18
+ <div class="container">
19
+ <div class="navbar navbar-default">
20
+ <div class="navbar-header">
21
+ <a class="navbar-brand" href="#">API Documentation</a>
22
+ </div>
23
+ <div class="navbar-collapse collapse">
24
+ <ul class="nav navbar-nav">
25
+ <li class="active"><a href="index.html">Home</a></li>
26
+ </ul>
27
+ </div>
28
+ </div>
29
+
30
+ <div class="row">
31
+ <div class="sidebar col-md-3 well">
32
+ <h5>Resources</h5>
33
+ <ul>
34
+ <% WSList.all.sort{|a,b| a.url <=> b.url}.each do |api| %>
35
+ <li><a href="#<%= "raw-#{api.verb}-#{api.url}" %>"><%= api.verb.upcase %> <%= api.url %></a></li>
36
+ <% end %>
37
+ </ul>
38
+ </div>
39
+ <div class="content col-md-9">
40
+ <div class="jumbotron">
41
+ <h1>API Documentation</h1>
42
+ <p>Resources are listed on the sidebar to the left.</p>
43
+ </div>
44
+
45
+ <% WSList.all.each do |api| %>
46
+ <div id="<%= "raw-#{api.verb}-#{api.url}" %>">
47
+ <div class="overall">
48
+ <h2><%= api.verb.upcase %> <%= '[SSL]' if api.ssl %> <%= api.url %></h2>
49
+ <% if api.auth_required %>
50
+ <span class='label label-danger'>Authentication required</span>
51
+ <% end %>
52
+
53
+ <% if api.doc.desc %>
54
+ <p>
55
+ <%= "#{api.doc.desc}" %>
56
+ </p>
57
+ <% end %>
58
+ </div>
59
+
60
+ <div class="parameters">
61
+ <h3>Parameters</h3>
62
+ <% if api.required_rules.any? || api.optional_rules.any? %>
63
+ <%= input_params_html(api.required_rules, api.optional_rules) %>
64
+ <% end %>
65
+
66
+ <ul class="list-unstyled">
67
+ <% api.params.namespaced_params.each do |params| %>
68
+ <li>
69
+ <strong><%= params.space_name.name %></strong>
70
+ <em>
71
+ (<%= params.space_name.null ? 'optional' : 'required' %>)
72
+ </em>
73
+ <%= input_params_html(params.list_required, params.list_optional) %>
74
+ </li>
75
+ <% end %>
76
+ </ul>
77
+ </div>
78
+
79
+ <div class="example-request">
80
+ <h3>Example Request</h3>
81
+ <% api.doc.examples.each do |example| %>
82
+ <pre class="example"><%= example %></pre>
83
+ <% end %>
84
+ </div>
85
+
86
+ <div class="response">
87
+ <% if api.response.nodes.any? %>
88
+ <h3>Response</h3>
89
+ <% if api.response.arrays %>
90
+ <span class="response-array">(Array, these are the properties of each array item)</span>
91
+ <% end %>
92
+
93
+ <% api.response.arrays.each do |array| %>
94
+ <%= response_element_html(array) %>
95
+ <% end %>
96
+ <% api.response.elements.each do |el| %>
97
+ <%= response_element_html(el) %>
98
+ <% end %>
99
+ <% end %>
100
+ <h4>Example</h4>
101
+ <pre><%= JSON.pretty_generate(JSON.parse(api.response.to_json)) %></pre>
102
+ </div>
103
+
104
+ </div>
105
+ <hr>
106
+ <% end %>
107
+
108
+ <footer>
109
+ <p>&copy; <%= Time.now.year %></p>
110
+ </footer>
111
+ </div>
112
+ </div>
113
+ </div>
114
+
115
+ <!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
116
+ <script src="https://code.jquery.com/jquery.js"></script>
117
+ <!-- Include all compiled plugins (below), or include individual files as needed -->
118
+ <script src="https://netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script>
119
+ </body>
120
+ </html>
@@ -0,0 +1,34 @@
1
+ # Extending the top level module to add some helpers
2
+ #
3
+ # @api public
4
+ class WeaselDiesel
5
+ module DSL
6
+ private
7
+
8
+ # Base DSL method called to describe a service
9
+ #
10
+ # @param [String] url The url of the service to add.
11
+ # @yield [WeaselDiesel] The newly created service.
12
+ # @return [Array] The services already defined
13
+ # @example Describing a basic service
14
+ # describe_service "hello-world.xml" do |service|
15
+ # # describe the service
16
+ # end
17
+ #
18
+ # @api public
19
+ def describe_service(url, &block)
20
+ service = WeaselDiesel.new(url)
21
+ yield service
22
+
23
+ service.sync_input_param_doc
24
+ WSList.add(service)
25
+
26
+ service
27
+ end
28
+
29
+ end
30
+ end
31
+
32
+ # Extend the main object with the DSL methods. This allows top-level calls
33
+ # without polluting the object inheritance tree.
34
+ self.extend WeaselDiesel::DSL
@@ -1,3 +1,3 @@
1
1
  class WeaselDiesel
2
- VERSION = "1.2.2"
2
+ VERSION = "1.3.0"
3
3
  end
@@ -7,14 +7,14 @@ describe_service "hello_world.xml" do |service|
7
7
 
8
8
  service.response do |response|
9
9
  response.element(:name => "greeting") do |e|
10
- e.attribute "message" => :string, :doc => "The greeting message sent back."
11
- end
10
+ e.attribute "message" => :string, :doc => "The greeting message sent back."
11
+ end
12
12
  end
13
13
 
14
14
  service.documentation do |doc|
15
- doc.overall "This service provides a simple hello world implementation example."
16
- doc.params :name, "The name of the person to greet."
17
- doc.example "<code>http://ps3.yourgame.com/hello_world.xml?name=Matt</code>"
15
+ doc.overall "This service provides a simple hello world implementation example."
16
+ doc.params :name, "The name of the person to greet."
17
+ doc.example "<code>http://ps3.yourgame.com/hello_world.xml?name=Matt</code>"
18
18
  end
19
19
 
20
20
  end
@@ -1,34 +1,34 @@
1
- require File.expand_path("spec_helper", File.dirname(__FILE__))
1
+ require_relative 'spec_helper'
2
2
 
3
3
  describe "WeaselDiesel JSON response description" do
4
4
 
5
5
  # JSON response example
6
6
  =begin
7
- { vouchers: [
8
- {
9
- id : 1,
7
+ { vouchers: [
8
+ {
9
+ id : 1,
10
10
  redeemed : false,
11
- created_at : 123123123123,
11
+ created_at : 123123123123,
12
12
  option: {
13
13
  id : 1231,
14
14
  price: 123.32
15
15
  }
16
- },
17
- {
18
- id : 2,
16
+ },
17
+ {
18
+ id : 2,
19
19
  redeemed : true,
20
- created_at : 123123123123,
20
+ created_at : 123123123123,
21
21
  option: {
22
22
  id : 1233,
23
23
  price: 1.32
24
24
  }
25
- },
25
+ },
26
26
  ] }
27
27
  =end
28
28
 
29
29
  before :all do
30
30
  @timestamp = Time.now.to_i
31
- @service = describe_service "json_list" do |service|
31
+ @service = describe_service "json_list" do |service|
32
32
  service.formats :json
33
33
  service.response do |response|
34
34
  response.array :vouchers do |node|
@@ -104,7 +104,7 @@ describe "WeaselDiesel JSON response description" do
104
104
  end
105
105
 
106
106
  it "should allow an anonymous object at the root of the response" do
107
- service = describe_service "json_anonymous_obj" do |service|
107
+ service = describe_service "json_anonymous_obj" do |service|
108
108
  service.formats :json
109
109
  service.response do |response|
110
110
  response.object do |obj|
@@ -120,7 +120,7 @@ describe "WeaselDiesel JSON response description" do
120
120
  obj.properties.find{|prop| prop.name == :id}.should_not be_nil
121
121
  obj.properties.find{|prop| prop.name == :foo}.should_not be_nil
122
122
  end
123
-
123
+
124
124
  end
125
125
 
126
126
 
@@ -134,7 +134,7 @@ describe "WeaselDiesel simple JSON object response description" do
134
134
 
135
135
  before :all do
136
136
  @timestamp = Time.now.to_i
137
- @service = describe_service "json_obj" do |service|
137
+ @service = describe_service "json_obj" do |service|
138
138
  service.formats :json
139
139
  service.response do |response|
140
140
  response.object :organization do |node|
@@ -165,7 +165,7 @@ describe "WeaselDiesel anonymous JSON object response description" do
165
165
 
166
166
  before :all do
167
167
  @timestamp = Time.now.to_i
168
- @service = describe_service "anon_json_obj" do |service|
168
+ @service = describe_service "anon_json_obj" do |service|
169
169
  service.formats :json
170
170
  service.response do |response|
171
171
  response.object do |node|
@@ -1,5 +1,5 @@
1
- require File.expand_path("spec_helper", File.dirname(__FILE__))
2
- require File.expand_path("../lib/json_response_verification", File.dirname(__FILE__))
1
+ require_relative 'spec_helper'
2
+ require_relative '../lib/json_response_verification'
3
3
 
4
4
  WeaselDiesel.send(:include, JSONResponseVerification)
5
5
 
@@ -70,7 +70,7 @@ describe ParamsVerification do
70
70
  params['timestamp'] = Time.now.iso8601
71
71
  lambda { ParamsVerification.validate!(params, @service.defined_params) }.should_not raise_error
72
72
  params['timestamp'] = Time.now.getutc.iso8601
73
- lambda { ParamsVerification.validate!(params, @service.defined_params) }.should_not raise_error(ParamsVerification::InvalidParamType)
73
+ lambda { ParamsVerification.validate!(params, @service.defined_params) }.should_not raise_error
74
74
  end
75
75
 
76
76
  it "should set the default value for a namespace optional param" do
@@ -111,7 +111,7 @@ describe ParamsVerification do
111
111
  it "should not raise an exception if a req array param doesn't contain a comma" do
112
112
  service = WSList.find(:post, "/services/array_param.xml")
113
113
  params = {'seq' => "a b c d e g"}
114
- lambda{ ParamsVerification.validate!(params, service.defined_params) }.should_not raise_exception(ParamsVerification::InvalidParamType)
114
+ lambda{ ParamsVerification.validate!(params, service.defined_params) }.should_not raise_exception
115
115
  end
116
116
 
117
117
  it "should raise an exception when a param is of the wrong type" do
@@ -1,9 +1,3 @@
1
- if RUBY_VERSION =~ /1.8/
2
- require 'rubygems'
3
- require 'backports'
4
- require 'json'
5
- end
6
-
7
1
  require 'rspec'
8
2
  require 'rack/test'
9
3
  require 'sinatra'
@@ -16,5 +10,6 @@ require_relative "../lib/framework_ext/sinatra_controller"
16
10
  ENV["RACK_ENV"] = 'test'
17
11
 
18
12
  RSpec.configure do |conf|
13
+ conf.include WeaselDiesel::DSL
19
14
  conf.include Rack::Test::Methods
20
15
  end
@@ -7,8 +7,8 @@ describe_service "services/test.xml" do |service|
7
7
  service.http_verb :get
8
8
 
9
9
  service.params do |p|
10
- p.string :framework,
11
- :in => WeaselDieselSpecOptions,
10
+ p.string :framework,
11
+ :in => WeaselDieselSpecOptions,
12
12
  :required => true,
13
13
  :doc => "The test framework used, could be one of the two following: #{WeaselDieselSpecOptions.join(", ")}."
14
14
 
@@ -38,7 +38,6 @@ describe_service "services/test.xml" do |service|
38
38
  # the response contains a list of player creation ratings each object in the list
39
39
 
40
40
  =begin
41
- #Format not supported by Ruby 1.8 due to hash insertion order not being maintained.
42
41
  service.response do |response|
43
42
  response.element(:name => "player_creation_ratings") do |e|
44
43
  e.attribute :id => :integer, :doc => "id doc"
@@ -1,4 +1,4 @@
1
- require File.expand_path("spec_helper", File.dirname(__FILE__))
1
+ require_relative 'spec_helper'
2
2
 
3
3
  describe "WeaselDiesel #controller_dispatch" do
4
4
 
@@ -1,4 +1,4 @@
1
- require File.expand_path("spec_helper", File.dirname(__FILE__))
1
+ require_relative 'spec_helper'
2
2
 
3
3
  describe WeaselDiesel::Documentation do
4
4
 
@@ -77,7 +77,9 @@ The most common way to use this service looks like that:
77
77
  it "should have a json representation of an response element" do
78
78
  json = @service.response.elements.first.to_json
79
79
  loaded_json = JSON.load(json)
80
- loaded_json[@service.response.elements.first.doc.name].should_not be_empty
80
+ loaded_json.has_key?(@service.response.elements.first.name).should be_true
81
+ loaded_json[@service.response.elements.first.name].should_not be_empty
82
+ loaded_json[@service.response.elements.first.name].has_key?("player_creation_rating").should be_true
81
83
  end
82
84
 
83
85
  it "should have documentation for a response element attribute" do
@@ -1,4 +1,4 @@
1
- require File.expand_path("spec_helper", File.dirname(__FILE__))
1
+ require_relative 'spec_helper'
2
2
 
3
3
  describe WeaselDiesel::Params do
4
4
 
@@ -69,7 +69,7 @@ describe WeaselDiesel::Params do
69
69
 
70
70
  it "should have options" do
71
71
  @rule.options[:type].should == :string
72
- @rule.options[:in].should == WeaselDieselSpecOptions
72
+ @rule.options[:in].should == WeaselDieselSpecOptions
73
73
  @rule.options[:null].should be_false
74
74
  end
75
75
  end
@@ -1,4 +1,4 @@
1
- require File.expand_path("spec_helper", File.dirname(__FILE__))
1
+ require_relative 'spec_helper'
2
2
 
3
3
  describe WeaselDiesel do
4
4
 
@@ -1,4 +1,4 @@
1
- require File.expand_path("spec_helper", File.dirname(__FILE__))
1
+ require_relative 'spec_helper'
2
2
 
3
3
  describe WSList do
4
4
 
@@ -1,6 +1,6 @@
1
- require File.expand_path("spec_helper", File.dirname(__FILE__))
1
+ require_relative 'spec_helper'
2
2
  require 'sinatra'
3
- require File.expand_path("./../lib/framework_ext/sinatra.rb", File.dirname(__FILE__))
3
+ require_relative '../lib/framework_ext/sinatra'
4
4
  WeaselDiesel.send(:include, WeaselDieselSinatraExtension)
5
5
 
6
6
  describe "Hello World example" do
@@ -10,6 +10,7 @@ Gem::Specification.new do |s|
10
10
  s.homepage = "https://github.com/mattetti/Weasel-Diesel"
11
11
  s.summary = %q{Web Service DSL}
12
12
  s.description = %q{Ruby DSL describing Web Services without implementation details.}
13
+ s.license = 'MIT'
13
14
 
14
15
  s.rubyforge_project = "wsdsl"
15
16
 
@@ -18,14 +19,12 @@ Gem::Specification.new do |s|
18
19
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
20
  s.require_paths = ["lib"]
20
21
 
22
+ s.add_dependency('thor')
23
+
21
24
  # specify any dependencies here; for example:
22
25
  s.add_development_dependency "rspec"
23
26
  s.add_development_dependency "rack-test"
24
27
  s.add_development_dependency "yard"
25
28
  s.add_development_dependency "sinatra"
26
- s.add_development_dependency "rake"
27
- if RUBY_VERSION =~ /1.8/
28
- s.add_runtime_dependency "backports"
29
- s.add_runtime_dependency "json"
30
- end
29
+ s.add_development_dependency "rake"
31
30
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: weasel_diesel
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.2
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Aimonetti
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-08-24 00:00:00.000000000 Z
11
+ date: 2013-08-30 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: thor
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: rspec
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -83,7 +97,8 @@ dependencies:
83
97
  description: Ruby DSL describing Web Services without implementation details.
84
98
  email:
85
99
  - mattaimonetti@gmail.com
86
- executables: []
100
+ executables:
101
+ - weasel_diesel
87
102
  extensions: []
88
103
  extra_rdoc_files: []
89
104
  files:
@@ -94,16 +109,21 @@ files:
94
109
  - LICENSE
95
110
  - README.md
96
111
  - Rakefile
112
+ - bin/weasel_diesel
97
113
  - lib/documentation.rb
98
114
  - lib/framework_ext/sinatra.rb
99
115
  - lib/framework_ext/sinatra_controller.rb
100
116
  - lib/inflection.rb
101
117
  - lib/json_response_verification.rb
102
- - lib/kernel_ext.rb
103
118
  - lib/params.rb
104
119
  - lib/params_verification.rb
105
120
  - lib/response.rb
106
121
  - lib/weasel_diesel.rb
122
+ - lib/weasel_diesel/cli.rb
123
+ - lib/weasel_diesel/doc_generator/_input_params.erb
124
+ - lib/weasel_diesel/doc_generator/_response_element.erb
125
+ - lib/weasel_diesel/doc_generator/template.erb
126
+ - lib/weasel_diesel/dsl.rb
107
127
  - lib/weasel_diesel/version.rb
108
128
  - lib/ws_list.rb
109
129
  - spec/hello_world_controller.rb
@@ -122,7 +142,8 @@ files:
122
142
  - spec/wsdsl_sinatra_ext_spec.rb
123
143
  - weasel_diesel.gemspec
124
144
  homepage: https://github.com/mattetti/Weasel-Diesel
125
- licenses: []
145
+ licenses:
146
+ - MIT
126
147
  metadata: {}
127
148
  post_install_message:
128
149
  rdoc_options: []
@@ -140,7 +161,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
140
161
  version: '0'
141
162
  requirements: []
142
163
  rubyforge_project: wsdsl
143
- rubygems_version: 2.0.3
164
+ rubygems_version: 2.0.7
144
165
  signing_key:
145
166
  specification_version: 4
146
167
  summary: Web Service DSL
@@ -1,27 +0,0 @@
1
- # Extending the top level module to add some helpers
2
- #
3
- # @api public
4
- module Kernel
5
-
6
- # Base DSL method called to describe a service
7
- #
8
- # @param [String] url The url of the service to add.
9
- # @yield [WeaselDiesel] The newly created service.
10
- # @return [Array] The services already defined
11
- # @example Describing a basic service
12
- # describe_service "hello-world.xml" do |service|
13
- # # describe the service
14
- # end
15
- #
16
- # @api public
17
- def describe_service(url, &block)
18
- service = WeaselDiesel.new(url)
19
- yield service
20
-
21
- service.sync_input_param_doc
22
- WSList.add(service)
23
-
24
- service
25
- end
26
-
27
- end