praxis 0.20.1 → 0.21

Sign up to get free protection for your applications and to get access to all the features.
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