lurker 0.6.2 → 0.6.3

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 (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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 434f91796b5945b641bb46e3c89a336a3e7e0393
4
- data.tar.gz: b1fbb76bebd71b403470dd80dc31f350a53b364d
3
+ metadata.gz: c59974fdb662a2fbadcb01539d0fe9a1b919da2c
4
+ data.tar.gz: 476c5cad93b78d96f397fae62ed32ae19ad30c21
5
5
  SHA512:
6
- metadata.gz: c23242ced7fa8084458d50cc4b8f405c0fa72f5111ff26a7c695a4901d301515199cdebf086fb3a30acf4f32e4f03ae540ec31192fb5f62bcead79e57249d8cb
7
- data.tar.gz: ae52d6101d2093832c6b8c1fa44391218589460e0deb98bc9526c900bcf4796660c35f1838c1da51ce70bfd708117841234f1934a457d9cef79293ce804baa4b
6
+ metadata.gz: a2a7af02fa6d092a639b79f5f903c373c33b40cc3b8ad621aa69eb6ebdd056998b66cfdb57d93be644dff1c0dd2e3cf554aec85a30fd61bc65038b6060a1ace9
7
+ data.tar.gz: d2afae3c32e2adce210fb74144e0078ea8db8241b7aebeb0afeb474ec67dcf16083ad8153fc405fde8924af7d4986602575078d836be4c5b2c1e5d7a20f28fd3
checksums.yaml.gz.sig CHANGED
@@ -1 +1 @@
1
- r����A!A����oAڡ�Ҧ ]-x���G��':�$�$DM���T̜K���gޭ�6:U��r�%�]����_���G���d3'�]ޒK�����|ڲ��T�?'�@�%��3���X��$�ЎKqvX0��D
1
+ �ƴs���:�vsT�\�k���Ȑj<Dk9�s:d�������L��{o�z���W_�W���aA�za�G��q#Mz^)8K㊥����r_~����%��Մ��H���'�0���51rÝRy�NW��ݧmFZ��#��a൥� I9�g��b�:Kj��,i|�g\C}��F��Ѥs��A�;��*���PiM7�5aܶ��/ ����U���7A�>/K���Sh�Ņm; 5;��P�oX�3��
data/Gemfile CHANGED
@@ -6,6 +6,7 @@ group :development do
6
6
  # tools
7
7
  gem 'pry-byebug'
8
8
  gem 'pry-stack_explorer'
9
+ gem 'pry-session'
9
10
  # rspec --format fuubar
10
11
  gem 'fuubar'
11
12
  # cucumber --format fuubar
@@ -0,0 +1,102 @@
1
+ Feature: $ref defererencing through inlining
2
+ Background:
3
+ Given a checked file "lurker/api/v3/users/__id-PATCH.json.yml" with:
4
+ """yml
5
+ ---
6
+ description: user updating
7
+ prefix: users management
8
+ requestParameters:
9
+ description: ''
10
+ type: object
11
+ additionalProperties: false
12
+ required: []
13
+ properties:
14
+ user:
15
+ "$ref": "../../../definitions/user_request_parameters.json#/"
16
+ responseCodes:
17
+ - status: 200
18
+ successful: true
19
+ description: ''
20
+ responseParameters:
21
+ "$ref": "../../../definitions/user.json#/"
22
+ extensions:
23
+ method: PATCH
24
+ path_info: "/api/v3/users/1"
25
+ path_params:
26
+ id: '1'
27
+ controller: api/v3/users
28
+ action: update
29
+
30
+ """
31
+ And a file named "lurker/definitions/user_request_parameters.json" with:
32
+ """json
33
+ {
34
+ "properties": {
35
+ "name": {
36
+ "type": "string",
37
+ "example": "Bob"
38
+ }
39
+ }
40
+ }
41
+ """
42
+ And a file named "lurker/definitions/user.json.yml" with:
43
+ """yml
44
+ ---
45
+ properties:
46
+ id:
47
+ type: integer
48
+ example: 1
49
+ name:
50
+ type: string
51
+ example: razum2um
52
+ surname:
53
+ type: string
54
+ example: Unknown
55
+ """
56
+ And a file named "spec/controllers/api/v3/users_controller_spec.rb" with:
57
+ """ruby
58
+ require "spec_helper"
59
+
60
+ describe Api::V3::UsersController, :lurker do
61
+ render_views
62
+
63
+ let(:user) do
64
+ User.where(name: 'razum2um', surname: 'Unknown').first_or_create!
65
+ end
66
+
67
+ it "updates a user surname as string" do
68
+ patch :update, id: user.id, user: { surname: 'Marley' }
69
+ expect(response).to be_success
70
+ end
71
+ end
72
+ """
73
+
74
+ Scenario: json schema left $ref keyword as is using "users/update"
75
+ When I run `bin/rspec spec/controllers/api/v3/users_controller_spec.rb`
76
+ Then the example should pass
77
+ Then a file named "lurker/api/v3/users/__id-PATCH.json.yml" should exist
78
+ Then the checked file "lurker/api/v3/users/__id-PATCH.json.yml" should not change
79
+ Then a file named "lurker/definitions/user_request_parameters.json" should exist
80
+ Then the file "lurker/definitions/user_request_parameters.json" should contain exactly:
81
+ """json
82
+ {
83
+ "description": "",
84
+ "type": "object",
85
+ "additionalProperties": false,
86
+ "required": [
87
+
88
+ ],
89
+ "properties": {
90
+ "name": {
91
+ "description": "",
92
+ "type": "string",
93
+ "example": "Bob"
94
+ },
95
+ "surname": {
96
+ "description": "",
97
+ "type": "string",
98
+ "example": "Marley"
99
+ }
100
+ }
101
+ }
102
+ """
@@ -20,6 +20,9 @@ Feature: partials
20
20
  name:
21
21
  type: string
22
22
  example: razum2um
23
+ surname:
24
+ type: string
25
+ example: Unknown
23
26
  """
24
27
  And a file named "lurker/definitions/repo.json.yml" with:
25
28
  """yml
@@ -94,4 +97,3 @@ Feature: partials
94
97
  create index.html
95
98
  create api/v1/users/__user_id/repos-POST.html
96
99
  """
97
-
@@ -1,3 +1,10 @@
1
+ Given /^a checked file "([^"]*)" with:$/ do |file_name, file_content|
2
+ write_file(file_name, file_content)
3
+
4
+ @files ||= {}
5
+ in_current_dir { @files[md5(file_name)] = checksum(file_name) }
6
+ end
7
+
1
8
  Given /^an empty directory named "([^"]*)"$/ do |dir_name|
2
9
  FileUtils.rm_rf File.expand_path("../../../tmp/lurker_app/#{dir_name}", __FILE__)
3
10
  create_dir(dir_name)
@@ -83,3 +90,15 @@ Then(/^I should see JSON response with "([^"]*)"$/) do |name|
83
90
  expect(page).to have_content name
84
91
  end
85
92
  end
93
+
94
+ Then /(?:a|the) checked file "([^"]*)" should not change$/ do |file_name|
95
+ in_current_dir do
96
+ expect(@files.try(:[], md5(file_name))).to eq checksum(file_name)
97
+ end
98
+ end
99
+
100
+ Then /(?:a|the) checked file "([^"]*)" should change$/ do |file_name|
101
+ in_current_dir do
102
+ expect(@files.try(:[], md5(file_name))).not_to eq checksum(file_name)
103
+ end
104
+ end
@@ -0,0 +1,7 @@
1
+ def md5(file_name)
2
+ Digest::MD5.hexdigest(file_name)
3
+ end
4
+
5
+ def checksum(file_name)
6
+ Digest::SHA2.hexdigest(File.read file_name)
7
+ end
@@ -8,24 +8,43 @@ module Lurker
8
8
  class Endpoint
9
9
  include Lurker::Utils
10
10
 
11
+ PREFIX = 'prefix'.freeze
12
+ ACTION = 'action'.freeze
13
+ CONTROLLER = 'controller'.freeze
14
+ EXTENSIONS = 'extensions'.freeze
15
+ PATH_PARAMS = 'path_params'.freeze
16
+ QUERY_PARAMS = 'query_params'.freeze
17
+ DEPRECATED = 'deprecated'.freeze
18
+ DESCRIPTION = 'description'.freeze
19
+ RESPONSE_CODES = 'responseCodes'.freeze
20
+ REQUEST_PARAMETERS = 'requestParameters'.freeze
21
+ RESPONSE_PARAMETERS = 'responseParameters'.freeze
22
+ DESCRPTIONS = {
23
+ 'index' => 'listing',
24
+ 'show' => '',
25
+ 'edit' => 'editing',
26
+ 'create' => 'creation',
27
+ 'update' => 'updating',
28
+ 'destroy' => 'descruction'
29
+ }.freeze
30
+
11
31
  attr_reader :schema, :service, :endpoint_path, :extensions
12
- attr_reader :request_parameters, :response_parameters, :response_codes
13
- attr_accessor :errors
14
32
 
15
33
  def initialize(endpoint_path, extensions = {}, service = Lurker::Service.default_service)
16
34
  @endpoint_path = endpoint_path
17
35
  @extensions = extensions
18
36
  @service = service
19
- @errors = []
20
37
  @persisted = false
21
38
  @schema = File.exist?(endpoint_path) ? load_schema : build_schema
22
-
23
- initialize_schema_properties
39
+ @request_errors = []
40
+ @response_errors = []
24
41
  end
25
42
 
26
43
  def persist!
27
- schema.ordered! unless persisted?
28
- schema.write_to(endpoint_path)
44
+ finalize_schema!
45
+
46
+ Lurker::Json::Orderer.reorder(schema) unless persisted?
47
+ Lurker::Json::Writter.write(schema, endpoint_path)
29
48
 
30
49
  @persisted = true
31
50
  end
@@ -42,21 +61,30 @@ module Lurker
42
61
  end
43
62
 
44
63
  def consume_request(params, successful = true)
45
- request_parameters.validate(params) if persisted?
46
- request_parameters.add(params) if successful
64
+ parameters = stringify_keys(params)
65
+
66
+ if persisted?
67
+ @request_errors = request_parameters.validate(parameters)
68
+ @request_errors.unshift('Request') unless @request_errors.empty?
69
+ end
70
+
71
+ request_parameters.merge!(parameters) if successful
47
72
  end
48
73
 
49
74
  def consume_response(params, status_code, successful = true)
75
+ parameters = stringify_keys(params)
76
+
50
77
  if persisted?
51
78
  response_codes.validate!(status_code, successful)
52
- response_parameters.validate(params) if successful
79
+
80
+ @response_errors = response_parameters.validate(parameters)
81
+ @response_errors.unshift('Response') unless @response_errors.empty?
53
82
 
54
83
  return
55
84
  end
56
85
 
57
- response_parameters.add(params) if successful
58
- response_codes.add(status_code, successful) unless response_codes.exists?(
59
- status_code, successful)
86
+ response_parameters.merge!(parameters) if successful
87
+ response_codes.merge!(status_code, successful)
60
88
  end
61
89
 
62
90
  def verb
@@ -65,44 +93,46 @@ module Lurker
65
93
 
66
94
  def path
67
95
  @path ||= endpoint_path.
68
- gsub(service.service_dir, "").
96
+ gsub(service.service_dir, '').
69
97
  match(/\/?(.*)[-\/][A-Z]+\.json(\.yml)?(\.erb)?$/)[1]
70
98
  end
71
99
 
72
100
  # properties
73
101
 
74
102
  def deprecated?
75
- @schema["deprecated"]
103
+ @schema[DEPRECATED]
76
104
  end
77
105
 
78
106
  def prefix
79
- @schema["prefix"]
107
+ @schema[PREFIX]
80
108
  end
81
109
 
82
110
  def description
83
- @schema["description"]
111
+ @schema[DESCRIPTION]
84
112
  end
85
113
 
86
114
  def url_params
87
- (schema.extensions['path_params'] || {}).reject { |k, _| %w(action controller format).include? k }
115
+ (@schema[EXTENSIONS][PATH_PARAMS] || {}).reject { |k, _| %w(action controller format).include? k }
88
116
  end
89
117
 
90
118
  def query_params
91
- (schema.extensions['query_params'] || {})
119
+ (@schema[EXTENSIONS][QUERY_PARAMS] || {})
92
120
  end
93
121
 
94
- protected
95
-
96
- def initialize_schema_properties
97
- @response_codes = ResponseCodes.new(schema)
122
+ def request_parameters
123
+ @schema[REQUEST_PARAMETERS]
124
+ end
98
125
 
99
- @response_parameters = HttpParameters.new(schema,
100
- schema_key: 'responseParameters', schema_id: endpoint_path, human_name: 'Response')
126
+ def response_parameters
127
+ @schema[RESPONSE_PARAMETERS]
128
+ end
101
129
 
102
- @request_parameters = HttpParameters.new(schema,
103
- schema_key: 'requestParameters', schema_id: endpoint_path, human_name: 'Request')
130
+ def response_codes
131
+ @schema[RESPONSE_CODES]
104
132
  end
105
133
 
134
+ protected
135
+
106
136
  def persisted?
107
137
  !!@persisted
108
138
  end
@@ -110,43 +140,48 @@ module Lurker
110
140
  def load_schema
111
141
  @persisted = true
112
142
 
113
- Lurker::Schema.new(
114
- load_file(endpoint_path),
115
- stringify_keys(extensions)
116
- )
143
+ reader = Lurker::Json::Reader.new(endpoint_path)
144
+ schemify(reader.payload)
117
145
  end
118
146
 
119
147
  def build_schema
120
148
  @persisted = false
121
149
 
122
- Lurker::Schema.new(
123
- {
124
- "prefix" => "",
125
- "description" => "",
126
- "responseCodes" => []
127
- },
128
- stringify_keys(extensions)
129
- )
130
- end
131
-
132
- def load_file(fname)
133
- if fname.match(/\.erb$/)
134
- context = Lurker::ErbSchemaContext.new
135
- erb = ERB.new(IO.read(fname)).result(context.get_binding)
136
- YAML.load(erb)
137
- else
138
- YAML.load_file(fname)
150
+ payload = {
151
+ DESCRIPTION => '',
152
+ PREFIX => '',
153
+ REQUEST_PARAMETERS => {},
154
+ RESPONSE_CODES => [],
155
+ RESPONSE_PARAMETERS => {}
156
+ }
157
+ schemify(payload)
158
+ end
159
+
160
+ def schemify(payload)
161
+ Lurker::Json::Parser.plain(uri: endpoint_path).parse(payload).tap do |schm|
162
+ ext = Lurker::Json::Extensions.new(stringify_keys extensions)
163
+ schm.merge!(EXTENSIONS => ext)
139
164
  end
140
165
  end
141
166
 
167
+ def finalize_schema!
168
+ path_params = schema[EXTENSIONS][PATH_PARAMS] || {}
169
+ subject = path_params[CONTROLLER].to_s.split(/\//).last.to_s
170
+ description = DESCRPTIONS[path_params[ACTION]]
171
+
172
+ schema[DESCRIPTION] = "#{subject.singularize} #{description}".strip if schema[DESCRIPTION].blank?
173
+ schema[PREFIX] = "#{subject} management" if schema[PREFIX].blank?
174
+ end
175
+
142
176
  def raise_errors!
143
- return if response_parameters.errors.empty?
177
+ return if @response_errors.empty?
144
178
 
145
- errors = (request_parameters.errors | response_parameters.errors) * "\n"
179
+ errors = (@request_errors | @response_errors) * "\n"
146
180
  exception = Lurker::ValidationError.new(word_wrap errors)
147
181
  if (example = Lurker::Spy.current.block).respond_to?(:metadata) && (metadata = example.metadata).respond_to?(:location, true)
148
182
  exception.set_backtrace [metadata.send(:location)]
149
183
  end
184
+
150
185
  raise exception
151
186
  end
152
187
 
@@ -0,0 +1,47 @@
1
+ module Lurker
2
+ module Json
3
+ module Concerns
4
+ module Validatable
5
+ ID = 'id'.freeze
6
+ TYPE = 'type'.freeze
7
+ OBJECT = 'object'.freeze
8
+ PROPERTIES = 'properties'.freeze
9
+ ADDITIONAL_PROPERTIES = 'additionalProperties'.freeze
10
+
11
+ def validate(data)
12
+ Lurker::Validator.new(to_validation_schema, data,
13
+ record_errors: true).validate.map { |error| "- #{error}" }
14
+ end
15
+
16
+ def to_validation_schema
17
+ set_additional_properties_false_on(to_hash).tap do |schema|
18
+ schema[ID] = "file://#{uri}"
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def set_additional_properties_false_on(object)
25
+ case object
26
+ when Hash
27
+ copy = object.dup
28
+
29
+ if object[TYPE] == OBJECT || object.key?(PROPERTIES)
30
+ copy[ADDITIONAL_PROPERTIES] ||= false
31
+ end
32
+
33
+ object.each do |key, value|
34
+ next if key == ADDITIONAL_PROPERTIES
35
+ copy[key] = set_additional_properties_false_on(value)
36
+ end
37
+
38
+ copy
39
+ when Array
40
+ copy = object.map { |value| set_additional_properties_false_on(value) }
41
+ else object
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,19 @@
1
+ module Lurker
2
+ module Json
3
+ class Orderer
4
+ EXTENSIONS = 'extensions'.freeze
5
+
6
+ class << self
7
+ def reorder(schema)
8
+ new.reorder(schema)
9
+ end
10
+ end
11
+
12
+ def reorder(schema)
13
+ extensions = schema.delete(EXTENSIONS).try(:reorder!)
14
+ schema.reorder!
15
+ schema[EXTENSIONS] = extensions
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,30 @@
1
+ module Lurker
2
+ module Json
3
+ class Parser
4
+ module Expertise
5
+ REF = '$ref'.freeze
6
+ TYPE = 'type'.freeze
7
+ ANYOF = 'anyOf'.freeze
8
+ ALLOF = 'allOf'.freeze
9
+ ONEOF = 'oneOf'.freeze
10
+ ITEMS = 'items'.freeze
11
+ PROPERTIES = 'properties'.freeze
12
+
13
+ module_function
14
+
15
+ def type_defined?(hash)
16
+ return false unless hash.is_a?(Hash)
17
+
18
+ hash.key? TYPE
19
+ end
20
+
21
+ def type_supposed?(hash)
22
+ return false unless hash.is_a?(Hash)
23
+
24
+ hash.key?(ANYOF) || hash.key?(ALLOF) || hash.key?(ONEOF) ||
25
+ hash.key?(ITEMS) || hash.key?(PROPERTIES) || hash.key?(REF)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,39 @@
1
+ module Lurker
2
+ module Json
3
+ class Parser
4
+ class PlainStrategy
5
+ include Lurker::Json::Parser::Expertise
6
+
7
+ attr_reader :schema_options
8
+
9
+ def initialize(options)
10
+ @schema_options = options.dup
11
+ end
12
+
13
+ def parse(payload)
14
+ case payload
15
+ when Lurker::Json::Schema
16
+ payload
17
+ when Hash
18
+ return parse_as_typed(payload) if type_defined?(payload) ||
19
+ type_supposed?(payload)
20
+
21
+ Lurker::Json::Schema.new(payload, schema_options)
22
+ when Array
23
+ payload.map do |schema|
24
+ Lurker::Json::Parser.plain(schema_options).parse(schema)
25
+ end
26
+ else
27
+ payload
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ def parse_as_typed(payload)
34
+ Lurker::Json::Parser.typed(schema_options).parse(payload)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,71 @@
1
+ module Lurker
2
+ module Json
3
+ class Parser
4
+ class TypedStrategy
5
+ include Lurker::Json::Parser::Expertise
6
+
7
+ ANYOF = 'anyOf'.freeze
8
+ ALLOF = 'allOf'.freeze
9
+ ONEOF = 'oneOf'.freeze
10
+ ITEMS = 'items'.freeze
11
+ TYPE = 'type'.freeze
12
+ ARRAY = 'array'.freeze
13
+ OBJECT = 'object'.freeze
14
+ PROPERTIES = 'properties'.freeze
15
+
16
+ attr_reader :schema_options
17
+
18
+ def initialize(options)
19
+ @schema_options = options.dup
20
+ end
21
+
22
+ def parse(payload)
23
+ case payload
24
+ when Lurker::Json::Schema
25
+ payload
26
+ when Hash
27
+ return create_by_type(payload) if type_defined?(payload)
28
+ return create_by_supposition(payload) if type_supposed?(payload)
29
+
30
+ Lurker::Json::Object.new(payload, schema_options)
31
+ when Array
32
+ Lurker::Json::List.new(payload, schema_options)
33
+ else
34
+ Lurker::Json::Attribute.new(payload, schema_options)
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ def create_by_supposition(payload)
41
+ if payload.key?(ITEMS)
42
+ Lurker::Json::List.new(payload, schema_options)
43
+ elsif payload.key?(PROPERTIES)
44
+ Lurker::Json::Object.new(payload, schema_options)
45
+ elsif payload.key?(ANYOF)
46
+ Lurker::Json::Tuple::AnyOf.new(payload, schema_options)
47
+ elsif payload.key?(ALLOF)
48
+ Lurker::Json::Tuple::AllOf.new(payload, schema_options)
49
+ elsif payload.key?(ONEOF)
50
+ Lurker::Json::Tuple::OneOf.new(payload, schema_options)
51
+ elsif payload.key?(REF)
52
+ Lurker::Json::Reference.new(payload, schema_options)
53
+ else
54
+ raise "Unknown type supposition for #{payload}"
55
+ end
56
+ end
57
+
58
+ def create_by_type(payload)
59
+ case payload[TYPE]
60
+ when OBJECT
61
+ Lurker::Json::Object.new(payload, schema_options)
62
+ when ARRAY
63
+ Lurker::Json::List.new(payload, schema_options)
64
+ else
65
+ Lurker::Json::Attribute.new(payload, schema_options)
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,73 @@
1
+ module Lurker
2
+ module Json
3
+ class Parser
4
+ class << self
5
+ def plain(options = {})
6
+ new(options).plain
7
+ end
8
+
9
+ def typed(options = {})
10
+ new(options).typed
11
+ end
12
+ end
13
+
14
+ def initialize(options = {})
15
+ @root_schema = options[:root_schema]
16
+ @parent_schema = options[:parent_schema]
17
+ @parent_property = options[:parent_property]
18
+ @uri = options[:uri] || @parent_schema.try(:uri)
19
+ @strategy = nil
20
+ end
21
+
22
+ def parse(payload)
23
+ parse_once { @strategy.new(schema_options_once).parse(payload) }
24
+ end
25
+
26
+ def parse_property(property, payload)
27
+ options = schema_options_once.merge!(parent_property: property)
28
+ parse_once { @strategy.new(options).parse(payload) }
29
+ end
30
+
31
+ def plain(options = {})
32
+ @options = schema_options.merge!(options)
33
+ @strategy = strategy_klass(:plain)
34
+ self
35
+ end
36
+
37
+ def typed(options = {})
38
+ @options = schema_options.merge!(options)
39
+ @strategy = strategy_klass(:typed)
40
+ self
41
+ end
42
+
43
+ private
44
+
45
+ def parse_once(&block)
46
+ raise 'Define parsing strategy [plain|typed] before using' unless @strategy.present?
47
+
48
+ result = block.call
49
+ @strategy = nil
50
+
51
+ result
52
+ end
53
+
54
+ def schema_options_once
55
+ options = @options.present? ? @options.dup : schema_options
56
+ @options = {}
57
+
58
+ options
59
+ end
60
+
61
+ def strategy_klass(name)
62
+ "lurker/json/parser/#{name}_strategy".camelize.constantize
63
+ end
64
+
65
+ def schema_options
66
+ {
67
+ uri: @uri, root_schema: @root_schema,
68
+ parent_schema: @parent_schema, parent_property: @parent_property
69
+ }
70
+ end
71
+ end
72
+ end
73
+ end