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