restapi 0.0.1

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.
Files changed (78) hide show
  1. data/.autotest +3 -0
  2. data/.gitignore +6 -0
  3. data/.rspec +2 -0
  4. data/.rvmrc +1 -0
  5. data/Gemfile +15 -0
  6. data/Gemfile.lock +118 -0
  7. data/MIT-LICENSE +20 -0
  8. data/README.rdoc +0 -0
  9. data/Rakefile +13 -0
  10. data/app/controllers/restapis_controller.rb +11 -0
  11. data/app/public/javascripts/backbone.js +1431 -0
  12. data/app/public/javascripts/bootstrap-collapse.js +138 -0
  13. data/app/public/javascripts/bootstrap.js +1726 -0
  14. data/app/public/javascripts/jquery-1.7.2.js +9404 -0
  15. data/app/public/javascripts/json2.js +487 -0
  16. data/app/public/javascripts/restapi/jst.js +108 -0
  17. data/app/public/javascripts/restapi/restapi.js +14 -0
  18. data/app/public/javascripts/restapi/routers/documentation_router.js +47 -0
  19. data/app/public/javascripts/underscore.js +999 -0
  20. data/app/public/stylesheets/bootstrap-responsive.css +686 -0
  21. data/app/public/stylesheets/bootstrap-responsive.min.css +12 -0
  22. data/app/public/stylesheets/bootstrap.css +3990 -0
  23. data/app/public/stylesheets/bootstrap.min.css +689 -0
  24. data/app/public/stylesheets/restapi/application.css +7 -0
  25. data/app/views/restapis/index.html.erb +47 -0
  26. data/lib/restapi.rb +22 -0
  27. data/lib/restapi/application.rb +155 -0
  28. data/lib/restapi/dsl_definition.rb +119 -0
  29. data/lib/restapi/error_description.rb +15 -0
  30. data/lib/restapi/method_description.rb +38 -0
  31. data/lib/restapi/param_description.rb +48 -0
  32. data/lib/restapi/railtie.rb +9 -0
  33. data/lib/restapi/resource_description.rb +75 -0
  34. data/lib/restapi/restapi_module.rb +54 -0
  35. data/lib/restapi/routing.rb +15 -0
  36. data/lib/restapi/validator.rb +193 -0
  37. data/lib/restapi/version.rb +4 -0
  38. data/restapi.gemspec +23 -0
  39. data/spec/controllers/restapis_controller_spec.rb +13 -0
  40. data/spec/controllers/users_controller_spec.rb +175 -0
  41. data/spec/dummy/Rakefile +7 -0
  42. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  43. data/spec/dummy/app/controllers/dogs_controller.rb +22 -0
  44. data/spec/dummy/app/controllers/twitter_example_controller.rb +306 -0
  45. data/spec/dummy/app/controllers/users_controller.rb +211 -0
  46. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  47. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  48. data/spec/dummy/app/views/users/doc.html.erb +24 -0
  49. data/spec/dummy/config.ru +4 -0
  50. data/spec/dummy/config/application.rb +45 -0
  51. data/spec/dummy/config/boot.rb +10 -0
  52. data/spec/dummy/config/database.yml +22 -0
  53. data/spec/dummy/config/environment.rb +5 -0
  54. data/spec/dummy/config/environments/development.rb +25 -0
  55. data/spec/dummy/config/environments/production.rb +49 -0
  56. data/spec/dummy/config/environments/test.rb +35 -0
  57. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  58. data/spec/dummy/config/initializers/inflections.rb +10 -0
  59. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  60. data/spec/dummy/config/initializers/restapi.rb +30 -0
  61. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  62. data/spec/dummy/config/initializers/session_store.rb +8 -0
  63. data/spec/dummy/config/locales/en.yml +5 -0
  64. data/spec/dummy/config/routes.rb +69 -0
  65. data/spec/dummy/public/404.html +26 -0
  66. data/spec/dummy/public/422.html +26 -0
  67. data/spec/dummy/public/500.html +26 -0
  68. data/spec/dummy/public/favicon.ico +0 -0
  69. data/spec/dummy/public/javascripts/application.js +2 -0
  70. data/spec/dummy/public/javascripts/controls.js +965 -0
  71. data/spec/dummy/public/javascripts/dragdrop.js +974 -0
  72. data/spec/dummy/public/javascripts/effects.js +1123 -0
  73. data/spec/dummy/public/javascripts/prototype.js +6001 -0
  74. data/spec/dummy/public/javascripts/rails.js +202 -0
  75. data/spec/dummy/public/stylesheets/.gitkeep +0 -0
  76. data/spec/dummy/script/rails +6 -0
  77. data/spec/spec_helper.rb +32 -0
  78. metadata +207 -0
@@ -0,0 +1,7 @@
1
+ body {
2
+ padding-top: 20px;
3
+ padding-bottom: 40px;
4
+ }
5
+ .sidebar-nav {
6
+ padding: 9px 0;
7
+ }
@@ -0,0 +1,47 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>API documentation</title>
5
+
6
+ <link type="text/css" rel="stylesheet" href="/stylesheets/bootstrap.css">
7
+ <link type="text/css" rel="stylesheet" href="/stylesheets/restapi/application.css">
8
+ <link type="text/css" rel="stylesheet" href="/stylesheets/bootstrap-responsive.css">
9
+ <!-- Le HTML5 shim, for IE6-8 support of HTML5 elements -->
10
+ <!--[if lt IE 9]>
11
+ <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
12
+ <![endif]-->
13
+ </head>
14
+ <body>
15
+ <!-- <div class="navbar navbar-fixed-top">
16
+ <div class="navbar-inner">
17
+ <div class="container-fluid">
18
+ <a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
19
+ <span class="icon-bar"></span>
20
+ <span class="icon-bar"></span>
21
+ <span class="icon-bar"></span>
22
+ </a>
23
+ <a id="api-title" class="brand" href="">Loading...</a>
24
+ </div>
25
+ </div>
26
+ </div>
27
+ </div> -->
28
+
29
+ <div class="container">
30
+ <div class="row">
31
+ <div id='container'></div>
32
+ </div>
33
+ <hr>
34
+ <footer></footer>
35
+
36
+ </div>
37
+
38
+ <script type="text/javascript" src="/javascripts/jquery-1.7.2.js"></script>
39
+ <script type="text/javascript" src="/javascripts/json2.js"></script>
40
+ <script type="text/javascript" src="/javascripts/underscore.js"></script>
41
+ <script type="text/javascript" src="/javascripts/backbone.js"></script>
42
+ <script type="text/javascript" src="/javascripts/bootstrap-collapse.js"></script>
43
+ <script type="text/javascript" src="/javascripts/restapi/restapi.js"></script>
44
+ <script type="text/javascript" src="/javascripts/restapi/jst.js"></script>
45
+ <script type="text/javascript" src="/javascripts/restapi/routers/documentation_router.js"></script>
46
+ </body>
47
+ </html>
@@ -0,0 +1,22 @@
1
+ require 'active_support/dependencies'
2
+ require 'rdoc'
3
+ require 'rdoc/markup/to_html'
4
+
5
+ # add path to restapi controller to ActiveSupport paths
6
+ %w{ controllers views }.each do |dir|
7
+ path = File.join(File.dirname(__FILE__), 'app', dir)
8
+ $LOAD_PATH << path
9
+ ActiveSupport::Dependencies.autoload_paths << path
10
+ ActiveSupport::Dependencies.autoload_once_paths.delete(path)
11
+ end
12
+
13
+ require "restapi/routing"
14
+ require "restapi/restapi_module"
15
+ require "restapi/method_description"
16
+ require "restapi/resource_description"
17
+ require "restapi/param_description"
18
+ require "restapi/error_description"
19
+ require "restapi/validator"
20
+ require "restapi/dsl_definition"
21
+ require "restapi/railtie"
22
+ require "restapi/version"
@@ -0,0 +1,155 @@
1
+ require 'ostruct'
2
+
3
+ module Restapi
4
+
5
+ class Application
6
+
7
+ # we need engine just for serving static assets
8
+ class Engine < Rails::Engine
9
+ initializer "static assets" do |app|
10
+ # app.middleware.use ::ActionDispatch::Static, "#{root}/app/public"
11
+ app.middleware.insert_after ::ActionDispatch::Static, ::ActionDispatch::Static, "#{root}/app/public"
12
+ end
13
+ end
14
+
15
+ attr_accessor :last_api_args, :last_errors, :last_params, :last_description
16
+ attr_reader :method_descriptions, :resource_descriptions
17
+
18
+ def initialize
19
+ super
20
+ @method_descriptions = Hash.new
21
+ @resource_descriptions = Hash.new
22
+ clear_last
23
+ end
24
+
25
+ # create new method api description
26
+ def define_method_description(resource_name, method_name)
27
+ resource_name = get_resource_name(resource_name)
28
+
29
+ puts "defining api for #{resource_name}:#{method_name}"
30
+
31
+ # create new or get existing api
32
+ key = [resource_name, method_name].join('#')
33
+ @method_descriptions[key] ||= Restapi::MethodDescription.new(method_name, resource_name, self)
34
+
35
+ # add mapi key to resource api
36
+ define_resource_description(resource_name).add_method(key)
37
+
38
+ @method_descriptions[key]
39
+ end
40
+
41
+ # create new resource api description
42
+ def define_resource_description(resource_name, &block)
43
+ resource_name = get_resource_name(resource_name)
44
+
45
+ @resource_descriptions[resource_name] ||= Restapi::ResourceDescription.new(resource_name, &block)
46
+ end
47
+
48
+ # check if there is some saved description
49
+ def restapi_provided?
50
+ true unless last_api_args.blank?
51
+ end
52
+
53
+ # List of all the apis defined in the given scope (and its sub-scopes).
54
+ # def mapis_for_resource(resource_name)
55
+ # @method_descriptions.select { |key,val| key.first == controller_name }
56
+ # end
57
+
58
+ # get api for given method
59
+ def get_method_description(resource_name, method_name)
60
+ resource_name = get_resource_name(resource_name)
61
+
62
+ @method_descriptions[[resource_name, method_name].join('#')]
63
+ end
64
+ alias :[] :get_method_description
65
+
66
+ # get api for given resource
67
+ def get_resource_description(resource_name)
68
+ resource_name = get_resource_name(resource_name)
69
+
70
+ @resource_descriptions[resource_name]
71
+ end
72
+
73
+ def remove_method_description(resource_name, method_name)
74
+ resource_name = get_resource_name(resource_name)
75
+
76
+ @method_descriptions.delete [resource_name, method_name].join('#')
77
+ end
78
+
79
+ def remove_resource_description(resource_name)
80
+ resource_name = get_resource_name(resource_name)
81
+
82
+ @resource_descriptions.delete resource_name
83
+ end
84
+
85
+ # Clear all apis in this application.
86
+ def clear
87
+ @resource_descriptions.clear
88
+ @method_descriptions.clear
89
+ end
90
+
91
+ # clear all saved data
92
+ def clear_last
93
+ @last_api_args = nil
94
+ @last_errors = Array.new
95
+ @last_params = Hash.new
96
+ @last_description = nil
97
+ end
98
+
99
+ # Return the current description, clearing it in the process.
100
+ def get_description
101
+ desc = @last_description
102
+ @last_description = nil
103
+ desc
104
+ end
105
+
106
+ def get_errors
107
+ errors = @last_errors.clone
108
+ @last_errors.clear
109
+ errors
110
+ end
111
+
112
+ def get_api_args
113
+ api_args = @last_api_args.clone
114
+ @last_api_args.clear
115
+ api_args
116
+ end
117
+
118
+ def get_params
119
+ params = @last_params.clone
120
+ @last_params.clear
121
+ params
122
+ end
123
+
124
+ def to_json(resource_name, method_name)
125
+
126
+ _resources = if resource_name.blank?
127
+ resource_descriptions.collect { |_,v| v.to_json }
128
+ else
129
+ [@resource_descriptions[resource_name].to_json(method_name)]
130
+ end
131
+
132
+ {
133
+ 'docs' => {
134
+ 'name' => Restapi.configuration.app_name,
135
+ 'info' => Restapi.configuration.app_info,
136
+ 'copyright' => Restapi.configuration.copyright,
137
+ 'doc_url' => Restapi.configuration.doc_base_url,
138
+ 'api_url' => Restapi.configuration.api_base_url,
139
+ 'resources' => _resources
140
+ }
141
+ }
142
+ end
143
+
144
+ private
145
+
146
+ def get_resource_name(klass)
147
+ if klass.class == String
148
+ klass
149
+ elsif klass.class == Class && klass.ancestors.include?(ActionController::Base)
150
+ klass.controller_name
151
+ end
152
+ end
153
+
154
+ end
155
+ end
@@ -0,0 +1,119 @@
1
+ # Restapi DSL functions.
2
+
3
+ module Restapi
4
+
5
+ # DSL is a module that provides #api, #error, #param, #error. Use this
6
+ # when you'd like to use restapi outside the top level scope.
7
+
8
+ module DSL
9
+
10
+ private
11
+
12
+ # Describe whole resource
13
+ #
14
+ # Example:
15
+ # api :desc => "Show user profile", :path => "/users/", :version => '1.0 - 3.4.2012'
16
+ # param :id, Fixnum, :desc => "User ID", :required => true
17
+ # desc <<-EOS
18
+ # Long description...
19
+ # EOS
20
+ def resource_description(options = {}, &block)
21
+ Restapi.define_resource_description(self, &block) if block_given?
22
+ end
23
+
24
+ # Declare an api.
25
+ #
26
+ # Example:
27
+ # api :desc => "short description",
28
+ # :path => "/resource_route",
29
+ # :method => "GET"
30
+ #
31
+ def api(args)
32
+ Restapi.last_api_args = args
33
+ end
34
+
35
+ # Describe the next method.
36
+ #
37
+ # Example:
38
+ # desc "print hello world"
39
+ # def hello_world
40
+ # puts "hello world"
41
+ # end
42
+ #
43
+ def desc(description)
44
+ if Restapi.last_description
45
+ raise "Double method description."
46
+ end
47
+ Restapi.last_description = description
48
+ end
49
+ alias :description :desc
50
+
51
+ # Describe possible errors
52
+ #
53
+ # Example:
54
+ # error :desc => "speaker is sleeping", :code => 500
55
+ # def hello_world
56
+ # return 500 if self.speaker.sleeping?
57
+ # puts "hello world"
58
+ # end
59
+ #
60
+ def error(args)
61
+ Restapi.last_errors << Restapi::ErrorDescription.new(args)
62
+ end
63
+
64
+ # Describe method's parameter
65
+ #
66
+ # Example:
67
+ # param :greeting, String, :desc => "arbitrary text", :required => true
68
+ # def hello_world(greeting)
69
+ # puts greeting
70
+ # end
71
+ #
72
+ def param(param_name, *args, &block)
73
+ Restapi.last_params[param_name.to_sym] = Restapi::ParamDescription.new(param_name, *args, &block)
74
+ end
75
+
76
+ # create method api and redefine newly added method
77
+ def method_added(method_name)
78
+
79
+ super
80
+
81
+ return unless Restapi.restapi_provided?
82
+
83
+ method_name = method_name.to_sym
84
+ resource_name = self.controller_name
85
+
86
+ # remove mapi if exists and create new one
87
+ Restapi.remove_method_description(resource_name, method_name)
88
+ mapi = Restapi.define_method_description(resource_name, method_name)
89
+
90
+ # redefine method only if validation is turned on
91
+ if Restapi.configuration.validate == true
92
+
93
+ old_method = instance_method(method_name)
94
+
95
+ define_method(method_name) do |*args|
96
+
97
+ mapi.params.each do |_, param|
98
+
99
+ # check if required parameters are present
100
+ if param.required && !params.has_key?(param.name)
101
+ raise ArgumentError.new("Expecting #{param.name} parameter.")
102
+ end
103
+
104
+ # params validations
105
+ if params.has_key?(param.name)
106
+ param.validate(params[:"#{param.name}"])
107
+ end
108
+
109
+ end # params.each
110
+
111
+ # run the original method code
112
+ old_method.bind(self).call(*args)
113
+ end
114
+
115
+ end
116
+
117
+ end # def method_added
118
+ end # module DSL
119
+ end # module Restapi
@@ -0,0 +1,15 @@
1
+ module Restapi
2
+
3
+ class ErrorDescription
4
+
5
+ attr_reader :code, :description
6
+
7
+ def initialize(args)
8
+ args ||= []
9
+ @code = args[:code] || args['code']
10
+ @description = args[:desc] || args[:description] || args['desc'] || args['description']
11
+ end
12
+
13
+ end
14
+
15
+ end
@@ -0,0 +1,38 @@
1
+ module Restapi
2
+
3
+ class MethodDescription
4
+
5
+ attr_reader :errors, :params, :full_description, :method, :resource, :short_description, :path, :http
6
+
7
+ def initialize(method, resource, app)
8
+ @method = method
9
+ @resource = resource
10
+ args = app.get_api_args
11
+ @short_description = args[:desc] || args[:short] || args[:description]
12
+ @path = args[:path]
13
+ @http = args[:method]
14
+ desc = app.get_description || ''
15
+ @full_description = Restapi.rdoc.convert(desc.strip_heredoc)
16
+ @errors = app.get_errors
17
+ @params = app.get_params
18
+ end
19
+
20
+ def doc_url; "#{Restapi.configuration.doc_base_url}/#{@resource}/#{@method}"; end
21
+ def api_url; "#{Restapi.configuration.api_base_url}#{@path}"; end
22
+
23
+ def to_json
24
+ {
25
+ :doc_url => doc_url,
26
+ :api_url => api_url,
27
+ :name => @method,
28
+ :http_method => @http,
29
+ :short_description => @short_description,
30
+ :full_description => @full_description,
31
+ :errors => @errors,
32
+ :params => @params.collect { |_,v| v.to_json }
33
+ }
34
+ end
35
+
36
+ end
37
+
38
+ end
@@ -0,0 +1,48 @@
1
+ module Restapi
2
+
3
+ # method parameter description
4
+ #
5
+ # name - method name (show)
6
+ # desc - description
7
+ # required - boolean if required
8
+ # validator - Validator::BaseValidator subclass
9
+ class ParamDescription
10
+
11
+ attr_reader :name, :desc, :required, :validator
12
+
13
+ def initialize(name, *args, &block)
14
+
15
+ if args.size > 1 || !args.first.is_a?(Hash)
16
+ validator_type = args.shift || nil
17
+ else
18
+ validator_type = nil
19
+ end
20
+ options = args.pop || {}
21
+
22
+ @name = name
23
+ @desc = Restapi.rdoc.convert((options[:desc] || '').strip_heredoc)
24
+ @required = options[:required] || false
25
+
26
+ @validator = Validator::BaseValidator.find(validator_type, options, block)
27
+ raise "Validator not found." unless validator
28
+ @validator.param_name = @name
29
+ end
30
+
31
+ def validate(value)
32
+ unless @validator.valid?(value)
33
+ raise ArgumentError.new(@validator.error)
34
+ end
35
+ end
36
+
37
+ def to_json
38
+ {
39
+ :name => name,
40
+ :description => desc,
41
+ :required => required,
42
+ :validator => validator.to_s
43
+ }
44
+ end
45
+
46
+ end
47
+
48
+ end