praxis 0.10.1 → 0.11pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -1
  3. data/CHANGELOG.md +47 -10
  4. data/Gemfile +1 -1
  5. data/Guardfile +1 -0
  6. data/bin/praxis +33 -4
  7. data/lib/api_browser/app/css/main.css +0 -3
  8. data/lib/praxis.rb +16 -0
  9. data/lib/praxis/action_definition.rb +16 -18
  10. data/lib/praxis/application.rb +31 -2
  11. data/lib/praxis/bootloader.rb +37 -4
  12. data/lib/praxis/bootloader_stages/environment.rb +3 -7
  13. data/lib/praxis/bootloader_stages/plugin_config_load.rb +20 -0
  14. data/lib/praxis/bootloader_stages/plugin_config_prepare.rb +18 -0
  15. data/lib/praxis/bootloader_stages/plugin_loader.rb +19 -0
  16. data/lib/praxis/bootloader_stages/plugin_setup.rb +13 -0
  17. data/lib/praxis/bootloader_stages/routing.rb +16 -6
  18. data/lib/praxis/callbacks.rb +0 -2
  19. data/lib/praxis/config.rb +3 -2
  20. data/lib/praxis/dispatcher.rb +25 -13
  21. data/lib/praxis/error_handler.rb +16 -0
  22. data/lib/praxis/links.rb +9 -4
  23. data/lib/praxis/media_type_collection.rb +2 -3
  24. data/lib/praxis/notifications.rb +41 -0
  25. data/lib/praxis/plugin.rb +18 -8
  26. data/lib/praxis/plugin_concern.rb +40 -0
  27. data/lib/praxis/request.rb +27 -7
  28. data/lib/praxis/request_stages/action.rb +7 -2
  29. data/lib/praxis/request_stages/response.rb +7 -3
  30. data/lib/praxis/request_stages/validate_payload.rb +7 -1
  31. data/lib/praxis/resource_definition.rb +37 -16
  32. data/lib/praxis/response.rb +1 -0
  33. data/lib/praxis/responses/internal_server_error.rb +13 -8
  34. data/lib/praxis/responses/validation_error.rb +10 -7
  35. data/lib/praxis/restful_doc_generator.rb +312 -0
  36. data/lib/praxis/router.rb +7 -5
  37. data/lib/praxis/skeletor/restful_routing_config.rb +12 -5
  38. data/lib/praxis/stage.rb +5 -1
  39. data/lib/praxis/stats.rb +106 -0
  40. data/lib/praxis/tasks/api_docs.rb +8 -314
  41. data/lib/praxis/version.rb +1 -1
  42. data/praxis.gemspec +4 -1
  43. data/spec/functional_spec.rb +87 -32
  44. data/spec/praxis/action_definition_spec.rb +13 -12
  45. data/spec/praxis/bootloader_spec.rb +12 -5
  46. data/spec/praxis/notifications_spec.rb +23 -0
  47. data/spec/praxis/plugin_concern_spec.rb +21 -0
  48. data/spec/praxis/request_spec.rb +56 -1
  49. data/spec/praxis/request_stages_validate_spec.rb +3 -3
  50. data/spec/praxis/resource_definition_spec.rb +44 -60
  51. data/spec/praxis/responses/internal_server_error_spec.rb +32 -16
  52. data/spec/praxis/restful_routing_config_spec.rb +15 -2
  53. data/spec/praxis/router_spec.rb +5 -3
  54. data/spec/praxis/stats_spec.rb +9 -0
  55. data/spec/praxis_mapper_plugin_spec.rb +71 -0
  56. data/spec/spec_app/app/controllers/instances.rb +12 -0
  57. data/spec/spec_app/app/controllers/volumes.rb +5 -0
  58. data/spec/spec_app/app/models/person.rb +3 -0
  59. data/spec/spec_app/config/active_record.yml +2 -0
  60. data/spec/spec_app/config/authentication.yml +3 -0
  61. data/spec/spec_app/config/authorization.yml +4 -0
  62. data/spec/spec_app/config/environment.rb +28 -1
  63. data/spec/spec_app/config/praxis_mapper.yml +6 -0
  64. data/spec/spec_app/config/sequel_model.yml +2 -0
  65. data/spec/spec_app/config/stats.yml +8 -0
  66. data/spec/spec_app/config/stats.yml.dis +8 -0
  67. data/spec/spec_app/design/resources/instances.rb +53 -16
  68. data/spec/spec_app/design/resources/volumes.rb +13 -2
  69. data/spec/spec_helper.rb +14 -0
  70. data/spec/support/spec_authentication_plugin.rb +126 -0
  71. data/spec/support/spec_authorization_plugin.rb +95 -0
  72. data/spec/support/spec_praxis_mapper_plugin.rb +157 -0
  73. data/tasks/loader.thor +6 -0
  74. data/tasks/thor/app.rb +48 -0
  75. data/tasks/thor/example.rb +283 -0
  76. data/tasks/thor/templates/generator/empty_app/.gitignore +3 -0
  77. data/tasks/thor/templates/generator/empty_app/.rspec +1 -0
  78. data/tasks/thor/templates/generator/empty_app/Gemfile +29 -0
  79. data/tasks/thor/templates/generator/empty_app/Guardfile +3 -0
  80. data/tasks/thor/templates/generator/empty_app/README.md +4 -0
  81. data/tasks/thor/templates/generator/empty_app/Rakefile +25 -0
  82. data/tasks/thor/templates/generator/empty_app/app/models/.empty_directory +0 -0
  83. data/tasks/thor/templates/generator/empty_app/app/models/.gitkeep +0 -0
  84. data/tasks/thor/templates/generator/empty_app/app/responses/.empty_directory +0 -0
  85. data/tasks/thor/templates/generator/empty_app/app/responses/.gitkeep +0 -0
  86. data/tasks/thor/templates/generator/empty_app/app/v1/controllers/.empty_directory +0 -0
  87. data/tasks/thor/templates/generator/empty_app/app/v1/controllers/.gitkeep +0 -0
  88. data/tasks/thor/templates/generator/empty_app/config.ru +7 -0
  89. data/tasks/thor/templates/generator/empty_app/config/environment.rb +17 -0
  90. data/tasks/thor/templates/generator/empty_app/config/rainbows.rb +57 -0
  91. data/tasks/thor/templates/generator/empty_app/design/api.rb +0 -0
  92. data/tasks/thor/templates/generator/empty_app/design/response_templates/.empty_directory +0 -0
  93. data/tasks/thor/templates/generator/empty_app/design/response_templates/.gitkeep +0 -0
  94. data/tasks/thor/templates/generator/empty_app/design/v1/media_types/.empty_directory +0 -0
  95. data/tasks/thor/templates/generator/empty_app/design/v1/media_types/.gitkeep +0 -0
  96. data/tasks/thor/templates/generator/empty_app/design/v1/resources/.empty_directory +0 -0
  97. data/tasks/thor/templates/generator/empty_app/design/v1/resources/.gitkeep +0 -0
  98. data/tasks/thor/templates/generator/empty_app/spec/spec_helper.rb +18 -0
  99. metadata +97 -6
  100. data/tasks/praxis_app_generator.thor +0 -307
data/lib/praxis/router.rb CHANGED
@@ -11,7 +11,7 @@ module Praxis
11
11
  @version = version
12
12
  end
13
13
  def call(request)
14
- if request.version == @version
14
+ if request.version(@target.action.resource_definition.version_options) == @version
15
15
  @target.call(request)
16
16
  else
17
17
  # Version doesn't match, pass and continue
@@ -63,18 +63,20 @@ module Praxis
63
63
  raise ArgumentError, "received #{env_or_request.class}"
64
64
  end
65
65
 
66
- version = request.version
67
66
  verb = request.verb
68
67
  result = @routes[verb].call(request)
68
+
69
69
  if result == :not_found
70
-
70
+ # no need to try :path as we cannot really know if you've attempted to pass a version through it here
71
+ # plus we wouldn't have tracked it as unmatched
72
+ version = request.version(using: [:header,:params])
71
73
  attempted_versions = request.unmatched_versions
72
74
  body = "NotFound"
73
75
  unless attempted_versions.empty? || (attempted_versions.size == 1 && attempted_versions.first == 'n/a')
74
- body += if request.version == 'n/a'
76
+ body += if version == 'n/a'
75
77
  ". Your request did not specify an API version.".freeze
76
78
  else
77
- ". Your request speficied API version = \"#{request.version}\"."
79
+ ". Your request speficied API version = \"#{version}\"."
78
80
  end
79
81
  pretty_versions = attempted_versions.collect(&:inspect).join(', ')
80
82
  body += " Available versions = #{pretty_versions}."
@@ -9,8 +9,15 @@ module Praxis
9
9
  @resource_definition = resource_definition
10
10
  @routes = []
11
11
 
12
- @prefix = "/" + resource_definition.name.split("::").last.underscore
13
-
12
+ @version_prefix = ""
13
+ if resource_definition.version_options
14
+ version_using = Array(resource_definition.version_options[:using])
15
+ if version_using.include?(:path)
16
+ @version_prefix = "#{Praxis::Request::path_version_prefix}#{resource_definition.version}"
17
+ end
18
+ end
19
+ prefix( "/" + resource_definition.name.split("::").last.underscore )
20
+
14
21
  if resource_definition.routing_config
15
22
  instance_eval(&resource_definition.routing_config)
16
23
  end
@@ -19,8 +26,8 @@ module Praxis
19
26
  end
20
27
 
21
28
  def prefix(prefix=nil)
22
- return @prefix unless prefix
23
- @prefix = prefix
29
+ @path_prefix = prefix if prefix
30
+ @version_prefix + @path_prefix
24
31
  end
25
32
 
26
33
  def options(path, opts={}) add_route 'OPTIONS', path, opts end
@@ -34,7 +41,7 @@ module Praxis
34
41
  def patch(path, opts={}) add_route 'PATCH', path, opts end
35
42
 
36
43
  def add_route(verb, path, options={})
37
- path = Mustermann.new(@prefix + path)
44
+ path = Mustermann.new(prefix + path)
38
45
 
39
46
  @routes << Route.new(verb, path, resource_definition.version, **options)
40
47
  end
data/lib/praxis/stage.rb CHANGED
@@ -2,7 +2,11 @@ module Praxis
2
2
 
3
3
  class Stage
4
4
 
5
- attr_reader :name, :context, :stages, :before_callbacks, :after_callbacks
5
+ attr_reader :name
6
+ attr_reader :context
7
+ attr_reader :stages
8
+ attr_reader :before_callbacks
9
+ attr_reader :after_callbacks
6
10
 
7
11
  def application
8
12
  context
@@ -0,0 +1,106 @@
1
+ require 'singleton'
2
+
3
+ require 'harness'
4
+
5
+ module Praxis
6
+
7
+ module Stats
8
+ include Praxis::PluginConcern
9
+
10
+ class Statsd < ::Statsd
11
+ def initialize(host: '127.0.0.1', port: 8125, prefix: nil, postfix: nil)
12
+ self.host = host
13
+ self.port = port
14
+ self.namespace = prefix if prefix
15
+ self.postfix = postfix
16
+ @batch_size = 10
17
+ end
18
+ end
19
+
20
+ class Plugin < Praxis::Plugin
21
+ include Singleton
22
+
23
+ def initialize
24
+ @options = {config_file: 'config/stats.yml'}
25
+ end
26
+
27
+ def config_key
28
+ :stats # 'praxis.stats'
29
+ end
30
+
31
+ def prepare_config!(node)
32
+ node.attributes do
33
+ attribute :collector, Hash, default: {type: 'Harness::FakeCollector'} do
34
+ key :type, String, required: true
35
+ key :args, Hash
36
+ end
37
+ attribute :queue, Hash, default: {type: 'Harness::AsyncQueue' } do
38
+ key :type, String, required: true
39
+ key :args, Hash
40
+ end
41
+ end
42
+ end
43
+
44
+ def setup!
45
+ Harness.config.collector = load_type(config.collector)
46
+ Harness.config.queue = load_type(config.queue)
47
+ end
48
+
49
+ def load_type(hash)
50
+ type = hash[:type].constantize
51
+ args = hash[:args]
52
+ case args
53
+ when Attributor::Hash
54
+ type.new(**args.contents.symbolize_keys)
55
+ when Hash
56
+ type.new(**args.symbolize_keys)
57
+ when nil
58
+ type.new
59
+ else
60
+ raise "unknown args type: #{args.class.name}"
61
+ end
62
+ end
63
+
64
+ end
65
+
66
+
67
+ def self.collector
68
+ Harness.collector
69
+ end
70
+
71
+ def self.config
72
+ Harness.config
73
+ end
74
+
75
+ def self.queue
76
+ Harness.queue
77
+ end
78
+
79
+ def self.count(*args)
80
+ Harness.count(*args)
81
+ end
82
+
83
+ def self.decrement(*args)
84
+ Harness.decrement(*args)
85
+ end
86
+
87
+ def self.gauge(*args)
88
+ Harness.gauge(*args)
89
+ end
90
+
91
+ def self.increment(*args)
92
+ Harness.increment(*args)
93
+ end
94
+
95
+ def self.time(stat, sample_rate = 1, &block)
96
+ Harness.time(stat, sample_rate, &block)
97
+ end
98
+
99
+ def self.timing(*args)
100
+ Harness.timing(*args)
101
+ end
102
+
103
+
104
+ end
105
+
106
+ end
@@ -1,328 +1,20 @@
1
- class RestfulDocGenerator
2
-
3
- class << self
4
- attr_reader :inspected_types
5
- end
6
-
7
- @inspected_types = Set.new
8
- API_DOCS_DIRNAME = 'api_docs'
9
-
10
- EXCLUDED_TYPES_FROM_TOP_LEVEL = Set.new( [Attributor::Boolean, Attributor::CSV, Attributor::DateTime, Attributor::Float, Attributor::Hash, Attributor::Ids, Attributor::Integer, Attributor::Object, Attributor::String ] ).freeze
11
-
12
- def self.inspect_attributes(the_type)
13
-
14
- reachable = Set.new
15
- return reachable if the_type.nil? || the_type.is_a?(Praxis::SimpleMediaType)
16
-
17
- # If an attribute comes in, get its type
18
- the_type = the_type.type if the_type.is_a? Attributor::Attribute
19
-
20
- # Collection types are special since they wrap a member type, so let's reach in and grab it
21
- the_type = the_type.member_attribute.type if the_type < Attributor::Collection
22
-
23
- if @inspected_types.include? the_type
24
- # We're done if we've already inspected it
25
- return reachable
26
- else
27
- # Mark it as inspected (before recursing)
28
- @inspected_types << the_type unless the_type.name == nil # Don't bother with anon structs
29
- end
30
- #puts "Inspecting type: #{the_type.name}" if the_type.name != nil
31
-
32
- reachable << the_type unless the_type.name == nil # Don't bother with anon structs
33
- if the_type.respond_to? :attributes
34
- the_type.attributes.each do |name, attr|
35
- attr_type = attr.type
36
- #puts "Inspecting attr: #{name} (class: #{attr_type.name}) #{attr_type.inspect}"
37
- reachable += self.inspect_attributes(attr_type)
38
- end
39
- end
40
- reachable
41
- end
42
-
43
- class Resource
44
-
45
- attr_accessor :media_type, :reachable_types, :version, :controller_config
46
-
47
- def initialize( definition )
48
- @controller_config = definition
49
- if controller_config.version == 'n/a'
50
- @version = 'unversioned'
51
- else
52
- @version = controller_config.version
53
- end
54
- @media_type = controller_config.media_type
55
- @reachable_types = Set.new
56
-
57
- # Collect reachable types from the media_type if any (plus itself)
58
- if @media_type && ! @media_type.is_a?(Praxis::SimpleMediaType)
59
- add_to_reachable RestfulDocGenerator.inspect_attributes(@media_type)
60
- @media_type.attributes.each do |name, attr|
61
- add_to_reachable RestfulDocGenerator.inspect_attributes(attr)
62
- end
63
- @generated_example = @media_type.example(self.id)
64
- end
65
-
66
- # Collect reachable types from the params and payload definitions
67
- @controller_config.actions.each do |name, action_config|
68
- add_to_reachable RestfulDocGenerator.inspect_attributes(action_config.params)
69
- add_to_reachable RestfulDocGenerator.inspect_attributes(action_config.payload)
70
- end
71
-
72
- end
73
-
74
- # TODO: I think that the "id"/"name" of a resource should be provided by the definition/controller...not derived here
75
- def id
76
- if @controller_config.controller
77
- @controller_config.controller.name
78
- else
79
- # If an API doesn't quite have the controller defined, let's use the name from the resource definition
80
- @controller_config.name
81
- end
82
- end
83
-
84
- def add_to_reachable( found )
85
- return if found == nil
86
- @reachable_types += found
87
- end
88
- end
89
-
90
- def initialize(root_dir)
91
- @root_dir = root_dir
92
- @doc_root_dir = File.join(@root_dir, API_DOCS_DIRNAME)
93
- @resources = []
94
-
95
- remove_previous_doc_data
96
- load_resources
97
-
98
- # Gather all reachable types (grouped by version)
99
- types_for = Hash.new
100
- @resources.each do |r|
101
- types_for[r.version] ||= Set.new
102
- types_for[r.version] += r.reachable_types
103
- end
104
-
105
- write_resources
106
- write_types(types_for)
107
- write_index(types_for)
108
- write_templates(types_for)
109
- end
110
-
111
- def load_resources
112
- Praxis::Application.instance.resource_definitions.map do |resource|
113
- @resources << Resource.new(resource)
114
- end
115
-
116
- end
117
-
118
- def dump_example_for(context_name, object)
119
- example = object.example(Array(context_name))
120
- if object.is_a? Praxis::Blueprint
121
- example.render(:master)
122
- else
123
- example.dump
124
- end
125
- end
126
-
127
- def write_resources
128
- @resources.each do |r|
129
- filename = File.join(@doc_root_dir, r.version, "resources","#{r.id}.json")
130
- #puts "Dumping #{r.id} to #{filename}"
131
- base = File.dirname(filename)
132
- FileUtils.mkdir_p base unless File.exists? base
133
- resource_description = r.controller_config.describe
134
- # Go through the params/payload of each action and generate an example for them (then stick it into the description hash)
135
- r.controller_config.actions.each do |action_name, action|
136
- generated_examples = {}
137
- if action.params
138
- generated_examples[:params] = dump_example_for( r.id, action.params )
139
- end
140
- if action.payload
141
- generated_examples[:payload] = dump_example_for( r.id, action.payload )
142
- end
143
- action_description = resource_description[:actions].find{|a| a[:name] == action_name }
144
- action_description[:params][:example] = generated_examples[:params] if generated_examples[:params]
145
- action_description[:payload][:example] = generated_examples[:payload] if generated_examples[:payload]
146
- end
147
-
148
- File.open(filename, 'w') {|f| f.write(JSON.dump(resource_description))}
149
- end
150
- end
151
-
152
- def write_types( versioned_types )
153
- versioned_types.each do |version, types|
154
- dirname = File.join(@doc_root_dir, version, "types")
155
- FileUtils.mkdir_p dirname unless File.exists? dirname
156
- reportable_types = types - EXCLUDED_TYPES_FROM_TOP_LEVEL
157
- reportable_types.each do |type|
158
- filename = File.join(dirname, "#{type.name}.json")
159
- #puts "Dumping #{type.name} to #{filename}"
160
- type_output = type.describe
161
- example_data = type.example(type.to_s)
162
- if type_output[:views]
163
- type_output[:views].delete(:master)
164
- type_output[:views].each do |view_name, view_info|
165
- # Add and example for each view
166
- unless( type < Praxis::Links ) #TODO: why do we need to skip an example for links?
167
- view_info[:example] = example_data.render(view_name)
168
- end
169
- end
170
- end
171
- # Save a full type example
172
- # ...but not for links or link classes (there's no object container context if done alone!!)
173
- unless( type < Praxis::Links ) #TODO: again, why is this special?
174
- type_output[:example] = if example_data.respond_to? :render
175
- example_data.render(:master)
176
- else
177
- example_data.dump
178
- end
179
- end
180
-
181
- # add an example for each attribute??
182
- File.open(filename, 'w') {|f| f.write(JSON.dump(type_output))}
183
- end
184
- end
185
- end
186
-
187
- # index looks like something like this:
188
- # {'1.0':
189
- # {
190
- # // Typical entry for controller with an associated mediatype
191
- # "Post" : { media_type: "V1::MT:Post" , controller: "V1:Ctrl:Post"}
192
- # // Unusual entry for controller without an associated mediatype
193
- # "Admin" : { controller: "V1:Ctrl:Admin" }
194
- # // Entry for mediatype that somehow is not associated with any controller...
195
- # "RemoteMT" : { media_type: "V1:Ctrl:RemoteMT" }
196
- # // Entry to a non-primitive type (but not a mediatype), that it is not covered by any related controller or mt
197
- # "Locale" : { kind: "Module::Locale"}
198
- # }
199
- #
200
- # '2.0': { ... }
201
- # }
202
- def write_index( versioned_types )
203
- index = Hash.new
204
- media_types_seen_from_controllers = Set.new
205
- # Process the resources first
206
-
207
- @resources.each do |r|
208
- index[r.version] ||= Hash.new
209
- info = {controller: r.id}
210
- if r.media_type
211
- info[:media_type] = r.media_type.name
212
- media_types_seen_from_controllers << r.media_type
213
- end
214
- display_name = r.id.split("::").last
215
- index[r.version][display_name] = info
216
- end
217
-
218
- versioned_types.each do |version, types|
219
- # Discard any mediatypes that we've already seen and processed as controller related
220
- reportable_types = types - media_types_seen_from_controllers - EXCLUDED_TYPES_FROM_TOP_LEVEL
221
- #TODO: think about these special cases, is it needed?
222
- reportable_types.reject!{|type| type < Praxis::Links || type < Praxis::MediaTypeCollection }
223
-
224
- reportable_types.each do |type|
225
- index[version] ||= Hash.new
226
- display_name = type.name.split("::").last + " (*)" #somehow this is just a MT so we probably wanna mark it different
227
- if index[version].has_key? display_name
228
- raise "Display name already taken for version #{version}! #{display_name}"
229
- end
230
- index[version][display_name] = if type < Praxis::MediaType
231
- {media_type: type.name }
232
- else
233
- {kind: type.name}
234
- end
235
- end
236
- end
237
- filename = File.join(@doc_root_dir, "index.json")
238
- dirname = File.dirname(filename)
239
- FileUtils.mkdir_p dirname unless File.exists? dirname
240
- File.open(filename, 'w') {|f| f.write(JSON.dump(index))}
241
- end
242
-
243
- def write_templates(versioned_types)
244
- # Calculate and write top-level (non-versioned) templates
245
- top_templates = write_template("")
246
- # Calculate and write versioned templates (passing the top level ones for inheritance)
247
- versioned_types.keys.each do |version|
248
- write_template(version,top_templates)
249
- end
250
- end
251
-
252
- def write_template(version,top_templates=nil)
253
- # Collect template filenames (grouped by type: embedded vs. standalone)
254
- templates_dir = File.join(@root_dir,"doc_browser","templates",version)
255
-
256
- # Slurp in any top level (unversioned) templates if any
257
- # Top level templates will apply to any versioned one (and can be overwritten
258
- # if the version defines their own)
259
- templates = {embedded: {}, standalone: {} }
260
- if top_templates
261
- templates[:embedded] = top_templates[:embedded].clone
262
- templates[:standalone] = top_templates[:standalone].clone
263
- end
264
-
265
- dual = Dir.glob(File.join(templates_dir,"*.tmpl"))
266
- embedded = Dir.glob(File.join(templates_dir,"embedded","*.tmpl"))
267
- standalone = Dir.glob(File.join(templates_dir,"standalone","*.tmpl"))
268
-
269
- # TODO: Encode the contents more appropriately rather than dumping a string
270
- # Templates defined at the top will apply to both embedded and standalone
271
- # But it can be overriden if the same type exists in the more specific directory
272
- dual.each do |filename|
273
- type_key = File.basename(filename).gsub(/.tmpl$/,'')
274
- contents = IO.read(filename)
275
- templates[:embedded][type_key] = contents
276
- templates[:standalone][type_key] = contents
277
- end
278
-
279
- # For each embedded one, create a key in the embedded section, and encode the file contents in the value
280
- embedded.each do |filename|
281
- type_key = File.basename(filename).gsub(/.tmpl$/,'')
282
- templates[:embedded][type_key] = IO.read(filename)
283
- end
284
- # For each standalone one, create a key in the standalone section, and encode the file contents in the value
285
- standalone.each do |filename|
286
- type_key = File.basename(filename).gsub(/.tmpl$/,'')
287
- templates[:standalone][type_key] = IO.read(filename)
288
- end
289
-
290
- [:embedded,:standalone].each do |type|
291
- v = version.empty? ? "top level": version
292
- puts "Packaging #{v} #{type} templates for: #{templates[type].keys}" unless templates[type].keys.empty?
293
- end
294
-
295
- # Write the resulting hash to the final file in the docs directory
296
- filename = File.join(@doc_root_dir, version, "templates.json")
297
- dirname = File.dirname(filename)
298
- FileUtils.mkdir_p dirname unless File.exists? dirname
299
- File.open(filename, 'w') {|f| f.write(JSON.dump(templates))}
300
- return templates
301
- end
302
- private
303
-
304
- def remove_previous_doc_data
305
- FileUtils.rm_rf @doc_root_dir if File.exists?(@doc_root_dir)
306
- end
307
-
308
- end
309
-
310
1
  namespace :praxis do
311
2
  desc "Generate API docs (JSON definitions) for a Praxis App"
312
3
  task :api_docs => [:environment] do |t, args|
313
4
  require 'fileutils'
314
5
 
315
6
  Praxis::Blueprint.caching_enabled = false
316
- generator = RestfulDocGenerator.new(Dir.pwd)
7
+ generator = Praxis::RestfulDocGenerator.new(Dir.pwd)
317
8
  end
318
9
 
319
- desc "API Documenation Browser"
320
10
  desc "API Documentation Browser"
321
- task :doc_browser => [:api_docs] do
11
+ task :doc_browser, [:port] => :api_docs do |t, args|
12
+ args.with_defaults port: 4567
13
+
322
14
  public_folder = File.expand_path("../../../", __FILE__) + "/api_browser/app"
323
15
  app = Rack::Builder.new do
324
16
  map "/docs" do # application JSON docs
325
- use Rack::Static, urls: [""], root: File.join(Dir.pwd, RestfulDocGenerator::API_DOCS_DIRNAME)
17
+ use Rack::Static, urls: [""], root: File.join(Dir.pwd, Praxis::RestfulDocGenerator::API_DOCS_DIRNAME)
326
18
  end
327
19
  map "/" do # Assets mapping
328
20
  use Rack::Static, urls: [""], root: public_folder, index: "index.html"
@@ -331,6 +23,8 @@ namespace :praxis do
331
23
  run lambda { |env| [404, {'Content-Type' => 'text/plain'}, ['Not Found']] }
332
24
  end
333
25
 
334
- Rack::Server.start app: app, Port: 4567
26
+
27
+ Rack::Server.start app: app, Port: args[:port]
335
28
  end
29
+
336
30
  end