lurker 0.6.2 → 0.6.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +1 -1
  3. data/Gemfile +1 -0
  4. data/features/dereferencing_through_inlining.feature +102 -0
  5. data/features/partials.feature +3 -1
  6. data/features/step_definitions/additional_cli_steps.rb +19 -0
  7. data/features/support/files_helper.rb +7 -0
  8. data/lib/lurker/endpoint.rb +85 -50
  9. data/lib/lurker/json/concerns/validatable.rb +47 -0
  10. data/lib/lurker/json/orderer.rb +19 -0
  11. data/lib/lurker/json/parser/expertise.rb +30 -0
  12. data/lib/lurker/json/parser/plain_strategy.rb +39 -0
  13. data/lib/lurker/json/parser/typed_strategy.rb +71 -0
  14. data/lib/lurker/json/parser.rb +73 -0
  15. data/lib/lurker/json/reader.rb +28 -0
  16. data/lib/lurker/json/schema/attribute.rb +115 -0
  17. data/lib/lurker/json/schema/extensions.rb +19 -0
  18. data/lib/lurker/json/schema/list.rb +51 -0
  19. data/lib/lurker/json/schema/object.rb +67 -0
  20. data/lib/lurker/json/schema/reference.rb +34 -0
  21. data/lib/lurker/{endpoint → json/schema}/response_codes.rb +13 -16
  22. data/lib/lurker/json/schema/tuple/all_of.rb +17 -0
  23. data/lib/lurker/json/schema/tuple/any_of.rb +17 -0
  24. data/lib/lurker/json/schema/tuple/one_of.rb +17 -0
  25. data/lib/lurker/json/schema/tuple.rb +38 -0
  26. data/lib/lurker/json/schema.rb +122 -0
  27. data/lib/lurker/json/writter.rb +58 -0
  28. data/lib/lurker/presenters/endpoint_presenter.rb +2 -2
  29. data/lib/lurker/presenters/schema_presenter.rb +4 -1
  30. data/lib/lurker/presenters/service_presenter.rb +8 -2
  31. data/lib/lurker/service.rb +4 -4
  32. data/lib/lurker/version.rb +1 -1
  33. data/lib/lurker.rb +19 -8
  34. data/spec/lurker/json/list_spec.rb +101 -0
  35. data/spec/lurker/json/schema_spec.rb +126 -0
  36. data/spec/spec_helper.rb +2 -0
  37. data/spec/support/matchers/json_attribute.rb +27 -0
  38. data/spec/support/matchers/json_object.rb +33 -0
  39. data/templates/generate_stuff.rb +19 -10
  40. data.tar.gz.sig +2 -4
  41. metadata +33 -9
  42. metadata.gz.sig +0 -0
  43. data/lib/lurker/endpoint/http_parameters.rb +0 -77
  44. data/lib/lurker/schema.rb +0 -89
  45. data/lib/lurker/schema_modifier/array.rb +0 -28
  46. data/lib/lurker/schema_modifier/atom.rb +0 -97
  47. data/lib/lurker/schema_modifier/hash.rb +0 -30
  48. data/lib/lurker/schema_modifier.rb +0 -48
@@ -0,0 +1,28 @@
1
+ module Lurker
2
+ module Json
3
+ class Reader
4
+ attr_reader :path
5
+
6
+ def initialize(path)
7
+ @path = path
8
+ @attempts_left = 1
9
+ end
10
+
11
+ def read
12
+ return YAML.load_file(@path) unless @path.match(/\.erb$/)
13
+
14
+ context = Lurker::ErbSchemaContext.new
15
+ erb = ERB.new(IO.read @path).result(context.get_binding)
16
+ YAML.load(erb)
17
+ rescue Errno::ENOENT
18
+ raise if @attempts_left.zero?
19
+
20
+ @path = @path.sub(/\#\/?$/, '').sub(/\.json/, '.json.yml')
21
+ @attempts_left -= 1
22
+
23
+ retry
24
+ end
25
+ alias_method :payload, :read
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,115 @@
1
+ require 'action_dispatch'
2
+
3
+ module Lurker
4
+ module Json
5
+ class Attribute < Schema
6
+ URI = 'uri'.freeze
7
+ TYPE = 'type'.freeze
8
+ COLOR = 'color'.freeze
9
+ FORMAT = 'format'.freeze
10
+ EXAMPLE = 'example'.freeze
11
+ DATE_TIME = 'date-time'.freeze
12
+ DESCRIPTION = 'description'.freeze
13
+
14
+ TYPE_MAP = {
15
+ 'Time' => 'string',
16
+ 'Hash' => 'object',
17
+ 'Float' => 'number',
18
+ 'Fixnum' => 'integer',
19
+ 'NilClass' => 'null',
20
+ 'TrueClass' => 'boolean',
21
+ 'FalseClass' => 'boolean',
22
+ 'ActionDispatch::Http::UploadedFile' => 'string'
23
+ }.freeze
24
+
25
+ def merge!(schema)
26
+ return replace!(schema) if @schema[TYPE].blank?
27
+
28
+ schema = attributify(schema)
29
+ return if eql?(schema)
30
+
31
+ replace_options = {root_schema: root_schema, parent_schema: parent_schema,
32
+ parent_property: parent_property}
33
+
34
+ attributes_tuple = Lurker::Json::Tuple::AnyOf.new(
35
+ [to_hash, schema], replace_options)
36
+
37
+ parent_schema.replace!(parent_property, attributes_tuple)
38
+ end
39
+
40
+ def replace!(schema)
41
+ @schema.clear.merge!(attributify schema)
42
+ end
43
+
44
+ def eql?(schema)
45
+ @schema[TYPE] == attributify(schema)[TYPE]
46
+ end
47
+
48
+ private
49
+
50
+ def parse_schema(schema)
51
+ @schema = {}
52
+ initialize_properties
53
+
54
+ if schema.is_a?(Hash)
55
+ @schema.merge!(schema)
56
+ else
57
+ @schema = attributify(schema)
58
+ end
59
+ end
60
+
61
+ def attributify(schema)
62
+ return schema if schema.is_a?(Hash) || schema.is_a?(Lurker::Json::Schema)
63
+
64
+ attribute = {
65
+ DESCRIPTION => '',
66
+ TYPE => guess_type(schema),
67
+ EXAMPLE => serialize_example(schema)
68
+ }
69
+
70
+ if format = guess_format(schema)
71
+ attribute[FORMAT] = format
72
+ end
73
+
74
+ attribute
75
+ end
76
+
77
+ def initialize_properties
78
+ @schema[DESCRIPTION] ||= ''
79
+ @schema[TYPE] ||= ''
80
+ @schema[EXAMPLE] ||= ''
81
+ end
82
+
83
+ def serialize_example(data)
84
+ if data.is_a?(ActionDispatch::Http::UploadedFile)
85
+ data.headers
86
+ else
87
+ data
88
+ end
89
+ end
90
+
91
+ def guess_type(data)
92
+ data_type = data.class.to_s
93
+ TYPE_MAP[data_type] || data_type.downcase
94
+ end
95
+
96
+ def guess_format(data)
97
+ if data.is_a?(Time)
98
+ DATE_TIME
99
+ elsif data.is_a?(String)
100
+ if data.start_with? 'http://'
101
+ URI
102
+ elsif data.match(/\#[0-9a-fA-F]{3}(?:[0-9a-fA-F]{3})?\b/)
103
+ COLOR
104
+ else
105
+ begin
106
+ DATE_TIME if Time.iso8601(data)
107
+ rescue
108
+ nil
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,19 @@
1
+ module Lurker
2
+ module Json
3
+ class Extensions < Schema
4
+ EXTENSIONS = 'extensions'.freeze
5
+
6
+ def initialize(schema, options = {})
7
+ @parent_property = EXTENSIONS
8
+
9
+ super
10
+ end
11
+
12
+ def merge!(schema)
13
+ return unless Lurker.upgrade?
14
+
15
+ @schema = @parser.parse_property(parent_property, schema)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,51 @@
1
+ module Lurker
2
+ module Json
3
+ class List < Schema
4
+ TYPE = 'type'.freeze
5
+ ARRAY = 'array'.freeze
6
+ ITEMS = 'items'.freeze
7
+
8
+ def merge!(schema)
9
+ if schema.is_a?(Array)
10
+ schema.each { |payload| @schema[ITEMS].merge!(payload) }
11
+ else
12
+ @schema[ITEMS].merge!(schema)
13
+ end
14
+ end
15
+
16
+ def replace!(property, schema)
17
+ if @schema[ITEMS].is_a?(Lurker::Json::Attribute)
18
+ @schema[ITEMS] = schema
19
+ else
20
+ @schema[ITEMS].replace!(property, schema)
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def parse_schema(schema)
27
+ @schema = {}
28
+ initialize_properties
29
+
30
+ return if schema.empty?
31
+
32
+ schema = schema.dup
33
+ if schema.is_a?(Array)
34
+ @schema[ITEMS] = @parser.typed.parse(schema.shift)
35
+
36
+ schema.each { |payload| @schema[ITEMS].merge!(payload) }
37
+ else
38
+ @schema[ITEMS] = @parser.typed.parse(schema.delete ITEMS) if schema.key?(ITEMS)
39
+ @schema.merge!(schema)
40
+ end
41
+
42
+ @schema
43
+ end
44
+
45
+ def initialize_properties
46
+ @schema[TYPE] ||= ARRAY
47
+ @schema[ITEMS] ||= []
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,67 @@
1
+ module Lurker
2
+ module Json
3
+ class Object < Schema
4
+ TYPE = 'type'.freeze
5
+ OBJECT = 'object'.freeze
6
+ REQUIRED = 'required'.freeze
7
+ PROPERTIES = 'properties'.freeze
8
+ DESCRIPTION = 'description'.freeze
9
+ ADDITIONAL_PROPERTIES = 'additionalProperties'.freeze
10
+
11
+ def merge!(schema)
12
+ unless schema.is_a?(Hash)
13
+ return replace_with_new_type(schema) if @schema[PROPERTIES].blank?
14
+
15
+ raise TypeError, "Unable to merge #{schema.class} into JSON object"
16
+ end
17
+
18
+ schema.each do |property, property_schema|
19
+ if @schema[PROPERTIES].key?(property)
20
+ @schema[PROPERTIES][property].merge!(property_schema)
21
+ next
22
+ end
23
+
24
+ replace!(property, property_schema)
25
+ end
26
+ end
27
+
28
+ def replace!(property, property_schema)
29
+ @schema[PROPERTIES][property] = Lurker::Json::Parser.typed(subschema_options)
30
+ .parse_property(property, property_schema)
31
+ end
32
+
33
+ private
34
+
35
+ def parse_schema(schema)
36
+ @schema = {}
37
+ initialize_properties
38
+
39
+ schema = schema.dup
40
+ merge_required = schema.key?(PROPERTIES)
41
+
42
+ (schema.delete(PROPERTIES) || schema).each do |property, property_schema|
43
+ @schema[PROPERTIES][property] = @parser.typed.parse_property(
44
+ property, property_schema)
45
+ end
46
+
47
+ @schema.merge!(schema) if merge_required
48
+ end
49
+
50
+ def replace_with_new_type(schema)
51
+ replace_options = {root_schema: root_schema, parent_schema: parent_schema,
52
+ parent_property: parent_property}
53
+
54
+ new_schema = Lurker::Json::Parser.typed(replace_options).parse(schema)
55
+ parent_schema.replace!(parent_property, new_schema)
56
+ end
57
+
58
+ def initialize_properties
59
+ @schema[DESCRIPTION] ||= ''
60
+ @schema[TYPE] ||= OBJECT
61
+ @schema[ADDITIONAL_PROPERTIES] = !!@schema[ADDITIONAL_PROPERTIES]
62
+ @schema[REQUIRED] ||= []
63
+ @schema[PROPERTIES] ||= {}
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,34 @@
1
+ module Lurker
2
+ module Json
3
+ class Reference < Schema
4
+ REF = '$ref'.freeze
5
+
6
+ attr_reader :original_uri
7
+
8
+ delegate :merge!, :replace!, :reorder!, to: :@schema
9
+
10
+ def to_original_hash(options = {})
11
+ @original_schema.to_hash
12
+ end
13
+
14
+ private
15
+
16
+ def parse_schema(schema)
17
+ @original_schema = schema.dup
18
+
19
+ # NOTE : We decide that reference is relative, so we are using merge
20
+ # We use first read for correct relative path resolving
21
+ reader = Lurker::Json::Reader.new(@uri.merge(schema[REF]).path)
22
+ payload = reader.payload
23
+
24
+ @original_uri = parse_uri(reader.path)
25
+ @schema = @parser.plain(uri: reader.path).parse(payload)
26
+
27
+ # NOTE : The easiest way to get schema copy is to parse it again.
28
+ # It's faster and reliable
29
+ # @_schema = @parser.plain(uri: reader.path).parse(reader.payload)
30
+ # @_schema_file = URI.parse(reader.path)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -1,24 +1,21 @@
1
1
  module Lurker
2
- class Endpoint
3
- class ResponseCodes
4
- RESPONSE_CODES = 'responseCodes'.freeze
5
- DESCRIPTION = 'description'.freeze
6
- SUCCESSFUL = 'successful'.freeze
2
+ module Json
3
+ class ResponseCodes < Schema
7
4
  STATUS = 'status'.freeze
5
+ SUCCESSFUL = 'successful'.freeze
6
+ DESCRIPTION = 'description'.freeze
7
+
8
+ def initialize(schema, options = {})
9
+ @parent_property = 'responseCodes'
8
10
 
9
- def initialize(schema)
10
- @schema = schema
11
- @schema[RESPONSE_CODES] ||= []
11
+ super
12
12
  end
13
13
 
14
- def add(status_code, successful, options = {})
15
- response_code = {
16
- STATUS => status_code,
17
- SUCCESSFUL => successful,
18
- DESCRIPTION => options.fetch(:description, '')
19
- }
14
+ def merge!(status_code, successful)
15
+ return if exists?(status_code, successful)
20
16
 
21
- Lurker::SchemaModifier.append!(@schema[RESPONSE_CODES], response_code)
17
+ payload = {STATUS => status_code, SUCCESSFUL => successful, DESCRIPTION => ''}
18
+ @schema << Lurker::Json::Parser.plain(root_schema: root_schema).parse(payload)
22
19
  end
23
20
 
24
21
  def validate!(status_code, successful)
@@ -31,7 +28,7 @@ module Lurker
31
28
  end
32
29
 
33
30
  def exists?(status_code, successful)
34
- !!@schema[RESPONSE_CODES].detect do |response_code|
31
+ !!@schema.detect do |response_code|
35
32
  response_code[SUCCESSFUL] == successful &&
36
33
  (response_code[STATUS] == status_code || # 200
37
34
  response_code[STATUS].to_i == status_code) # "200 OK"
@@ -0,0 +1,17 @@
1
+ module Lurker
2
+ module Json
3
+ module Tuple
4
+ class AllOf < Json::Schema
5
+ include Tuple::InstanceMethods
6
+
7
+ ALLOF = 'allOf'.freeze
8
+
9
+ private
10
+
11
+ def tuple_key
12
+ ALLOF
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ module Lurker
2
+ module Json
3
+ module Tuple
4
+ class AnyOf < Json::Schema
5
+ include Tuple::InstanceMethods
6
+
7
+ ANYOF = 'anyOf'.freeze
8
+
9
+ private
10
+
11
+ def tuple_key
12
+ ANYOF
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ module Lurker
2
+ module Json
3
+ module Tuple
4
+ class OneOf < Json::Schema
5
+ include Tuple::InstanceMethods
6
+
7
+ ONEOF = 'oneOf'.freeze
8
+
9
+ private
10
+
11
+ def tuple_key
12
+ ONEOF
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,38 @@
1
+ module Lurker
2
+ module Json
3
+ module Tuple
4
+ module InstanceMethods
5
+ def merge!(schema)
6
+ return if exists?(schema)
7
+
8
+ @schema[tuple_key] << @parser.typed.parse_property(
9
+ parent_property, schema)
10
+ end
11
+
12
+ def replace!(schema)
13
+ raise NotImplementedError
14
+ end
15
+
16
+ def exists?(schema)
17
+ raise NotImplementedError
18
+ end
19
+
20
+ private
21
+
22
+ def parse_schema(schema)
23
+ @schema = {}
24
+ initialize_properties
25
+
26
+ schema = schema.dup
27
+ @schema[tuple_key] = (schema.delete(tuple_key) || schema).map do |payload|
28
+ @parser.typed.parse_property(parent_property, payload)
29
+ end
30
+ end
31
+
32
+ def initialize_properties
33
+ @schema[tuple_key] ||= []
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,122 @@
1
+ module Lurker
2
+ module Json
3
+ class Schema
4
+ include Lurker::Json::Concerns::Validatable
5
+
6
+ EXTENSIONS = 'extensions'.freeze
7
+ RESPONSE_CODES = 'responseCodes'.freeze
8
+ REQUEST_PARAMETERS = 'requestParameters'.freeze
9
+ RESPONSE_PARAMETERS = 'responseParameters'.freeze
10
+
11
+ attr_reader :uri, :root_schema, :parent_schema, :parent_property
12
+
13
+ def initialize(schema, options = {})
14
+ @root_schema = options[:root_schema]
15
+ @parent_schema = options[:parent_schema]
16
+ @parent_property = options[:parent_property]
17
+ @uri = parse_uri(options[:uri])
18
+
19
+ @parser = Lurker::Json::Parser.new(subschema_options)
20
+
21
+ parse_schema(schema)
22
+ end
23
+
24
+ def root?
25
+ root_schema.blank?
26
+ end
27
+
28
+ def merge!(schema)
29
+ return if schema.blank?
30
+
31
+ schema.each do |property, property_schema|
32
+ if @schema[property].is_a?(Lurker::Json::Schema)
33
+ @schema[property].merge!(property_schema)
34
+
35
+ next
36
+ end
37
+
38
+ replace!(property, property_schema)
39
+ end
40
+ end
41
+
42
+ def replace!(property, property_schema)
43
+ @schema[property] = Lurker::Json::Parser.plain(subschema_options)
44
+ .parse_property(property, property_schema)
45
+ end
46
+
47
+ def reorder!
48
+ @schema = Hash[@schema.sort]
49
+ self
50
+ end
51
+
52
+ def to_hash(options = {})
53
+ hashify(@schema, options)
54
+ end
55
+
56
+ def to_json(options = {})
57
+ hashify(@schema, options).to_json
58
+ end
59
+
60
+ def to_yaml(options = {})
61
+ YAML.dump(to_hash(options))
62
+ end
63
+
64
+ def method_missing(method, *args, &block)
65
+ if @schema.is_a?(Lurker::Json::Schema) || @schema.respond_to?(method)
66
+ return @schema.send(method, *args, &block)
67
+ end
68
+
69
+ super
70
+ end
71
+
72
+ private
73
+
74
+ def hashify(object, options = {})
75
+ case object
76
+ when Lurker::Json::Reference
77
+ options[:reference] == :original ? object.to_original_hash(options)
78
+ : object.to_hash(options)
79
+ when Lurker::Json::Schema then object.to_hash(options)
80
+ when Array then object.map { |x| hashify(x, options) }
81
+ when Hash
82
+ object.each_with_object({}) do |(property, property_schema), memo|
83
+ memo[property] = hashify(property_schema, options)
84
+ end
85
+ else object
86
+ end
87
+ end
88
+
89
+ def parse_uri(uri)
90
+ return if uri.blank?
91
+
92
+ uri = uri.respond_to?(:scheme) ? uri : URI.parse(uri)
93
+ uri.relative? ? URI.parse("file://#{uri}") : uri
94
+ end
95
+
96
+ def parse_schema(schema)
97
+ @schema = {}
98
+
99
+ unless schema.is_a?(Hash)
100
+ return @schema = @parser.plain.parse(schema)
101
+ end
102
+
103
+ schema.each do |property, property_schema|
104
+ @schema[property] = case property
105
+ when EXTENSIONS
106
+ Lurker::Json::Extensions.new(property_schema, subschema_options)
107
+ when RESPONSE_CODES
108
+ Lurker::Json::ResponseCodes.new(property_schema, subschema_options)
109
+ when REQUEST_PARAMETERS, RESPONSE_PARAMETERS
110
+ @parser.typed.parse_property(property, property_schema)
111
+ else
112
+ @parser.plain.parse_property(property, property_schema)
113
+ end
114
+ end
115
+ end
116
+
117
+ def subschema_options
118
+ {uri: uri, root_schema: (root? ? self : root_schema), parent_schema: self}
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,58 @@
1
+ module Lurker
2
+ module Json
3
+ class Writter
4
+ class << self
5
+ def write(schema, path)
6
+ new(path).write(schema)
7
+ end
8
+ end
9
+
10
+ def initialize(path)
11
+ @path = path
12
+ @dirname = File.dirname(path)
13
+ end
14
+
15
+ def write(schema)
16
+ write_to_file(schema)
17
+
18
+ extract_references(schema).each do |reference|
19
+ Lurker::Json::Writter.write(reference, reference.original_uri.path)
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ # TODO : Separate schema dumping for JSON & YAML files
26
+ def write_to_file(schema)
27
+ FileUtils.mkdir_p(@dirname) unless File.directory?(@dirname)
28
+ File.open(@path, File::WRONLY | File::TRUNC | File::CREAT) do |file|
29
+ file.write(present_by_extension schema)
30
+ end
31
+ end
32
+
33
+ def extract_references(schema, memo = [])
34
+ case schema
35
+ when Array
36
+ schema.each { |payload| extract_references(payload, memo) }
37
+ when Hash, Lurker::Json::Schema
38
+ schema.each do |_, payload|
39
+ memo << payload if payload.is_a?(Lurker::Json::Reference)
40
+ extract_references(payload, memo)
41
+ end
42
+ else
43
+ # no-op
44
+ end
45
+
46
+ memo
47
+ end
48
+
49
+ def present_by_extension(schema)
50
+ case File.extname(@path)
51
+ when '.yml' then schema.to_yaml(reference: :original)
52
+ when '.json' then JSON.pretty_generate(schema.to_hash reference: :original)
53
+ else raise TypeError, "Unknown schema file extension '#{File.extname(@path)}'"
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -115,7 +115,7 @@ class Lurker::EndpointPresenter < Lurker::BasePresenter
115
115
  # TODO: remove in favor of named_path
116
116
  def path
117
117
  return @path if @path
118
- unless (@path = @endpoint.schema.extensions.try(:[], 'path_info')).present?
118
+ unless (@path = @endpoint.schema['extensions'].try(:[], 'path_info')).present?
119
119
  @path = @endpoint.path.gsub(/__/, ':')
120
120
  @path = @path.gsub(/-#{@end}/) if @endpoint.schema.extensions.try(:[], 'suffix').present?
121
121
  end
@@ -127,7 +127,7 @@ class Lurker::EndpointPresenter < Lurker::BasePresenter
127
127
  def named_path
128
128
  return @named_path if @named_path
129
129
  @named_path = base_path.sub(/\/?$/, '/') + endpoint.path.gsub(/__/, ':')
130
- if (suffix = endpoint.schema.extensions.try(:[], 'suffix')).present?
130
+ if (suffix = endpoint.schema['extensions'].try(:[], 'suffix')).present?
131
131
  @named_path = @named_path.gsub(/-#{suffix}/, '')
132
132
  end
133
133
  @named_path
@@ -48,7 +48,10 @@ class Lurker::SchemaPresenter < Lurker::BasePresenter
48
48
  html << enum_html
49
49
 
50
50
  (@schema.keys - FORMATTED_KEYS).each do |key|
51
- html << '<li>%s: %s</li>' % [key, @schema[key]]
51
+ value = @schema[key]
52
+ value = value.to_hash if value.is_a?(Lurker::Json::Schema)
53
+
54
+ html << '<li>%s: %s</li>' % [key, value]
52
55
  end
53
56
 
54
57
  html << items_html
@@ -22,12 +22,12 @@ class Lurker::ServicePresenter < Lurker::BasePresenter
22
22
  end
23
23
 
24
24
  def domains
25
- return service.domains if service.domains.present?
25
+ return service_domains if service_domains.present?
26
26
  { '/' => 'Local' }
27
27
  end
28
28
 
29
29
  def default_domain
30
- return service.domains.to_a[0][1] if service.domains.present?
30
+ return service_domains.to_a[0][1] if service_domains.present?
31
31
  '/'
32
32
  end
33
33
 
@@ -87,4 +87,10 @@ class Lurker::ServicePresenter < Lurker::BasePresenter
87
87
  hash
88
88
  end
89
89
  end
90
+
91
+ private
92
+
93
+ def service_domains
94
+ service.domains.to_hash
95
+ end
90
96
  end