lurker 0.6.1 → 0.6.2

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 (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