Skip to content

Commit

Permalink
Merge pull request #82 from Strech/list-items-anything
Browse files Browse the repository at this point in the history
Added Lurker::Json::List of whatever
  • Loading branch information
razum2um committed Aug 24, 2014
2 parents db1d1d5 + 94c6f0d commit 37156bb
Show file tree
Hide file tree
Showing 10 changed files with 270 additions and 18 deletions.
1 change: 1 addition & 0 deletions lib/lurker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ class UndocumentedResponseCode < ValidationError; end
require 'lurker/json/schema/object'
require 'lurker/json/schema/list'
require 'lurker/json/schema/attribute'
require 'lurker/json/schema/polymorph'
require 'lurker/json/schema/tuple'
require 'lurker/json/schema/tuple/all_of'
require 'lurker/json/schema/tuple/any_of'
Expand Down
3 changes: 2 additions & 1 deletion lib/lurker/json/parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ def initialize(options = {})
@root_schema = options[:root_schema]
@parent_schema = options[:parent_schema]
@parent_property = options[:parent_property]
@polymorph_if_empty = options.fetch(:polymorph_if_empty, false)
@uri = options[:uri] || @parent_schema.try(:uri)
@strategy = nil
end
Expand Down Expand Up @@ -64,7 +65,7 @@ def strategy_klass(name)

def schema_options
{
uri: @uri, root_schema: @root_schema,
uri: @uri, root_schema: @root_schema, polymorph_if_empty: @polymorph_if_empty,
parent_schema: @parent_schema, parent_property: @parent_property
}
end
Expand Down
6 changes: 6 additions & 0 deletions lib/lurker/json/parser/expertise.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ class Parser
module Expertise
module_function

def type_polymorph?(array_or_hash)
return false unless array_or_hash.is_a?(Hash) || array_or_hash.is_a?(Array)

array_or_hash.empty?
end

def type_defined?(hash)
return false unless hash.is_a?(Hash)

Expand Down
16 changes: 15 additions & 1 deletion lib/lurker/json/parser/typed_strategy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ class TypedStrategy
attr_reader :schema_options

def initialize(options)
@schema_options = options.dup
options = options.dup

@polymorph_if_empty = options.delete(:polymorph_if_empty)
@schema_options = options
end

def parse(payload)
Expand All @@ -17,9 +20,12 @@ def parse(payload)
when Hash
return create_by_type(payload) if type_defined?(payload)
return create_by_supposition(payload) if type_supposed?(payload)
return create_polymorph(payload) if polymorph_if_empty? && type_polymorph?(payload)

Lurker::Json::Object.new(payload, schema_options)
when Array
return create_polymorph(payload) if polymorph_if_empty? && type_polymorph?(payload)

Lurker::Json::List.new(payload, schema_options)
else
Lurker::Json::Attribute.new(payload, schema_options)
Expand Down Expand Up @@ -56,6 +62,14 @@ def create_by_type(payload)
Lurker::Json::Attribute.new(payload, schema_options)
end
end

def polymorph_if_empty?
@polymorph_if_empty
end

def create_polymorph(payload)
Lurker::Json::Polymorph.new(payload, schema_options)
end
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/lurker/json/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ def parse_schema(schema)
when RESPONSE_CODES
Lurker::Json::ResponseCodes.new(property_schema, subschema_options)
when REQUEST_PARAMETERS, RESPONSE_PARAMETERS
@parser.typed.parse_property(property, property_schema)
@parser.typed(polymorph_if_empty: true).parse_property(property, property_schema)
else
@parser.plain.parse_property(property, property_schema)
end
Expand Down
23 changes: 18 additions & 5 deletions lib/lurker/json/schema/list.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,19 @@ def replace!(property, schema)

private

def initialize_properties
@schema[Json::TYPE] ||= Json::ARRAY
@schema[Json::ITEMS] ||= polymorph_items({})
end

def parse_schema(schema)
@schema = {}
initialize_properties

return if schema.empty?
if schema_of_any_kind?(schema)
@schema[Json::ITEMS] = polymorph_items(schema)
return
end

schema = schema.dup
if schema.is_a?(Array)
Expand All @@ -34,13 +42,18 @@ def parse_schema(schema)
@schema[Json::ITEMS] = @parser.typed.parse(schema.delete Json::ITEMS) if schema.key?(Json::ITEMS)
@schema.merge!(schema)
end
end

def schema_of_any_kind?(schema)
return true if schema.empty?
return false unless schema.respond_to?(:key?) && schema.key?(Json::ITEMS)

@schema
schema[Json::ITEMS].empty?
end

def initialize_properties
@schema[Json::TYPE] ||= Json::ARRAY
@schema[Json::ITEMS] ||= []
def polymorph_items(schema)
options = subschema_options.merge!(parent_property: Json::ITEMS)
Lurker::Json::Polymorph.new(schema, options)
end
end
end
Expand Down
10 changes: 0 additions & 10 deletions lib/lurker/json/schema/object.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ module Json
class Object < Schema
def merge!(schema)
unless schema.is_a?(Hash)
return replace_with_new_type(schema) if @schema[Json::PROPERTIES].blank?

raise TypeError, "Unable to merge #{schema.class} into JSON object"
end

Expand Down Expand Up @@ -40,14 +38,6 @@ def parse_schema(schema)
@schema.merge!(schema) if merge_required
end

def replace_with_new_type(schema)
replace_options = {root_schema: root_schema, parent_schema: parent_schema,
parent_property: parent_property}

new_schema = Lurker::Json::Parser.typed(replace_options).parse(schema)
parent_schema.replace!(parent_property, new_schema)
end

def initialize_properties
@schema[Json::DESCRIPTION] ||= ''
@schema[Json::TYPE] ||= Json::OBJECT
Expand Down
54 changes: 54 additions & 0 deletions lib/lurker/json/schema/polymorph.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
module Lurker
module Json
class Polymorph < Schema
def merge!(schema)
case parent_schema
when Lurker::Json::Object
parent_schema[Json::PROPERTIES][parent_property] = @parser.typed.parse(schema)
when Lurker::Json::List
if schema.is_a?(Array)
return if schema.empty?

schema = schema.dup
parent_schema[Json::ITEMS] = @parser.typed.parse(schema.shift)
parent_schema.merge!(schema)
else
parent_schema[Json::ITEMS] = @parser.typed.parse(schema)
end
else
parent_schema[parent_property] = @parser.typed.parse(schema)
end
end

def replace!(property, schema)
morph = Lurker::Json::Object.new({}, subschema_options)

case parent_schema
when Lurker::Json::Object
parent_schema[Json::PROPERTIES][parent_property] = morph
parent_schema.replace!(property, schema)
when Lurker::Json::List
parent_schema[Json::ITEMS] = morph
parent_schema.replace!(property, schema)
else
parent_schema[parent_property] = morph
parent_schema.replace!(property, schema)
end
end

private

def parse_schema(schema)
@schema = schema
end

# NOTE : The parser will ref to parent_schema instead
def subschema_options
{uri: parent_schema.uri,
root_schema: parent_schema.root? ? parent_schema : parent_schema.root_schema,
parent_schema: parent_schema,
parent_property: parent_property}
end
end
end
end
127 changes: 127 additions & 0 deletions spec/lurker/json/list_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,134 @@
describe Lurker::Json::List do
let(:klass) { described_class }

describe '#parse_schema' do
context 'when schema is empty array' do
let(:list) { klass.new([]) }
let(:expected) do
{
'type' => 'array',
'items' => []
}
end

it { expect(list.to_hash).to eq expected }
end

context 'when schema is empty hash' do
let(:list) { klass.new({}) }
let(:expected) do
{
'type' => 'array',
'items' => {}
}
end

it { expect(list.to_hash).to eq expected }
end
end

describe '#merge!' do
context 'when list is not specify concrete type' do
let(:list) { klass.new([]) }

context 'when merge an array' do
let(:expected) do
{
'type' => 'array',
'items' => {
'description' => '',
'type' => 'integer',
'example' => 42
}
}
end
before { list.merge!([42]) }

it { expect(list.to_hash).to eq expected }
end

context 'when merge an array of different types' do
let(:expected) do
{
'type' => 'array',
'items' => {
'anyOf' => [
{
'description' => '',
'type' => 'integer',
'example' => 42
},
{
'description' => '',
'type' => 'string',
'example' => 'razum2um'
}
]
}
}
end
before { list.merge!([42, 'razum2um']) }

it { expect(list.to_hash).to eq expected }
end

context 'when merge a fixnum' do
let(:expected) do
{
'type' => 'array',
'items' => {
'description' => '',
'type' => 'integer',
'example' => 999
}
}
end
before { list.merge!(999) }

it { expect(list.to_hash).to eq expected }
end

context 'when merge a string' do
let(:expected) do
{
'type' => 'array',
'items' => {
'description' => '',
'type' => 'string',
'example' => 'razum2um'
}
}
end
before { list.merge!('razum2um') }

it { expect(list.to_hash).to eq expected }
end

context 'when merge a hash' do
let(:expected) do
{
'type' => 'array',
'items' => {
'description' => '',
'type' => 'object',
'additionalProperties' => false,
'required' => [],
'properties' => {
'name' => {
'description' => '',
'type' => 'string',
'example' => 'razum2um'
}
}
}
}
end
before { list.merge!('name' => 'razum2um') }

it { expect(list.to_hash).to eq expected }
end
end

context 'when list is an array of attributes' do
let(:list) { klass.new([1, 2, 3]) }

Expand Down
46 changes: 46 additions & 0 deletions spec/lurker/json/object_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,53 @@
describe Lurker::Json::Object do
let(:klass) { described_class }

describe '#parse_schema' do
context 'when schema is empty hash' do
let(:object) { klass.new({}) }
let(:expected) do
{
'description' => '',
'type' => 'object',
'additionalProperties' => false,
'required' => [],
'properties' => {}
}
end

it { expect(object.to_hash).to eq expected }
end
end

describe '#merge!' do
context 'when merge a hash with properties containing empty hash' do
let(:object) { klass.new({}) }
let(:expected) do
{
'description' => '',
'type' => 'object',
'additionalProperties' => false,
'required' => [],
'properties' => {
'name' => {
'description' => '',
'type' => 'string',
'example' => 'razum2um'
},
'repo' => {
'description' => '',
'type' => 'object',
'additionalProperties' => false,
'required' => [],
'properties' => {}
}
}
}
end

before { object.merge!('name' => 'razum2um', 'repo' => {}) }

it { expect(object.to_hash).to eq expected }
end
context 'when merge a hash with keywords' do
let(:object) { klass.new('name' => 'razum2um') }
let(:expected) do
Expand Down

0 comments on commit 37156bb

Please sign in to comment.