praxis 0.20.1 → 0.21

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 (51) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/.travis.yml +2 -2
  4. data/CHANGELOG.md +36 -0
  5. data/lib/api_browser/Gruntfile.js +33 -3
  6. data/lib/api_browser/app/index.html +3 -0
  7. data/lib/api_browser/app/js/factories/Example.js +4 -0
  8. data/lib/api_browser/app/js/factories/template_for.js +14 -0
  9. data/lib/api_browser/app/js/filters/attribute_name.js +3 -2
  10. data/lib/api_browser/app/js/filters/header_info.js +9 -0
  11. data/lib/api_browser/app/views/action.html +11 -2
  12. data/lib/api_browser/app/views/controller.html +5 -5
  13. data/lib/api_browser/app/views/menu.html +1 -1
  14. data/lib/api_browser/app/views/type.html +4 -23
  15. data/lib/api_browser/app/views/type/details.html +7 -4
  16. data/lib/api_browser/app/views/types/main/array.html +22 -0
  17. data/lib/api_browser/app/views/types/main/default.html +23 -0
  18. data/lib/api_browser/app/views/types/main/hash.html +23 -0
  19. data/lib/praxis.rb +2 -0
  20. data/lib/praxis/action_definition.rb +0 -8
  21. data/lib/praxis/api_definition.rb +11 -6
  22. data/lib/praxis/api_general_info.rb +13 -0
  23. data/lib/praxis/bootloader.rb +11 -5
  24. data/lib/praxis/docs/generator.rb +31 -11
  25. data/lib/praxis/docs/link_builder.rb +30 -0
  26. data/lib/praxis/extensions/field_expansion.rb +2 -2
  27. data/lib/praxis/media_type.rb +1 -1
  28. data/lib/praxis/middleware_app.rb +30 -0
  29. data/lib/praxis/resource_definition.rb +24 -2
  30. data/lib/praxis/response.rb +2 -1
  31. data/lib/praxis/response_definition.rb +2 -2
  32. data/lib/praxis/responses/http.rb +28 -92
  33. data/lib/praxis/responses/validation_error.rb +4 -1
  34. data/lib/praxis/tasks/api_docs.rb +11 -24
  35. data/lib/praxis/trait.rb +12 -7
  36. data/lib/praxis/validation_handler.rb +2 -1
  37. data/lib/praxis/version.rb +1 -1
  38. data/praxis.gemspec +11 -7
  39. data/spec/api_browser/filters/attribute_name_spec.js +2 -2
  40. data/spec/praxis/action_definition_spec.rb +23 -1
  41. data/spec/praxis/bootloader_spec.rb +28 -0
  42. data/spec/praxis/extensions/field_expansion_spec.rb +10 -0
  43. data/spec/praxis/middleware_app_spec.rb +55 -0
  44. data/spec/praxis/plugins/praxis_mapper_plugin_spec.rb +8 -3
  45. data/spec/praxis/resource_definition_spec.rb +51 -2
  46. data/spec/praxis/response_definition_spec.rb +16 -4
  47. data/spec/praxis/response_spec.rb +1 -1
  48. data/spec/praxis/trait_spec.rb +13 -0
  49. data/spec/spec_app/config/environment.rb +11 -1
  50. metadata +30 -25
  51. data/lib/praxis/restful_doc_generator.rb +0 -439
@@ -117,7 +117,7 @@ describe Praxis::Response do
117
117
  end
118
118
 
119
119
  context 'multipart responses' do
120
- let(:part) { Praxis::MultipartPart.new('not so ok', {'Status' => 400, "Location" => "somewhere"}) }
120
+ let(:part) { Praxis::MultipartPart.new('not so ok', {'Status' => 400, "Location" => "somewhere"}, {}) }
121
121
 
122
122
  context '#add_part' do
123
123
 
@@ -13,8 +13,11 @@ describe Praxis::Trait do
13
13
  response :something
14
14
  response :nothing
15
15
 
16
+ # Double params to test "additive behavior"
16
17
  params do
17
18
  attribute :app_name, String
19
+ end
20
+ params do
18
21
  attribute :order, String,
19
22
  description: "Field to sort by."
20
23
  end
@@ -48,4 +51,14 @@ describe Praxis::Trait do
48
51
 
49
52
  end
50
53
 
54
+ context 'apply!' do
55
+ let(:target) { double("Target") }
56
+ it 'does' do
57
+ expect(target).to receive(:routing).once
58
+ expect(target).to receive(:response).twice
59
+ expect(target).to receive(:params).twice
60
+ expect(target).to receive(:headers).once
61
+ subject.apply!(target)
62
+ end
63
+ end
51
64
  end
@@ -21,9 +21,19 @@ Praxis::Application.configure do |application|
21
21
  application.bootloader.use SimpleAuthenticationPlugin, config_file: 'config/authentication.yml'
22
22
  application.bootloader.use AuthorizationPlugin
23
23
 
24
+
25
+ adapter_name = 'sqlite'
26
+ db_name = ':memory:'
27
+ connection_opts = if RUBY_PLATFORM !~ /java/
28
+ { adapter: adapter_name , database: db_name }
29
+ else
30
+ require 'jdbc/sqlite3'
31
+ { adapter: 'jdbc', uri: "jdbc:#{adapter_name}:#{db_name}" }
32
+ end
33
+
24
34
  application.bootloader.use Praxis::Plugins::PraxisMapperPlugin, {
25
35
  config_data: {
26
- repositories: { default: {adapter: 'sqlite', database: ':memory:'} },
36
+ repositories: { default: connection_opts },
27
37
  log_stats: 'detailed'
28
38
  }
29
39
  }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: praxis
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.20.1
4
+ version: '0.21'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Josep M. Blanquer
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2016-03-15 00:00:00.000000000 Z
12
+ date: 2016-08-19 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rack
@@ -73,42 +73,42 @@ dependencies:
73
73
  requirements:
74
74
  - - "~>"
75
75
  - !ruby/object:Gem::Version
76
- version: '4.2'
76
+ version: '4.3'
77
77
  type: :runtime
78
78
  prerelease: false
79
79
  version_requirements: !ruby/object:Gem::Requirement
80
80
  requirements:
81
81
  - - "~>"
82
82
  - !ruby/object:Gem::Version
83
- version: '4.2'
83
+ version: '4.3'
84
84
  - !ruby/object:Gem::Dependency
85
85
  name: praxis-blueprints
86
86
  requirement: !ruby/object:Gem::Requirement
87
87
  requirements:
88
88
  - - "~>"
89
89
  - !ruby/object:Gem::Version
90
- version: '3.2'
90
+ version: '3.3'
91
91
  type: :runtime
92
92
  prerelease: false
93
93
  version_requirements: !ruby/object:Gem::Requirement
94
94
  requirements:
95
95
  - - "~>"
96
96
  - !ruby/object:Gem::Version
97
- version: '3.2'
97
+ version: '3.3'
98
98
  - !ruby/object:Gem::Dependency
99
99
  name: attributor
100
100
  requirement: !ruby/object:Gem::Requirement
101
101
  requirements:
102
102
  - - "~>"
103
103
  - !ruby/object:Gem::Version
104
- version: 5.0.2
104
+ version: '5.1'
105
105
  type: :runtime
106
106
  prerelease: false
107
107
  version_requirements: !ruby/object:Gem::Requirement
108
108
  requirements:
109
109
  - - "~>"
110
110
  - !ruby/object:Gem::Version
111
- version: 5.0.2
111
+ version: '5.1'
112
112
  - !ruby/object:Gem::Dependency
113
113
  name: thor
114
114
  requirement: !ruby/object:Gem::Requirement
@@ -235,6 +235,20 @@ dependencies:
235
235
  - - "~>"
236
236
  - !ruby/object:Gem::Version
237
237
  version: '0'
238
+ - !ruby/object:Gem::Dependency
239
+ name: sqlite3
240
+ requirement: !ruby/object:Gem::Requirement
241
+ requirements:
242
+ - - "~>"
243
+ - !ruby/object:Gem::Version
244
+ version: '1'
245
+ type: :development
246
+ prerelease: false
247
+ version_requirements: !ruby/object:Gem::Requirement
248
+ requirements:
249
+ - - "~>"
250
+ - !ruby/object:Gem::Version
251
+ version: '1'
238
252
  - !ruby/object:Gem::Dependency
239
253
  name: rspec
240
254
  requirement: !ruby/object:Gem::Requirement
@@ -375,20 +389,6 @@ dependencies:
375
389
  - - "~>"
376
390
  - !ruby/object:Gem::Version
377
391
  version: '0'
378
- - !ruby/object:Gem::Dependency
379
- name: sqlite3
380
- requirement: !ruby/object:Gem::Requirement
381
- requirements:
382
- - - "~>"
383
- - !ruby/object:Gem::Version
384
- version: '1'
385
- type: :development
386
- prerelease: false
387
- version_requirements: !ruby/object:Gem::Requirement
388
- requirements:
389
- - - "~>"
390
- - !ruby/object:Gem::Version
391
- version: '1'
392
392
  - !ruby/object:Gem::Dependency
393
393
  name: coveralls
394
394
  requirement: !ruby/object:Gem::Requirement
@@ -456,6 +456,7 @@ files:
456
456
  - lib/api_browser/app/js/factories/template_for.js
457
457
  - lib/api_browser/app/js/filters/attribute_name.js
458
458
  - lib/api_browser/app/js/filters/friendly_json.js
459
+ - lib/api_browser/app/js/filters/header_info.js
459
460
  - lib/api_browser/app/js/filters/is_empty.js
460
461
  - lib/api_browser/app/js/filters/markdown.js
461
462
  - lib/api_browser/app/js/filters/resource_name.js
@@ -497,6 +498,9 @@ files:
497
498
  - lib/api_browser/app/views/types/label/primitive_collection.html
498
499
  - lib/api_browser/app/views/types/label/type.html
499
500
  - lib/api_browser/app/views/types/label/type_collection.html
501
+ - lib/api_browser/app/views/types/main/array.html
502
+ - lib/api_browser/app/views/types/main/default.html
503
+ - lib/api_browser/app/views/types/main/hash.html
500
504
  - lib/api_browser/app/views/types/standalone/array.html
501
505
  - lib/api_browser/app/views/types/standalone/default.html
502
506
  - lib/api_browser/app/views/types/standalone/struct.html
@@ -524,6 +528,7 @@ files:
524
528
  - lib/praxis/controller.rb
525
529
  - lib/praxis/dispatcher.rb
526
530
  - lib/praxis/docs/generator.rb
531
+ - lib/praxis/docs/link_builder.rb
527
532
  - lib/praxis/error_handler.rb
528
533
  - lib/praxis/exception.rb
529
534
  - lib/praxis/exceptions/config.rb
@@ -548,6 +553,7 @@ files:
548
553
  - lib/praxis/media_type.rb
549
554
  - lib/praxis/media_type_collection.rb
550
555
  - lib/praxis/media_type_identifier.rb
556
+ - lib/praxis/middleware_app.rb
551
557
  - lib/praxis/multipart/parser.rb
552
558
  - lib/praxis/multipart/part.rb
553
559
  - lib/praxis/notifications.rb
@@ -570,7 +576,6 @@ files:
570
576
  - lib/praxis/responses/internal_server_error.rb
571
577
  - lib/praxis/responses/multipart_ok.rb
572
578
  - lib/praxis/responses/validation_error.rb
573
- - lib/praxis/restful_doc_generator.rb
574
579
  - lib/praxis/route.rb
575
580
  - lib/praxis/router.rb
576
581
  - lib/praxis/router/rack.rb
@@ -621,6 +626,7 @@ files:
621
626
  - spec/praxis/media_type_collection_spec.rb
622
627
  - spec/praxis/media_type_identifier_spec.rb
623
628
  - spec/praxis/media_type_spec.rb
629
+ - spec/praxis/middleware_app_spec.rb
624
630
  - spec/praxis/multipart/parser_spec.rb
625
631
  - spec/praxis/notifications_spec.rb
626
632
  - spec/praxis/plugin_concern_spec.rb
@@ -725,9 +731,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
725
731
  version: '0'
726
732
  requirements: []
727
733
  rubyforge_project:
728
- rubygems_version: 2.4.5.1
734
+ rubygems_version: 2.5.1
729
735
  signing_key:
730
736
  specification_version: 4
731
737
  summary: Building APIs the way you want it.
732
738
  test_files: []
733
- has_rdoc:
@@ -1,439 +0,0 @@
1
- module Praxis
2
- class RestfulDocGenerator
3
-
4
- class << self
5
- attr_reader :inspected_types
6
- end
7
-
8
- @inspected_types = Set.new
9
- API_DOCS_DIRNAME = 'docs/api'
10
-
11
- EXCLUDED_TYPES_FROM_TOP_LEVEL = Set.new([
12
- Attributor::Boolean,
13
- Attributor::CSV,
14
- Attributor::DateTime,
15
- Attributor::Float,
16
- Attributor::Hash,
17
- Attributor::Ids,
18
- Attributor::Integer,
19
- Attributor::Object,
20
- Attributor::String,
21
- Attributor::Symbol
22
- ]).freeze
23
-
24
- def self.inspect_attributes(the_type)
25
-
26
- reachable = Set.new
27
- return reachable if the_type.nil? || the_type.is_a?(Praxis::SimpleMediaType)
28
-
29
- # If an attribute comes in, get its type
30
- the_type = the_type.type if the_type.is_a? Attributor::Attribute
31
-
32
- # Collection types are special since they wrap a member type, so let's reach in and grab it
33
- if the_type < Attributor::Collection
34
- if the_type.member_attribute.nil?
35
- the_type = the_type.member_type
36
- else
37
- the_type = the_type.member_attribute.type
38
- end
39
- end
40
-
41
- if @inspected_types.include? the_type
42
- # We're done if we've already inspected it
43
- return reachable
44
- else
45
- # Mark it as inspected (before recursing)
46
- @inspected_types << the_type unless the_type.name == nil # Don't bother with anon structs
47
- end
48
- #puts "Inspecting type: #{the_type.name}" if the_type.name != nil
49
-
50
- reachable << the_type unless the_type.name == nil # Don't bother with anon structs
51
- if the_type.respond_to? :attributes
52
- the_type.attributes.each do |name, attr|
53
- attr_type = attr.type
54
- #puts "Inspecting attr: #{name} (class: #{attr_type.name}) #{attr_type.inspect}"
55
- reachable += self.inspect_attributes(attr_type)
56
- end
57
- end
58
- reachable
59
- end
60
-
61
- class Resource
62
-
63
- attr_accessor :media_type, :reachable_types, :version, :controller_config
64
-
65
- def initialize( definition )
66
- @controller_config = definition
67
- if controller_config.version == 'n/a'
68
- @version = 'unversioned'
69
- else
70
- @version = controller_config.version
71
- end
72
- @media_type = controller_config.media_type
73
- @reachable_types = Set.new
74
-
75
- # Collect reachable types from the media_type if any (plus itself)
76
- if @media_type && ! @media_type.is_a?(Praxis::SimpleMediaType)
77
- add_to_reachable RestfulDocGenerator.inspect_attributes(@media_type)
78
- @generated_example = @media_type.example(self.id)
79
- end
80
-
81
- # Collect reachable types from the params and payload definitions
82
- @controller_config.actions.each do |name, action_config|
83
- # skip actions with doc_visibility of :none
84
- next if action_config.metadata[:doc_visibility] == :none
85
-
86
- add_to_reachable RestfulDocGenerator.inspect_attributes(action_config.params)
87
- add_to_reachable RestfulDocGenerator.inspect_attributes(action_config.payload)
88
-
89
- action_config.responses.values.each do |response|
90
- add_to_reachable RestfulDocGenerator.inspect_attributes(response.media_type)
91
- end
92
- end
93
-
94
- end
95
-
96
- # TODO: I think that the "id"/"name" of a resource should be provided by the definition/controller...not derived here
97
- def id
98
- if @controller_config.controller
99
- @controller_config.controller.id
100
- else
101
- # If an API doesn't quite have the controller defined, let's use the name from the resource definition
102
- @controller_config.id
103
- end
104
- end
105
-
106
- def name
107
- if @controller_config.controller
108
- @controller_config.controller.name
109
- else
110
- # If an API doesn't quite have the controller defined, let's use the name from the resource definition
111
- @controller_config.name
112
- end
113
- end
114
-
115
- def parent
116
- @controller_config.parent
117
- end
118
-
119
- def friendly_name
120
- # FIXME: is it really about the controller? or the attached resource definition?
121
- segments = self.name.split("::")
122
- # FIXME: Crappy hack to derive a friendly name
123
- if ["Collection","Links"].include? segments.last
124
- segments[-2] + segments[-1] # concat the last 2
125
- else
126
- segments.last
127
- end
128
- end
129
-
130
- def add_to_reachable( found )
131
- return if found == nil
132
- @reachable_types += found
133
- end
134
-
135
- def display_name
136
- @controller_config.display_name
137
- end
138
- end
139
-
140
- def initialize(root_dir)
141
- @root_dir = root_dir
142
- @doc_root_dir = File.join(@root_dir, API_DOCS_DIRNAME)
143
- @resources = []
144
-
145
- Attributor::AttributeResolver.current = Attributor::AttributeResolver.new
146
-
147
- remove_previous_doc_data
148
- load_resources
149
-
150
- # Gather all reachable types (grouped by version)
151
- types_for = Hash.new
152
- @resources.each do |r|
153
- types_for[r.version] ||= Set.new
154
- types_for[r.version] += r.reachable_types
155
- end
156
-
157
- write_resources
158
- write_types(types_for)
159
- write_index(types_for)
160
- write_info(types_for)
161
- write_templates(types_for)
162
- end
163
-
164
- def load_resources
165
- Praxis::Application.instance.resource_definitions.map do |resource|
166
- # skip resources with doc_visibility of :none
167
- next if resource.metadata[:doc_visibility] == :none
168
-
169
- @resources << Resource.new(resource)
170
- end
171
-
172
- end
173
-
174
- def dump_example_for(context_name, object)
175
- example = object.example(Array(context_name))
176
- if object.is_a? Praxis::Blueprint
177
- example.render(view: :master)
178
- elsif object.is_a? Attributor::Attribute
179
- object.dump(example)
180
- else
181
- raise "Do not know how to dump this object (it is not a Blueprint or an Attribute): #{object}"
182
- end
183
- end
184
-
185
- def write_resources
186
- @resources.each do |r|
187
-
188
- filename = File.join(@doc_root_dir, r.version, "resources","#{r.id}.json")
189
- #puts "Dumping #{r.id} to #{filename}"
190
- base = File.dirname(filename)
191
- FileUtils.mkdir_p base unless File.exists? base
192
- resource_description = r.controller_config.describe
193
-
194
- # strip actions with doc_visibility of :none
195
- resource_description[:actions].reject! { |a| a[:metadata][:doc_visibility] == :none }
196
-
197
- # Go through the params/payload of each action and generate an example for them (then stick it into the description hash)
198
- r.controller_config.actions.each do |action_name, action|
199
- # skip actions with doc_visibility of :none
200
- next if action.metadata[:doc_visibility] == :none
201
-
202
- generated_examples = {}
203
- if action.params
204
- generated_examples[:params] = dump_example_for( r.id, action.params )
205
- end
206
- if action.payload
207
- generated_examples[:payload] = dump_example_for( r.id, action.payload )
208
- end
209
- action_description = resource_description[:actions].find{|a| a[:name] == action_name }
210
- action_description[:params][:example] = generated_examples[:params] if generated_examples[:params]
211
- action_description[:payload][:example] = generated_examples[:payload] if generated_examples[:payload]
212
- end
213
-
214
- File.open(filename, 'w') {|f| f.write(JSON.pretty_generate(resource_description))}
215
- end
216
- end
217
-
218
- def write_types( versioned_types )
219
- versioned_types.each do |version, types|
220
- dirname = File.join(@doc_root_dir, version, "types")
221
- FileUtils.mkdir_p dirname unless File.exists? dirname
222
- reportable_types = types - EXCLUDED_TYPES_FROM_TOP_LEVEL
223
- reportable_types.each do |type|
224
- filename = File.join(dirname, "#{type.id}.json")
225
- #puts "Dumping #{type.name} to #{filename}"
226
- type_output = type.describe
227
- example_data = type.example(type.to_s)
228
- if type_output[:views]
229
- type_output[:views].delete(:master)
230
- type_output[:views].each do |view_name, view_info|
231
- # Add and example for each view
232
- unless( type < Praxis::Links ) #TODO: why do we need to skip an example for links?
233
- view_info[:example] = example_data.render(view: view_name)
234
- end
235
- end
236
- end
237
- # Save a full type example
238
- # ...but not for links or link classes (there's no object container context if done alone!!)
239
- unless( type < Praxis::Links ) #TODO: again, why is this special?
240
- type_output[:example] = if example_data.respond_to? :render
241
- example_data.render(view: :master)
242
- else
243
- type.dump(example_data)
244
- end
245
- end
246
-
247
- # add an example for each attribute??
248
- File.open(filename, 'w') {|f| f.write(JSON.pretty_generate(type_output))}
249
- end
250
- end
251
- end
252
-
253
- # index looks like something like this:
254
- # {'1.0':
255
- # {
256
- # // Typical entry for controller with an associated mediatype
257
- # "Post" : { media_type: "V1::MT:Post" , controller: "V1:Ctrl:Post"}
258
- # // Unusual entry for controller without an associated mediatype
259
- # "Admin" : { controller: "V1:Ctrl:Admin" }
260
- # // Entry for mediatype that somehow is not associated with any controller...
261
- # "RemoteMT" : { media_type: "V1:Ctrl:RemoteMT" }
262
- # // Entry to a non-primitive type (but not a mediatype), that it is not covered by any related controller or mt
263
- # "Locale" : { kind: "Module::Locale"}
264
- # }
265
- #
266
- # '2.0': { ... }
267
- # }
268
- def write_index( versioned_types )
269
- index = Hash.new
270
- media_types_seen_from_controllers = Set.new
271
- # Process the resources first
272
-
273
- @resources.each do |r|
274
- index[r.version] ||= Hash.new
275
- info = {controller: r.id, name: r.name}
276
- info[:parent] = r.parent.controller ? r.parent.controller.id : r.parent.id if r.parent
277
- if r.media_type
278
- info[:media_type] = r.media_type.id
279
- media_types_seen_from_controllers << r.media_type
280
- end
281
- display_name = r.display_name #.name.split("::").last
282
- index[r.version][display_name] = info
283
- end
284
-
285
- versioned_types.each do |version, types|
286
- # Discard any mediatypes that we've already seen and processed as controller related
287
- reportable_types = types - media_types_seen_from_controllers - EXCLUDED_TYPES_FROM_TOP_LEVEL
288
- #TODO: think about these special cases, is it needed?
289
- reportable_types.reject!{|type| type < Praxis::Links || type < Praxis::MediaTypeCollection }
290
-
291
- reportable_types.each do |type|
292
- index[version] ||= Hash.new
293
- next unless type.respond_to?(:display_name)
294
- display_name = type.display_name #.name.split("::").last
295
- if index[version].has_key? display_name
296
- raise "Display name already taken for version #{version}! #{display_name}"
297
- end
298
- index[version][display_name] = if type < Praxis::MediaType
299
- {media_type: type.id }
300
- else
301
- {kind: type.id}
302
- end
303
- end
304
- end
305
- filename = File.join(@doc_root_dir, "index.json")
306
- dirname = File.dirname(filename)
307
- FileUtils.mkdir_p dirname unless File.exists? dirname
308
- File.open(filename, 'w') {|f| f.write(JSON.pretty_generate(index))}
309
- end
310
-
311
- # Writes an "index" type file inside each version, with some higher level information about the API
312
- def write_info( versioned_types )
313
-
314
- resources_by_version = Hash.new do |hash, v|
315
- hash[v] = Set.new
316
- end
317
- types_by_version = Hash.new do |hash, v|
318
- hash[v] = Set.new
319
- end
320
-
321
- @resources.each do |r|
322
- resources_by_version[r.version] << { id: r.id, name: r.name, friendly_name: r.friendly_name}
323
- end
324
-
325
- versioned_types.each do |version, types|
326
- # # Discard any mediatypes that we've already seen and processed as controller related
327
- reportable_types = types - EXCLUDED_TYPES_FROM_TOP_LEVEL
328
- # #TODO: think about these special cases, is it needed?
329
- # reportable_types.reject!{|type| type < Praxis::Links || type < Praxis::MediaTypeCollection }
330
-
331
- reportable_types.each do |type|
332
- segments = type.name.split("::")
333
- # FIXME: Crappy hack to derive a friendly name
334
- friendly_name = if ["Collection","Links"].include? segments.last
335
- segments[-2] + segments[-1] # concat the last 2
336
- else
337
- segments.last
338
- end
339
-
340
- types_by_version[version] << { id: type.id, name: type.name, friendly_name: friendly_name}
341
- end
342
- end
343
- ###############################
344
-
345
- diff = resources_by_version.keys - types_by_version.keys - versioned_types.keys
346
- raise "!!!!!!!! somehow we have a list of different versions from the types vs. resources seen" unless diff.empty?
347
-
348
- infos = {}
349
- versioned_types.each do |version, types|
350
- infos[version] = {info:{}}
351
- end
352
- infos.merge!(ApiDefinition.instance.describe)
353
-
354
- # Add resources and types list
355
- versioned_types.keys.each do |v|
356
-
357
- infos[v][:resources] = resources_by_version[v].each_with_object({}) do |element,hash|
358
- hash[element[:id]] = { name: element[:name], friendly_name: element[:friendly_name] }
359
- end
360
- infos[v][:schemas] = types_by_version[v].each_with_object({}) do |element,hash|
361
- hash[element[:id]] = { name: element[:name], friendly_name: element[:friendly_name] }
362
- end
363
- end
364
-
365
- versioned_types.each do |version, types|
366
- dirname = File.join(@doc_root_dir, version)
367
- filename = File.join(dirname, "version_index.json")
368
- File.open(filename, 'w') {|f| f.write(JSON.pretty_generate(infos[version]))}
369
- end
370
- end
371
-
372
-
373
- def write_templates(versioned_types)
374
- # Calculate and write top-level (non-versioned) templates
375
- top_templates = write_template("")
376
- # Calculate and write versioned templates (passing the top level ones for inheritance)
377
- versioned_types.keys.each do |version|
378
- write_template(version,top_templates)
379
- end
380
- end
381
-
382
- def write_template(version,top_templates=nil)
383
- # Collect template filenames (grouped by type: embedded vs. standalone)
384
- templates_dir = File.join(@root_dir,"doc_browser","templates",version)
385
-
386
- # Slurp in any top level (unversioned) templates if any
387
- # Top level templates will apply to any versioned one (and can be overwritten
388
- # if the version defines their own)
389
- templates = {embedded: {}, standalone: {} }
390
- if top_templates
391
- templates[:embedded] = top_templates[:embedded].clone
392
- templates[:standalone] = top_templates[:standalone].clone
393
- end
394
-
395
- dual = Dir.glob(File.join(templates_dir,"*.tmpl"))
396
- embedded = Dir.glob(File.join(templates_dir,"embedded","*.tmpl"))
397
- standalone = Dir.glob(File.join(templates_dir,"standalone","*.tmpl"))
398
-
399
- # TODO: Encode the contents more appropriately rather than dumping a string
400
- # Templates defined at the top will apply to both embedded and standalone
401
- # But it can be overriden if the same type exists in the more specific directory
402
- dual.each do |filename|
403
- type_key = File.basename(filename).gsub(/.tmpl$/,'')
404
- contents = IO.read(filename)
405
- templates[:embedded][type_key] = contents
406
- templates[:standalone][type_key] = contents
407
- end
408
-
409
- # For each embedded one, create a key in the embedded section, and encode the file contents in the value
410
- embedded.each do |filename|
411
- type_key = File.basename(filename).gsub(/.tmpl$/,'')
412
- templates[:embedded][type_key] = IO.read(filename)
413
- end
414
- # For each standalone one, create a key in the standalone section, and encode the file contents in the value
415
- standalone.each do |filename|
416
- type_key = File.basename(filename).gsub(/.tmpl$/,'')
417
- templates[:standalone][type_key] = IO.read(filename)
418
- end
419
-
420
- [:embedded,:standalone].each do |type|
421
- v = version.empty? ? "top level": version
422
- puts "Packaging #{v} #{type} templates for: #{templates[type].keys}" unless templates[type].keys.empty?
423
- end
424
-
425
- # Write the resulting hash to the final file in the docs directory
426
- filename = File.join(@doc_root_dir, version, "templates.json")
427
- dirname = File.dirname(filename)
428
- FileUtils.mkdir_p dirname unless File.exists? dirname
429
- File.open(filename, 'w') {|f| f.write(JSON.pretty_generate(templates))}
430
- return templates
431
- end
432
- private
433
-
434
- def remove_previous_doc_data
435
- FileUtils.rm_rf @doc_root_dir if File.exists?(@doc_root_dir)
436
- end
437
-
438
- end
439
- end