full_circle 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/full_circle.gemspec +1 -1
- data/lib/full_circle.rb +5 -2
- data/lib/full_circle/api.rb +12 -8
- data/lib/full_circle/connection.rb +35 -3
- data/lib/full_circle/parsed_response.rb +58 -0
- data/lib/full_circle/response.rb +14 -0
- data/lib/full_circle/response_builder.rb +26 -0
- data/lib/full_circle/upcoming_event_builder.rb +44 -0
- data/lib/full_circle/version.rb +1 -1
- data/spec/full_circle/api_spec.rb +24 -5
- data/spec/full_circle/connection_spec.rb +36 -2
- data/spec/full_circle/object_builder_spec.rb +2 -0
- data/spec/full_circle/parsed_response_spec.rb +136 -0
- data/spec/full_circle/response_builder_spec.rb +50 -0
- data/spec/full_circle/response_spec.rb +29 -0
- data/spec/full_circle/upcoming_event_builder_spec.rb +51 -0
- data/spec/spec_helper.rb +6 -0
- data/spec/support/shared/cacher_spec.rb +9 -0
- data/spec/support/shared/event_spec.rb +10 -0
- data/spec/support/shared/object_builder_spec.rb +3 -0
- metadata +21 -6
- data/lib/full_circle/response_parser.rb +0 -44
- data/spec/full_circle/response_parser_spec.rb +0 -187
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
YTEwOGEyZGIxMWMyODc4Y2M5ZDEyNDZhODEzMTczNTUwN2VhNjJmMg==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
YmRkNjQ4NjA5MTdmNzUyYjFhOTZjNGQ2YTZkNjU3ZmViMTM3MzVjNQ==
|
7
7
|
!binary "U0hBNTEy":
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
NGIyMzMwNDFkOGQxZDk3YjljNjMxYmQ1ZDkzMTcwMDFkYmQ5N2JhNzFhZWJl
|
10
|
+
MGNlMzliYmRlNzA3OWNjNDYxYjBiMjRjYzgwZTdkMmM1MDk1ZjA4NjA0ZTFi
|
11
|
+
NTIyODM1NTI4ZDRjZjQ2NjhhNDVjNTdhYjc4ZGU5NjZiYjg4Mzk=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
Zjk0YTY1YWY4ODA5NzA3Yjc1ODJkMTQ2ZDhiMDIxZmRjNGY2MmEzMjI1NTY4
|
14
|
+
OWRiNTFlOWEyZmNjZjE2MTZjZTI4YTZmZmExMzNlNzhmZGM2N2VmMTY1NDAz
|
15
|
+
NWFiODg1NDE0ZTY2OGJiY2NmMGI2Njc5MmVlYWM5OTY2YTc0ZmY=
|
data/full_circle.gemspec
CHANGED
@@ -18,7 +18,7 @@ Gem::Specification.new do |gem|
|
|
18
18
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
19
19
|
gem.require_paths = ["lib"]
|
20
20
|
|
21
|
-
gem.add_dependency "
|
21
|
+
gem.add_dependency "multi_xml"
|
22
22
|
gem.add_dependency "activesupport", "~>3.2.12"
|
23
23
|
gem.add_development_dependency "rspec"
|
24
24
|
gem.add_development_dependency "vcr"
|
data/lib/full_circle.rb
CHANGED
@@ -1,10 +1,13 @@
|
|
1
|
-
require '
|
1
|
+
require 'multi_xml'
|
2
2
|
require "active_support/core_ext"
|
3
3
|
|
4
4
|
require_relative 'full_circle/version'
|
5
5
|
require_relative 'full_circle/connection'
|
6
6
|
require_relative 'full_circle/object_builder'
|
7
|
-
require_relative 'full_circle/
|
7
|
+
require_relative 'full_circle/upcoming_event_builder'
|
8
|
+
require_relative 'full_circle/parsed_response'
|
9
|
+
require_relative 'full_circle/response_builder'
|
10
|
+
require_relative 'full_circle/response'
|
8
11
|
require_relative 'full_circle/api'
|
9
12
|
|
10
13
|
module FullCircle
|
data/lib/full_circle/api.rb
CHANGED
@@ -8,27 +8,31 @@ module FullCircle
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def fetch_events_for_ad(id)
|
11
|
-
parser = ResponseParser.new "ad.getEvents", "event"
|
12
11
|
response = connection.call_api_method("ad.getEvents", adId: id)
|
13
|
-
|
12
|
+
pr = ParsedResponse.new(response.body)
|
13
|
+
builder = ResponseBuilder.new pr.results, metadata: pr.metadata
|
14
|
+
builder.build.results
|
14
15
|
end
|
15
16
|
|
16
17
|
def fetch_coupons_for_ad(id)
|
17
|
-
parser = ResponseParser.new "ad.getCoupons", "coupon"
|
18
18
|
response = connection.call_api_method("ad.getCoupons", adId: id)
|
19
|
-
|
19
|
+
pr = ParsedResponse.new(response.body)
|
20
|
+
builder = ResponseBuilder.new pr.results, metadata: pr.metadata
|
21
|
+
builder.build.results
|
20
22
|
end
|
21
23
|
|
22
24
|
def fetch_event_areas()
|
23
|
-
parser = ResponseParser.new "city.getEventAreas", "eventArea"
|
24
25
|
response = connection.call_api_method("city.getEventAreas")
|
25
|
-
|
26
|
+
pr = ParsedResponse.new(response.body)
|
27
|
+
builder = ResponseBuilder.new pr.results, metadata: pr.metadata
|
28
|
+
builder.build.results
|
26
29
|
end
|
27
30
|
|
28
31
|
def fetch_upcoming_events(params={})
|
29
|
-
parser = ResponseParser.new "city.getUpcomingEvents", "event"
|
30
32
|
response = connection.call_api_method("city.getUpcomingEvents", params)
|
31
|
-
|
33
|
+
pr = ParsedResponse.new(response.body)
|
34
|
+
builder = ResponseBuilder.new pr.results, metadata: pr.metadata, object_builder: UpcomingEventBuilder.new
|
35
|
+
builder.build.results
|
32
36
|
end
|
33
37
|
end
|
34
38
|
end
|
@@ -1,10 +1,15 @@
|
|
1
|
+
require "net/http"
|
1
2
|
module FullCircle
|
2
3
|
class Connection
|
3
4
|
|
4
|
-
attr_reader :domain
|
5
|
+
attr_reader :domain, :cache
|
5
6
|
|
6
|
-
|
7
|
+
# domain - domain of directory (ex. 360durango.com)
|
8
|
+
# args
|
9
|
+
# - cache - Caching object to use
|
10
|
+
def initialize(domain, args={})
|
7
11
|
@domain = domain
|
12
|
+
@cache = args.fetch(:cache){NullCache.new}
|
8
13
|
end
|
9
14
|
|
10
15
|
def base_uri
|
@@ -12,7 +17,34 @@ module FullCircle
|
|
12
17
|
end
|
13
18
|
|
14
19
|
def call_api_method(method_name, query_params={})
|
15
|
-
|
20
|
+
uri_str = uri_string(method_name, query_params)
|
21
|
+
uri = URI(uri_str)
|
22
|
+
|
23
|
+
body = cache.fetch(uri_str) do
|
24
|
+
response_text = Net::HTTP.get(uri)
|
25
|
+
cache.store(uri_str, response_text)
|
26
|
+
end
|
27
|
+
|
28
|
+
Response.new body
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
NullCache = Class.new do
|
33
|
+
def fetch(key)
|
34
|
+
yield
|
35
|
+
end
|
36
|
+
|
37
|
+
def store(key, value)
|
38
|
+
value
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
Response = Struct.new(:body)
|
44
|
+
|
45
|
+
|
46
|
+
def uri_string(method_name, query_params)
|
47
|
+
"#{base_uri}#{method_name}?#{query_params.to_query}"
|
16
48
|
end
|
17
49
|
end
|
18
50
|
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
require 'multi_xml'
|
3
|
+
|
4
|
+
module FullCircle
|
5
|
+
class ParsedResponse
|
6
|
+
|
7
|
+
attr_reader :raw_xml
|
8
|
+
|
9
|
+
def initialize(raw_xml)
|
10
|
+
@raw_xml = raw_xml
|
11
|
+
end
|
12
|
+
|
13
|
+
def metadata
|
14
|
+
@metadata ||= build_metadata
|
15
|
+
end
|
16
|
+
|
17
|
+
def results
|
18
|
+
collection_array
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def response_hash
|
25
|
+
@response_hash ||= parsed_xml.fetch(parsed_xml.keys.first)
|
26
|
+
end
|
27
|
+
|
28
|
+
def collection_array
|
29
|
+
if @collection_array.nil?
|
30
|
+
result = collection_wrapper_hash.fetch(sub_enumerable_key_names(collection_wrapper_hash).first){[]}
|
31
|
+
@collection_array = Array.wrap(result);
|
32
|
+
end
|
33
|
+
@collection_array
|
34
|
+
end
|
35
|
+
|
36
|
+
def collection_wrapper_hash
|
37
|
+
@wrapped_collection ||= response_hash.fetch(sub_enumerable_key_names(response_hash).first){{}}
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
def parsed_xml
|
42
|
+
@parsed_xml ||= MultiXml.parse(raw_xml)
|
43
|
+
end
|
44
|
+
|
45
|
+
def build_metadata
|
46
|
+
{
|
47
|
+
page: Integer(response_hash.fetch("page"){1}),
|
48
|
+
total_pages: Integer(response_hash.fetch("totalPages"){1}),
|
49
|
+
total_results: Integer(response_hash.fetch("totalResults"){results.length}),
|
50
|
+
results_per_page: Integer(response_hash.fetch("resultsPerPage"){results.length})
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
def sub_enumerable_key_names(hash)
|
55
|
+
hash.map {|k,v| k if v.kind_of? Enumerable }.reject{|k| k.nil?}
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module FullCircle
|
2
|
+
class Response
|
3
|
+
attr_reader :results, :page, :total_pages, :total_results, :results_per_page
|
4
|
+
|
5
|
+
def initialize(results, metadata)
|
6
|
+
@results = results
|
7
|
+
@page = metadata.fetch(:page){1}
|
8
|
+
@total_pages = metadata.fetch(:total_pages){1}
|
9
|
+
@total_results = metadata.fetch(:total_results){results.length}
|
10
|
+
@results_per_page = metadata.fetch(:results_per_page){results.length}
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module FullCircle
|
2
|
+
class ResponseBuilder
|
3
|
+
|
4
|
+
attr_reader :api_results, :metadata, :object_builder
|
5
|
+
|
6
|
+
# Creates a new response builder
|
7
|
+
# api_results - collection of objects to be built into the response
|
8
|
+
# args
|
9
|
+
# metadata: hash of metadata
|
10
|
+
# object_builder: The object builder to use for building api objects
|
11
|
+
def initialize(api_results, args={})
|
12
|
+
@api_results = api_results
|
13
|
+
@metadata = args.fetch(:metadata){{}}
|
14
|
+
@object_builder = args.fetch(:object_builder){ObjectBuilder.new}
|
15
|
+
end
|
16
|
+
|
17
|
+
def build
|
18
|
+
objects = api_results.map do |result|
|
19
|
+
object_builder.from_api_hash(result)
|
20
|
+
end
|
21
|
+
|
22
|
+
Response.new(objects,metadata)
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
module FullCircle
|
4
|
+
class UpcomingEventBuilder
|
5
|
+
def initialize
|
6
|
+
@domain = "360durango.com"
|
7
|
+
end
|
8
|
+
|
9
|
+
def from_api_hash(hash)
|
10
|
+
@hash = hash
|
11
|
+
result = OpenStruct.new({
|
12
|
+
id: hash.fetch("id"),
|
13
|
+
date: hash.fetch("date"),
|
14
|
+
title: hash.fetch("title"),
|
15
|
+
time: hash.fetch("time"),
|
16
|
+
all_day_event: hash.fetch("allDayEvent"),
|
17
|
+
date_mode: hash.fetch("dateMode"),
|
18
|
+
site_id: hash.fetch("siteId"),
|
19
|
+
type: hash.fetch("type"),
|
20
|
+
|
21
|
+
image_url: hash.fetch("ad"){{}}.fetch("logoImage"){{}}["url"],
|
22
|
+
end_date: hash["endDate"],
|
23
|
+
expire_date: hash["expireDate"],
|
24
|
+
end_time: hash["endTime"],
|
25
|
+
description: hash["__content__"],
|
26
|
+
ad_id: ad_id,
|
27
|
+
})
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
private
|
32
|
+
attr_reader :hash
|
33
|
+
attr_reader :domain
|
34
|
+
|
35
|
+
def event_id
|
36
|
+
hash.fetch("id")
|
37
|
+
end
|
38
|
+
|
39
|
+
def ad_id
|
40
|
+
hash.fetch("ad"){{}}["id"]
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
data/lib/full_circle/version.rb
CHANGED
@@ -6,13 +6,25 @@ describe FullCircle::API do
|
|
6
6
|
let!(:api){FullCircle::API.new(FullCircle::Connection.new("360durango.com"))}
|
7
7
|
|
8
8
|
describe "#fetch_events_for_ad" do
|
9
|
-
|
9
|
+
pending "calls the appropriate method on call_api_method" do
|
10
10
|
mock_connection = double()
|
11
|
-
mock_connection.should_receive(:call_api_method).with("ad.getEvents",{adId: "1"})
|
11
|
+
mock_connection.should_receive(:call_api_method).with("ad.getEvents",{adId: "1"}) do
|
12
|
+
class ResponseDouble
|
13
|
+
def body
|
14
|
+
end
|
15
|
+
end
|
16
|
+
ResponseDouble.new
|
17
|
+
end
|
12
18
|
|
13
19
|
described_class.new(mock_connection).fetch_events_for_ad("1")
|
14
20
|
end
|
15
21
|
|
22
|
+
describe "event object" do
|
23
|
+
use_vcr_cassette "single_get_events_response"
|
24
|
+
subject {api.fetch_events_for_ad("81213").first}
|
25
|
+
it_behaves_like "an event"
|
26
|
+
end
|
27
|
+
|
16
28
|
context "with one event" do
|
17
29
|
it "returns an array of one event" do
|
18
30
|
VCR.use_cassette "single_get_events_response" do
|
@@ -48,7 +60,7 @@ describe FullCircle::API do
|
|
48
60
|
|
49
61
|
describe "#fetch_coupons_for_ad" do
|
50
62
|
|
51
|
-
|
63
|
+
pending "calls the appropriate method on call_api_method" do
|
52
64
|
mock_connection = double()
|
53
65
|
mock_connection.should_receive(:call_api_method).with("ad.getCoupons",{adId: "1"})
|
54
66
|
|
@@ -86,7 +98,7 @@ describe FullCircle::API do
|
|
86
98
|
end
|
87
99
|
|
88
100
|
describe "#fetch_event_areas" do
|
89
|
-
|
101
|
+
pending "calls the appropriate method on call_api_method" do
|
90
102
|
mock_connection = double()
|
91
103
|
mock_connection.should_receive(:call_api_method).with("city.getEventAreas")
|
92
104
|
|
@@ -127,13 +139,20 @@ describe FullCircle::API do
|
|
127
139
|
|
128
140
|
describe "#fetch_upcoming_events" do
|
129
141
|
|
130
|
-
|
142
|
+
pending "calls the appropriate method on call_api_method" do
|
131
143
|
mock_connection = double()
|
132
144
|
mock_connection.should_receive(:call_api_method).with("city.getUpcomingEvents",{})
|
133
145
|
|
134
146
|
described_class.new(mock_connection).fetch_upcoming_events
|
135
147
|
end
|
136
148
|
|
149
|
+
describe "event object" do
|
150
|
+
use_vcr_cassette "single_get_upcoming_events_response"
|
151
|
+
|
152
|
+
subject {api.fetch_upcoming_events(areaId:"592").first}
|
153
|
+
it_behaves_like "an event"
|
154
|
+
end
|
155
|
+
|
137
156
|
context "with one event" do
|
138
157
|
it "returns an array of one event" do
|
139
158
|
VCR.use_cassette "single_get_upcoming_events_response" do
|
@@ -7,14 +7,48 @@ describe FullCircle::Connection do
|
|
7
7
|
its(:domain) {should == "360durango.com"}
|
8
8
|
its(:base_uri) {should == "http://api.360durango.com/1.0/"}
|
9
9
|
|
10
|
+
let!(:req_stub) {stub_http_request(:get, "api.360durango.com/1.0/ad.getCoupons").with(query: {adId: "81304"})
|
11
|
+
.to_return(body: "abc")
|
12
|
+
}
|
13
|
+
|
10
14
|
describe ".call_api_method" do
|
11
|
-
it "should call HTTParty.get with the correct arguments" do
|
12
|
-
HTTParty.should_receive(:get).with("ad.getCoupons",base_uri: subject.base_uri, query: {adId: "81304"})
|
13
15
|
|
16
|
+
it "should request the correct uri" do
|
14
17
|
subject.call_api_method "ad.getCoupons", adId: "81304"
|
18
|
+
|
19
|
+
req_stub.should have_been_requested
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should return the correct data" do
|
23
|
+
response = subject.call_api_method "ad.getCoupons", adId: "81304"
|
24
|
+
response.body.should eq("abc")
|
25
|
+
end
|
26
|
+
|
27
|
+
context "without cache" do
|
28
|
+
it "should make the http request each time" do
|
29
|
+
subject.call_api_method "ad.getCoupons", adId: "81304"
|
30
|
+
subject.call_api_method "ad.getCoupons", adId: "81304"
|
31
|
+
|
32
|
+
req_stub.should have_been_requested.twice
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context "with cache" do
|
37
|
+
subject {FullCircle::Connection.new "360durango.com", cache: {}}
|
38
|
+
|
39
|
+
it "should make the http request each time" do
|
40
|
+
subject.call_api_method "ad.getCoupons", adId: "81304"
|
41
|
+
subject.call_api_method "ad.getCoupons", adId: "81304"
|
42
|
+
|
43
|
+
req_stub.should have_been_requested.once
|
44
|
+
end
|
15
45
|
end
|
16
46
|
|
47
|
+
end
|
17
48
|
|
49
|
+
describe FullCircle::Connection::NullCache do
|
50
|
+
subject {FullCircle::Connection::NullCache.new}
|
51
|
+
it_behaves_like "a cacher"
|
18
52
|
end
|
19
53
|
|
20
54
|
|
@@ -0,0 +1,136 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe FullCircle::ParsedResponse do
|
4
|
+
|
5
|
+
describe "#metadata" do
|
6
|
+
context "with provided metadata" do
|
7
|
+
let(:xml) do
|
8
|
+
<<"EOS"
|
9
|
+
<?xml version="1.0"?>
|
10
|
+
<ad-getListResponse page="2" resultsPerPage="2" totalPages="541" totalResults="1081">
|
11
|
+
<ads>
|
12
|
+
<ad id="81009" name="4Core" url="http://360Durango.com/Environmental/4core.html"
|
13
|
+
eventCount="0" couponCount="0" jobCount="0" locationCount="1" popupCount="0"
|
14
|
+
slideshowCount="0" linkCount="0">
|
15
|
+
<address addr1="949 E 2ND AV" city="DURANGO" state="CO" zipCode="81301"></address>
|
16
|
+
</ad>
|
17
|
+
<ad id="123760" name="4x4 Auto " url="http://360Durango.com/Automotive/4x4Auto.html"
|
18
|
+
description="Used Car Dealership Cortez 360Durango Colorado" eventCount="0"
|
19
|
+
couponCount="0" jobCount="0" locationCount="0" popupCount="0" slideshowCount="0"
|
20
|
+
linkCount="0">
|
21
|
+
</ad>
|
22
|
+
</ads>
|
23
|
+
</ad-getListResponse>
|
24
|
+
EOS
|
25
|
+
end
|
26
|
+
|
27
|
+
subject { described_class.new(xml).metadata }
|
28
|
+
|
29
|
+
its([:page]){should eq 2}
|
30
|
+
its([:results_per_page]){should eq 2}
|
31
|
+
its([ :total_pages ]){should eq 541 }
|
32
|
+
its([ :total_results ]){should eq 1081}
|
33
|
+
|
34
|
+
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
context "with no provided metadata" do
|
39
|
+
let (:xml) do
|
40
|
+
<<"EOS"
|
41
|
+
<?xml version="1.0"?>
|
42
|
+
<ad-getCouponsResponse>
|
43
|
+
<coupons>
|
44
|
+
<coupon id="32025" name="90 for 90!" begin="2013-04-04"
|
45
|
+
expire="2013-04-30" accept_offline="1" accept_online="0" acceptMobile="1" acceptPrint="1">
|
46
|
+
<offer>Get a relaxing and restorative 90 minute massage for just $90!</offer>
|
47
|
+
<restrictions>Service must be booked in the month of April.</restrictions>
|
48
|
+
</coupon>
|
49
|
+
<coupon id="32025" name="90 for 90!" begin="2013-04-04"
|
50
|
+
expire="2013-04-30" accept_offline="1" accept_online="0" acceptMobile="1" acceptPrint="1">
|
51
|
+
<offer>Get a relaxing and restorative 90 minute massage for just $90!</offer>
|
52
|
+
<restrictions>Service must be booked in the month of April.</restrictions>
|
53
|
+
</coupon>
|
54
|
+
</coupons>
|
55
|
+
</ad-getCouponsResponse>
|
56
|
+
EOS
|
57
|
+
end
|
58
|
+
|
59
|
+
subject { described_class.new(xml).metadata }
|
60
|
+
|
61
|
+
its([ :page ]){should eq 1}
|
62
|
+
its([ :total_pages ]){should eq 1 }
|
63
|
+
its([ :total_results ]) {should eq 2}
|
64
|
+
its([ :results_per_page ]) {should eq 2 }
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
describe "#results" do
|
71
|
+
|
72
|
+
context "with no results" do
|
73
|
+
let(:xml) do
|
74
|
+
<<"EOS"
|
75
|
+
<?xml version="1.0"?>
|
76
|
+
<ad-getCouponsResponse>
|
77
|
+
<coupons></coupons>
|
78
|
+
</ad-getCouponsResponse>
|
79
|
+
EOS
|
80
|
+
end
|
81
|
+
|
82
|
+
subject{described_class.new(xml).results}
|
83
|
+
it { should eq [] }
|
84
|
+
end
|
85
|
+
|
86
|
+
context "with one result" do
|
87
|
+
|
88
|
+
let (:xml) do
|
89
|
+
<<"EOS"
|
90
|
+
<?xml version="1.0"?>
|
91
|
+
<ad-getCouponsResponse>
|
92
|
+
<coupons>
|
93
|
+
<coupon id="32025" name="90 for 90!" begin="2013-04-04"
|
94
|
+
expire="2013-04-30" accept_offline="1" accept_online="0" acceptMobile="1" acceptPrint="1">
|
95
|
+
<offer>Get a relaxing and restorative 90 minute massage for just $90!</offer>
|
96
|
+
<restrictions>Service must be booked in the month of April.</restrictions>
|
97
|
+
</coupon>
|
98
|
+
</coupons>
|
99
|
+
</ad-getCouponsResponse>
|
100
|
+
EOS
|
101
|
+
end
|
102
|
+
|
103
|
+
subject{described_class.new(xml).results}
|
104
|
+
its(:length){should eq 1 }
|
105
|
+
|
106
|
+
end
|
107
|
+
|
108
|
+
context "with multiple results" do
|
109
|
+
let(:xml) do
|
110
|
+
<<"EOS"
|
111
|
+
<?xml version="1.0"?>
|
112
|
+
<ad-getListResponse page="2" resultsPerPage="2" totalPages="541" totalResults="1081">
|
113
|
+
<ads>
|
114
|
+
<ad id="81009" name="4Core" url="http://360Durango.com/Environmental/4core.html"
|
115
|
+
eventCount="0" couponCount="0" jobCount="0" locationCount="1" popupCount="0"
|
116
|
+
slideshowCount="0" linkCount="0">
|
117
|
+
<address addr1="949 E 2ND AV" city="DURANGO" state="CO" zipCode="81301"></address>
|
118
|
+
</ad>
|
119
|
+
<ad id="123760" name="4x4 Auto " url="http://360Durango.com/Automotive/4x4Auto.html"
|
120
|
+
description="Used Car Dealership Cortez 360Durango Colorado" eventCount="0"
|
121
|
+
couponCount="0" jobCount="0" locationCount="0" popupCount="0" slideshowCount="0"
|
122
|
+
linkCount="0">
|
123
|
+
</ad>
|
124
|
+
</ads>
|
125
|
+
</ad-getListResponse>
|
126
|
+
EOS
|
127
|
+
end
|
128
|
+
|
129
|
+
subject { described_class.new(xml).results }
|
130
|
+
|
131
|
+
its(:length){should eq 2}
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
describe FullCircle::ResponseBuilder do
|
5
|
+
let(:object_builder_double) do
|
6
|
+
class ObjectBuilderDouble
|
7
|
+
def from_api_hash(arg)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
ObjectBuilderDouble.new
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "object_builder_double" do
|
14
|
+
subject {object_builder_double}
|
15
|
+
it_behaves_like "an object builder"
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "#build" do
|
19
|
+
context "with empty array of api_results" do
|
20
|
+
subject{ described_class.new [], object_builder: object_builder_double }
|
21
|
+
|
22
|
+
it "calls object_builder.from_api_hash" do
|
23
|
+
object_builder_double.should_not_receive(:from_api_hash)
|
24
|
+
subject.build
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
context "with an array of multiple api_results" do
|
30
|
+
subject{ described_class.new [{},{}], object_builder: object_builder_double }
|
31
|
+
|
32
|
+
it "calls object_builder.from_api_hash" do
|
33
|
+
object_builder_double.should_receive(:from_api_hash).with({}).exactly(2).times
|
34
|
+
subject.build
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "response outgoing messages" do
|
39
|
+
subject{ described_class.new [{},{}], object_builder: object_builder_double }
|
40
|
+
|
41
|
+
it "should create a response with the correct arguments" do
|
42
|
+
FullCircle::Response.should_receive(:new).with([nil,nil],{})
|
43
|
+
|
44
|
+
subject.build
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe FullCircle::Response do
|
4
|
+
|
5
|
+
describe "defaults" do
|
6
|
+
subject{described_class.new([],{})}
|
7
|
+
|
8
|
+
its(:results){should eq []}
|
9
|
+
its(:page){should eq 1}
|
10
|
+
its(:total_pages){should eq 1}
|
11
|
+
its(:results_per_page){should eq 0 }
|
12
|
+
its(:total_results){should eq 0 }
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "with metadata" do
|
17
|
+
subject{described_class.new([],{page: 2, total_pages:5, results_per_page: 5, total_results: 25})}
|
18
|
+
|
19
|
+
its(:results){should eq []}
|
20
|
+
its(:page){should eq 2}
|
21
|
+
its(:total_pages){should eq 5}
|
22
|
+
its(:results_per_page){should eq 5 }
|
23
|
+
its(:total_results){should eq 25 }
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe FullCircle::UpcomingEventBuilder do
|
4
|
+
it_behaves_like "an object builder"
|
5
|
+
|
6
|
+
describe "event output" do
|
7
|
+
let(:api_hash) do
|
8
|
+
{
|
9
|
+
"id" => "51019",
|
10
|
+
"date" => "2013-04-22",
|
11
|
+
"expireDate" => "2013-06-24",
|
12
|
+
"time" => "16:00:00",
|
13
|
+
"endTime" => "22:00:00",
|
14
|
+
"title" => "Burger Nite",
|
15
|
+
"allDayEvent" => "0",
|
16
|
+
"type" => "Food Special",
|
17
|
+
"dateMode" => "4",
|
18
|
+
"siteId" => "77",
|
19
|
+
"price" => "$4.95",
|
20
|
+
"linkText" => "Like us on Facebook!",
|
21
|
+
"linkUrl" => "https://www.facebook.com/pages/Olde-Tymers-Cafe/107138292687599",
|
22
|
+
"featured" => "1",
|
23
|
+
"ad" => {
|
24
|
+
"id" => "81277",
|
25
|
+
"name" => "Olde Tymers Cafe",
|
26
|
+
"url" => "http://360Durango.com/Attractions/oldetymerscafe.html",
|
27
|
+
"description" => "Downtown Durango's Best Burger Cafe with Outdoor Patio!",
|
28
|
+
"eventCount" => "6",
|
29
|
+
"couponCount" => "0",
|
30
|
+
"jobCount" => "0",
|
31
|
+
"locationCount" => "1",
|
32
|
+
"popupCount" => "0",
|
33
|
+
"slideshowCount" => "2",
|
34
|
+
"linkCount" => "2",
|
35
|
+
"address" => {
|
36
|
+
"addr1" => "1000 Main Avenue",
|
37
|
+
"city" => "Durango",
|
38
|
+
"state" => "CO",
|
39
|
+
"zipCode" => "81301",
|
40
|
+
"phone" => "970.259.2990"
|
41
|
+
}
|
42
|
+
},
|
43
|
+
"__content__" => "Since 1981 Olde Tymers Cafe has been bringing people together with a relaxing atmosphere, great food, and libations."
|
44
|
+
}
|
45
|
+
end
|
46
|
+
|
47
|
+
subject {described_class.new.from_api_hash(api_hash)}
|
48
|
+
|
49
|
+
it_behaves_like "an event"
|
50
|
+
end
|
51
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -2,10 +2,16 @@
|
|
2
2
|
$:.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
|
3
3
|
|
4
4
|
require 'rspec'
|
5
|
+
require 'webmock/rspec'
|
5
6
|
require 'vcr'
|
6
7
|
require 'full_circle'
|
8
|
+
Dir["./spec/support/**/*.rb"].sort.each {|f| require f}
|
7
9
|
|
8
10
|
VCR.configure do |c|
|
9
11
|
c.cassette_library_dir = "spec/fixtures/vcr_cassettes"
|
10
12
|
c.hook_into :webmock
|
11
13
|
end
|
14
|
+
|
15
|
+
RSpec.configure do |c|
|
16
|
+
c.extend VCR::RSpec::Macros
|
17
|
+
end
|
metadata
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: full_circle
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aaron Renner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-04-
|
11
|
+
date: 2013-04-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: multi_xml
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - ! '>='
|
@@ -140,7 +140,10 @@ files:
|
|
140
140
|
- lib/full_circle/api.rb
|
141
141
|
- lib/full_circle/connection.rb
|
142
142
|
- lib/full_circle/object_builder.rb
|
143
|
-
- lib/full_circle/
|
143
|
+
- lib/full_circle/parsed_response.rb
|
144
|
+
- lib/full_circle/response.rb
|
145
|
+
- lib/full_circle/response_builder.rb
|
146
|
+
- lib/full_circle/upcoming_event_builder.rb
|
144
147
|
- lib/full_circle/version.rb
|
145
148
|
- spec/fixtures/vcr_cassettes/empty_event_area_response.yml
|
146
149
|
- spec/fixtures/vcr_cassettes/empty_get_coupons_response.yml
|
@@ -157,8 +160,14 @@ files:
|
|
157
160
|
- spec/full_circle/api_spec.rb
|
158
161
|
- spec/full_circle/connection_spec.rb
|
159
162
|
- spec/full_circle/object_builder_spec.rb
|
160
|
-
- spec/full_circle/
|
163
|
+
- spec/full_circle/parsed_response_spec.rb
|
164
|
+
- spec/full_circle/response_builder_spec.rb
|
165
|
+
- spec/full_circle/response_spec.rb
|
166
|
+
- spec/full_circle/upcoming_event_builder_spec.rb
|
161
167
|
- spec/spec_helper.rb
|
168
|
+
- spec/support/shared/cacher_spec.rb
|
169
|
+
- spec/support/shared/event_spec.rb
|
170
|
+
- spec/support/shared/object_builder_spec.rb
|
162
171
|
homepage: https://github.com/aaronrenner/full_circle
|
163
172
|
licenses: []
|
164
173
|
metadata: {}
|
@@ -198,5 +207,11 @@ test_files:
|
|
198
207
|
- spec/full_circle/api_spec.rb
|
199
208
|
- spec/full_circle/connection_spec.rb
|
200
209
|
- spec/full_circle/object_builder_spec.rb
|
201
|
-
- spec/full_circle/
|
210
|
+
- spec/full_circle/parsed_response_spec.rb
|
211
|
+
- spec/full_circle/response_builder_spec.rb
|
212
|
+
- spec/full_circle/response_spec.rb
|
213
|
+
- spec/full_circle/upcoming_event_builder_spec.rb
|
202
214
|
- spec/spec_helper.rb
|
215
|
+
- spec/support/shared/cacher_spec.rb
|
216
|
+
- spec/support/shared/event_spec.rb
|
217
|
+
- spec/support/shared/object_builder_spec.rb
|
@@ -1,44 +0,0 @@
|
|
1
|
-
module FullCircle
|
2
|
-
class ResponseParser
|
3
|
-
|
4
|
-
attr_reader :api_method_name, :object_name
|
5
|
-
|
6
|
-
def initialize(api_method_name, object_name)
|
7
|
-
@api_method_name= api_method_name
|
8
|
-
@object_name = object_name
|
9
|
-
end
|
10
|
-
|
11
|
-
def parse(response)
|
12
|
-
if response
|
13
|
-
attrs = response.parsed_response
|
14
|
-
|
15
|
-
parse_response(attrs, response_name, object_name)
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
private
|
23
|
-
|
24
|
-
# example
|
25
|
-
# parseResponse(attrs, "city_getEventAreasResponse", "eventArea")
|
26
|
-
def parse_response(attrs, response_name, object_name)
|
27
|
-
if attrs[response_name][object_name.pluralize].nil?
|
28
|
-
[]
|
29
|
-
else
|
30
|
-
response_attrs = Array.wrap(attrs[response_name][object_name.pluralize][object_name])
|
31
|
-
|
32
|
-
builder = ObjectBuilder.new
|
33
|
-
response_attrs.collect do |response_attr_set|
|
34
|
-
builder.from_api_hash(response_attr_set)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
def response_name
|
40
|
-
@response_name ||= "#{api_method_name.gsub(/\./,'_')}Response"
|
41
|
-
end
|
42
|
-
|
43
|
-
end
|
44
|
-
end
|
@@ -1,187 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
|
3
|
-
describe FullCircle::ResponseParser do
|
4
|
-
|
5
|
-
describe "#parse" do
|
6
|
-
|
7
|
-
|
8
|
-
context "parsing coupons" do
|
9
|
-
|
10
|
-
let(:parser) {described_class.new "ad.getCoupons","coupon"}
|
11
|
-
|
12
|
-
context "with one coupon" do
|
13
|
-
it "returns an array of one coupon" do
|
14
|
-
response_double = double("response")
|
15
|
-
response_double.stub(:parsed_response) do
|
16
|
-
{"ad_getCouponsResponse" => {"coupons" => {"coupon" =>
|
17
|
-
{"id" => "58794", :name => "Early Bird Discount"}
|
18
|
-
}}}
|
19
|
-
end
|
20
|
-
|
21
|
-
|
22
|
-
results = parser.parse response_double
|
23
|
-
|
24
|
-
results.should be_a Array
|
25
|
-
results.length.should == 1
|
26
|
-
coupon = results.first
|
27
|
-
coupon.id.should eq "58794"
|
28
|
-
coupon.name.should eq "Early Bird Discount"
|
29
|
-
|
30
|
-
#TODO Test command message is sent to object builder
|
31
|
-
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
context "with multiple coupons" do
|
36
|
-
it "returns an array of multiple coupons" do
|
37
|
-
response_double = double("response")
|
38
|
-
response_double.stub(:parsed_response) do
|
39
|
-
{"ad_getCouponsResponse" => {"coupons" => {"coupon" =>[
|
40
|
-
{"id" => "58794", :name => "TWO FOR ONE ENTREES"},
|
41
|
-
{"id" => "12345", :name => "Three FOR ONE ENTREES"}
|
42
|
-
]}}}
|
43
|
-
end
|
44
|
-
|
45
|
-
results = parser.parse response_double
|
46
|
-
|
47
|
-
results.should be_a Array
|
48
|
-
results.length.should == 2
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
context "with no coupons" do
|
53
|
-
it "returns an empty array" do
|
54
|
-
response_double = double("response")
|
55
|
-
response_double.stub(:parsed_response) do
|
56
|
-
{"ad_getCouponsResponse" => {"coupons" => nil}}
|
57
|
-
end
|
58
|
-
|
59
|
-
results = parser.parse response_double
|
60
|
-
|
61
|
-
results.should eq []
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
context "parsing events" do
|
67
|
-
|
68
|
-
let(:parser) {described_class.new "ad.getEvents","event"}
|
69
|
-
|
70
|
-
context "with one event" do
|
71
|
-
|
72
|
-
it "returns an array of one event" do
|
73
|
-
response_double = double("response")
|
74
|
-
response_double.stub(:parsed_response) do
|
75
|
-
{"ad_getEventsResponse" => {"events" => {"event" =>
|
76
|
-
{"id" => "58794", :title => "TWO FOR ONE ENTREES"}
|
77
|
-
}}}
|
78
|
-
end
|
79
|
-
|
80
|
-
|
81
|
-
results = parser.parse response_double
|
82
|
-
|
83
|
-
results.should be_a Array
|
84
|
-
results.length.should == 1
|
85
|
-
|
86
|
-
end
|
87
|
-
|
88
|
-
end
|
89
|
-
|
90
|
-
context "with multiple events" do
|
91
|
-
|
92
|
-
it "returns an array of multiple events" do
|
93
|
-
response_double = double("response")
|
94
|
-
response_double.stub(:parsed_response) do
|
95
|
-
{"ad_getEventsResponse" => {"events" => {"event" =>[
|
96
|
-
{"id" => "58794", :title => "TWO FOR ONE ENTREES"},
|
97
|
-
{"id" => "12345", :title => "Three FOR ONE ENTREES"}
|
98
|
-
]}}}
|
99
|
-
end
|
100
|
-
|
101
|
-
results = parser.parse response_double
|
102
|
-
|
103
|
-
results.should be_a Array
|
104
|
-
results.length.should == 2
|
105
|
-
end
|
106
|
-
|
107
|
-
end
|
108
|
-
|
109
|
-
context "with no events" do
|
110
|
-
it "returns an emtpy array" do
|
111
|
-
|
112
|
-
response_double = double("response")
|
113
|
-
response_double.stub(:parsed_response) do
|
114
|
-
{"ad_getEventsResponse" => {"events" => nil}}
|
115
|
-
end
|
116
|
-
|
117
|
-
results = parser.parse response_double
|
118
|
-
|
119
|
-
results.should eq []
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
end
|
124
|
-
|
125
|
-
context "parsing event areas" do
|
126
|
-
|
127
|
-
let(:parser) {described_class.new "city.getEventAreas","eventArea"}
|
128
|
-
|
129
|
-
context "with one event areas" do
|
130
|
-
|
131
|
-
it "returns an array of one event area" do
|
132
|
-
response_double = double("response")
|
133
|
-
response_double.stub(:parsed_response) do
|
134
|
-
{"city_getEventAreasResponse" => {"eventAreas" => {"eventArea" =>
|
135
|
-
{"id" => "736", :title => "Bayfield"}
|
136
|
-
}}}
|
137
|
-
end
|
138
|
-
|
139
|
-
|
140
|
-
results = parser.parse response_double
|
141
|
-
|
142
|
-
results.should be_a Array
|
143
|
-
results.length.should == 1
|
144
|
-
|
145
|
-
|
146
|
-
end
|
147
|
-
|
148
|
-
end
|
149
|
-
|
150
|
-
context "with multiple event areas" do
|
151
|
-
|
152
|
-
it "returns an array of multiple event areas" do
|
153
|
-
response_double = double("response")
|
154
|
-
response_double.stub(:parsed_response) do
|
155
|
-
{"city_getEventAreasResponse" => {"eventAreas" => {"eventArea" =>[
|
156
|
-
{"id" => "58794", :title => "Bayfield"},
|
157
|
-
{"id" => "12345", :title => "Vallecito"}
|
158
|
-
]}}}
|
159
|
-
end
|
160
|
-
|
161
|
-
results = parser.parse response_double
|
162
|
-
|
163
|
-
results.should be_a Array
|
164
|
-
results.length.should == 2
|
165
|
-
end
|
166
|
-
|
167
|
-
end
|
168
|
-
|
169
|
-
context "with no event areas" do
|
170
|
-
it "returns an emtpy array" do
|
171
|
-
|
172
|
-
response_double = double("response")
|
173
|
-
response_double.stub(:parsed_response) do
|
174
|
-
{"city_getEventAreasResponse" => {"eventAreas" => nil}}
|
175
|
-
end
|
176
|
-
|
177
|
-
results = parser.parse response_double
|
178
|
-
|
179
|
-
results.should eq []
|
180
|
-
end
|
181
|
-
end
|
182
|
-
|
183
|
-
end
|
184
|
-
|
185
|
-
end
|
186
|
-
|
187
|
-
end
|