apipie-rails 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) hide show
  1. data/.autotest +3 -0
  2. data/.gitignore +6 -0
  3. data/.rspec +2 -0
  4. data/.rvmrc +1 -0
  5. data/.travis.yml +5 -0
  6. data/APACHE-LICENSE-2.0 +202 -0
  7. data/Gemfile +3 -0
  8. data/Gemfile.lock +115 -0
  9. data/MIT-LICENSE +20 -0
  10. data/NOTICE +4 -0
  11. data/README.rdoc +365 -0
  12. data/Rakefile +13 -0
  13. data/apipie-rails.gemspec +27 -0
  14. data/app/controllers/apipie/apipies_controller.rb +60 -0
  15. data/app/public/apipie/javascripts/apipie.js +6 -0
  16. data/app/public/apipie/javascripts/bundled/bootstrap-collapse.js +138 -0
  17. data/app/public/apipie/javascripts/bundled/bootstrap.js +1726 -0
  18. data/app/public/apipie/javascripts/bundled/jquery-1.7.2.js +9404 -0
  19. data/app/public/apipie/javascripts/bundled/prettify.js +28 -0
  20. data/app/public/apipie/stylesheets/application.css +7 -0
  21. data/app/public/apipie/stylesheets/bundled/bootstrap-responsive.min.css +12 -0
  22. data/app/public/apipie/stylesheets/bundled/bootstrap.min.css +689 -0
  23. data/app/public/apipie/stylesheets/bundled/prettify.css +30 -0
  24. data/app/views/apipie/apipies/_params.html.erb +22 -0
  25. data/app/views/apipie/apipies/_params_plain.html.erb +16 -0
  26. data/app/views/apipie/apipies/index.html.erb +36 -0
  27. data/app/views/apipie/apipies/method.html.erb +63 -0
  28. data/app/views/apipie/apipies/plain.html.erb +70 -0
  29. data/app/views/apipie/apipies/resource.html.erb +82 -0
  30. data/app/views/apipie/apipies/static.html.erb +101 -0
  31. data/app/views/layouts/apipie/apipie.html.erb +37 -0
  32. data/lib/apipie-rails.rb +12 -0
  33. data/lib/apipie/apipie_module.rb +105 -0
  34. data/lib/apipie/application.rb +225 -0
  35. data/lib/apipie/client/generator.rb +105 -0
  36. data/lib/apipie/client/template/Gemfile.tt +5 -0
  37. data/lib/apipie/client/template/README.tt +3 -0
  38. data/lib/apipie/client/template/base.rb.tt +33 -0
  39. data/lib/apipie/client/template/bin.rb.tt +110 -0
  40. data/lib/apipie/client/template/cli.rb.tt +25 -0
  41. data/lib/apipie/client/template/cli_command.rb.tt +129 -0
  42. data/lib/apipie/client/template/client.rb.tt +10 -0
  43. data/lib/apipie/client/template/resource.rb.tt +17 -0
  44. data/lib/apipie/dsl_definition.rb +139 -0
  45. data/lib/apipie/error_description.rb +21 -0
  46. data/lib/apipie/extractor.rb +143 -0
  47. data/lib/apipie/extractor/collector.rb +113 -0
  48. data/lib/apipie/extractor/recorder.rb +122 -0
  49. data/lib/apipie/extractor/writer.rb +356 -0
  50. data/lib/apipie/helpers.rb +24 -0
  51. data/lib/apipie/markup.rb +45 -0
  52. data/lib/apipie/method_description.rb +150 -0
  53. data/lib/apipie/param_description.rb +87 -0
  54. data/lib/apipie/railtie.rb +9 -0
  55. data/lib/apipie/resource_description.rb +83 -0
  56. data/lib/apipie/routing.rb +13 -0
  57. data/lib/apipie/static_dispatcher.rb +60 -0
  58. data/lib/apipie/validator.rb +292 -0
  59. data/lib/apipie/version.rb +3 -0
  60. data/lib/tasks/apipie.rake +156 -0
  61. data/rel-eng/packages/.readme +3 -0
  62. data/rel-eng/tito.props +5 -0
  63. data/rubygem-apipie-rails.spec +72 -0
  64. data/spec/controllers/apipies_controller_spec.rb +132 -0
  65. data/spec/controllers/users_controller_spec.rb +390 -0
  66. data/spec/dummy/Rakefile +7 -0
  67. data/spec/dummy/app/controllers/application_controller.rb +6 -0
  68. data/spec/dummy/app/controllers/twitter_example_controller.rb +302 -0
  69. data/spec/dummy/app/controllers/users_controller.rb +223 -0
  70. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  71. data/spec/dummy/config.ru +4 -0
  72. data/spec/dummy/config/application.rb +45 -0
  73. data/spec/dummy/config/boot.rb +10 -0
  74. data/spec/dummy/config/database.yml +21 -0
  75. data/spec/dummy/config/environment.rb +8 -0
  76. data/spec/dummy/config/environments/development.rb +25 -0
  77. data/spec/dummy/config/environments/production.rb +49 -0
  78. data/spec/dummy/config/environments/test.rb +35 -0
  79. data/spec/dummy/config/initializers/apipie.rb +64 -0
  80. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  81. data/spec/dummy/config/initializers/inflections.rb +10 -0
  82. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  83. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  84. data/spec/dummy/config/initializers/session_store.rb +8 -0
  85. data/spec/dummy/config/locales/en.yml +5 -0
  86. data/spec/dummy/config/routes.rb +21 -0
  87. data/spec/dummy/doc/apipie_examples.yml +28 -0
  88. data/spec/dummy/public/404.html +26 -0
  89. data/spec/dummy/public/422.html +26 -0
  90. data/spec/dummy/public/500.html +26 -0
  91. data/spec/dummy/public/favicon.ico +0 -0
  92. data/spec/dummy/public/javascripts/application.js +2 -0
  93. data/spec/dummy/public/javascripts/controls.js +965 -0
  94. data/spec/dummy/public/javascripts/dragdrop.js +974 -0
  95. data/spec/dummy/public/javascripts/effects.js +1123 -0
  96. data/spec/dummy/public/javascripts/prototype.js +6001 -0
  97. data/spec/dummy/public/javascripts/rails.js +202 -0
  98. data/spec/dummy/public/stylesheets/.gitkeep +0 -0
  99. data/spec/dummy/script/rails +6 -0
  100. data/spec/spec_helper.rb +32 -0
  101. metadata +312 -0
@@ -0,0 +1,292 @@
1
+ # -*- coding: utf-8 -*-
2
+ module Apipie
3
+
4
+ module Validator
5
+
6
+ # to create new validator, inherit from Apipie::Validator::Base
7
+ # and implement class method build and instance method validate
8
+ class BaseValidator
9
+
10
+ attr_accessor :param_description
11
+
12
+ def initialize(param_description)
13
+ @param_description = param_description
14
+ end
15
+
16
+ def self.inherited(subclass)
17
+ @validators ||= []
18
+ @validators.insert 0, subclass
19
+ end
20
+
21
+ # find the right validator for given options
22
+ def self.find(param_description, argument, options, block)
23
+ @validators.each do |validator_type|
24
+ validator = validator_type.build(param_description, argument, options, block)
25
+ return validator if validator
26
+ end
27
+ return nil
28
+ end
29
+
30
+ # check if value is valid
31
+ def valid?(value)
32
+ if self.validate(value)
33
+ @error_value = nil
34
+ true
35
+ else
36
+ @error_value = value
37
+ false
38
+ end
39
+ end
40
+
41
+ def param_name
42
+ @param_description.name
43
+ end
44
+
45
+ # validator description
46
+ def description
47
+ "TODO: validator description"
48
+ end
49
+
50
+ def to_s
51
+ self.description
52
+ end
53
+
54
+ def to_json
55
+ self.description
56
+ end
57
+
58
+ # what type is expected, mostly string
59
+ # this information is used in cli client
60
+ # thor supported types — :string, :hash, :array, :numeric, or :boolean
61
+ def expected_type
62
+ 'string'
63
+ end
64
+
65
+ end
66
+
67
+ # validate arguments type
68
+ class TypeValidator < BaseValidator
69
+
70
+ def initialize(param_description, argument)
71
+ super(param_description)
72
+ @type = argument
73
+ end
74
+
75
+ def validate(value)
76
+ return false if value.nil?
77
+ value.is_a? @type
78
+ end
79
+
80
+ def self.build(param_description, argument, options, block)
81
+ if argument.is_a?(Class) && (argument != Hash || block.nil?)
82
+ self.new(param_description, argument)
83
+ end
84
+ end
85
+
86
+ def error
87
+ "Parameter #{param_name} expecting to be #{@type.name}, got: #{@error_value.class.name}"
88
+ end
89
+
90
+ def description
91
+ "Parameter has to be #{@type}."
92
+ end
93
+
94
+ def expected_type
95
+ if @type.ancestors.include? Hash
96
+ 'hash'
97
+ elsif @type.ancestors.include? Numeric
98
+ 'numeric'
99
+ else
100
+ 'string'
101
+ end
102
+ end
103
+ end
104
+
105
+ # validate arguments value with regular expression
106
+ class RegexpValidator < BaseValidator
107
+
108
+ def initialize(param_description, argument)
109
+ super(param_description)
110
+ @regexp = argument
111
+ end
112
+
113
+ def validate(value)
114
+ value =~ @regexp
115
+ end
116
+
117
+ def self.build(param_description, argument, options, proc)
118
+ self.new(param_description, argument) if argument.is_a? Regexp
119
+ end
120
+
121
+ def error
122
+ "Parameter #{param_name} expecting to match /#{@regexp.source}/, got '#{@error_value}'"
123
+ end
124
+
125
+ def description
126
+ "Parameter has to match regular expression /#{@regexp.source}/."
127
+ end
128
+ end
129
+
130
+ # arguments value must be one of given in array
131
+ class ArrayValidator < BaseValidator
132
+
133
+ def initialize(param_description, argument)
134
+ super(param_description)
135
+ @array = argument
136
+ end
137
+
138
+ def validate(value)
139
+ @array.include?(value)
140
+ end
141
+
142
+ def self.build(param_description, argument, options, proc)
143
+ self.new(param_description, argument) if argument.is_a?(Array)
144
+ end
145
+
146
+ def error
147
+ "Parameter #{param_name} has bad value (#{@error_value.inspect}). Expecting one of: #{@array.join(',')}."
148
+ end
149
+
150
+ def description
151
+ "Parameter has to be one of: #{@array.join(', ')}."
152
+ end
153
+ end
154
+
155
+ class ProcValidator < BaseValidator
156
+
157
+ def initialize(param_description, argument)
158
+ super(param_description)
159
+ @proc = argument
160
+ end
161
+
162
+ def validate(value)
163
+ (@help = @proc.call(value)) === true
164
+ end
165
+
166
+ def self.build(param_description, argument, options, proc)
167
+ self.new(param_description, argument) if argument.is_a?(Proc) && argument.arity == 1
168
+ end
169
+
170
+ def error
171
+ "Parameter #{param_name} has bad value (\"#{@error_value}\"). #{@help}"
172
+ end
173
+
174
+ def description
175
+ ""
176
+ end
177
+ end
178
+
179
+ class HashValidator < BaseValidator
180
+
181
+ attr_reader :hash_params_ordered
182
+
183
+ def self.build(param_description, argument, options, block)
184
+ self.new(param_description, block) if block.is_a?(Proc) && block.arity <= 0 && argument == Hash
185
+ end
186
+
187
+ def initialize(param_description, argument)
188
+ super(param_description)
189
+ @proc = argument
190
+ @hash_params_ordered = []
191
+ @hash_params = {}
192
+
193
+ self.instance_exec(&@proc)
194
+ end
195
+
196
+ def validate(value)
197
+ if @hash_params
198
+ @hash_params.each do |k, p|
199
+ p.validate(value[k]) if value.has_key?(k) || p.required
200
+ end
201
+ end
202
+ return true
203
+ end
204
+
205
+ def error
206
+ "Has to be hash."
207
+ end
208
+
209
+ def description
210
+ "Has to be hash."
211
+ end
212
+
213
+ def param(param_name, *args, &block)
214
+ param_description = Apipie::ParamDescription.new(param_name, *args, &block)
215
+ param_description.parent = self.param_description
216
+ @hash_params_ordered << param_description
217
+ @hash_params[param_name.to_sym] = param_description
218
+ end
219
+
220
+ def expected_type
221
+ 'hash'
222
+ end
223
+ end
224
+
225
+
226
+ # special type of validator: we say that it's not specified
227
+ class UndefValidator < Apipie::Validator::BaseValidator
228
+
229
+ def validate(value)
230
+ true
231
+ end
232
+
233
+ def self.build(param_description, argument, options, block)
234
+ if argument == :undef
235
+ self.new(param_description)
236
+ end
237
+ end
238
+
239
+ def description
240
+ nil
241
+ end
242
+ end
243
+
244
+ class NumberValidator < Apipie::Validator::BaseValidator
245
+
246
+ def validate(value)
247
+ self.class.validate(value)
248
+ end
249
+
250
+ def self.build(param_description, argument, options, block)
251
+ if argument == :number
252
+ self.new(param_description)
253
+ end
254
+ end
255
+
256
+ def error
257
+ "Parameter #{param_name} expecting to be a number, got: #{@error_value}"
258
+ end
259
+
260
+ def description
261
+ "Has to be a number."
262
+ end
263
+
264
+ def self.validate(value)
265
+ value.to_s =~ /\A(0|[1-9]\d*)\Z$/
266
+ end
267
+ end
268
+
269
+ class BooleanValidator < Apipie::Validator::BaseValidator
270
+
271
+ def validate(value)
272
+ %w[true false].include?(value.to_s)
273
+ end
274
+
275
+ def self.build(param_description, argument, options, block)
276
+ if argument == :bool
277
+ self.new(param_description)
278
+ end
279
+ end
280
+
281
+ def error
282
+ "Parameter #{param_name} expecting to be a boolean value, got: #{@error_value}"
283
+ end
284
+
285
+ def description
286
+ "Has to be a boolean"
287
+ end
288
+ end
289
+
290
+ end
291
+ end
292
+
@@ -0,0 +1,3 @@
1
+ module Apipie
2
+ VERSION = '0.0.7'
3
+ end
@@ -0,0 +1,156 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'fileutils'
3
+ require 'apipie/client/generator'
4
+
5
+ namespace :apipie do
6
+
7
+ desc "Generate static documentation"
8
+ # You can specify OUT=output_base_file to have the following structure:
9
+ #
10
+ # output_base_file.html
11
+ # output_base_file-onepage.html
12
+ # output_base_file
13
+ # | - resource1.html
14
+ # | - resource1
15
+ # | - | - method1.html
16
+ # | - | - method2.html
17
+ # | - resource2.html
18
+ #
19
+ # By default OUT="#{Rails.root}/doc/apidoc"
20
+ task :static => :environment do
21
+ with_loaded_documentation do
22
+ out = ENV["OUT"] || File.join(::Rails.root, 'doc', 'apidoc')
23
+ subdir = File.basename(out)
24
+
25
+ copy_jscss(out)
26
+
27
+ Apipie.url_prefix = "./#{subdir}"
28
+ doc = Apipie.to_json
29
+ generate_one_page(out, doc)
30
+ generate_plain_page(out, doc)
31
+ generate_index_page(out, doc)
32
+ Apipie.url_prefix = "../#{subdir}"
33
+ generate_resource_pages(out, doc)
34
+ Apipie.url_prefix = "../../#{subdir}"
35
+ generate_method_pages(out, doc)
36
+ end
37
+ end
38
+
39
+ desc "Generate cache to avoid production dependencies on markup languages"
40
+ task :cache => :environment do
41
+ with_loaded_documentation do
42
+ cache_dir = Apipie.configuration.cache_dir
43
+ file_base = File.join(cache_dir, Apipie.configuration.doc_base_url)
44
+ doc = Apipie.to_json
45
+ generate_index_page(file_base, doc, true)
46
+ generate_resource_pages(file_base, doc, true)
47
+ generate_method_pages(file_base, doc, true)
48
+ end
49
+ end
50
+
51
+ def renderer
52
+ ActionView::Base.new(File.expand_path("../../../app/views/apipie/apipies", __FILE__))
53
+ end
54
+
55
+ def render_page(file_name, template, variables, layout = 'apipie')
56
+ av = renderer
57
+ File.open(file_name, "w") do |f|
58
+ variables.each do |var, val|
59
+ av.instance_variable_set("@#{var}", val)
60
+ end
61
+ f.write av.render(
62
+ :template => "#{template}",
63
+ :layout => (layout && "../../layouts/apipie/#{layout}"))
64
+ end
65
+ end
66
+
67
+ def generate_one_page(file_base, doc)
68
+ FileUtils.mkdir_p(File.dirname(file_base)) unless File.exists?(File.dirname(file_base))
69
+
70
+ render_page("#{file_base}-onepage.html", "static", {:doc => doc[:docs]})
71
+ end
72
+
73
+ def generate_plain_page(file_base, doc)
74
+ FileUtils.mkdir_p(File.dirname(file_base)) unless File.exists?(File.dirname(file_base))
75
+
76
+ render_page("#{file_base}-plain.html", "plain", {:doc => doc[:docs]}, nil)
77
+ end
78
+
79
+ def generate_index_page(file_base, doc, include_json = false)
80
+ FileUtils.mkdir_p(File.dirname(file_base)) unless File.exists?(File.dirname(file_base))
81
+
82
+ render_page("#{file_base}.html", "index", {:doc => doc[:docs]})
83
+
84
+ File.open("#{file_base}.json", "w") { |f| f << doc.to_json } if include_json
85
+ end
86
+
87
+ def generate_resource_pages(file_base, doc, include_json = false)
88
+ doc[:docs][:resources].each do |resource_name, _|
89
+ resource_file_base = File.join(file_base, resource_name.to_s)
90
+ FileUtils.mkdir_p(File.dirname(resource_file_base)) unless File.exists?(File.dirname(resource_file_base))
91
+
92
+ doc = Apipie.to_json(resource_name)
93
+ render_page("#{resource_file_base}.html", "resource", {:doc => doc[:docs],
94
+ :resource => doc[:docs][:resources].first})
95
+ File.open("#{resource_file_base}.json", "w") { |f| f << doc.to_json } if include_json
96
+ end
97
+ end
98
+
99
+ def generate_method_pages(file_base, doc, include_json = false)
100
+ doc[:docs][:resources].each do |resource_name, resource_params|
101
+ resource_params[:methods].each do |method|
102
+ method_file_base = File.join(file_base, resource_name.to_s, method[:name].to_s)
103
+ FileUtils.mkdir_p(File.dirname(method_file_base)) unless File.exists?(File.dirname(method_file_base))
104
+
105
+ doc = Apipie.to_json(resource_name, method[:name])
106
+ render_page("#{method_file_base}.html", "method", {:doc => doc[:docs],
107
+ :resource => doc[:docs][:resources].first,
108
+ :method => doc[:docs][:resources].first[:methods].first})
109
+
110
+ File.open("#{method_file_base}.json", "w") { |f| f << doc.to_json } if include_json
111
+ end
112
+ end
113
+ end
114
+
115
+ def with_loaded_documentation
116
+ Apipie.configuration.use_cache = false # we don't want to skip DSL evaluation
117
+ Dir[File.join(Rails.root, "app", "controllers", "**","*.rb")].each {|f| load f}
118
+ yield
119
+ end
120
+
121
+ def copy_jscss(dest)
122
+ src = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'app', 'public', 'apipie'))
123
+ FileUtils.mkdir_p dest
124
+ FileUtils.cp_r "#{src}/.", dest
125
+ end
126
+
127
+
128
+ desc "Generate CLI client for API documented with apipie gem."
129
+ task :client => [:environment] do |t, args|
130
+ Apipie.configuration.use_cache = false # we don't want to skip DSL evaluation
131
+ Apipie.api_controllers_paths.each { |file| require file }
132
+
133
+ Apipie::Client::Generator.start(Apipie.configuration.app_name)
134
+ end
135
+
136
+ def plaintext(text)
137
+ text.gsub(/<.*?>/, '').gsub("\n",' ').strip
138
+ end
139
+
140
+ desc "Update api description in controllers base on routes"
141
+ task :update_from_routes => [:environment] do
142
+ Apipie.configuration.force_dsl = true
143
+ ignored = Apipie.configuration.ignored_by_recorder
144
+ with_loaded_documentation do
145
+ apis_from_routes = Apipie::Extractor.apis_from_routes
146
+ apis_from_routes.each do |(controller, action), apis|
147
+ next if ignored.include?(controller)
148
+ next if ignored.include?("#{controller}##{action}")
149
+ Apipie::Extractor::Writer.update_action_description(controller.constantize, action) do |u|
150
+ u.update_apis(apis)
151
+ end
152
+ end
153
+ end
154
+ end
155
+
156
+ end