Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pass context through parsing process to enable following all content API... #10

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/kosapi_client/api_client.rb
Original file line number Diff line number Diff line change
@@ -37,4 +37,4 @@ def find_builder_class(builder_name)
end

end
end
end
2 changes: 1 addition & 1 deletion lib/kosapi_client/entity/author.rb
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@ def initialize(name)
@name = name
end

def self.parse(contents)
def self.parse(contents, context = {})
new(contents[:atom_name])
end
end
2 changes: 1 addition & 1 deletion lib/kosapi_client/entity/boolean.rb
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@ module KOSapiClient
module Entity
class Boolean

def self.parse(str)
def self.parse(str, context = {})
return true if str == 'true'
return false if str == 'false'
raise "Boolean parsing failed, invalid string: #{str}"
30 changes: 17 additions & 13 deletions lib/kosapi_client/entity/data_mappings.rb
Original file line number Diff line number Diff line change
@@ -12,6 +12,10 @@ def to_hash
result
end

def dump
self.to_hash.to_yaml
end

private
def convert_value(val)
if val.respond_to? :to_hash
@@ -44,26 +48,26 @@ def attr_mappings
# @param [Hash] content hash structure from API response corresponding to single domain object
# @return [BaseEntity] parsed domain object
def parse(content, context = {})
instance = new()
set_mapped_attributes(instance, content)
instance = new
set_mapped_attributes(instance, content, context)
instance
end

# Creates new domain object instance and sets values
# of mapped domain object attributes from source hash.
# Attributes are mapped by .map_data method.
def set_mapped_attributes(instance, source_hash)
def set_mapped_attributes(instance, source_hash, context)
if self.superclass.respond_to? :set_mapped_attributes
self.superclass.set_mapped_attributes(instance, source_hash)
self.superclass.set_mapped_attributes(instance, source_hash, context)
end
raise "Missing data mappings for entity #{self}" unless @data_mappings
@data_mappings.each do |name, options|
set_mapped_attribute(instance, name, source_hash, options)
set_mapped_attribute(instance, name, source_hash, options, context)
end
end

private
def set_mapped_attribute(instance, name, source_hash, mapping_options)
def set_mapped_attribute(instance, name, source_hash, mapping_options, context)
namespace = mapping_options[:namespace]
src_element = mapping_options[:element] || name
if namespace
@@ -80,29 +84,29 @@ def set_mapped_attribute(instance, name, source_hash, mapping_options)
return
end
else
value = convert_type(value, mapping_options[:type])
value = convert_type value, mapping_options[:type], context
end
instance.send("#{name}=".to_sym, value)
end

def convert_type(value, type)
def convert_type(value, type, context = {})
return value.to_i if type == Integer
return value if type == String
return convert_array(value, type.first) if type.is_a?(Array)
return convert_array(value, type.first, context) if type.is_a?(Array)

return type.parse(value) if type.respond_to? :parse
return type.parse(value, context) if type.respond_to? :parse
raise "Unknown type #{type} to convert value #{value} to."
end

# Converts values of array type to proper domain objects.
# It checks whether the value is really an array, because
# when API returns a single value it does not get parsed
# into an array.
def convert_array(values, type)
def convert_array(values, type, context)
if values.is_a?(Array)
values.map { |it| convert_type(it, type) }
values.map { |it| convert_type(it, type, context) }
else
[ convert_type(values, type) ]
[ convert_type(values, type, context) ]
end
end

2 changes: 1 addition & 1 deletion lib/kosapi_client/entity/enum.rb
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@ module KOSapiClient
module Entity
class Enum

def self.parse(contents)
def self.parse(contents, context = {})
contents.downcase.to_sym
end

2 changes: 1 addition & 1 deletion lib/kosapi_client/entity/id.rb
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@ module KOSapiClient
module Entity
class Id < String

def self.parse(str)
def self.parse(str, context = {})
id = str.split(':').last
new(id)
end
12 changes: 5 additions & 7 deletions lib/kosapi_client/entity/link.rb
Original file line number Diff line number Diff line change
@@ -4,31 +4,29 @@ class Link

attr_reader :link_title, :link_href, :link_rel

def initialize(title, href, rel, client = nil)
def initialize(title, href, rel, client)
@link_title = title
@link_href = escape_url(href)
@link_rel = rel
@client = client
end

def self.parse(contents)
def self.parse(contents, context)
href = contents[:xlink_href] || contents[:href]
new(contents[:__content__], href, contents[:rel])
new(contents[:__content__], href, contents[:rel], context[:client])
end

def link_id
@link_href.split('/').last
end

def follow
return @target unless @target.nil?

raise "HTTP client not set, cannot send request to #{link_href}" unless @client
@client.send_request(:get, link_href)
end

def inject_client(client)
@client = client
end

def target
@target ||= follow
end
2 changes: 1 addition & 1 deletion lib/kosapi_client/entity/ml_string.rb
Original file line number Diff line number Diff line change
@@ -17,7 +17,7 @@ def to_s(lang = :implicit)
@translations[lang]
end

def self.parse(item)
def self.parse(item, context = {})
unless item.is_a?(Array)
item = [item]
end
12 changes: 9 additions & 3 deletions lib/kosapi_client/http_client.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module KOSapiClient
class HTTPClient

def initialize(http_adapter, preprocessor = ResponsePreprocessor.new, converter = ResponseConverter.new(self))
def initialize(http_adapter, preprocessor = ResponsePreprocessor.new, converter = ResponseConverter.new)
@http_adapter = http_adapter
@preprocessor = preprocessor
@converter = converter
@@ -15,8 +15,8 @@ def send_request(verb, url, options = {})

def process_response(result)
preprocessed = @preprocessor.preprocess(result)
response = KOSapiClient::KOSapiResponse.new(preprocessed)
@converter.convert(response)
response = KOSapiClient::KOSapiResponse.new preprocessed
@converter.convert response, create_context
end

def get_absolute_url(url)
@@ -32,5 +32,11 @@ def is_absolute(url)
url.start_with?('http')
end

def create_context
{
client: self
}
end

end
end
2 changes: 1 addition & 1 deletion lib/kosapi_client/kosapi_client.rb
Original file line number Diff line number Diff line change
@@ -42,4 +42,4 @@ def config

end

end
end
30 changes: 12 additions & 18 deletions lib/kosapi_client/response_converter.rb
Original file line number Diff line number Diff line change
@@ -6,16 +6,11 @@ module KOSapiClient
# determined at runtime based on API response.

class ResponseConverter

def initialize(client)
@client = client
end

def convert(response)
def convert(response, context = {})
if response.is_paginated?
convert_paginated(response)
convert_paginated(response, context)
else
convert_single(response.item)
convert_single(response.item, context)
end
end

@@ -24,20 +19,20 @@ def convert(response)
# @param response [KOSapiResponse] Response object wrapping array of hashes corresponding to entries
# @return [ResultPage] ResultPage of domain objects

def convert_paginated(response)
def convert_paginated(response, context)
items = response.items || []
converted_items = items.map{ |p| convert_single(p) }
Entity::ResultPage.new(converted_items, create_links(response))
converted_items = items.map{ |p| convert_single(p, context) }
Entity::ResultPage.new(converted_items, create_links(response, context))
end

def convert_single(item)
def convert_single(item, context)
type = detect_type(item)
convert_type(item, type)
convert_type(item, type, context)
end

private
def convert_type(hash, type)
type.parse(hash)
def convert_type(hash, type, context)
type.parse(hash, context)
end

def detect_type(hash)
@@ -55,9 +50,8 @@ def extract_type(type_str)
entity_type
end

def create_links(response)
ResponseLinks.parse(response.links_hash, @client)
def create_links(response, context)
ResponseLinks.parse(response.links_hash, context)
end

end
end
13 changes: 6 additions & 7 deletions lib/kosapi_client/response_links.rb
Original file line number Diff line number Diff line change
@@ -12,20 +12,19 @@ def initialize(prev_link, next_link)

class << self

def parse(hash, client)
prev_link = parse_link(hash, 'prev', client)
next_link = parse_link(hash, 'next', client)
def parse(hash, context)
prev_link = parse_link(hash, 'prev', context)
next_link = parse_link(hash, 'next', context)
new(prev_link, next_link)
end

private
def parse_link(hash, rel, client)
def parse_link(hash, rel, context)
return nil unless hash
link_hash = extract_link_hash(hash, rel)

if link_hash
link = Entity::Link.parse(link_hash)
link.inject_client(client)
link
Entity::Link.parse(link_hash, context)
end
end

8 changes: 7 additions & 1 deletion spec/integration/parallels_spec.rb
Original file line number Diff line number Diff line change
@@ -43,8 +43,14 @@
expect(parallel.link.link_rel).not_to be_nil
end

it 'follows reference link properly' do
teacher = client.parallels.find(339540000).teachers.first

expect(teacher.username).to eq("balikm")
end

it 'parses timetable slot ID' do
page = client.parallels
page = client.parallels.query('course.code' => 'MI-PAA')
slot = page.items.first.timetable_slots.first
expect(slot.id).not_to be_nil
end
10 changes: 2 additions & 8 deletions spec/kosapi_client/entity/link_spec.rb
Original file line number Diff line number Diff line change
@@ -5,9 +5,8 @@
Link = KOSapiClient::Entity::Link

let(:client) { instance_double(KOSapiClient::HTTPClient) }
subject(:link) { Link.parse({href: 'http://example.com/foo/bar/42', __content__: 'Example Site', rel: 'next'}) }
subject(:link) { Link.parse({href: 'http://example.com/foo/bar/42', __content__: 'Example Site', rel: 'next'}, {client: client}) }
let(:result) { double(:result, foo: :bar) }
before(:example) { link.inject_client(client) }

describe '.parse' do

@@ -23,7 +22,7 @@

it 'encodes href URL' do
href = 'parallels?query=(lastUpdatedDate%3E=2014-07-01T00:00:00;lastUpdatedDate%3C=2014-07-10T00:00:00)&offset=10&limit=10'
link = Link.new(nil, href, nil)
link = Link.new(nil, href, nil, nil)
expect(link.link_href).to eq 'parallels?query=(lastUpdatedDate%3E=2014-07-01T00:00:00%3BlastUpdatedDate%3C=2014-07-10T00:00:00)&offset=10&limit=10'
end

@@ -39,11 +38,6 @@

describe '#follow' do

it 'throws error when not http client set' do
link.inject_client(nil)
expect { link.follow }.to raise_error(RuntimeError)
end

it 'calls http client with href' do
expect(client).to receive(:send_request).with(:get, 'http://example.com/foo/bar/42')
link.follow
14 changes: 10 additions & 4 deletions spec/kosapi_client/entity/parallel_spec.rb
Original file line number Diff line number Diff line change
@@ -6,13 +6,19 @@
teacher: [{ xlink_href: 'teachers/smitkdan/', __content__: 'Ing. arch. Daniel Smitka Ph.D.' }]
} }

let(:client) { instance_double(KOSapiClient::HTTPClient) }

it 'parses parallel attributes' do
parallel = KOSapiClient::Entity::Parallel.parse(attributes)
parallel = KOSapiClient::Entity::Parallel.parse(attributes, {client: client})
expect(parallel.code).to eq 42
expect(parallel.capacity_overfill).to eq :denied
expect(parallel.teachers.first).to be_an_instance_of KOSapiClient::Entity::Link
expect(parallel.teachers.first.link_href).to eq 'teachers/smitkdan/'
expect(parallel.teachers.first.link_title).to eq 'Ing. arch. Daniel Smitka Ph.D.'
expect(parallel.teachers).to be_a(Array)
expect(parallel.teachers.first).to be_instance_of KOSapiClient::Entity::Link

link_data = parallel.teachers.first.to_hash

expect(link_data[:href]).to eq 'teachers/smitkdan/'
expect(link_data[:title]).to eq 'Ing. arch. Daniel Smitka Ph.D.'

end
end
12 changes: 9 additions & 3 deletions spec/kosapi_client/entity/result_page_spec.rb
Original file line number Diff line number Diff line change
@@ -4,16 +4,22 @@

ResultPage = KOSapiClient::Entity::ResultPage

let(:links) { KOSapiClient::ResponseLinks.new(nil, next_link) }
subject(:result_page) { ResultPage.new([item], links) }
let(:item) { double(:item) }
let(:item2) { double(:second_item) }
let(:links) { instance_double(KOSapiClient::ResponseLinks, next: next_link) }
let(:next_page) { ResultPage.new([item2], instance_double(KOSapiClient::ResponseLinks, next: nil)) }
let(:next_link) { instance_double(KOSapiClient::Entity::Link, follow: next_page) }
let(:next_page) { ResultPage.new([item2], KOSapiClient::ResponseLinks.new(nil, nil)) }
let(:next_link) { link = KOSapiClient::Entity::Link.new(nil, "/", nil, nil) }

before(:each) do
next_link.instance_variable_set(:@target, next_page)
end


describe '#each' do

it 'is auto-paginated by default' do

[item, item2].each { |it| expect(it).to receive(:foo) }
result_page.each { |it| it.foo }
end
Loading