lurker 0.6.1 → 0.6.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/.hound.yml +62 -0
  5. data/.rubocop.yml +62 -0
  6. data/Gemfile +1 -1
  7. data/Rakefile +2 -268
  8. data/features/atom_persistent_within_the_same_type.feature +0 -2
  9. data/features/controller_nested_schema_scaffolding.feature +0 -1
  10. data/features/controller_schema_scaffolding.feature +0 -1
  11. data/features/html_generation.feature +0 -1
  12. data/features/minitest.feature +0 -2
  13. data/features/multidomain_support.feature +0 -1
  14. data/features/multitype_request_support.feature +0 -2
  15. data/features/partials.feature +0 -1
  16. data/features/request_nested_schema_scaffolding.feature +0 -1
  17. data/features/request_schema_scaffolding.feature +0 -1
  18. data/features/schema_suffixes.feature +0 -1
  19. data/features/schema_updating_within_test_suite.feature +37 -2
  20. data/features/test_endpoint.feature +0 -1
  21. data/lib/lurker.rb +5 -1
  22. data/lib/lurker/endpoint.rb +121 -176
  23. data/lib/lurker/endpoint/http_parameters.rb +77 -0
  24. data/lib/lurker/endpoint/response_codes.rb +42 -0
  25. data/lib/lurker/erb_schema_context.rb +8 -6
  26. data/lib/lurker/jaml_descriptor.rb +8 -1
  27. data/lib/lurker/json_schema_hash.rb +48 -0
  28. data/lib/lurker/presenters/base_presenter.rb +1 -2
  29. data/lib/lurker/presenters/endpoint_presenter.rb +17 -10
  30. data/lib/lurker/presenters/json_presenter.rb +6 -6
  31. data/lib/lurker/presenters/schema_presenter.rb +15 -14
  32. data/lib/lurker/presenters/service_presenter.rb +5 -6
  33. data/lib/lurker/rendering_controller.rb +0 -1
  34. data/lib/lurker/request.rb +3 -1
  35. data/lib/lurker/sandbox.rb +0 -1
  36. data/lib/lurker/schema_modifier.rb +4 -2
  37. data/lib/lurker/schema_modifier/atom.rb +14 -5
  38. data/lib/lurker/server.rb +4 -5
  39. data/lib/lurker/service.rb +3 -4
  40. data/lib/lurker/spec_helper/rspec.rb +15 -57
  41. data/lib/lurker/spy.rb +9 -5
  42. data/lib/lurker/templates/layouts/_sidemenu.html.erb +1 -1
  43. data/lib/lurker/templates/lurker/rendering/show.html.erb +5 -1
  44. data/lib/lurker/templates/public/application.css +2 -2
  45. data/lib/lurker/templates/stylesheets/docs.css +0 -1
  46. data/lib/lurker/utils.rb +18 -0
  47. data/lib/lurker/validator.rb +3 -6
  48. data/lib/lurker/version.rb +1 -1
  49. data/tasks/build.rake +57 -0
  50. data/tasks/deploy.rake +139 -0
  51. data/tasks/generate.rake +78 -0
  52. data/templates/generate_stuff.rb +4 -4
  53. data/templates/lurker_app.rb +1 -1
  54. metadata +9 -2
  55. metadata.gz.sig +0 -0
@@ -0,0 +1,77 @@
1
+ module Lurker
2
+ class Endpoint
3
+ class HttpParameters
4
+ extend Forwardable
5
+ include Lurker::Utils
6
+
7
+ ID = 'id'.freeze
8
+ TYPE = 'type'.freeze
9
+ OBJECT = 'object'.freeze
10
+ PROPERTIES = 'properties'.freeze
11
+ ADDITIONAL_PROPERTIES = 'additionalProperties'.freeze
12
+
13
+ delegate [:[], :key?, :keys, :empty?] => :http_parameters
14
+
15
+ attr_reader :errors
16
+
17
+ def initialize(schema, options = {})
18
+ @schema = schema
19
+
20
+ @schema_key = options.fetch(:schema_key)
21
+ @schema_id = options.fetch(:schema_id)
22
+ @human_name = options.fetch(:human_name, @schema_key)
23
+
24
+ @schema[@schema_key] ||= {}
25
+ @errors = []
26
+ end
27
+
28
+ def add(parameters)
29
+ @schema[@schema_key] = Lurker::SchemaModifier.merge!(
30
+ Lurker::JsonSchemaHash.new(http_parameters, @schema_id), stringify_keys(parameters)
31
+ ).to_h
32
+ end
33
+
34
+ # TODO : Split the collecting of errors and representations of errors
35
+ def validate(parameters)
36
+ errors = Lurker::Validator.new(schemify(http_parameters), stringify_keys(parameters),
37
+ record_errors: true).validate
38
+
39
+ @errors = errors.map { |error| "- #{error}" }
40
+ @errors.unshift(@human_name) unless @errors.empty?
41
+ end
42
+
43
+ private
44
+
45
+ def http_parameters
46
+ @schema[@schema_key]
47
+ end
48
+
49
+ def schemify(object)
50
+ set_additional_properties_false_on(object).tap do |schema|
51
+ schema[ID] = "file://#{@schema_id}"
52
+ end
53
+ end
54
+
55
+ def set_additional_properties_false_on(object)
56
+ if object.is_a? Hash
57
+ copy = object.dup
58
+
59
+ if object[TYPE] == OBJECT || object.key?(PROPERTIES)
60
+ copy[ADDITIONAL_PROPERTIES] ||= false
61
+ end
62
+
63
+ object.each do |key, value|
64
+ next if key == ADDITIONAL_PROPERTIES
65
+ copy[key] = set_additional_properties_false_on(value)
66
+ end
67
+
68
+ copy
69
+ elsif object.is_a? Array
70
+ copy = object.map { |value| set_additional_properties_false_on(value) }
71
+ else
72
+ object
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,42 @@
1
+ module Lurker
2
+ class Endpoint
3
+ class ResponseCodes
4
+ RESPONSE_CODES = 'responseCodes'.freeze
5
+ DESCRIPTION = 'description'.freeze
6
+ SUCCESSFUL = 'successful'.freeze
7
+ STATUS = 'status'.freeze
8
+
9
+ def initialize(schema)
10
+ @schema = schema
11
+ @schema[RESPONSE_CODES] ||= []
12
+ end
13
+
14
+ def add(status_code, successful, options = {})
15
+ response_code = {
16
+ STATUS => status_code,
17
+ SUCCESSFUL => successful,
18
+ DESCRIPTION => options.fetch(:description, '')
19
+ }
20
+
21
+ Lurker::SchemaModifier.append!(@schema[RESPONSE_CODES], response_code)
22
+ end
23
+
24
+ def validate!(status_code, successful)
25
+ return if exists?(status_code, successful)
26
+
27
+ raise Lurker::UndocumentedResponseCode,
28
+ 'Undocumented response: %s, successful: %s' % [
29
+ status_code, successful
30
+ ]
31
+ end
32
+
33
+ def exists?(status_code, successful)
34
+ !!@schema[RESPONSE_CODES].detect do |response_code|
35
+ response_code[SUCCESSFUL] == successful &&
36
+ (response_code[STATUS] == status_code || # 200
37
+ response_code[STATUS].to_i == status_code) # "200 OK"
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -1,9 +1,11 @@
1
- class Lurker::ErbSchemaContext
2
- def allowed(*names)
3
- "Allowed values: #{names.join(', ')}"
4
- end
1
+ module Lurker
2
+ class ErbSchemaContext
3
+ def allowed(*names)
4
+ "Allowed values: #{names.join(', ')}"
5
+ end
5
6
 
6
- def get_binding
7
- binding
7
+ def get_binding
8
+ binding
9
+ end
8
10
  end
9
11
  end
@@ -2,6 +2,14 @@
2
2
  # readed as dumped JSON
3
3
  # to hack `open('...json').read`
4
4
  class Lurker::JamlDescriptor
5
+ module Rescue
6
+ def open(uri)
7
+ Kernel.open(uri)
8
+ rescue Errno::ENOENT
9
+ Lurker::JamlDescriptor.new(uri)
10
+ end
11
+ end
12
+
5
13
  def initialize(uri)
6
14
  @uri = uri.gsub(/\#$/, '').gsub(/\.json/, '.json.yml')
7
15
  @fd = open(@uri)
@@ -11,4 +19,3 @@ class Lurker::JamlDescriptor
11
19
  @read ||= JSON.dump(YAML.load(@fd.read))
12
20
  end
13
21
  end
14
-
@@ -0,0 +1,48 @@
1
+ module Lurker
2
+ class JsonSchemaHash
3
+ attr_accessor :schema_hash
4
+ include JamlDescriptor::Rescue
5
+
6
+ def initialize(schema_hash, uri)
7
+ @to_s = uri
8
+ @uri = URI.parse(uri)
9
+ @uri = URI.parse("file://#{uri}") if @uri.relative?
10
+
11
+ @schema_hash = Hash[schema_hash.map do |k, v|
12
+ if k == '$ref' && v.is_a?(String)
13
+ uri = @uri.merge(v)
14
+ schema_hash = JSON.parse(open(uri.to_s).read)
15
+ [k, JsonSchemaHash.new(schema_hash, uri.to_s)]
16
+ elsif v.is_a?(Hash)
17
+ [k, JsonSchemaHash.new(v, @to_s)]
18
+ else
19
+ [k, v]
20
+ end
21
+ end]
22
+ end
23
+
24
+ def respond_to_missing?(method, include_private = false)
25
+ @schema_hash.send(:respond_to_missing?, method, include_private)
26
+ end
27
+
28
+ def method_missing(method, *args, &block)
29
+ @schema_hash.send method, *args, &block
30
+ end
31
+
32
+ def is_a?(*args)
33
+ @schema_hash.is_a?(*args)
34
+ end
35
+
36
+ def to_h
37
+ Hash[@schema_hash.map do |k, v|
38
+ if JsonSchemaHash === v
39
+ [k, v.to_h]
40
+ elsif v.is_a?(Array)
41
+ [k, v.map { |i| JsonSchemaHash === i ? i.to_h : i }]
42
+ else
43
+ [k, v]
44
+ end
45
+ end]
46
+ end
47
+ end
48
+ end
@@ -3,7 +3,6 @@ require 'json'
3
3
  require 'forwardable'
4
4
 
5
5
  class Lurker::BasePresenter
6
-
7
6
  attr_reader :options
8
7
 
9
8
  def initialize(options = {})
@@ -61,7 +60,7 @@ class Lurker::BasePresenter
61
60
  def path_for_template(filename)
62
61
  template_dir = options[:template_directory]
63
62
  template_path = File.join(template_dir, filename) if template_dir
64
- if template_path.nil? || !File.exists?(template_path)
63
+ if template_path.nil? || !File.exist?(template_path)
65
64
  template_path = File.join(File.dirname(__FILE__), "../templates", filename)
66
65
  end
67
66
  template_path
@@ -2,9 +2,6 @@
2
2
  class Lurker::EndpointPresenter < Lurker::BasePresenter
3
3
  attr_accessor :service_presenter, :endpoint, :endpoint_presenter
4
4
 
5
- extend Forwardable
6
- def_delegators :endpoint, :description
7
-
8
5
  def initialize(endpoint, options = {})
9
6
  super(options)
10
7
  @endpoint = endpoint
@@ -21,7 +18,7 @@ class Lurker::EndpointPresenter < Lurker::BasePresenter
21
18
  end
22
19
 
23
20
  def relative_path(extension = ".html")
24
- '%s%s-%s%s' % [ options[:prefix], endpoint.path, endpoint.verb, extension ]
21
+ '%s%s-%s%s' % [options[:prefix], endpoint.path, endpoint.verb, extension]
25
22
  end
26
23
 
27
24
  def url(extension = ".html")
@@ -29,7 +26,7 @@ class Lurker::EndpointPresenter < Lurker::BasePresenter
29
26
  end
30
27
 
31
28
  def title
32
- '%s %s - %s' % [ endpoint.verb, endpoint.path, endpoint.service.name ]
29
+ description_parts.first.to_s
33
30
  end
34
31
 
35
32
  def prefix
@@ -38,10 +35,14 @@ class Lurker::EndpointPresenter < Lurker::BasePresenter
38
35
 
39
36
  def zws_ify(str)
40
37
  # zero-width-space, makes long lines friendlier for breaking
41
- #str.gsub(/\//, '&#8203;/') if str
38
+ # str.gsub(/\//, '&#8203;/') if str
42
39
  str
43
40
  end
44
41
 
42
+ def description
43
+ description_parts[1..-1].join("\n")
44
+ end
45
+
45
46
  def root_path
46
47
  URI.parse("file://#{endpoint.endpoint_path}")
47
48
  end
@@ -75,7 +76,7 @@ class Lurker::EndpointPresenter < Lurker::BasePresenter
75
76
  return if endpoint.request_parameters.empty?
76
77
  Lurker::JsonPresenter.new(
77
78
  example_from_schema(endpoint.request_parameters, endpoint.schema)
78
- .reject { |k,_| endpoint.url_params.keys.include? k }
79
+ .reject { |k, _| endpoint.url_params.keys.include? k }
79
80
  )
80
81
  end
81
82
 
@@ -208,20 +209,26 @@ class Lurker::EndpointPresenter < Lurker::BasePresenter
208
209
  end
209
210
 
210
211
  def example_from_array(array, parent=nil)
211
- if array["items"].kind_of? Array
212
+ if array["items"].is_a? Array
212
213
  example = []
213
214
  array["items"].each do |item|
214
215
  example << example_from_schema(item, parent)
215
216
  end
216
217
  example
217
- elsif (array["items"] || {})["type"].kind_of? Array
218
+ elsif (array["items"] || {})["type"].is_a? Array
218
219
  example = []
219
220
  array["items"]["type"].each do |item|
220
221
  example << example_from_schema(item, parent)
221
222
  end
222
223
  example
223
224
  else
224
- [ example_from_schema(array["items"], parent) ]
225
+ [example_from_schema(array["items"], parent)]
225
226
  end
226
227
  end
228
+
229
+ private
230
+
231
+ def description_parts
232
+ endpoint.description.to_s.strip.split(/\n+/)
233
+ end
227
234
  end
@@ -8,14 +8,14 @@ class Lurker::JsonPresenter
8
8
  end
9
9
 
10
10
  def to_html
11
- if json.kind_of? String
11
+ if json.is_a? String
12
12
  '<tt>&quot;%s&quot;</tt>' % json.gsub(/\"/, 'quot;')
13
- elsif json.kind_of?(Numeric) ||
14
- json.kind_of?(TrueClass) ||
15
- json.kind_of?(FalseClass)
13
+ elsif json.is_a?(Numeric) ||
14
+ json.is_a?(TrueClass) ||
15
+ json.is_a?(FalseClass)
16
16
  '<tt>%s</tt>' % json
17
- elsif json.kind_of?(Hash) ||
18
- json.kind_of?(Array)
17
+ elsif json.is_a?(Hash) ||
18
+ json.is_a?(Array)
19
19
  '<pre><code>%s</code></pre>' % JSON.pretty_generate(json)
20
20
  end
21
21
  end
@@ -1,6 +1,8 @@
1
1
  # An BasePresenter for a JSON Schema fragment. Like most JSON
2
2
  # schema things, has a tendency to recurse.
3
3
  class Lurker::SchemaPresenter < Lurker::BasePresenter
4
+ attr_reader :schema
5
+
4
6
  FORMATTED_KEYS = %w(
5
7
  description
6
8
  type
@@ -29,7 +31,7 @@ class Lurker::SchemaPresenter < Lurker::BasePresenter
29
31
  options[:nested] > 0
30
32
  end
31
33
 
32
- def to_html
34
+ def to_html(parent_key=nil)
33
35
  html = StringIO.new
34
36
 
35
37
  html << '<span class="deprecated">Deprecated</span>' if deprecated?
@@ -39,21 +41,20 @@ class Lurker::SchemaPresenter < Lurker::BasePresenter
39
41
 
40
42
  html << '<ul>'
41
43
  begin
42
- html << '<li>Required: %s</li>' % required? if nested?
44
+ html << '<li>Required: %s</li>' % required?(parent_key) if nested?
43
45
  html << '<li>Type: %s</li>' % type if type
44
46
  html << '<li>Format: %s</li>' % format if format
45
47
  html << '<li>Example: %s</li>' % example.to_html if example
46
48
  html << enum_html
47
49
 
48
50
  (@schema.keys - FORMATTED_KEYS).each do |key|
49
- html << '<li>%s: %s</li>' % [ key, @schema[key] ]
51
+ html << '<li>%s: %s</li>' % [key, @schema[key]]
50
52
  end
51
53
 
52
54
  html << items_html
53
55
  html << properties_html
54
56
  end
55
57
 
56
-
57
58
  html << '</ul>'
58
59
  html << '</div>'
59
60
 
@@ -62,10 +63,10 @@ class Lurker::SchemaPresenter < Lurker::BasePresenter
62
63
 
63
64
  def type
64
65
  t = @schema["type"]
65
- if t.kind_of? Array
66
+ if t.is_a? Array
66
67
  types = t.map do |type|
67
- if type.kind_of? Hash
68
- '<li>%s</li>' % self.class.new(type, options).to_html
68
+ if type.is_a? Hash
69
+ '<li>%s</li>' % self.class.new(type, options.merge(parent: self)).to_html
69
70
  else
70
71
  '<li>%s</li>' % type
71
72
  end
@@ -82,17 +83,17 @@ class Lurker::SchemaPresenter < Lurker::BasePresenter
82
83
  end
83
84
 
84
85
  def example
85
- return unless e = @schema["example"]
86
+ return unless @schema.key?("example")
86
87
 
87
- Lurker::JsonPresenter.new(e)
88
+ Lurker::JsonPresenter.new(@schema["example"])
88
89
  end
89
90
 
90
91
  def deprecated?
91
92
  @schema["deprecated"]
92
93
  end
93
94
 
94
- def required?
95
- @schema["required"] ? "yes" : "no"
95
+ def required?(parent_key=nil)
96
+ ((options[:parent].schema['required'] || []).include?(parent_key)) ? "yes" : "no"
96
97
  end
97
98
 
98
99
  def enum_html
@@ -116,9 +117,9 @@ class Lurker::SchemaPresenter < Lurker::BasePresenter
116
117
  html = ""
117
118
  html << '<li>Items'
118
119
 
119
- sub_options = options.merge(:nested => options[:nested] + 1)
120
+ sub_options = options.merge(:nested => options[:nested] + 1, :parent => self)
120
121
 
121
- if items.kind_of? Array
122
+ if items.is_a? Array
122
123
  item.compact.each do |item|
123
124
  html << self.class.new(item, sub_options).to_html
124
125
  end
@@ -153,7 +154,7 @@ class Lurker::SchemaPresenter < Lurker::BasePresenter
153
154
  '<tt>%s</tt>' % key,
154
155
  schema_slug(key, property)
155
156
  )
156
- html << self.class.new(property, options.merge(:nested => options[:nested] + 1)).to_html
157
+ html << self.class.new(property, options.merge(:nested => options[:nested] + 1, :parent => self)).to_html(key)
157
158
  html << '</li>'
158
159
  end
159
160
 
@@ -5,7 +5,6 @@ class Lurker::ServicePresenter < Lurker::BasePresenter
5
5
  extend Forwardable
6
6
  def_delegators :service, :description, :discussion, :name, :service_dir
7
7
 
8
-
9
8
  def initialize(service, options = {}, &block)
10
9
  super(options)
11
10
  @service = service
@@ -24,7 +23,7 @@ class Lurker::ServicePresenter < Lurker::BasePresenter
24
23
 
25
24
  def domains
26
25
  return service.domains if service.domains.present?
27
- { '/' => 'Local'}
26
+ { '/' => 'Local' }
28
27
  end
29
28
 
30
29
  def default_domain
@@ -34,7 +33,7 @@ class Lurker::ServicePresenter < Lurker::BasePresenter
34
33
 
35
34
  def name_as_link(options = {})
36
35
  path = index_path
37
- '<a href="%s">%s %s</a>' % [ path, options[:prefix], service.name ]
36
+ '<a href="%s">%s %s</a>' % [path, options[:prefix], service.name]
38
37
  end
39
38
 
40
39
  def slug_name
@@ -42,11 +41,11 @@ class Lurker::ServicePresenter < Lurker::BasePresenter
42
41
  end
43
42
 
44
43
  def url(extension = ".html")
45
- '%s-%s%s' % [ @endpoint.path, @endpoint.verb, extension ]
44
+ '%s-%s%s' % [@endpoint.path, @endpoint.verb, extension]
46
45
  end
47
46
 
48
47
  def endpoints
49
- if !@endpoints
48
+ unless @endpoints
50
49
  @endpoints = []
51
50
  prefix = nil
52
51
 
@@ -73,7 +72,7 @@ class Lurker::ServicePresenter < Lurker::BasePresenter
73
72
 
74
73
  def endpoints_by_prefix
75
74
  @endpoints_by_prefix ||= begin
76
- hash = Hash.new { |h,k| h[k] = Array.new }
75
+ hash = Hash.new { |h, k| h[k] = Array.new }
77
76
  service.endpoints.sort_by(&:endpoint_path).each do |endpoint|
78
77
  presenter = Lurker::EndpointPresenter.new(endpoint, options)
79
78
  presenter.service_presenter = self