apipierails3 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (171) hide show
  1. checksums.yaml +17 -0
  2. data/.gitignore +14 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +27 -0
  5. data/APACHE-LICENSE-2.0 +202 -0
  6. data/CHANGELOG.md +469 -0
  7. data/Gemfile +1 -0
  8. data/Gemfile.rails32 +6 -0
  9. data/Gemfile.rails41 +6 -0
  10. data/Gemfile.rails42 +11 -0
  11. data/Gemfile.rails50 +6 -0
  12. data/Gemfile.rails51 +7 -0
  13. data/MIT-LICENSE +20 -0
  14. data/NOTICE +4 -0
  15. data/PROPOSAL_FOR_RESPONSE_DESCRIPTIONS.md +244 -0
  16. data/README.rst +1874 -0
  17. data/Rakefile +13 -0
  18. data/apipierails3.gemspec +27 -0
  19. data/app/controllers/apipie/apipies_controller.rb +199 -0
  20. data/app/helpers/apipie_helper.rb +10 -0
  21. data/app/public/apipie/javascripts/apipie.js +6 -0
  22. data/app/public/apipie/javascripts/bundled/bootstrap-collapse.js +138 -0
  23. data/app/public/apipie/javascripts/bundled/bootstrap.js +1726 -0
  24. data/app/public/apipie/javascripts/bundled/jquery.js +5 -0
  25. data/app/public/apipie/javascripts/bundled/prettify.js +28 -0
  26. data/app/public/apipie/stylesheets/application.css +7 -0
  27. data/app/public/apipie/stylesheets/bundled/bootstrap-responsive.min.css +12 -0
  28. data/app/public/apipie/stylesheets/bundled/bootstrap.min.css +689 -0
  29. data/app/public/apipie/stylesheets/bundled/prettify.css +30 -0
  30. data/app/views/apipie/apipies/_disqus.html.erb +13 -0
  31. data/app/views/apipie/apipies/_errors.html.erb +23 -0
  32. data/app/views/apipie/apipies/_headers.html.erb +26 -0
  33. data/app/views/apipie/apipies/_languages.erb +6 -0
  34. data/app/views/apipie/apipies/_metadata.erb +1 -0
  35. data/app/views/apipie/apipies/_method_detail.erb +61 -0
  36. data/app/views/apipie/apipies/_params.html.erb +42 -0
  37. data/app/views/apipie/apipies/_params_plain.html.erb +20 -0
  38. data/app/views/apipie/apipies/apipie_404.html.erb +17 -0
  39. data/app/views/apipie/apipies/apipie_checksum.json.erb +1 -0
  40. data/app/views/apipie/apipies/getting_started.html.erb +6 -0
  41. data/app/views/apipie/apipies/index.html.erb +56 -0
  42. data/app/views/apipie/apipies/method.html.erb +41 -0
  43. data/app/views/apipie/apipies/plain.html.erb +77 -0
  44. data/app/views/apipie/apipies/resource.html.erb +80 -0
  45. data/app/views/apipie/apipies/static.html.erb +103 -0
  46. data/app/views/layouts/apipie/apipie.html.erb +27 -0
  47. data/config/locales/de.yml +28 -0
  48. data/config/locales/en.yml +32 -0
  49. data/config/locales/es.yml +28 -0
  50. data/config/locales/fr.yml +31 -0
  51. data/config/locales/it.yml +31 -0
  52. data/config/locales/ja.yml +31 -0
  53. data/config/locales/pl.yml +28 -0
  54. data/config/locales/pt-BR.yml +28 -0
  55. data/config/locales/ru.yml +28 -0
  56. data/config/locales/tr.yml +28 -0
  57. data/config/locales/zh-CN.yml +28 -0
  58. data/config/locales/zh-TW.yml +28 -0
  59. data/images/screenshot-1.png +0 -0
  60. data/images/screenshot-2.png +0 -0
  61. data/lib/apipie/apipie_module.rb +83 -0
  62. data/lib/apipie/application.rb +462 -0
  63. data/lib/apipie/configuration.rb +186 -0
  64. data/lib/apipie/dsl_definition.rb +607 -0
  65. data/lib/apipie/error_description.rb +44 -0
  66. data/lib/apipie/errors.rb +86 -0
  67. data/lib/apipie/extractor.rb +177 -0
  68. data/lib/apipie/extractor/collector.rb +117 -0
  69. data/lib/apipie/extractor/recorder.rb +166 -0
  70. data/lib/apipie/extractor/writer.rb +454 -0
  71. data/lib/apipie/helpers.rb +73 -0
  72. data/lib/apipie/markup.rb +48 -0
  73. data/lib/apipie/method_description.rb +273 -0
  74. data/lib/apipie/middleware/checksum_in_headers.rb +35 -0
  75. data/lib/apipie/param_description.rb +280 -0
  76. data/lib/apipie/railtie.rb +9 -0
  77. data/lib/apipie/resource_description.rb +124 -0
  78. data/lib/apipie/response_description.rb +131 -0
  79. data/lib/apipie/response_description_adapter.rb +200 -0
  80. data/lib/apipie/routes_formatter.rb +33 -0
  81. data/lib/apipie/routing.rb +16 -0
  82. data/lib/apipie/rspec/response_validation_helper.rb +192 -0
  83. data/lib/apipie/see_description.rb +39 -0
  84. data/lib/apipie/static_dispatcher.rb +69 -0
  85. data/lib/apipie/swagger_generator.rb +707 -0
  86. data/lib/apipie/tag_list_description.rb +11 -0
  87. data/lib/apipie/validator.rb +526 -0
  88. data/lib/apipie/version.rb +3 -0
  89. data/lib/apipierails3.rb +25 -0
  90. data/lib/generators/apipie/install/README +6 -0
  91. data/lib/generators/apipie/install/install_generator.rb +25 -0
  92. data/lib/generators/apipie/install/templates/initializer.rb.erb +7 -0
  93. data/lib/generators/apipie/views_generator.rb +11 -0
  94. data/lib/tasks/apipie.rake +345 -0
  95. data/rel-eng/packages/.readme +3 -0
  96. data/rel-eng/packages/rubygem-apipie-rails +1 -0
  97. data/rel-eng/tito.props +5 -0
  98. data/spec/controllers/api/v1/architectures_controller_spec.rb +29 -0
  99. data/spec/controllers/api/v2/architectures_controller_spec.rb +12 -0
  100. data/spec/controllers/api/v2/nested/resources_controller_spec.rb +11 -0
  101. data/spec/controllers/apipies_controller_spec.rb +273 -0
  102. data/spec/controllers/concerns_controller_spec.rb +42 -0
  103. data/spec/controllers/extended_controller_spec.rb +11 -0
  104. data/spec/controllers/users_controller_spec.rb +740 -0
  105. data/spec/dummy/Rakefile +7 -0
  106. data/spec/dummy/app/controllers/api/base_controller.rb +4 -0
  107. data/spec/dummy/app/controllers/api/v1/architectures_controller.rb +43 -0
  108. data/spec/dummy/app/controllers/api/v1/base_controller.rb +11 -0
  109. data/spec/dummy/app/controllers/api/v2/architectures_controller.rb +30 -0
  110. data/spec/dummy/app/controllers/api/v2/base_controller.rb +11 -0
  111. data/spec/dummy/app/controllers/api/v2/nested/architectures_controller.rb +32 -0
  112. data/spec/dummy/app/controllers/api/v2/nested/resources_controller.rb +33 -0
  113. data/spec/dummy/app/controllers/application_controller.rb +18 -0
  114. data/spec/dummy/app/controllers/concerns/extending_concern.rb +11 -0
  115. data/spec/dummy/app/controllers/concerns/sample_controller.rb +41 -0
  116. data/spec/dummy/app/controllers/concerns_controller.rb +8 -0
  117. data/spec/dummy/app/controllers/extended_controller.rb +14 -0
  118. data/spec/dummy/app/controllers/files_controller.rb +5 -0
  119. data/spec/dummy/app/controllers/overridden_concerns_controller.rb +31 -0
  120. data/spec/dummy/app/controllers/pets_controller.rb +408 -0
  121. data/spec/dummy/app/controllers/pets_using_auto_views_controller.rb +73 -0
  122. data/spec/dummy/app/controllers/pets_using_self_describing_classes_controller.rb +95 -0
  123. data/spec/dummy/app/controllers/tagged_cats_controller.rb +32 -0
  124. data/spec/dummy/app/controllers/tagged_dogs_controller.rb +15 -0
  125. data/spec/dummy/app/controllers/twitter_example_controller.rb +307 -0
  126. data/spec/dummy/app/controllers/users_controller.rb +297 -0
  127. data/spec/dummy/app/views/layouts/application.html.erb +21 -0
  128. data/spec/dummy/config.ru +4 -0
  129. data/spec/dummy/config/application.rb +49 -0
  130. data/spec/dummy/config/boot.rb +10 -0
  131. data/spec/dummy/config/database.yml +21 -0
  132. data/spec/dummy/config/environment.rb +8 -0
  133. data/spec/dummy/config/environments/development.rb +28 -0
  134. data/spec/dummy/config/environments/production.rb +52 -0
  135. data/spec/dummy/config/environments/test.rb +38 -0
  136. data/spec/dummy/config/initializers/apipie.rb +110 -0
  137. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  138. data/spec/dummy/config/initializers/inflections.rb +10 -0
  139. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  140. data/spec/dummy/config/initializers/secret_token.rb +8 -0
  141. data/spec/dummy/config/initializers/session_store.rb +8 -0
  142. data/spec/dummy/config/locales/en.yml +5 -0
  143. data/spec/dummy/config/routes.rb +51 -0
  144. data/spec/dummy/db/.gitkeep +0 -0
  145. data/spec/dummy/doc/apipie_examples.json +1 -0
  146. data/spec/dummy/doc/users/desc_from_file.md +1 -0
  147. data/spec/dummy/public/404.html +26 -0
  148. data/spec/dummy/public/422.html +26 -0
  149. data/spec/dummy/public/500.html +26 -0
  150. data/spec/dummy/public/favicon.ico +0 -0
  151. data/spec/dummy/public/stylesheets/.gitkeep +0 -0
  152. data/spec/dummy/script/rails +6 -0
  153. data/spec/lib/application_spec.rb +49 -0
  154. data/spec/lib/extractor/extractor_spec.rb +9 -0
  155. data/spec/lib/extractor/middleware_spec.rb +44 -0
  156. data/spec/lib/extractor/writer_spec.rb +110 -0
  157. data/spec/lib/file_handler_spec.rb +18 -0
  158. data/spec/lib/method_description_spec.rb +98 -0
  159. data/spec/lib/param_description_spec.rb +345 -0
  160. data/spec/lib/param_group_spec.rb +60 -0
  161. data/spec/lib/rake_spec.rb +71 -0
  162. data/spec/lib/resource_description_spec.rb +48 -0
  163. data/spec/lib/swagger/openapi_2_0_schema.json +1607 -0
  164. data/spec/lib/swagger/rake_swagger_spec.rb +139 -0
  165. data/spec/lib/swagger/response_validation_spec.rb +104 -0
  166. data/spec/lib/swagger/swagger_dsl_spec.rb +658 -0
  167. data/spec/lib/validator_spec.rb +113 -0
  168. data/spec/lib/validators/array_validator_spec.rb +85 -0
  169. data/spec/spec_helper.rb +109 -0
  170. data/spec/support/rake.rb +21 -0
  171. metadata +415 -0
@@ -0,0 +1,186 @@
1
+ module Apipie
2
+ class Configuration
3
+
4
+ attr_accessor :app_name, :app_info, :copyright, :compress_examples,
5
+ :markup, :disqus_shortname,
6
+ :api_base_url, :doc_base_url, :required_by_default, :layout,
7
+ :default_version, :debug, :version_in_url, :namespaced_resources,
8
+ :validate, :validate_value, :validate_presence, :validate_key, :authenticate, :doc_path,
9
+ :show_all_examples, :process_params, :update_checksum, :checksum_path,
10
+ :link_extension, :record, :languages, :translate, :locale, :default_locale,
11
+ :persist_show_in_doc, :authorize,
12
+ :swagger_include_warning_tags, :swagger_content_type_input, :swagger_json_input_uses_refs,
13
+ :swagger_suppress_warnings, :swagger_api_host, :swagger_generate_x_computed_id_field,
14
+ :swagger_allow_additional_properties_in_response, :swagger_responses_use_refs
15
+
16
+ alias_method :validate?, :validate
17
+ alias_method :required_by_default?, :required_by_default
18
+ alias_method :namespaced_resources?, :namespaced_resources
19
+ alias_method :swagger_include_warning_tags?, :swagger_include_warning_tags
20
+ alias_method :swagger_json_input_uses_refs?, :swagger_json_input_uses_refs
21
+ alias_method :swagger_responses_use_refs?, :swagger_responses_use_refs
22
+ alias_method :swagger_generate_x_computed_id_field?, :swagger_generate_x_computed_id_field
23
+
24
+ # matcher to be used in Dir.glob to find controllers to be reloaded e.g.
25
+ #
26
+ # "#{Rails.root}/app/controllers/api/*.rb"
27
+ attr_accessor :api_controllers_matcher
28
+
29
+ # set to true if you want to reload the controllers at each refresh of the
30
+ # documentation. It requires +:api_controllers_matcher+ to be set to work
31
+ # properly.
32
+ attr_writer :reload_controllers
33
+
34
+ # specify routes if used router differ from default e.g.
35
+ #
36
+ # Api::Engine.routes
37
+ attr_accessor :api_routes
38
+
39
+ # a object responsible for transforming the routes loaded from Rails to a form
40
+ # to be used in the documentation, when using the `api!` keyword. By default,
41
+ # it's Apipie::RoutesFormatter. To customize the behaviour, one can inherit from
42
+ # from this class and override the methods as needed.
43
+ attr_accessor :routes_formatter
44
+
45
+ def reload_controllers?
46
+ @reload_controllers = Rails.env.development? unless defined? @reload_controllers
47
+ return @reload_controllers && @api_controllers_matcher
48
+ end
49
+
50
+ def validate_value
51
+ return (validate? && @validate_value)
52
+ end
53
+ alias_method :validate_value?, :validate_value
54
+
55
+ def validate_presence
56
+ return (validate? && @validate_presence)
57
+ end
58
+ alias_method :validate_presence?, :validate_presence
59
+
60
+ def validate_key
61
+ return (validate? && @validate_key)
62
+ end
63
+ alias_method :validate_key?, :validate_key
64
+
65
+ def process_value?
66
+ @process_params
67
+ end
68
+ # set to true if you want to use pregenerated documentation cache and avoid
69
+ # generating the documentation on runtime (usefull for production
70
+ # environment).
71
+ # You can generate the cache by running
72
+ #
73
+ # rake apipie:cache
74
+ attr_accessor :use_cache
75
+ alias_method :use_cache?, :use_cache
76
+
77
+ attr_writer :cache_dir
78
+ def cache_dir
79
+ @cache_dir ||= File.join(Rails.root, "public", "apipie-cache")
80
+ end
81
+
82
+ # if there is not obvious reason why the DSL should be turned on (no
83
+ # validations, cache turned on etc.), it's disabled to avoid unneeded
84
+ # allocation. It you need the DSL for other reasons, you can force the
85
+ # activation.
86
+ attr_writer :force_dsl
87
+ def force_dsl?
88
+ @force_dsl
89
+ end
90
+
91
+ # array of controller names (strings) (might include actions as well)
92
+ # to be ignored # when extracting description form calls.
93
+ # e.g. %w[Api::CommentsController Api::PostsController#post]
94
+ attr_writer :ignored_by_recorder
95
+ def ignored_by_recorder
96
+ @ignored_by_recorder ||= []
97
+ @ignored_by_recorder.map(&:to_s)
98
+ end
99
+
100
+ # array of controller names (strings) (might include actions as well)
101
+ # to be ignored # when generationg the documentation
102
+ # e.g. %w[Api::CommentsController Api::PostsController#post]
103
+ attr_writer :ignored
104
+ def ignored
105
+ @ignored ||= []
106
+ @ignored.map(&:to_s)
107
+ end
108
+
109
+ # Persist the show_in_doc value in the examples if true. Use this if you
110
+ # cannot set the flag in the tests themselves (no rspec for example).
111
+ attr_writer :persist_show_in_doc
112
+
113
+ # comment to put before docs that was generated automatically. It's used to
114
+ # determine if the description should be overwritten next recording.
115
+ # If you want to keep the documentation (prevent from overriding), remove
116
+ # the line above the docs.
117
+ attr_writer :generated_doc_disclaimer
118
+ def generated_doc_disclaimer
119
+ @generated_doc_disclaimer ||= "# DOC GENERATED AUTOMATICALLY: REMOVE THIS LINE TO PREVENT REGENERATING NEXT TIME"
120
+ end
121
+
122
+ def use_disqus?
123
+ !@disqus_shortname.blank?
124
+ end
125
+
126
+ # set app description for default version
127
+ # to maintain backward compatibility
128
+ # new way: config.app_info[version] = description
129
+ def app_info=(description)
130
+ version = Apipie.configuration.default_version
131
+ @app_info[version] = description
132
+ end
133
+
134
+ # set base url for default version of API
135
+ # to set it for specific version use
136
+ # config.api_base_url[version] = url
137
+ def api_base_url=(url)
138
+ version = Apipie.configuration.default_version
139
+ @api_base_url[version] = url
140
+ end
141
+
142
+ def api_routes
143
+ @api_routes || Rails.application.routes
144
+ end
145
+
146
+ def initialize
147
+ @markup = Apipie::Markup::RDoc.new
148
+ @app_name = "Another API"
149
+ @app_info = HashWithIndifferentAccess.new
150
+ @copyright = nil
151
+ @validate = :implicitly
152
+ @validate_value = true
153
+ @validate_presence = true
154
+ @validate_key = false
155
+ @required_by_default = false
156
+ @api_base_url = HashWithIndifferentAccess.new
157
+ @doc_base_url = "/apipie"
158
+ @layout = "apipie/apipie"
159
+ @disqus_shortname = nil
160
+ @default_version = "1.0"
161
+ @debug = false
162
+ @version_in_url = true
163
+ @namespaced_resources = false
164
+ @doc_path = "doc"
165
+ @process_params = false
166
+ @checksum_path = [@doc_base_url, '/api/']
167
+ @update_checksum = false
168
+ @link_extension = ".html"
169
+ @record = false
170
+ @languages = []
171
+ @default_locale = 'en'
172
+ @locale = lambda { |locale| @default_locale }
173
+ @translate = lambda { |str, locale| str }
174
+ @persist_show_in_doc = false
175
+ @routes_formatter = RoutesFormatter.new
176
+ @swagger_content_type_input = :form_data # this can be :json or :form_data
177
+ @swagger_json_input_uses_refs = false
178
+ @swagger_include_warning_tags = false
179
+ @swagger_suppress_warnings = false #[105,100,102]
180
+ @swagger_api_host = "localhost:3000"
181
+ @swagger_generate_x_computed_id_field = false
182
+ @swagger_allow_additional_properties_in_response = false
183
+ @swagger_responses_use_refs = true
184
+ end
185
+ end
186
+ end
@@ -0,0 +1,607 @@
1
+ # Apipie DSL functions.
2
+
3
+ module Apipie
4
+
5
+ # DSL is a module that provides #api, #error, #param, #returns.
6
+ module DSL
7
+
8
+ module Base
9
+ attr_reader :apipie_resource_descriptions, :api_params
10
+
11
+ def _apipie_eval_dsl(*args, &block)
12
+ raise 'The Apipie DLS data need to be cleared before evaluating new block' if @_apipie_dsl_data
13
+ instance_exec(*args, &block)
14
+ return _apipie_dsl_data
15
+ ensure
16
+ _apipie_dsl_data_clear
17
+ end
18
+
19
+ private
20
+
21
+ def _apipie_dsl_data
22
+ @_apipie_dsl_data ||= _apipie_dsl_data_init
23
+ end
24
+
25
+ def _apipie_dsl_data_clear
26
+ @_apipie_dsl_data = nil
27
+ end
28
+
29
+ def _apipie_dsl_data_init
30
+ @_apipie_dsl_data = {
31
+ :api => false,
32
+ :api_args => [],
33
+ :api_from_routes => nil,
34
+ :errors => [],
35
+ :tag_list => [],
36
+ :returns => {},
37
+ :params => [],
38
+ :headers => [],
39
+ :resource_id => nil,
40
+ :short_description => nil,
41
+ :description => nil,
42
+ :examples => [],
43
+ :see => [],
44
+ :formats => nil,
45
+ :api_versions => [],
46
+ :meta => nil,
47
+ :show => true,
48
+ :deprecated => false
49
+ }
50
+ end
51
+ end
52
+
53
+ module Resource
54
+ # by default, the resource id is derived from controller_name
55
+ # it can be overwritten with.
56
+ #
57
+ # resource_id "my_own_resource_id"
58
+ def resource_id(resource_id)
59
+ Apipie.set_resource_id(@controller, resource_id)
60
+ end
61
+
62
+ def name(name)
63
+ _apipie_dsl_data[:resource_name] = name
64
+ end
65
+
66
+ def api_base_url(url)
67
+ _apipie_dsl_data[:api_base_url] = url
68
+ end
69
+
70
+ def short(short)
71
+ _apipie_dsl_data[:short_description] = short
72
+ end
73
+ alias :short_description :short
74
+
75
+ def path(path)
76
+ _apipie_dsl_data[:path] = path
77
+ end
78
+
79
+ def app_info(app_info)
80
+ _apipie_dsl_data[:app_info] = app_info
81
+ end
82
+
83
+ def deprecated(value)
84
+ _apipie_dsl_data[:deprecated] = value
85
+ end
86
+
87
+ end
88
+
89
+ module Action
90
+
91
+ def def_param_group(name, &block)
92
+ Apipie.add_param_group(self, name, &block)
93
+ end
94
+
95
+ #
96
+ # # load paths from routes and don't provide description
97
+ # api
98
+ #
99
+ def api(method, path, desc = nil, options={}) #:doc:
100
+ return unless Apipie.active_dsl?
101
+ _apipie_dsl_data[:api] = true
102
+ _apipie_dsl_data[:api_args] << [method, path, desc, options]
103
+ end
104
+
105
+ # # load paths from routes
106
+ # api! "short description",
107
+ #
108
+ def api!(desc = nil, options={}) #:doc:
109
+ return unless Apipie.active_dsl?
110
+ _apipie_dsl_data[:api] = true
111
+ _apipie_dsl_data[:api_from_routes] = { :desc => desc, :options =>options }
112
+ end
113
+
114
+ # Reference other similar method
115
+ #
116
+ # api :PUT, '/articles/:id'
117
+ # see "articles#create"
118
+ # def update; end
119
+ def see(*args)
120
+ return unless Apipie.active_dsl?
121
+ _apipie_dsl_data[:see] << args
122
+ end
123
+
124
+ # Show some example of what does the described
125
+ # method return.
126
+ def example(example) #:doc:
127
+ return unless Apipie.active_dsl?
128
+ _apipie_dsl_data[:examples] << example.strip_heredoc
129
+ end
130
+
131
+ # Determine if the method should be included
132
+ # in the documentation
133
+ def show(show)
134
+ return unless Apipie.active_dsl?
135
+ _apipie_dsl_data[:show] = show
136
+ end
137
+
138
+ # Describe whole resource
139
+ #
140
+ # Example:
141
+ # api :desc => "Show user profile", :path => "/users/", :version => '1.0 - 3.4.2012'
142
+ # param :id, Fixnum, :desc => "User ID", :required => true
143
+ # desc <<-EOS
144
+ # Long description...
145
+ # EOS
146
+ def resource_description(options = {}, &block) #:doc:
147
+ return unless Apipie.active_dsl?
148
+ raise ArgumentError, "Block expected" unless block_given?
149
+
150
+ dsl_data = ResourceDescriptionDsl.eval_dsl(self, &block)
151
+ versions = dsl_data[:api_versions]
152
+ @apipie_resource_descriptions = versions.map do |version|
153
+ Apipie.define_resource_description(self, version, dsl_data)
154
+ end
155
+ Apipie.set_controller_versions(self, versions)
156
+ end
157
+ end
158
+
159
+ module Common
160
+ def api_versions(*versions)
161
+ _apipie_dsl_data[:api_versions].concat(versions)
162
+ end
163
+ alias :api_version :api_versions
164
+
165
+ # Describe the next method.
166
+ #
167
+ # Example:
168
+ # desc "print hello world"
169
+ # def hello_world
170
+ # puts "hello world"
171
+ # end
172
+ #
173
+ def desc(description) #:doc:
174
+ return unless Apipie.active_dsl?
175
+ if _apipie_dsl_data[:description]
176
+ raise "Double method description."
177
+ end
178
+ _apipie_dsl_data[:description] = description
179
+ end
180
+ alias :description :desc
181
+ alias :full_description :desc
182
+
183
+ # describe next method with document in given path
184
+ # in convension, these doc located under "#{Rails.root}/doc"
185
+ # Example:
186
+ # document "hello_world.md"
187
+ # def hello_world
188
+ # puts "hello world"
189
+ # end
190
+ def document path
191
+ content = File.open(File.join(Rails.root, Apipie.configuration.doc_path, path)).read
192
+ desc content
193
+ end
194
+
195
+ # Describe available request/response formats
196
+ #
197
+ # formats ['json', 'jsonp', 'xml']
198
+ def formats(formats) #:doc:
199
+ return unless Apipie.active_dsl?
200
+ _apipie_dsl_data[:formats] = formats
201
+ end
202
+
203
+ # Describe additional metadata
204
+ #
205
+ # meta :author => { :name => 'John', :surname => 'Doe' }
206
+ def meta(meta) #:doc:
207
+ _apipie_dsl_data[:meta] = meta
208
+ end
209
+
210
+
211
+ # Describe possible errors
212
+ #
213
+ # Example:
214
+ # error :desc => "speaker is sleeping", :code => 500, :meta => [:some, :more, :data]
215
+ # error 500, "speaker is sleeping"
216
+ # def hello_world
217
+ # return 500 if self.speaker.sleeping?
218
+ # puts "hello world"
219
+ # end
220
+ #
221
+ def error(code_or_options, desc=nil, options={}) #:doc:
222
+ return unless Apipie.active_dsl?
223
+ _apipie_dsl_data[:errors] << [code_or_options, desc, options]
224
+ end
225
+
226
+ # Add tags to resources and actions group operations together.
227
+ def tags(*args)
228
+ return unless Apipie.active_dsl?
229
+ tags = args.length == 1 ? args.first : args
230
+ _apipie_dsl_data[:tag_list] += tags
231
+ end
232
+
233
+ def _apipie_define_validators(description)
234
+
235
+ # [re]define method only if validation is turned on
236
+ if description && (Apipie.configuration.validate == true ||
237
+ Apipie.configuration.validate == :implicitly ||
238
+ Apipie.configuration.validate == :explicitly)
239
+
240
+ _apipie_save_method_params(description.method, description.params)
241
+
242
+ unless instance_methods.include?(:apipie_validations)
243
+ define_method(:apipie_validations) do
244
+ method_params = self.class._apipie_get_method_params(action_name)
245
+
246
+ if Apipie.configuration.validate_presence?
247
+ method_params.each do |_, param|
248
+ # check if required parameters are present
249
+ raise ParamMissing.new(param) if param.required && !params.has_key?(param.name)
250
+ end
251
+ end
252
+
253
+ if Apipie.configuration.validate_value?
254
+ method_params.each do |_, param|
255
+ # params validations
256
+ param.validate(params[:"#{param.name}"]) if params.has_key?(param.name)
257
+ end
258
+ end
259
+
260
+ # Only allow params passed in that are defined keys in the api
261
+ # Auto skip the default params (format, controller, action)
262
+ if Apipie.configuration.validate_key?
263
+ params.reject{|k,_| %w[format controller action].include?(k.to_s) }.each_pair do |param, _|
264
+ # params allowed
265
+ raise UnknownParam.new(param) if method_params.select {|_,p| p.name.to_s == param.to_s}.empty?
266
+ end
267
+ end
268
+
269
+ if Apipie.configuration.process_value?
270
+ @api_params ||= {}
271
+ method_params.each do |_, param|
272
+ # params processing
273
+ @api_params[param.as] = param.process_value(params[:"#{param.name}"]) if params.has_key?(param.name)
274
+ end
275
+ end
276
+ end
277
+ end
278
+
279
+ if (Apipie.configuration.validate == :implicitly || Apipie.configuration.validate == true)
280
+ old_method = instance_method(description.method)
281
+
282
+ define_method(description.method) do |*args|
283
+ apipie_validations
284
+
285
+ # run the original method code
286
+ old_method.bind(self).call(*args)
287
+ end
288
+ end
289
+
290
+ end
291
+ end
292
+
293
+ def _apipie_save_method_params(method, params)
294
+ @method_params ||= {}
295
+ @method_params[method] = params
296
+ end
297
+
298
+ def _apipie_get_method_params(method)
299
+ @method_params[method]
300
+ end
301
+
302
+ # Describe request header.
303
+ # Headers can't be validated with config.validate_presence = true
304
+ #
305
+ # Example:
306
+ # header 'ClientId', "client-id"
307
+ # def show
308
+ # render :text => headers['HTTP_CLIENT_ID']
309
+ # end
310
+ #
311
+ def header(header_name, description, options = {}) #:doc
312
+ return unless Apipie.active_dsl?
313
+ _apipie_dsl_data[:headers] << {
314
+ name: header_name,
315
+ description: description,
316
+ options: options
317
+ }
318
+ end
319
+ end
320
+
321
+
322
+ # this describes the params, it's in separate module because it's
323
+ # used in Validators as well
324
+ module Param
325
+ # Describe method's parameter
326
+ #
327
+ # Example:
328
+ # param :greeting, String, :desc => "arbitrary text", :required => true
329
+ # def hello_world(greeting)
330
+ # puts greeting
331
+ # end
332
+ #
333
+ def param(param_name, validator, desc_or_options = nil, options = {}, &block) #:doc:
334
+ return unless Apipie.active_dsl?
335
+ _apipie_dsl_data[:params] << [param_name,
336
+ validator,
337
+ desc_or_options,
338
+ options.merge(:param_group => @_current_param_group),
339
+ block]
340
+ end
341
+
342
+ def property(param_name, validator, desc_or_options = nil, options = {}, &block) #:doc:
343
+ return unless Apipie.active_dsl?
344
+ options[:only_in] ||= :response
345
+ options[:required] = true if options[:required].nil?
346
+ param(param_name, validator, desc_or_options, options, &block)
347
+ end
348
+
349
+ # Reuses param group for this method. The definition is looked up
350
+ # in scope of this controller. If the group was defined in
351
+ # different controller, the second param can be used to specify it.
352
+ # when using action_aware parmas, you can specify :as =>
353
+ # :create or :update to explicitly say how it should behave
354
+ def param_group(name, scope_or_options = nil, options = {})
355
+ if scope_or_options.is_a? Hash
356
+ options.merge!(scope_or_options)
357
+ scope = options[:scope]
358
+ else
359
+ scope = scope_or_options
360
+ end
361
+ scope ||= _default_param_group_scope
362
+
363
+ @_current_param_group = {
364
+ :scope => scope,
365
+ :name => name,
366
+ :options => options,
367
+ :from_concern => scope.apipie_concern?
368
+ }
369
+ self.instance_exec(&Apipie.get_param_group(scope, name))
370
+ ensure
371
+ @_current_param_group = nil
372
+ end
373
+
374
+ # Describe possible responses
375
+ #
376
+ # Example:
377
+ # def_param_group :user do
378
+ # param :user, Hash do
379
+ # param :name, String
380
+ # end
381
+ # end
382
+ #
383
+ # returns :user, "the speaker"
384
+ # returns "the speaker" do
385
+ # param_group: :user
386
+ # end
387
+ # returns :param_group => :user, "the speaker"
388
+ # returns :user, :code => 201, :desc => "the created speaker record"
389
+ # returns :array_of => :user, "many speakers"
390
+ # def hello_world
391
+ # render json: {user: {name: "Alfred"}}
392
+ # end
393
+ #
394
+ def returns(pgroup_or_options, desc_or_options=nil, options={}, &block) #:doc:
395
+ return unless Apipie.active_dsl?
396
+
397
+
398
+ if desc_or_options.is_a? Hash
399
+ options.merge!(desc_or_options)
400
+ elsif !desc_or_options.nil?
401
+ options[:desc] = desc_or_options
402
+ end
403
+
404
+ if pgroup_or_options.is_a? Hash
405
+ options.merge!(pgroup_or_options)
406
+ else
407
+ options[:param_group] = pgroup_or_options
408
+ end
409
+
410
+ code = options[:code] || 200
411
+ scope = options[:scope] || _default_param_group_scope
412
+ descriptor = options[:param_group] || options[:array_of]
413
+
414
+ if block.nil?
415
+ if descriptor.is_a? ResponseDescriptionAdapter
416
+ adapter = descriptor
417
+ elsif descriptor.respond_to? :describe_own_properties
418
+ adapter = ResponseDescriptionAdapter.from_self_describing_class(descriptor)
419
+ else
420
+ begin
421
+ block = Apipie.get_param_group(scope, descriptor) if descriptor
422
+ rescue
423
+ raise "No param_group or self-describing class named #{descriptor}"
424
+ end
425
+ end
426
+ elsif descriptor
427
+ raise "cannot specify both block and param_group"
428
+ end
429
+
430
+ _apipie_dsl_data[:returns][code] = [options, scope, block, adapter]
431
+ end
432
+
433
+ # where the group definition should be looked up when no scope
434
+ # given. This is expected to return a controller.
435
+ def _default_param_group_scope
436
+ self
437
+ end
438
+ end
439
+
440
+ module Controller
441
+ include Apipie::DSL::Base
442
+ include Apipie::DSL::Common
443
+ include Apipie::DSL::Action
444
+ include Apipie::DSL::Param
445
+
446
+ # defines the substitutions to be made in the API paths deifned
447
+ # in concerns included. For example:
448
+ #
449
+ # There is this method defined in concern:
450
+ #
451
+ # api GET ':controller_path/:id'
452
+ # def show
453
+ # # ...
454
+ # end
455
+ #
456
+ # If you include the concern into some controller, you can
457
+ # specify the value for :controller_path like this:
458
+ #
459
+ # apipie_concern_subst(:controller_path => '/users')
460
+ # include ::Concerns::SampleController
461
+ #
462
+ # The resulting path will be '/users/:id'.
463
+ #
464
+ # It has to be specified before the concern is included.
465
+ #
466
+ # If not specified, the default predefined substitions are
467
+ #
468
+ # {:conroller_path => controller.controller_path,
469
+ # :resource_id => `resource_id_from_apipie` }
470
+ def apipie_concern_subst(subst_hash)
471
+ _apipie_concern_subst.merge!(subst_hash)
472
+ end
473
+
474
+ # Allows to update existing params after definition was made (usually needed
475
+ # when extending the API form plugins).
476
+ #
477
+ # UsersController.apipie_update_params([:create, :update]) do
478
+ # param :user, Hash do
479
+ # param :oauth, String
480
+ # end
481
+ # end
482
+ #
483
+ # The block is evaluated in scope of the controller. Ohe can pass some additional
484
+ # objects via additional arguments like this:
485
+ #
486
+ # UsersController.apipie_update_params([:create, :update], [:name, :secret]) do |param_names|
487
+ # param :user, Hash do
488
+ # param_names.each { |p| param p, String }
489
+ # end
490
+ # end
491
+ def apipie_update_params(methods, *args, &block)
492
+ methods.each do |method|
493
+ method_description = Apipie.get_method_description(self, method)
494
+ unless method_description
495
+ raise "Could not find method description for #{self}##{method}. Was the method defined?"
496
+ end
497
+ dsl_data = _apipie_eval_dsl(*args, &block)
498
+ params_ordered = dsl_data[:params].map do |args|
499
+ Apipie::ParamDescription.from_dsl_data(method_description, args)
500
+ end
501
+ ParamDescription.merge(method_description.params_ordered_self, params_ordered)
502
+ end
503
+ end
504
+
505
+ def _apipie_concern_subst
506
+ @_apipie_concern_subst ||= {:controller_path => self.controller_path,
507
+ :resource_id => Apipie.get_resource_name(self)}
508
+ end
509
+
510
+ def _apipie_perform_concern_subst(string)
511
+ return _apipie_concern_subst.reduce(string) do |ret, (key, val)|
512
+ ret.gsub(":#{key}", val)
513
+ end
514
+ end
515
+
516
+ def apipie_concern?
517
+ false
518
+ end
519
+
520
+ # create method api and redefine newly added method
521
+ def method_added(method_name) #:doc:
522
+ super
523
+ return if !Apipie.active_dsl? || !_apipie_dsl_data[:api]
524
+
525
+ return if _apipie_dsl_data[:api_args].blank? && _apipie_dsl_data[:api_from_routes].blank?
526
+
527
+ # remove method description if exists and create new one
528
+ Apipie.remove_method_description(self, _apipie_dsl_data[:api_versions], method_name)
529
+ description = Apipie.define_method_description(self, method_name, _apipie_dsl_data)
530
+
531
+ _apipie_dsl_data_clear
532
+ _apipie_define_validators(description)
533
+ ensure
534
+ _apipie_dsl_data_clear
535
+ end
536
+ end
537
+
538
+ module Concern
539
+ include Apipie::DSL::Base
540
+ include Apipie::DSL::Common
541
+ include Apipie::DSL::Action
542
+ include Apipie::DSL::Param
543
+
544
+ # the concern was included into a controller
545
+ def included(controller)
546
+ super
547
+ _apipie_concern_data.each do |method_name, _apipie_dsl_data|
548
+ # remove method description if exists and create new one
549
+ description = Apipie.define_method_description(controller, method_name, _apipie_dsl_data)
550
+ controller._apipie_define_validators(description)
551
+ end
552
+ _apipie_concern_update_api_blocks.each do |(methods, block)|
553
+ controller.apipie_update_params(methods, &block)
554
+ end
555
+ end
556
+
557
+ def _apipie_concern_data
558
+ @_apipie_concern_data ||= []
559
+ end
560
+
561
+ def _apipie_concern_update_api_blocks
562
+ @_apipie_concern_update_api_blocks ||= []
563
+ end
564
+
565
+ def apipie_concern?
566
+ true
567
+ end
568
+
569
+ # create method api and redefine newly added method
570
+ def method_added(method_name) #:doc:
571
+ super
572
+
573
+ return if ! Apipie.active_dsl? || !_apipie_dsl_data[:api]
574
+
575
+ _apipie_concern_data << [method_name, _apipie_dsl_data.merge(:from_concern => true)]
576
+ ensure
577
+ _apipie_dsl_data_clear
578
+ end
579
+
580
+ def update_api(*methods, &block)
581
+ _apipie_concern_update_api_blocks << [methods, block]
582
+ end
583
+
584
+ end
585
+
586
+ class ResourceDescriptionDsl
587
+ include Apipie::DSL::Base
588
+ include Apipie::DSL::Common
589
+ include Apipie::DSL::Resource
590
+ include Apipie::DSL::Param
591
+
592
+ def initialize(controller)
593
+ @controller = controller
594
+ end
595
+
596
+ # evaluates resource description DSL and returns results
597
+ def self.eval_dsl(controller, &block)
598
+ dsl_data = self.new(controller)._apipie_eval_dsl(&block)
599
+ if dsl_data[:api_versions].empty?
600
+ dsl_data[:api_versions] = Apipie.controller_versions(controller)
601
+ end
602
+ dsl_data
603
+ end
604
+ end
605
+
606
+ end # module DSL
607
+ end # module Apipie