playerconnect-wsdsl 0.2.2

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.
data/.rvmrc_example ADDED
@@ -0,0 +1,15 @@
1
+ gemset_name=wsdsl
2
+ ruby_version=1.9.2-p180
3
+ rubygems_version=1.8.5
4
+ # specify multiple gems with spaces ex: ('foo 1.2.3' 'bar 5.6.7')
5
+ gems=('jeweler 1.6.4' 'rspec 2.6.0' 'yard 0.7.2')
6
+
7
+ export GIT_SSL_NO_VERIFY=true
8
+
9
+ rvm use $ruby_version@$gemset_name --create --install
10
+ [ $(gem -v) = $rubygems_version ] || rvm rubygems $rubygems_version
11
+ for g in "${gems[@]}"; do
12
+ read n v <<< $(echo $g | awk '{print $1 " " $2}')
13
+ gem list ${n} -v${v} -i >> /dev/null
14
+ [ $? -eq 0 ] || gem install ${n} -v${v} --no-ri --no-rdoc
15
+ done
data/LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ Copyright (c) 2011 Matt Aimonetti
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ --
23
+
data/README.md ADDED
@@ -0,0 +1,100 @@
1
+ # Web Service DSL
2
+
3
+ WSDSL is a simple DSL allowing developers to describe and
4
+ document their web APIs. For instance:
5
+
6
+
7
+ describe_service "hello_world" do |service|
8
+ service.formats :xml
9
+ service.http_verb :get
10
+ service.disable_auth # on by default
11
+
12
+ service.param.string :name, :default => 'World'
13
+
14
+ service.response do |response|
15
+ response.element(:name => "greeting") do |e|
16
+ e.attribute "message" => :string, :doc => "The greeting message sent back."
17
+ end
18
+ end
19
+
20
+ service.documentation do |doc|
21
+ doc.overall "This service provides a simple hello world implementation example."
22
+ doc.params :name, "The name of the person to greet."
23
+ doc.example "<code>http://example.com/hello_world.xml?name=Matt</code>"
24
+ end
25
+
26
+ end
27
+
28
+
29
+ Or a more complex example:
30
+
31
+ SpecOptions = ['RSpec', 'Bacon'] # usually pulled from a model
32
+
33
+ describe_service "wsdsl/test.xml" do |service|
34
+ service.formats :xml, :json
35
+ service.http_verb :get
36
+
37
+ service.params do |p|
38
+ p.string :framework, :in => SpecOptions, :null => false, :required => true
39
+
40
+ p.datetime :timestamp, :default => Time.now
41
+ p.string :alpha, :in => ['a', 'b', 'c']
42
+ p.string :version, :null => false
43
+ p.integer :num, :minvalue => 42
44
+ end
45
+
46
+ # service.param :delta, :optional => true, :type => 'float'
47
+ # if the optional flag isn't passed, the param is considered required.
48
+ # service.param :epsilon, :type => 'string'
49
+
50
+ service.params.namespace :user do |user|
51
+ user.integer :id, :required => :true
52
+ end
53
+
54
+ # the response contains a list of player creation ratings each object in the list
55
+
56
+ service.response do |response|
57
+ response.element(:name => "player_creation_ratings") do |e|
58
+ e.attribute :id => :integer, :doc => "id doc"
59
+ e.attribute :is_accepted => :boolean, :doc => "is accepted doc"
60
+ e.attribute :name => :string, :doc => "name doc"
61
+
62
+ e.array :name => 'player_creation_rating', :type => 'PlayerCreationRating' do |a|
63
+ a.attribute :comments => :string, :doc => "comments doc"
64
+ a.attribute :player_id => :integer, :doc => "player_id doc"
65
+ a.attribute :rating => :integer, :doc => "rating doc"
66
+ a.attribute :username => :string, :doc => "username doc"
67
+ end
68
+ end
69
+ end
70
+
71
+ service.documentation do |doc|
72
+ # doc.overall <markdown description text>
73
+ doc.overall <<-DOC
74
+ This is a test service used to test the framework.
75
+ DOC
76
+
77
+ # doc.params <name>, <definition>
78
+ doc.params :framework, "The test framework used, could be one of the two following: #{SpecOptions.join(", ")}."
79
+ doc.params :version, "The version of the framework to use."
80
+
81
+ # doc.example <markdown text>
82
+ doc.example <<-DOC
83
+ The most common way to use this service looks like that:
84
+ http://example.com/wsdsl/test.xml?framework=rspec&version=2.0.0
85
+ DOC
86
+ end
87
+ end
88
+
89
+
90
+ ## Test suite
91
+
92
+ This library comes with a test suite requiring Ruby 1.9.2.
93
+ The following gems need to be available:
94
+ Rspec, Rack, Sinatra
95
+
96
+
97
+ ## Copyright
98
+
99
+ Copyright (c) 2011 Matt Aimonetti. See LICENSE for
100
+ further details.
data/Rakefile ADDED
@@ -0,0 +1,36 @@
1
+ require 'rubygems'
2
+ require'rake'
3
+
4
+ require 'jeweler'
5
+ Jeweler::Tasks.new do |gem|
6
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
7
+ gem.name = "playerconnect-wsdsl"
8
+ gem.homepage = "http://github.com/playerconnect/wsdsl"
9
+ gem.license = "MIT"
10
+ gem.summary = %Q{Web Service DSL}
11
+ gem.description = %Q{A Ruby DSL describing Web Services without implementation details.}
12
+ gem.email = "sdod@scea.com"
13
+ gem.authors = ["Team SDOD"]
14
+ gem.version = "0.2.2"
15
+ # Include your dependencies below. Runtime dependencies are required when using your gem,
16
+ # and development dependencies are only needed for development (ie running rake tasks, tests, etc)
17
+ # gem.add_runtime_dependency 'jabber4r', '> 0.1'
18
+ # gem.add_development_dependency 'rspec', '> 1.2.3'
19
+ end
20
+ Jeweler::RubygemsDotOrgTasks.new
21
+
22
+ require 'rspec/core'
23
+ require 'rspec/core/rake_task'
24
+ RSpec::Core::RakeTask.new(:spec) do |spec|
25
+ spec.pattern = FileList['spec/**/*_spec.rb']
26
+ end
27
+
28
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
29
+ spec.pattern = 'spec/**/*_spec.rb'
30
+ spec.rcov = true
31
+ end
32
+
33
+ task :default => :spec
34
+
35
+ require 'yard'
36
+ YARD::Rake::YardocTask.new
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.2.0
@@ -0,0 +1,151 @@
1
+ class WSDSL
2
+ # Service documentation class
3
+ #
4
+ # @api public
5
+ class Documentation
6
+
7
+ # @api public
8
+ attr_reader :desc
9
+
10
+ # @api public
11
+ attr_reader :params_doc
12
+
13
+ # @api public
14
+ attr_reader :namespaced_params
15
+
16
+ # @api public
17
+ attr_reader :examples
18
+
19
+ # @api public
20
+ attr_reader :elements
21
+
22
+ # This class contains the documentation information regarding an element.
23
+ # Currently, elements are only used in the response info.
24
+ #
25
+ # @api public
26
+ class ElementDoc
27
+
28
+ # @api public
29
+ attr_reader :name, :attributes
30
+
31
+ # @param [String] The element's name
32
+ # @api public
33
+ def initialize(name)
34
+ # raise ArgumentError, "An Element doc needs to be initialize by passing a hash with a ':name' keyed entry." unless opts.is_a?(Hash) && opts.has_key?(:name)
35
+ @name = name
36
+ @attributes = {}
37
+ end
38
+
39
+ # @param [String] name The name of the attribute described
40
+ # @param [String] desc The description of the attribute
41
+ # @api public
42
+ def attribute(name, desc)
43
+ @attributes[name] = desc
44
+ end
45
+
46
+ end # of ElementDoc
47
+
48
+ # Namespaced param documentation
49
+ #
50
+ # @api public
51
+ class NamespacedParam
52
+
53
+ # @return [String, Symbol] The name of the namespaced, usually a symbol
54
+ # @api public
55
+ attr_reader :name
56
+
57
+ # @return [Hash] The list of params within the namespace
58
+ # @api public
59
+ attr_reader :params
60
+
61
+ # @api public
62
+ def initialize(name)
63
+ @name = name
64
+ @params = {}
65
+ end
66
+
67
+ # Sets the description/documentation of a specific namespaced param
68
+ #
69
+ # @return [String]
70
+ # @api public
71
+ def param(name, desc)
72
+ @params[name] = desc
73
+ end
74
+
75
+ end
76
+
77
+ # Initialize a Documentation object wrapping all the documentation aspect of the service.
78
+ # The response documentation is a Documentation instance living inside the service documentation object.
79
+ #
80
+ # @api public
81
+ def initialize
82
+ @params_doc = {}
83
+ @examples = []
84
+ @elements = []
85
+ @namespaced_params = []
86
+ end
87
+
88
+ # Sets or returns the overall description
89
+ #
90
+ # @param [String] desc Service overall description
91
+ # @api public
92
+ # @return [String] The overall service description
93
+ def overall(desc)
94
+ if desc.nil?
95
+ @desc
96
+ else
97
+ @desc = desc
98
+ end
99
+ end
100
+
101
+ # Sets the description/documentation of a specific param
102
+ #
103
+ # @return [String]
104
+ # @api public
105
+ def params(name, desc)
106
+ @params_doc[name] = desc
107
+ end
108
+ alias_method :param, :params
109
+
110
+ # Define a new namespaced param and yield it to the passed block
111
+ # if available.
112
+ #
113
+ # @return [Array] the namespaced params
114
+ # @api public
115
+ def namespace(ns_name)
116
+ new_ns_param = NamespacedParam.new(ns_name)
117
+ if block_given?
118
+ yield(new_ns_param)
119
+ end
120
+ @namespaced_params << new_ns_param
121
+ end
122
+
123
+ def response
124
+ @response ||= Documentation.new
125
+ end
126
+
127
+ # Service usage example
128
+ #
129
+ # @param [String] desc Usage example.
130
+ # @return [Array<String>] All the examples.
131
+ # @api public
132
+ def example(desc)
133
+ @examples << desc
134
+ end
135
+
136
+ # Add a new element to the doc
137
+ # currently only used for response doc
138
+ #
139
+ # @param [Hash] opts element's documentation options
140
+ # @yield [ElementDoc] The new element doc.
141
+ # @return [Array<ElementDoc>]
142
+ # @api public
143
+ def element(opts={})
144
+ element = ElementDoc.new(opts)
145
+ yield(element)
146
+ @elements << element
147
+ end
148
+
149
+
150
+ end # of Documentation
151
+ end
@@ -0,0 +1,30 @@
1
+ # Module used to extend {WSDSL} and add {#load_sinatra_route} to services.
2
+ # This code is Sinatra specific and therefore lives outside the {WSDSL}
3
+ # @see {WSDSL}
4
+ # @api public
5
+ module WSDSLSinatraExtension
6
+
7
+ # Defines a sinatra service route based on its settings
8
+ #
9
+ # @return [Nil]
10
+ # @api private
11
+ def load_sinatra_route
12
+ service = self
13
+ upcase_verb = service.verb.to_s.upcase
14
+ puts "/#{self.url} -> #{self.controller_name}##{self.action} - (#{upcase_verb})"
15
+
16
+ # Define the route directly to save some object allocations on the critical path
17
+ # Note that we are using a private API to define the route and that unlike sinatra usual DSL
18
+ # we do NOT define a HEAD route for every GET route.
19
+ Sinatra::Base.send(:route, upcase_verb, "/#{self.url}") do
20
+ service.controller_dispatch(self)
21
+ end
22
+
23
+ # Other alternative to the route definition, this time using the public API
24
+ # self.send(verb, "/#{service.url}") do
25
+ # service.controller_dispatch(self)
26
+ # end
27
+
28
+ end
29
+
30
+ end
@@ -0,0 +1,80 @@
1
+ require 'forwardable'
2
+ require File.expand_path('./../params_verification', File.dirname(__FILE__))
3
+
4
+ # Base code shared by all service controllers
5
+ # This allows us to share code between controllers
6
+ # more precisely to render templates and in general to use sinatra helpers
7
+ #
8
+ # @see Sinatra::Base and Sinatra::Helpers
9
+ # @api public
10
+ # @author Matt Aimonetti
11
+ class SinatraServiceController
12
+ extend Forwardable
13
+
14
+ # The service controller might be loaded outside of a Sinatra App
15
+ # in this case, we don't need to load the helpers
16
+ if Object.const_defined?(:Sinatra)
17
+ include Sinatra::Helpers
18
+ end
19
+
20
+ class AuthenticationFailed < StandardError; end
21
+
22
+ # @return [WSDSL] The service served by this controller
23
+ # @api public
24
+ attr_reader :service
25
+
26
+ # @return [Sinatra::Application]
27
+ # @api public
28
+ attr_reader :app
29
+
30
+ # @return [Hash]
31
+ # @api public
32
+ attr_reader :env
33
+
34
+ # @return [Sinatra::Request]
35
+ # @see http://rubydoc.info/github/sinatra/sinatra/Sinatra/Request
36
+ # @api public
37
+ attr_reader :request
38
+
39
+ # @return [Sinatra::Response]
40
+ # @see http://rubydoc.info/github/sinatra/sinatra/Sinatra/Response
41
+ # @api public
42
+ attr_reader :response
43
+
44
+ # @return [Hash]
45
+ # @api public
46
+ attr_accessor :params
47
+
48
+ # @param [Sinatra::Application] app The Sinatra app used as a reference and to access request params
49
+ # @param [WSDSL] service The service served by this controller
50
+ # @raise [ParamError, NoParamsDefined, MissingParam, UnexpectedParam, InvalidParamType, InvalidParamValue]
51
+ # If the params don't validate one of the {ParamsVerification} errors will be raised.
52
+ # @api public
53
+ def initialize(app, service)
54
+ @app = app
55
+ @env = app.env
56
+ @request = app.request
57
+ @response = app.response
58
+ @service = service
59
+
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
63
+ @params = app.params = ParamsVerification.validate!(app.params, service.defined_params)
64
+
65
+ # Authentication check
66
+ if service.auth_required
67
+ raise AuthenticationFailed unless logged_in?
68
+ end
69
+ end
70
+
71
+
72
+ # Forwarding some methods to the underlying app object
73
+ def_delegators :app, :settings, :halt, :compile_template, :session
74
+
75
+ # Returns true or false if the player is logged in.
76
+ def logged_in?
77
+ !session[:player_id].nil?
78
+ end
79
+
80
+ end