praxis 0.13.0 → 0.14.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (135) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/.travis.yml +15 -2
  4. data/CHANGELOG.md +54 -1
  5. data/bin/praxis +49 -2
  6. data/lib/api_browser/Gruntfile.js +247 -90
  7. data/lib/api_browser/app/bower_components/angular-mocks/.bower.json +19 -0
  8. data/lib/api_browser/app/bower_components/angular-mocks/README.md +57 -0
  9. data/lib/api_browser/app/bower_components/angular-mocks/angular-mocks.js +2193 -0
  10. data/lib/api_browser/app/bower_components/angular-mocks/bower.json +9 -0
  11. data/lib/api_browser/app/bower_components/angular-mocks/package.json +27 -0
  12. data/lib/api_browser/app/bower_components/angular/.bower.json +6 -5
  13. data/lib/api_browser/app/bower_components/angular/README.md +23 -4
  14. data/lib/api_browser/app/bower_components/angular/angular-csp.css +6 -0
  15. data/lib/api_browser/app/bower_components/angular/angular.js +2287 -1597
  16. data/lib/api_browser/app/bower_components/angular/angular.min.js +212 -205
  17. data/lib/api_browser/app/bower_components/angular/angular.min.js.gzip +0 -0
  18. data/lib/api_browser/app/bower_components/angular/angular.min.js.map +3 -3
  19. data/lib/api_browser/app/bower_components/angular/bower.json +2 -1
  20. data/lib/api_browser/app/bower_components/angular/package.json +25 -0
  21. data/lib/api_browser/app/bower_components/showdown/.bower.json +39 -0
  22. data/lib/api_browser/app/bower_components/showdown/.jshintignore +2 -0
  23. data/lib/api_browser/app/bower_components/showdown/.travis.yml +8 -0
  24. data/lib/api_browser/app/bower_components/showdown/Gruntfile.js +100 -0
  25. data/lib/api_browser/app/bower_components/showdown/README.md +317 -0
  26. data/lib/api_browser/app/bower_components/showdown/bower.json +26 -0
  27. data/lib/api_browser/app/bower_components/showdown/compressed/Showdown.js +1606 -0
  28. data/lib/api_browser/app/bower_components/showdown/compressed/Showdown.js.map +1 -0
  29. data/lib/api_browser/app/bower_components/showdown/compressed/Showdown.min.js +2 -0
  30. data/lib/api_browser/app/bower_components/showdown/compressed/extensions/github.min.js +2 -0
  31. data/lib/api_browser/app/bower_components/showdown/compressed/extensions/github.min.js.map +1 -0
  32. data/lib/api_browser/app/bower_components/showdown/compressed/extensions/prettify.min.js +2 -0
  33. data/lib/api_browser/app/bower_components/showdown/compressed/extensions/prettify.min.js.map +1 -0
  34. data/lib/api_browser/app/bower_components/showdown/compressed/extensions/table.min.js +2 -0
  35. data/lib/api_browser/app/bower_components/showdown/compressed/extensions/table.min.js.map +1 -0
  36. data/lib/api_browser/app/bower_components/showdown/compressed/extensions/twitter.min.js +2 -0
  37. data/lib/api_browser/app/bower_components/showdown/compressed/extensions/twitter.min.js.map +1 -0
  38. data/lib/api_browser/app/bower_components/showdown/license.txt +34 -0
  39. data/lib/api_browser/app/bower_components/showdown/package.json +47 -0
  40. data/lib/api_browser/app/bower_components/showdown/src/extensions/github.js +25 -0
  41. data/lib/api_browser/app/bower_components/showdown/src/extensions/prettify.js +29 -0
  42. data/lib/api_browser/app/bower_components/showdown/src/extensions/table.js +106 -0
  43. data/lib/api_browser/app/bower_components/showdown/src/extensions/twitter.js +42 -0
  44. data/lib/api_browser/app/bower_components/showdown/src/ng-showdown.js +150 -0
  45. data/lib/api_browser/app/bower_components/showdown/src/showdown.js +1454 -0
  46. data/lib/api_browser/app/index.html +6 -4
  47. data/lib/api_browser/app/js/app.js +1 -2
  48. data/lib/api_browser/app/js/controllers/action.js +4 -4
  49. data/lib/api_browser/app/js/controllers/controller.js +1 -1
  50. data/lib/api_browser/app/js/controllers/menu.js +5 -3
  51. data/lib/api_browser/app/js/controllers/type.js +5 -5
  52. data/lib/api_browser/app/js/directives/attribute_description.js +5 -5
  53. data/lib/api_browser/app/js/directives/attribute_table.js +1 -1
  54. data/lib/api_browser/app/js/directives/attribute_table_row.js +2 -2
  55. data/lib/api_browser/app/js/directives/no_container.js +1 -1
  56. data/lib/api_browser/app/js/directives/request_body.js +5 -5
  57. data/lib/api_browser/app/js/directives/request_headers.js +3 -6
  58. data/lib/api_browser/app/js/directives/request_parameters.js +3 -6
  59. data/lib/api_browser/app/js/directives/type_label.js +4 -5
  60. data/lib/api_browser/app/js/factories/Documentation.js +4 -4
  61. data/lib/api_browser/app/js/factories/PayloadTemplates.js +2 -2
  62. data/lib/api_browser/app/js/factories/TypeTemplates.js +3 -3
  63. data/lib/api_browser/app/js/filters/markdown.js +6 -0
  64. data/lib/api_browser/app/js/filters/resource_name.js +2 -2
  65. data/lib/api_browser/app/sass/modules/_header.scss +2 -7
  66. data/lib/api_browser/app/sass/{main.scss → praxis.scss} +0 -0
  67. data/lib/api_browser/app/sass/variables/_bootstrap-variables.scss +370 -367
  68. data/lib/api_browser/app/views/action.html +2 -2
  69. data/lib/api_browser/app/views/controller.html +2 -2
  70. data/lib/api_browser/app/views/directives/attribute_description.html +1 -1
  71. data/lib/api_browser/app/views/layout.html +2 -11
  72. data/lib/api_browser/app/views/navbar.html +9 -0
  73. data/lib/api_browser/app/views/resource/_actions.html +1 -1
  74. data/lib/api_browser/app/views/type.html +2 -2
  75. data/lib/api_browser/app/views/type/_details.html +2 -1
  76. data/lib/api_browser/bower.json +5 -0
  77. data/lib/api_browser/package.json +18 -7
  78. data/lib/praxis.rb +8 -3
  79. data/lib/praxis/action_definition.rb +28 -6
  80. data/lib/praxis/api_definition.rb +30 -2
  81. data/lib/praxis/api_general_info.rb +36 -0
  82. data/lib/praxis/bootloader.rb +1 -0
  83. data/lib/praxis/collection.rb +34 -0
  84. data/lib/praxis/controller.rb +7 -0
  85. data/lib/praxis/dispatcher.rb +3 -0
  86. data/lib/praxis/links.rb +2 -8
  87. data/lib/praxis/media_type.rb +6 -24
  88. data/lib/praxis/media_type_collection.rb +6 -2
  89. data/lib/praxis/plugin_concern.rb +2 -1
  90. data/lib/praxis/request.rb +24 -15
  91. data/lib/praxis/request_stages/request_stage.rb +19 -4
  92. data/lib/praxis/request_stages/validate_params_and_headers.rb +1 -1
  93. data/lib/praxis/request_stages/validate_payload.rb +1 -1
  94. data/lib/praxis/resource_definition.rb +45 -10
  95. data/lib/praxis/response_definition.rb +46 -27
  96. data/lib/praxis/restful_doc_generator.rb +94 -7
  97. data/lib/praxis/simple_media_type.rb +2 -9
  98. data/lib/praxis/stage.rb +1 -4
  99. data/lib/praxis/tasks/api_docs.rb +51 -19
  100. data/lib/praxis/tasks/routes.rb +19 -15
  101. data/lib/praxis/types/media_type_common.rb +31 -0
  102. data/lib/praxis/types/multipart.rb +4 -4
  103. data/lib/praxis/version.rb +1 -1
  104. data/praxis.gemspec +2 -2
  105. data/spec/api_browser/factories/documentation_spec.js +50 -0
  106. data/spec/api_browser/filters/attribute_name_spec.js +23 -0
  107. data/spec/functional_spec.rb +62 -10
  108. data/spec/praxis/action_definition_spec.rb +12 -4
  109. data/spec/praxis/api_definition_spec.rb +159 -0
  110. data/spec/praxis/api_general_info_spec.rb +36 -0
  111. data/spec/praxis/bootloader_spec.rb +10 -1
  112. data/spec/praxis/media_type_collection_spec.rb +46 -53
  113. data/spec/praxis/media_type_spec.rb +6 -6
  114. data/spec/praxis/request_stage_spec.rb +7 -2
  115. data/spec/praxis/request_stages_validate_spec.rb +12 -7
  116. data/spec/praxis/resource_definition_spec.rb +62 -0
  117. data/spec/praxis/response_definition_spec.rb +26 -16
  118. data/spec/praxis/stage_spec.rb +4 -8
  119. data/spec/praxis/types/collection_spec.rb +144 -0
  120. data/spec/spec_app/app/controllers/instances.rb +8 -2
  121. data/spec/spec_app/design/api.rb +11 -0
  122. data/spec/spec_app/design/media_types/instance.rb +12 -0
  123. data/spec/spec_app/design/media_types/volume.rb +9 -2
  124. data/spec/spec_app/design/media_types/volume_snapshot.rb +9 -6
  125. data/spec/spec_app/design/resources/instances.rb +25 -10
  126. data/spec/support/spec_media_types.rb +1 -1
  127. data/spec/support/spec_resource_definitions.rb +2 -0
  128. data/tasks/thor/app.rb +15 -10
  129. data/tasks/thor/example.rb +115 -115
  130. data/tasks/thor/templates/generator/empty_app/.gitignore +2 -0
  131. data/tasks/thor/templates/generator/empty_app/docs/app.js +1 -0
  132. data/tasks/thor/templates/generator/empty_app/docs/styles.scss +3 -0
  133. metadata +50 -9
  134. data/lib/api_browser/app/css/main.css +0 -4511
  135. data/lib/praxis/types/collection.rb +0 -17
@@ -6,7 +6,7 @@ module Praxis
6
6
  end
7
7
 
8
8
  @inspected_types = Set.new
9
- API_DOCS_DIRNAME = 'api_docs'
9
+ API_DOCS_DIRNAME = 'docs/api'
10
10
 
11
11
  EXCLUDED_TYPES_FROM_TOP_LEVEL = Set.new([
12
12
  Attributor::Boolean,
@@ -81,12 +81,25 @@ module Praxis
81
81
 
82
82
  add_to_reachable RestfulDocGenerator.inspect_attributes(action_config.params)
83
83
  add_to_reachable RestfulDocGenerator.inspect_attributes(action_config.payload)
84
+
85
+ action_config.responses.values.each do |response|
86
+ add_to_reachable RestfulDocGenerator.inspect_attributes(response.media_type)
87
+ end
84
88
  end
85
89
 
86
90
  end
87
91
 
88
92
  # TODO: I think that the "id"/"name" of a resource should be provided by the definition/controller...not derived here
89
93
  def id
94
+ if @controller_config.controller
95
+ @controller_config.controller.id
96
+ else
97
+ # If an API doesn't quite have the controller defined, let's use the name from the resource definition
98
+ @controller_config.id
99
+ end
100
+ end
101
+
102
+ def name
90
103
  if @controller_config.controller
91
104
  @controller_config.controller.name
92
105
  else
@@ -95,6 +108,17 @@ module Praxis
95
108
  end
96
109
  end
97
110
 
111
+ def friendly_name
112
+ # FIXME: is it really about the controller? or the attached resource definition?
113
+ segments = self.name.split("::")
114
+ # FIXME: Crappy hack to derive a friendly name
115
+ if ["Collection","Links"].include? segments.last
116
+ segments[-2] + segments[-1] # concat the last 2
117
+ else
118
+ segments.last
119
+ end
120
+ end
121
+
98
122
  def add_to_reachable( found )
99
123
  return if found == nil
100
124
  @reachable_types += found
@@ -119,6 +143,7 @@ module Praxis
119
143
  write_resources
120
144
  write_types(types_for)
121
145
  write_index(types_for)
146
+ write_info(types_for)
122
147
  write_templates(types_for)
123
148
  end
124
149
 
@@ -182,7 +207,7 @@ module Praxis
182
207
  FileUtils.mkdir_p dirname unless File.exists? dirname
183
208
  reportable_types = types - EXCLUDED_TYPES_FROM_TOP_LEVEL
184
209
  reportable_types.each do |type|
185
- filename = File.join(dirname, "#{type.name}.json")
210
+ filename = File.join(dirname, "#{type.id}.json")
186
211
  #puts "Dumping #{type.name} to #{filename}"
187
212
  type_output = type.describe
188
213
  example_data = type.example(type.to_s)
@@ -233,12 +258,12 @@ module Praxis
233
258
 
234
259
  @resources.each do |r|
235
260
  index[r.version] ||= Hash.new
236
- info = {controller: r.id}
261
+ info = {controller: r.id, name: r.name}
237
262
  if r.media_type
238
- info[:media_type] = r.media_type.name
263
+ info[:media_type] = r.media_type.id
239
264
  media_types_seen_from_controllers << r.media_type
240
265
  end
241
- display_name = r.id.split("::").last
266
+ display_name = r.name.split("::").last
242
267
  index[r.version][display_name] = info
243
268
  end
244
269
 
@@ -255,9 +280,9 @@ module Praxis
255
280
  raise "Display name already taken for version #{version}! #{display_name}"
256
281
  end
257
282
  index[version][display_name] = if type < Praxis::MediaType
258
- {media_type: type.name }
283
+ {media_type: type.id }
259
284
  else
260
- {kind: type.name}
285
+ {kind: type.id}
261
286
  end
262
287
  end
263
288
  end
@@ -267,6 +292,68 @@ module Praxis
267
292
  File.open(filename, 'w') {|f| f.write(JSON.pretty_generate(index))}
268
293
  end
269
294
 
295
+ # Writes an "index" type file inside each version, with some higher level information about the API
296
+ def write_info( versioned_types )
297
+
298
+ resources_by_version = Hash.new do |hash, v|
299
+ hash[v] = Set.new
300
+ end
301
+ types_by_version = Hash.new do |hash, v|
302
+ hash[v] = Set.new
303
+ end
304
+
305
+ @resources.each do |r|
306
+ resources_by_version[r.version] << { id: r.id, name: r.name, friendly_name: r.friendly_name}
307
+ end
308
+
309
+ versioned_types.each do |version, types|
310
+ # # Discard any mediatypes that we've already seen and processed as controller related
311
+ reportable_types = types - EXCLUDED_TYPES_FROM_TOP_LEVEL
312
+ # #TODO: think about these special cases, is it needed?
313
+ # reportable_types.reject!{|type| type < Praxis::Links || type < Praxis::MediaTypeCollection }
314
+
315
+ reportable_types.each do |type|
316
+ segments = type.name.split("::")
317
+ # FIXME: Crappy hack to derive a friendly name
318
+ friendly_name = if ["Collection","Links"].include? segments.last
319
+ segments[-2] + segments[-1] # concat the last 2
320
+ else
321
+ segments.last
322
+ end
323
+
324
+ types_by_version[version] << { id: type.id, name: type.name, friendly_name: friendly_name}
325
+ end
326
+ end
327
+ ###############################
328
+
329
+ diff = resources_by_version.keys - types_by_version.keys - versioned_types.keys
330
+ raise "!!!!!!!! somehow we have a list of different versions from the types vs. resources seen" unless diff.empty?
331
+
332
+ infos = {}
333
+ versioned_types.each do |version, types|
334
+ infos[version] = {info:{}}
335
+ end
336
+ infos.merge!(ApiDefinition.instance.describe)
337
+
338
+ # Add resources and types list
339
+ versioned_types.keys.each do |v|
340
+
341
+ infos[v][:resources] = resources_by_version[v].each_with_object({}) do |element,hash|
342
+ hash[element[:id]] = { name: element[:name], friendly_name: element[:friendly_name] }
343
+ end
344
+ infos[v][:schemas] = types_by_version[v].each_with_object({}) do |element,hash|
345
+ hash[element[:id]] = { name: element[:name], friendly_name: element[:friendly_name] }
346
+ end
347
+ end
348
+
349
+ versioned_types.each do |version, types|
350
+ dirname = File.join(@doc_root_dir, version)
351
+ filename = File.join(dirname, "version_index.json")
352
+ File.open(filename, 'w') {|f| f.write(JSON.pretty_generate(infos[version]))}
353
+ end
354
+ end
355
+
356
+
270
357
  def write_templates(versioned_types)
271
358
  # Calculate and write top-level (non-versioned) templates
272
359
  top_templates = write_template("")
@@ -5,15 +5,8 @@ module Praxis
5
5
  self.class.name
6
6
  end
7
7
 
8
- def ===(other_thing)
9
- case other_thing
10
- when String
11
- identifier == other_thing
12
- when MediaType
13
- identifier == other_thing.identifier
14
- else
15
- raise 'can not compare'
16
- end
8
+ def id
9
+ self.class.name.gsub("::",'-')
17
10
  end
18
11
 
19
12
  def describe(shallow=true)
data/lib/praxis/stage.rb CHANGED
@@ -24,14 +24,13 @@ module Praxis
24
24
  end
25
25
 
26
26
  def run
27
- setup!
28
- setup_deferred_callbacks!
29
27
  execute_callbacks(self.before_callbacks)
30
28
  execute
31
29
  execute_callbacks(self.after_callbacks)
32
30
  end
33
31
 
34
32
  def setup!
33
+ setup_deferred_callbacks!
35
34
  end
36
35
 
37
36
  def setup_deferred_callbacks!
@@ -48,8 +47,6 @@ module Praxis
48
47
  end
49
48
 
50
49
  def execute
51
- raise NotImplementedError, 'Subclass must implement Stage#execute' unless @stages.any?
52
-
53
50
  @stages.each do |stage|
54
51
  stage.run
55
52
  end
@@ -1,30 +1,62 @@
1
1
  namespace :praxis do
2
- desc "Generate API docs (JSON definitions) for a Praxis App"
3
- task :api_docs => [:environment] do |t, args|
4
- require 'fileutils'
5
2
 
6
- Praxis::Blueprint.caching_enabled = false
7
- generator = Praxis::RestfulDocGenerator.new(Dir.pwd)
8
- end
3
+ namespace :docs do
4
+ path = File.expand_path(File.join(File.dirname(__FILE__), '../../api_browser'))
9
5
 
10
- desc "Run API Documentation Browser"
11
- task :doc_browser, [:port] => :api_docs do |t, args|
12
- args.with_defaults port: 4567
6
+ desc "Install dependencies"
7
+ task :install do
8
+ unless system("npm install --production", chdir: path)
9
+ raise Exception.new("NPM Install Failed")
10
+ end
11
+
12
+ docs_dir = File.join(Dir.pwd, 'docs')
13
+ FileUtils.mkdir_p docs_dir unless File.directory? docs_dir
13
14
 
14
- public_folder = File.expand_path("../../../", __FILE__) + "/api_browser/app"
15
- app = Rack::Builder.new do
16
- map "/docs" do # application JSON docs
17
- use Rack::Static, urls: [""], root: File.join(Dir.pwd, Praxis::RestfulDocGenerator::API_DOCS_DIRNAME)
15
+ # The doc browser will need to have a minimal app.js and styles.css file at the root
16
+ # Let's add them if the app has not overriden them
17
+ js_file = File.join(Dir.pwd, 'docs', 'app.js')
18
+ scss_file = File.join(Dir.pwd, 'docs', 'styles.scss')
19
+ template_directory = File.expand_path(File.join(File.dirname(__FILE__), '../../../tasks/thor/templates/generator/empty_app/docs'))
20
+
21
+ unless File.exists? js_file
22
+ FileUtils.cp File.join(template_directory, 'app.js'), js_file
18
23
  end
19
- map "/" do # Assets mapping
20
- use Rack::Static, urls: [""], root: public_folder, index: "index.html"
24
+ unless File.exists? scss_file
25
+ FileUtils.cp File.join(template_directory, 'styles.scss'), scss_file
21
26
  end
27
+ end
22
28
 
23
- run lambda { |env| [404, {'Content-Type' => 'text/plain'}, ['Not Found']] }
29
+ desc "Run API Documentation Browser"
30
+ task :preview, [:port] => [:install, :generate] do |t, args|
31
+ doc_port = args[:port] || '9090'
32
+ exec({'USER_DOCS_PATH' => File.join(Dir.pwd, 'docs'), 'DOC_PORT' => doc_port}, "#{path}/node_modules/.bin/grunt serve --gruntfile '#{path}/Gruntfile.js'")
24
33
  end
25
34
 
26
-
27
- Rack::Server.start app: app, Port: args[:port]
35
+ desc "Build docs that can be shipped"
36
+ task :build => [:install, :generate] do
37
+ exec({'USER_DOCS_PATH' => File.join(Dir.pwd, 'docs')}, "#{path}/node_modules/.bin/grunt build --gruntfile '#{path}/Gruntfile.js'")
38
+ end
39
+
40
+ desc "Generate API docs (JSON definitions) for a Praxis App"
41
+ task :generate => [:environment] do |t, args|
42
+ require 'fileutils'
43
+
44
+ Praxis::Blueprint.caching_enabled = false
45
+ generator = Praxis::RestfulDocGenerator.new(Dir.pwd)
46
+ end
47
+
48
+ end
49
+
50
+ desc "Generate API docs (JSON definitions) for a Praxis App"
51
+ task :api_docs do
52
+ STDERR.puts "DEPRECATION: praxis:api_docs is deprecated and will be removed by 1.0. Please use praxis:docs:generate instead."
53
+ Rake::Task["praxis:docs:generate"].invoke
28
54
  end
29
-
55
+
56
+ desc "Run API Documentation Browser"
57
+ task :doc_browser, [:port] do |t, args|
58
+ STDERR.puts "DEPRECATION: praxis:doc_browser is deprecated and will be removed by 1.0. Please use praxis:docs:preview instead. The doc browser now runs on port 9090."
59
+ Rake::Task["praxis:docs:preview"].invoke
60
+ end
61
+
30
62
  end
@@ -4,7 +4,6 @@ namespace :praxis do
4
4
  task :routes, [:format] => [:environment] do |t, args|
5
5
  require 'terminal-table'
6
6
 
7
-
8
7
  table = Terminal::Table.new title: "Routes",
9
8
  headings: [
10
9
  "Version", "Path", "Verb",
@@ -22,23 +21,28 @@ namespace :praxis do
22
21
 
23
22
  method_name = method ? "#{method.owner.name}##{method.name}" : 'n/a'
24
23
 
25
- action.routes.each do |route|
26
- rows << {
27
- resource: resource_definition.name,
28
- version: route.version,
29
- verb: route.verb,
30
- path: route.path,
31
- action: name,
32
- implementation: method_name,
33
- name: route.name,
34
- primary: (action.primary_route == route ? 'yes' : '')
35
- }
24
+ row = {
25
+ resource: resource_definition.name,
26
+ action: name,
27
+ implementation: method_name,
28
+ }
29
+
30
+ if action.routes.empty?
31
+ warn "Warning: No routes defined for #{resource_definition.name}##{name}."
32
+ rows << row
33
+ else
34
+ action.routes.each do |route|
35
+ rows << row.merge({
36
+ version: route.version,
37
+ verb: route.verb,
38
+ path: route.path,
39
+ name: route.name,
40
+ primary: (action.primary_route == route ? 'yes' : '')
41
+ })
36
42
  end
37
43
  end
38
44
  end
39
-
40
-
41
-
45
+ end
42
46
 
43
47
  case args[:format] || "table"
44
48
  when "json"
@@ -0,0 +1,31 @@
1
+ module Praxis
2
+ module Types
3
+
4
+ module MediaTypeCommon
5
+ extend ::ActiveSupport::Concern
6
+
7
+ module ClassMethods
8
+ def describe(shallow = false)
9
+ hash = super
10
+ unless shallow
11
+ hash.merge!(identifier: @identifier, description: @description)
12
+ end
13
+ hash
14
+ end
15
+
16
+ def description(text=nil)
17
+ @description = text if text
18
+ @description
19
+ end
20
+
21
+ def identifier(identifier=nil)
22
+ return @identifier unless identifier
23
+ # TODO: parse the string and extract things like collection , and format type?...
24
+ @identifier = identifier
25
+ end
26
+ end
27
+
28
+ end
29
+
30
+ end
31
+ end
@@ -28,10 +28,10 @@ module Praxis
28
28
 
29
29
  super(context, options: options).each do |k,v|
30
30
  body = if v.respond_to?(:dump) && !v.kind_of?(String)
31
- JSON.pretty_generate(v.dump)
32
- else
33
- v
34
- end
31
+ JSON.pretty_generate(v.dump)
32
+ else
33
+ v
34
+ end
35
35
 
36
36
  entity = MIME::Text.new(body)
37
37
 
@@ -1,3 +1,3 @@
1
1
  module Praxis
2
- VERSION = '0.13.0'
2
+ VERSION = '0.14.0'
3
3
  end
data/praxis.gemspec CHANGED
@@ -25,8 +25,8 @@ Gem::Specification.new do |spec|
25
25
  spec.add_dependency 'activesupport', '>= 3'
26
26
  spec.add_dependency 'mime', '~> 0'
27
27
  spec.add_dependency 'praxis-mapper', '~> 3.3'
28
- spec.add_dependency 'praxis-blueprints', '~> 1.2'
29
- spec.add_dependency 'attributor', '~> 2.5.0'
28
+ spec.add_dependency 'praxis-blueprints', '~> 1.3'
29
+ spec.add_dependency 'attributor', '~> 2.6.0'
30
30
  spec.add_dependency 'thor', '~> 0.18'
31
31
  spec.add_dependency 'terminal-table', '~> 1.4'
32
32
  spec.add_dependency 'harness', '~> 2'
@@ -0,0 +1,50 @@
1
+ describe('Documentation service', function() {
2
+ var $scope, Documentation, $httpBackend;
3
+
4
+ beforeEach(angular.mock.module('PraxisDocBrowser'));
5
+
6
+ beforeEach(inject(function($rootScope, $injector) {
7
+ $scope = $rootScope.$new();
8
+ Documentation = $injector.get('Documentation');
9
+ $httpBackend = $injector.get('$httpBackend');
10
+ }));
11
+
12
+ afterEach(function() {
13
+ $httpBackend.verifyNoOutstandingExpectation();
14
+ $httpBackend.verifyNoOutstandingRequest();
15
+ });
16
+
17
+ describe('#getIndex', function() {
18
+ var result, response = {
19
+ '1.0': {
20
+ 'Blogs': {
21
+ 'controller': 'V1-Controllers-Blogs',
22
+ 'name': 'V1::Controllers::Blogs',
23
+ 'media_type': 'V1-MediaTypes-Blog'
24
+ },
25
+ 'Posts': {
26
+ 'controller': 'V1-ResourceDefinitions-Posts',
27
+ 'name': 'V1::ResourceDefinitions::Posts',
28
+ 'media_type': 'V1-MediaTypes-Post'
29
+ },
30
+ 'Users': {
31
+ 'controller': 'V1-ResourceDefinitions-Users',
32
+ 'name': 'V1::ResourceDefinitions::Users',
33
+ 'media_type': 'V1-MediaTypes-User'
34
+ }
35
+ }
36
+ };
37
+ beforeEach(function() {
38
+ $httpBackend.expectGET('api/index.json').respond(response);
39
+ Documentation.getIndex().then(function(data) {
40
+ result = data;
41
+ });
42
+ $httpBackend.flush();
43
+ $scope.$apply();
44
+ });
45
+
46
+ it('returns the index data', function() {
47
+ expect(result.data).toEqual(response);
48
+ });
49
+ });
50
+ });