flurry_harvest 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .rvmrc
@@ -0,0 +1 @@
1
+ rvm use 1.9.2@flurry_harvest
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source :rubygems
2
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Tom Meinlschmidt
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,51 @@
1
+ # FlurryHarvest
2
+
3
+ Fetch Flurry app _offers_ through their API.
4
+
5
+ See ``http://support.flurry.com/index.php?title=API/Code/GetRecommendations`` for more info.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem "flurry_harvest"
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install flurry_harvest
20
+
21
+ ## Usage
22
+
23
+ @client = FlurryHarvest::Client.new(:api_access_code => "my_api_access_code", :api_key => "my_api_key")
24
+ @response = @client.fetch_offers(:udid => "asfereffajnfasfafafawfafaoweb", :mac => "D9:2A:1A:0C:FD:0B", :ip => "127.0.0.2")
25
+
26
+ _offers_ are then in ``@response.offers`` array
27
+
28
+ ## Available options for Client
29
+
30
+ * ``api_access_code`` - your API Access code, **required**
31
+ * ``api_key`` - your API key, **required**
32
+ * ``api_host`` - default set to ``api.flurry.com``
33
+ * ``api_url`` - default set to ``/appCircle/v2/getRecommendations``
34
+ * ``api_port`` - 80 or 443 for SSL
35
+ * ``agent`` - optionally Agent string, sent with request
36
+
37
+ ## Available options for fetching _offers_
38
+
39
+ all options are required
40
+
41
+ * ``udid`` - non-modified UDID from device,
42
+ * ``mac`` - MAC address of the device
43
+ * ``ip`` - IP address of the user
44
+
45
+ ## Contributing
46
+
47
+ 1. Fork it
48
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
49
+ 3. Commit your changes (`git commit -am "Add some feature"`)
50
+ 4. Push to the branch (`git push origin my-new-feature`)
51
+ 5. Create new Pull Request
@@ -0,0 +1,11 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require "rake/testtask"
4
+
5
+ task :default => :test
6
+
7
+ Rake::TestTask.new do |t|
8
+ t.libs << "."
9
+ t.test_files = FileList["test/**/*_test.rb"]
10
+ t.verbose = true
11
+ end
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "flurry_harvest/version"
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "flurry_harvest"
8
+ gem.version = FlurryHarvest::VERSION
9
+ gem.authors = ["Tom Meinlschmidt"]
10
+ gem.email = ["tom@meinlschmidt.org"]
11
+ gem.description = %q{Used to fetch and communcate with Flurry service}
12
+ gem.summary = %q{Flurre offers, AppCircle etc}
13
+ gem.homepage = ""
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_development_dependency "fakeweb", "1.3.0"
21
+ gem.add_development_dependency "mocha", "0.13.1"
22
+ gem.add_development_dependency "delorean", "2.1.0"
23
+
24
+ gem.add_dependency "json", "~> 1.7.7"
25
+ end
@@ -0,0 +1,16 @@
1
+ require "ostruct"
2
+ require "json"
3
+ require "net/http"
4
+ require "net/https"
5
+ require "uri"
6
+
7
+ require_relative "flurry_harvest/version"
8
+ require_relative "flurry_harvest/offer"
9
+ require_relative "flurry_harvest/feed"
10
+ require_relative "flurry_harvest/exceptions"
11
+ require_relative "flurry_harvest/client"
12
+ require_relative "flurry_harvest/mock_client"
13
+
14
+
15
+ module FlurryHarvest
16
+ end
@@ -0,0 +1,115 @@
1
+ class FlurryHarvest::Client
2
+ attr_reader :api_access_code
3
+ attr_reader :api_key
4
+ attr_reader :api_host # api.flurry.com
5
+ attr_reader :api_url # appCircle/v2/getRecommendations
6
+ attr_reader :api_port # 80 || 443
7
+ attr_reader :raw_data
8
+ attr_reader :query
9
+ attr_reader :agent # custom agent string
10
+ attr_reader :debug_mode
11
+
12
+ def initialize(options = {})
13
+ @api_access_code = options[:api_access_code] || (raise FlurryHarvest::ApiAccessCodeNotSet)
14
+ @api_key = options[:api_key] || (raise FlurryHarvest::ApiKeyNotSet)
15
+ @api_host = options[:api_host] || 'api.flurry.com'
16
+ @api_url = options[:api_url] || '/appCircle/v2/getRecommendations'
17
+ @api_port = options[:api_port] || 80
18
+ @agent = options[:agent]
19
+ @debug_mode = options[:debug_mode] || false
20
+
21
+ log "Initialize:"
22
+ log "-----------"
23
+ log "api_access_code: #{api_access_code}"
24
+ log "api_key: #{api_key}"
25
+ log "api_host: #{api_host}"
26
+ log "api_url: #{api_url}"
27
+ log "api_port: #{api_port}"
28
+ log "agent: #{agent}"
29
+ log "full options: #{options}"
30
+ end
31
+
32
+ # mandatory params
33
+ # iosUdid - :udid
34
+ # sha1Mac - :mac
35
+ # platform - :platform (IPHONE, IPAD, AND)
36
+ # ipAddress - :ip
37
+ def fetch_offers(options = {})
38
+ log "fetch_offers.options: #{options}"
39
+
40
+ raise Exception, "UDID is Required" if !options.has_key?(:udid)
41
+ raise Exception, "IP is Required" if !options.has_key?(:ip)
42
+
43
+ query_options = {
44
+ :apiAccessCode => @api_access_code,
45
+ :apiKey => @api_key,
46
+ :iosUdid => options[:udid],
47
+ :sha1Mac => options[:mac] ? encode_mac(options[:mac]) : nil,
48
+ :platform => (options[:platform] || 'IPHONE').upcase,
49
+ :ipAddress => options[:ip]
50
+ }
51
+
52
+ @query = to_query(query_options)
53
+
54
+ http = Net::HTTP.new(@api_host, @api_port)
55
+ http.use_ssl = (@api_port.to_i == 443)
56
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
57
+
58
+ path = "#{@api_url}?#{@query}"
59
+ log "Request: http://#{api_host}:#{api_port}#{path}"
60
+
61
+ response = http.get(path, headers)
62
+ log "Response.body: #{response.body}"
63
+
64
+ @raw_data = JSON.parse(response.body)
65
+ log "Response.raw_data: #{@raw_data}"
66
+
67
+ if response.code.to_i == 200
68
+ result = decode
69
+ log "Result: #{result.inspect}"
70
+
71
+ return result unless @raw_data.empty?
72
+ else
73
+ raise FlurryHarvest::ApiError, message: "#{@raw_data['code']} - #{@raw_data['message']}"
74
+ end
75
+
76
+ false
77
+ end
78
+
79
+ # SHA1 encode of mac address
80
+ # D9:2A:1A:0C:FD:0B should become CF41A96B9A6E4FE2942B4A51F350D7FD722E38B2
81
+ def encode_mac(mac_address)
82
+ Digest::SHA1.hexdigest(mac_address).upcase
83
+ end
84
+
85
+ def log(message)
86
+ return unless debug_mode
87
+
88
+ final_message = "FlurryHarvest [#{Time.now.strftime("%Y-%m-%d %H:%M:%S")}]: #{message}"
89
+
90
+ if defined? ::Rails
91
+ ::Rails.logger.info final_message
92
+ else
93
+ Kernel.puts final_message
94
+ end
95
+ end
96
+
97
+ private
98
+
99
+ def to_query(data = {})
100
+ _data = data.select { |k,v| !v.nil? }
101
+ _data.map{|k,v| "#{k.to_s}=#{URI.escape(v.to_s)}"}.join('&')
102
+ end
103
+
104
+ # parse json and make object
105
+ def decode
106
+ FlurryHarvest::Feed.new(@raw_data).offers
107
+ end
108
+
109
+ # fetch headers
110
+ def headers
111
+ {'User-Agent' => "Flurry-#{@agent}",
112
+ 'Accept' => "application/json"}
113
+ end
114
+
115
+ end
@@ -0,0 +1,6 @@
1
+ module FlurryHarvest
2
+ class ApiAccessCodeNotSet < Exception;end
3
+ class ApiKeyNotSet < Exception;end
4
+ class ApiError < Exception;end
5
+ class UnknownError < Exception;end
6
+ end
@@ -0,0 +1,10 @@
1
+ class FlurryHarvest::Feed
2
+ attr_reader :offers
3
+
4
+ def initialize(data)
5
+ flurry_offers = data["recommendation"] ? data["recommendation"] : []
6
+ flurry_offers = [flurry_offers] if flurry_offers.is_a? Hash
7
+
8
+ @offers = flurry_offers.map{ |r| FlurryHarvest::Offer.new(r) }
9
+ end
10
+ end
@@ -0,0 +1,24 @@
1
+ module FlurryHarvest
2
+ class MockClient < FlurryHarvest::Client
3
+ def initialize(options = {})
4
+ @debug_mode = options[:debug_mode] || false
5
+
6
+ log "Initialize MockClient:"
7
+ log "-------------"
8
+ log "full options: #{options}"
9
+ end
10
+
11
+ def fetch_offers(options = {})
12
+ log "fetch_offers.options: #{options}"
13
+
14
+ @raw_data = JSON.parse(File.read("#{File.dirname(__FILE__)}/../../test/fixtures/data.json"))
15
+
16
+ result = decode
17
+
18
+ log "Response.raw_data: #{@raw_data}"
19
+ log "Result: #{result.inspect}"
20
+
21
+ return result
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,19 @@
1
+ class FlurryHarvest::Offer < OpenStruct
2
+ def initialize(data)
3
+ super
4
+
5
+ self.icon = data["@appIconUrl"]
6
+ self.time_to_payout = nil
7
+ self.title = data["@appName"]
8
+ self.required_actions = "Download and Start"
9
+ self.link = data["@actionUrl"]
10
+ self.price = data["@appPrice"].to_f / 100
11
+ self.provider = "Flurry"
12
+ self.credits = nil
13
+ self.id = get_id
14
+ end
15
+
16
+ def get_id
17
+ Digest::MD5.hexdigest("#{title}-#{icon}")
18
+ end
19
+ end
@@ -0,0 +1,3 @@
1
+ module FlurryHarvest
2
+ VERSION = "0.1.3"
3
+ end
@@ -0,0 +1,157 @@
1
+ require_relative "test_helper"
2
+
3
+ class FlurryHarvest::ClientTest < Test::Unit::TestCase
4
+ def setup
5
+ FlurryHarvest::Client.any_instance.stubs(:log)
6
+ end
7
+
8
+ def test_initialize
9
+ client =
10
+ FlurryHarvest::Client.new(
11
+ :api_access_code => "access_code",
12
+ :api_key => "api_key"
13
+ )
14
+
15
+ assert_equal("access_code", client.api_access_code)
16
+ assert_equal("api_key", client.api_key)
17
+ end
18
+
19
+ def test_debug_mode
20
+ client =
21
+ FlurryHarvest::Client.new(
22
+ :api_access_code => "access_code",
23
+ :api_key => "api_key",
24
+ :debug_mode => true
25
+ )
26
+
27
+ FlurryHarvest::Client.any_instance.unstub(:log)
28
+
29
+ Kernel.expects(:puts).with("FlurryHarvest [2012-02-03 10:11:12]: wadus")
30
+
31
+ Delorean.time_travel_to("2012-02-03 10:11:12") do
32
+ client.log "wadus"
33
+ end
34
+ end
35
+
36
+ def test_debug_mode_off
37
+ client =
38
+ FlurryHarvest::Client.new(
39
+ :api_access_code => "access_code",
40
+ :api_key => "api_key"
41
+ )
42
+
43
+ Kernel.expects(:puts).never
44
+ client.log "wadus"
45
+ end
46
+
47
+ def test_debug_mode_with_rails_defined
48
+ client =
49
+ FlurryHarvest::Client.new(
50
+ :api_access_code => "access_code",
51
+ :api_key => "api_key",
52
+ :debug_mode => true
53
+ )
54
+
55
+ FlurryHarvest::Client.any_instance.unstub(:log)
56
+
57
+ rails_mock = mock();
58
+ logger_mock = mock();
59
+
60
+ Kernel.send(:const_set, :Rails, rails_mock)
61
+ rails_mock.stubs(:logger).returns(logger_mock)
62
+ logger_mock.expects(:info).with("FlurryHarvest [2012-02-03 10:11:12]: wadus")
63
+ Kernel.expects(:puts).never
64
+
65
+ Delorean.time_travel_to("2012-02-03 10:11:12") do
66
+ client.log "wadus"
67
+ end
68
+ end
69
+
70
+ def test_no_api_access_code
71
+ assert_raise FlurryHarvest::ApiAccessCodeNotSet do
72
+ FlurryHarvest::Client.new
73
+ end
74
+ end
75
+
76
+ def test_no_api_key
77
+ assert_raise FlurryHarvest::ApiKeyNotSet do
78
+ FlurryHarvest::Client.new(:api_access_code => "access_code")
79
+ end
80
+ end
81
+
82
+ # missing UDID
83
+ def test_udid_not_found
84
+ client = FlurryHarvest::Client.new(:api_access_code => "access_code", :api_key => "my_key")
85
+ assert_raise(Exception) { client.fetch_offers(:mac => "abcd", :ip => "192") }
86
+ end
87
+
88
+ # missing ip
89
+ def test_ip_not_found
90
+ client = FlurryHarvest::Client.new(:api_access_code => "access_code", :api_key => "my_key")
91
+ assert_raise(Exception) { client.fetch_offers(:udid => "sadfadsfas", :mac => "abcd") }
92
+ end
93
+
94
+ # test valid feed
95
+ def test_valid_request
96
+ data = File.read("#{FIXTURES}/data.json")
97
+ FakeWeb.allow_net_connect = "http://api.example.com"
98
+ FakeWeb.register_uri(:get, "http://api.example.com/appCircle/v2/getRecommendations?apiAccessCode=4CWRDCC8PDJC99R8BSPC&apiKey=ZM7M97D79X3D5258M2BF&iosUdid=ea750fdbc619ca406d066d7ed158d54483fc382f&sha1Mac=CF41A96B9A6E4FE2942B4A51F360D7FD722E38B2&platform=IPHONE&ipAddress=92.240.181.8", :body => data, :content_type => "application/json")
99
+
100
+ client = FlurryHarvest::Client.new(:api_access_code => "4CWRDCC8PDJC99R8BSPC", :api_key => "ZM7M97D79X3D5258M2BF", :api_host => "api.example.com")
101
+
102
+ offers = client.fetch_offers(:udid => "ea750fdbc619ca406d066d7ed158d54483fc382f", :mac => "D9:2A:1A:0C:FD:0B", :ip => "92.240.181.8")
103
+
104
+ assert_equal(5, offers.count)
105
+ assert_equal(["System Status Lite - device activity monitor app", "Gossip Fix", "ART Jigsaw Puzzles. Van Gogh", "travelmeter", "Poker Dawgs"], offers.map(&:title))
106
+
107
+ offer = offers.first
108
+ assert_equal("http://ad.flurry.com/manualGetIPhoneApp.do?v=1&e=NsuJZSsBlxdPFL3Zi4IK97tHKU19JIjZbVS7gXuaqHb9SMkFnZQ7GUIcJWFxWslAKWFDJRCidAnzWhAEZG58VAiBgta3aOrpxBTsHAgwzZHqHr8hwbw4p0WxHL_AZSJnC4Xm3snrURgTMpY_6wZKgLtrFoyh5EMWVFVq2bOMds-3zgj7pKzti5ZWkVvvhzOJUEjBSf2ZQbPsgDTmMoMgmiLUA60I0t4BTMp3v8aw2sOoNfwGv1F4sw", offer.link)
109
+ assert_equal("http://flurry.cachefly.net/ad/ad_image/5275_80x80.png", offer.icon)
110
+ assert_equal("System Status Lite - device activity monitor app", offer.title)
111
+ assert_equal("Download and Start", offer.required_actions)
112
+ assert_equal(1.12, offer.price)
113
+ assert_equal(nil, offer.time_to_payout)
114
+ assert_equal("f0359fc7fd864408ca29fdf1bb0c26fe", offer.id)
115
+ assert_equal(nil, offer.credits)
116
+ assert_equal("Flurry", offer.provider)
117
+ end
118
+
119
+ def test_valid_request_with_only_one_element
120
+ data = File.read("#{FIXTURES}/data_only_one_element.json")
121
+ FakeWeb.allow_net_connect = "http://api.example.com"
122
+ FakeWeb.register_uri(:get, "http://api.example.com/appCircle/v2/getRecommendations?apiAccessCode=4CWRDCC8PDJC99R8BSPC&apiKey=ZM7M97D79X3D5258M2BF&iosUdid=ea750fdbc619ca406d066d7ed158d54483fc382f&sha1Mac=CF41A96B9A6E4FE2942B4A51F360D7FD722E38B2&platform=IPHONE&ipAddress=92.240.181.8", :body => data, :content_type => "application/json")
123
+
124
+ client = FlurryHarvest::Client.new(:api_access_code => "4CWRDCC8PDJC99R8BSPC", :api_key => "ZM7M97D79X3D5258M2BF", :api_host => "api.example.com")
125
+
126
+ offers = client.fetch_offers(:udid => "ea750fdbc619ca406d066d7ed158d54483fc382f", :mac => "D9:2A:1A:0C:FD:0B", :ip => "92.240.181.8")
127
+
128
+ assert_equal(1, offers.count)
129
+ assert_equal(["System Status Lite - device activity monitor app"], offers.map(&:title))
130
+
131
+ offer = offers.first
132
+ assert_equal("http://ad.flurry.com/manualGetIPhoneApp.do?v=1&e=NsuJZSsBlxdPFL3Zi4IK97tHKU19JIjZbVS7gXuaqHb9SMkFnZQ7GUIcJWFxWslAKWFDJRCidAnzWhAEZG58VAiBgta3aOrpxBTsHAgwzZHqHr8hwbw4p0WxHL_AZSJnC4Xm3snrURgTMpY_6wZKgLtrFoyh5EMWVFVq2bOMds-3zgj7pKzti5ZWkVvvhzOJUEjBSf2ZQbPsgDTmMoMgmiLUA60I0t4BTMp3v8aw2sOoNfwGv1F4sw", offer.link)
133
+ end
134
+
135
+ # test without mac address
136
+ def test_valid_request_without_mac_address
137
+ data = File.read("#{FIXTURES}/data.json")
138
+ FakeWeb.allow_net_connect = "http://api.example.com"
139
+ FakeWeb.register_uri(:get, "http://api.example.com/appCircle/v2/getRecommendations?apiAccessCode=4CWRDCC8PDJC99R8BSPC&apiKey=ZM7M97D79X3D5258M2BF&iosUdid=ea750fdbc619ca406d066d7ed158d54483fc382f&platform=IPHONE&ipAddress=92.240.181.8", :body => data, :content_type => "application/json")
140
+
141
+ client = FlurryHarvest::Client.new(:api_access_code => "4CWRDCC8PDJC99R8BSPC", :api_key => "ZM7M97D79X3D5258M2BF", :api_host => "api.example.com")
142
+
143
+ client.fetch_offers(:udid => "ea750fdbc619ca406d066d7ed158d54483fc382f", :ip => "92.240.181.8")
144
+ end
145
+
146
+ # any error
147
+ def test_invalid_udid
148
+ FakeWeb.allow_net_connect = "http://api.example.com"
149
+ FakeWeb.register_uri(:get, "http://api.example.com/appCircle/v2/getRecommendations?apiAccessCode=4CWRDCC8PDJC99R8BSPC&apiKey=ZM7M97D79X3DF&iosUdid=ea750fdbc619ca406d066d7ed158d54483fc382f&sha1Mac=CF41A96B9A6E4FE2942B4A51F360D7FD722E38B2&platform=IPHONE&ipAddress=92.240.181.8", :body => '{"code":100, "message":"API Key not found"}', :content_type => "application/json", :status => ["400","Error"])
150
+
151
+ client = FlurryHarvest::Client.new(:api_access_code => "4CWRDCC8PDJC99R8BSPC", :api_key => "ZM7M97D79X3DF", :api_host => "api.example.com")
152
+ assert_raise FlurryHarvest::ApiError do
153
+ client.fetch_offers(:udid => "ea750fdbc619ca406d066d7ed158d54483fc382f", :mac => "D9:2A:1A:0C:FD:0B", :ip => "92.240.181.8")
154
+ end
155
+ end
156
+
157
+ end
@@ -0,0 +1,40 @@
1
+ { "@generatedDate" : "12/5/12 2:40 PM",
2
+ "@type" : "Recommendations",
3
+ "@version" : "1.0",
4
+ "recommendation" : [ { "@actionUrl" : "http://ad.flurry.com/manualGetIPhoneApp.do?v=1&e=NsuJZSsBlxdPFL3Zi4IK97tHKU19JIjZbVS7gXuaqHb9SMkFnZQ7GUIcJWFxWslAKWFDJRCidAnzWhAEZG58VAiBgta3aOrpxBTsHAgwzZHqHr8hwbw4p0WxHL_AZSJnC4Xm3snrURgTMpY_6wZKgLtrFoyh5EMWVFVq2bOMds-3zgj7pKzti5ZWkVvvhzOJUEjBSf2ZQbPsgDTmMoMgmiLUA60I0t4BTMp3v8aw2sOoNfwGv1F4sw",
5
+ "@appIconUrl" : "http://flurry.cachefly.net/ad/ad_image/5275_80x80.png",
6
+ "@appName" : "System Status Lite - device activity monitor app",
7
+ "@appPrice" : "112",
8
+ "@description" : "System Status Lite is a lite version of System Status - the ultimate application for monitoring and optimizing your device's performance. The lite version monitors your device's basic parameters such as battery level, disk usage, CPU load and connections.<br /><br />GENERAL FEATURES<br />- Full iOS 4 multitasking support<br />- Universal binary - full support of all iPhone, iPod Touch and iPad models<br />- Graphics optimized both for retina and older displays for pixel perfect graphics<br />- Well-arranged user interface so the most frequently used information available immediately<br />- Use of color coding for indicators and graphs to improve readability <br />- Real time updates of the display once the monitored parameters change<br />- Detailed help<br /><br />BATTERY MONITORING<br />- Graphical battery level display<br />- Battery state monitoring (discharging, charging, full)<br /><br />DISK MONITORING<br />- Used and free disk capacity monitoring<br /><br />CPU MONITORING<br />- CPU usage updated in real time<br />- Average load over the last 1, 5 and 15 minutes<br /><br />OPERATING SYSTEM <br />- System boot time and uptime<br /><br />CELL AND NETWORK MONITORING<br />- 3G and Wi-Fi network connection information<br />- IP and MAC addresses of the current connection<br />- Carrier information such as network provider and MCC/MNC codes<br />- External IP address<br /><br />Facebook fan page: www.facebook.com/SystemStatusApp<br /><br />Full version YouTube video: http://youtu.be/0n9DduGUGyk<br /><br />▶ Search the store for 'System Status' to see the extra features the full version offers ◀",
9
+ "@publisherName" : "Jiri Techet"
10
+ },
11
+ { "@actionUrl" : "http://ad.flurry.com/manualGetIPhoneApp.do?v=1&e=Pb6RlE7WxF29wRbs80vmsfVV6Uf3V-pPbLBcQeA_fMifgUjI-Xn2J7ELuER93UkF8AIl9RRt944wN1vwRcPcJIDDhll0biCSnIE7FZpUnMajqRxMd5eaiuX75jJTMDbLe_tHoukH0qWmd08nA28KNQ1fDvgS_gyaq1EflSRtF7bbsWHweRZRuplEKPfSujbvglfTXA1nDwn-NgXanwOeg5UnHqpjfM4FmdUPdefQ0r4QwRwdi-TBdw",
12
+ "@appIconUrl" : "http://flurry.cachefly.net/ad/ad_image/2076_80x80.png",
13
+ "@appName" : "Gossip Fix",
14
+ "@appPrice" : "99",
15
+ "@description" : "Gossip Fix is what you need to easily view the most popular celebrity news sites today! Find yourself constantly checking gossip sites for the latest news? Gossip Fix is fast, simple, and easy to use. \r\n \r\n Easily switch from one feed to the other, view the story with pictures, send the stories to friends, view the full story online, all in one simple app!",
16
+ "@publisherName" : "Charlie Elliott"
17
+ },
18
+ { "@actionUrl" : "http://ad.flurry.com/manualGetIPhoneApp.do?v=1&e=NbFlSUx_hIy9wRbs80vmsfVV6Uf3V-pPbLBcQeA_fMifgUjI-Xn2J7ELuER93UkF8AIl9RRt944wN1vwRcPcJIDDhll0biCSnIE7FZpUnMajqRxMd5eaiuX75jJTMDbLe_tHoukH0qWmd08nA28KNQ1fDvgS_gyaq1EflSRtF7bbsWHweRZRuplEKPfSujbvglfTXA1nDwn-NgXanwOeg5UnHqpjfM4FmdUPdefQ0r4QwRwdi-TBdw",
19
+ "@appIconUrl" : "http://flurry.cachefly.net/ad/ad_image/880_80x80.png",
20
+ "@appName" : "ART Jigsaw Puzzles. Van Gogh",
21
+ "@appPrice" : "199",
22
+ "@description" : "Jigsaw Puzzle with 40 masterpieces by Van Gogh. Enjoy great classical music while playing. Post your achievements on facebook and twitter. Send puzzles to challenge your friends.\r\n \r\n Features summary:\r\n ✓ Fullscreen jigsaw puzzle \r\n ✓ 3 levels: Easy (4x7 28 pieces), Medium (6x9 54 pieces) and Hard (7x10 70 pieces)\r\n ✓ 40 Masterpieces by Van Gogh\r\n ✓ Biography of Van Gogh\r\n ✓ My Gallery - gorgeous gallery of collected paintings\r\n ✓ Openfeint enabled: get achievements and send puzzles as challenges\r\n ✓ Charming classical background music\r\n ✓ Game state saved on exit or incoming call\r\n \r\n More educational applications by ADS Software Group:\r\n ✓ ART - Pocket Art Gallery. Great Artists. Slideshow. Quiz\r\n ✓ Classical Music - 50 Famous compositions and Quiz\r\n ✓ World Countries. Factbook, Quiz and Flag games\r\n ✓ JS Bach - Classical Music Collection and Quiz.\r\n ✓ LiveTrivia - realtime multiplayer trivia & chat\r\n \r\n Please also check our other ART Jigsaw puzzles - Michelangelo, Da Vinci and Rafaello.",
23
+ "@publisherName" : "ADS Software Group, Inc."
24
+ },
25
+ { "@actionUrl" : "http://ad.flurry.com/manualGetIPhoneApp.do?v=1&e=8jB8gex8SXLX7X4GNbPcp7tHKU19JIjZbVS7gXuaqHb9SMkFnZQ7GUIcJWFxWslAKWFDJRCidAnzWhAEZG58VAiBgta3aOrpxBTsHAgwzZHqHr8hwbw4p0WxHL_AZSJnC4Xm3snrURgTMpY_6wZKgLtrFoyh5EMWVFVq2bOMds-3zgj7pKzti5ZWkVvvhzOJUEjBSf2ZQbPsgDTmMoMgmiLUA60I0t4BTMp3v8aw2sOoNfwGv1F4sw",
26
+ "@appIconUrl" : "http://flurry.cachefly.net/ad/ad_image/11083_80x80.png",
27
+ "@appName" : "travelmeter",
28
+ "@appPrice" : "0",
29
+ "@description" : "Are we there yet? Travelmeter graphically shows the progress of your journey between two cities. The car, train, or boat advances as you get closer. The road, rails, or water moves depending on your speed. \r\n\r\nNOTE: Continued use of GPS running in the background can dramatically decrease battery life. Background operation is turned off by default and controlled on the 'settings' screen of the app.\r\n\r\n* Worldwide reverse geocoder: type in a city and it will look it up, no dropping pins on maps!\r\n\r\n* Alarm: notifies you when you get close.\r\n\r\n* Very little data usage: works in places with poor coverage, no waiting for maps to load. \r\n\r\n* Twitter: announce the progress of your journey on twitter.\r\n\r\n* Distances and direction.\r\n\r\n* Speed and movement.",
30
+ "@publisherName" : "Caffeine Fish LLC"
31
+ },
32
+ { "@actionUrl" : "http://ad.flurry.com/manualGetIPhoneApp.do?v=1&e=smNGyuWFlDP9okUCGcBQrbtHKU19JIjZbVS7gXuaqHb9SMkFnZQ7GUIcJWFxWslAKWFDJRCidAnzWhAEZG58VAiBgta3aOrpxBTsHAgwzZHqHr8hwbw4p0WxHL_AZSJnC4Xm3snrURgTMpY_6wZKgLtrFoyh5EMWVFVq2bOMds-3zgj7pKzti5ZWkVvvhzOJUEjBSf2ZQbPsgDTmMoMgmiLUA60I0t4BTMp3v8aw2sOoNfwGv1F4sw",
33
+ "@appIconUrl" : "http://flurry.cachefly.net/ad/ad_image/11396_80x80.jpg",
34
+ "@appName" : "Poker Dawgs",
35
+ "@appPrice" : "0",
36
+ "@description" : "Poker Dawgs provides a simple and unique combination of strategy, luck, scoring, competition and rewards.\r\nPoker Dawgs is an entirely new way to play video poker and the only game in the app store with a Ladder scoring system. Poker Dawgs provides a new level of strategy with wild cards, random score multipliers for each card dealt and a pick your own multiplier bonus round. Poker Dawgs also offers rewards for playing. Each time you play you will accumulate “Dawg Treats” that can be redeemed in the In-App “Pet Store” for more credits. Accumulate enough “Dawg Treats” and you can redeem them for a Gift Card through our web site. For our Top Dawg Players, you can use your video poker skills to win Gift Cards by scoring a million points or more on any game of 1,000 credits or higher. You can play for free by coming back every day to collect your random daily bonus of credits. You can earn more credits by achieving the high score of the day or the month. The higher you score the more credits you will collect from other players. All credits earned in Poker Dawgs can also be used to play our other app called “Dawgs”.\r\nHow the Ladder Scoring System Works:\r\nYour total score will be added up from all 5 hands and submitted once the final hand is complete. If your score qualifies for the leader board, your name and score will be displayed accordingly in the 8 slot 'Ladder' for everyone to see; you will also start accumulating credits while your name remains on the 'Ladder' and other people play. Every time you play, 10% of the credits you used to play will be distributed to each slot on the “Ladder”. For example, if you play the 100 credit game, 10 credits are added to each person currently on the “Ladder”. If you currently occupy 3 slots on the “Ladder” and someone else plays, 10 credits will be added to each of your 3 slots.\r\n\r\nThe 'Ladder' system contains 8 slots and an instant jackpot. Each slot represents a score range, as illustrated below:\r\nSlot 1: 300,000+\r\nSlot 2: 200,000 - 299,999\r\nSlot 3: 150,000 - 199,999\r\nSlot 4: 100,000 - 149,999\r\nSlot 5: 50,000 - 99,999\r\nSlot 6: 25,000 - 49,999\r\nSlot 7: 10,000 - 24,999\r\nSlot 8: 5,000 - 9,999\r\n\r\nIf your total score falls within these ranges your name will occupy the appropriate slot on the “Ladder” which also slides the previous occupant of your slot down 1 slot. For example, if you finish a play with a score of 25,000 and player 'David' was in slot 7 while player 'John' was in slot 8, you will take over slot 7 which pushes 'David' to slot 8 and 'John' is knocked off the “Ladder”. At this point, 'John' collects however many credits he accumulated while on the “Ladder”. Your name will remain in slot 7, collecting credits, until someone else scores 25,000 or more. The obvious goal is to score high enough to get into slot 1. The higher your slot, the longer you will stay on the “Ladder” and the more credits you will collect when you are finally knocked off the “Ladder”.\r\n\r\nThe Jackpot is an instant win of credits or a Gift Card, and you will also take over slot 1! You need to score 1,000,000 or more to win the jackpot for any game.",
37
+ "@publisherName" : "Travis croxford"
38
+ }
39
+ ]
40
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "@type":"Recommendations",
3
+ "@version":"1.0",
4
+ "@generatedDate":"1/9/13 6:55 AM",
5
+ "recommendation":{
6
+ "@actionUrl" : "http://ad.flurry.com/manualGetIPhoneApp.do?v=1&e=NsuJZSsBlxdPFL3Zi4IK97tHKU19JIjZbVS7gXuaqHb9SMkFnZQ7GUIcJWFxWslAKWFDJRCidAnzWhAEZG58VAiBgta3aOrpxBTsHAgwzZHqHr8hwbw4p0WxHL_AZSJnC4Xm3snrURgTMpY_6wZKgLtrFoyh5EMWVFVq2bOMds-3zgj7pKzti5ZWkVvvhzOJUEjBSf2ZQbPsgDTmMoMgmiLUA60I0t4BTMp3v8aw2sOoNfwGv1F4sw",
7
+ "@appIconUrl" : "http://flurry.cachefly.net/ad/ad_image/5275_80x80.png",
8
+ "@appName" : "System Status Lite - device activity monitor app",
9
+ "@appPrice" : "112",
10
+ "@description" : "System Status Lite is a lite version of System Status - the ultimate application for monitoring and optimizing your device's performance. The lite version monitors your device's basic parameters such as battery level, disk usage, CPU load and connections.<br /><br />GENERAL FEATURES<br />- Full iOS 4 multitasking support<br />- Universal binary - full support of all iPhone, iPod Touch and iPad models<br />- Graphics optimized both for retina and older displays for pixel perfect graphics<br />- Well-arranged user interface so the most frequently used information available immediately<br />- Use of color coding for indicators and graphs to improve readability <br />- Real time updates of the display once the monitored parameters change<br />- Detailed help<br /><br />BATTERY MONITORING<br />- Graphical battery level display<br />- Battery state monitoring (discharging, charging, full)<br /><br />DISK MONITORING<br />- Used and free disk capacity monitoring<br /><br />CPU MONITORING<br />- CPU usage updated in real time<br />- Average load over the last 1, 5 and 15 minutes<br /><br />OPERATING SYSTEM <br />- System boot time and uptime<br /><br />CELL AND NETWORK MONITORING<br />- 3G and Wi-Fi network connection information<br />- IP and MAC addresses of the current connection<br />- Carrier information such as network provider and MCC/MNC codes<br />- External IP address<br /><br />Facebook fan page: www.facebook.com/SystemStatusApp<br /><br />Full version YouTube video: http://youtu.be/0n9DduGUGyk<br /><br />▶ Search the store for 'System Status' to see the extra features the full version offers ◀",
11
+ "@publisherName" : "Jiri Techet"
12
+ }
13
+ }
@@ -0,0 +1,30 @@
1
+ require_relative "test_helper"
2
+
3
+ class FlurryHarvest::MockClientTest < Test::Unit::TestCase
4
+
5
+ def setup
6
+ FlurryHarvest::MockClient.any_instance.stubs(:log)
7
+ @mock_client = FlurryHarvest::MockClient.new
8
+ end
9
+
10
+ def test_initialize
11
+ mock_client =
12
+ FlurryHarvest::MockClient.new(
13
+ :debug_mode => "debug_mode"
14
+ )
15
+
16
+ assert_equal("debug_mode", mock_client.debug_mode)
17
+ end
18
+
19
+ def test_fetch_offers
20
+ offers = @mock_client.fetch_offers
21
+ assert_equal(5, offers.length)
22
+
23
+ offer = offers.first
24
+ assert_equal("http://ad.flurry.com/manualGetIPhoneApp.do?v=1&e=NsuJZSsBlxdPFL3Zi4IK97tHKU19JIjZbVS7gXuaqHb9SMkFnZQ7GUIcJWFxWslAKWFDJRCidAnzWhAEZG58VAiBgta3aOrpxBTsHAgwzZHqHr8hwbw4p0WxHL_AZSJnC4Xm3snrURgTMpY_6wZKgLtrFoyh5EMWVFVq2bOMds-3zgj7pKzti5ZWkVvvhzOJUEjBSf2ZQbPsgDTmMoMgmiLUA60I0t4BTMp3v8aw2sOoNfwGv1F4sw", offer.link)
25
+ assert_equal("f0359fc7fd864408ca29fdf1bb0c26fe", offer.id)
26
+ assert_equal(nil, offer.credits)
27
+ assert_equal(1.12, offer.price)
28
+ assert_equal("System Status Lite - device activity monitor app", offer.title)
29
+ end
30
+ end
@@ -0,0 +1,11 @@
1
+ require "test/unit"
2
+ require "fakeweb"
3
+ require "mocha/setup"
4
+ require "delorean"
5
+
6
+
7
+ require_relative "../lib/flurry_harvest"
8
+
9
+ class Test::Unit::TestCase
10
+ FIXTURES = "#{File.dirname(__FILE__)}/fixtures"
11
+ end
metadata ADDED
@@ -0,0 +1,113 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: flurry_harvest
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.3
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Tom Meinlschmidt
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-05-08 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: fakeweb
16
+ requirement: &70213290695120 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - =
20
+ - !ruby/object:Gem::Version
21
+ version: 1.3.0
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *70213290695120
25
+ - !ruby/object:Gem::Dependency
26
+ name: mocha
27
+ requirement: &70213290694620 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - =
31
+ - !ruby/object:Gem::Version
32
+ version: 0.13.1
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70213290694620
36
+ - !ruby/object:Gem::Dependency
37
+ name: delorean
38
+ requirement: &70213290694160 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - =
42
+ - !ruby/object:Gem::Version
43
+ version: 2.1.0
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *70213290694160
47
+ - !ruby/object:Gem::Dependency
48
+ name: json
49
+ requirement: &70213290693700 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 1.7.7
55
+ type: :runtime
56
+ prerelease: false
57
+ version_requirements: *70213290693700
58
+ description: Used to fetch and communcate with Flurry service
59
+ email:
60
+ - tom@meinlschmidt.org
61
+ executables: []
62
+ extensions: []
63
+ extra_rdoc_files: []
64
+ files:
65
+ - .gitignore
66
+ - .rvmrc.example
67
+ - Gemfile
68
+ - LICENSE.txt
69
+ - README.md
70
+ - Rakefile
71
+ - flurry_harvest.gemspec
72
+ - lib/flurry_harvest.rb
73
+ - lib/flurry_harvest/client.rb
74
+ - lib/flurry_harvest/exceptions.rb
75
+ - lib/flurry_harvest/feed.rb
76
+ - lib/flurry_harvest/mock_client.rb
77
+ - lib/flurry_harvest/offer.rb
78
+ - lib/flurry_harvest/version.rb
79
+ - test/client_test.rb
80
+ - test/fixtures/data.json
81
+ - test/fixtures/data_only_one_element.json
82
+ - test/mock_client_test.rb
83
+ - test/test_helper.rb
84
+ homepage: ''
85
+ licenses: []
86
+ post_install_message:
87
+ rdoc_options: []
88
+ require_paths:
89
+ - lib
90
+ required_ruby_version: !ruby/object:Gem::Requirement
91
+ none: false
92
+ requirements:
93
+ - - ! '>='
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ required_rubygems_version: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ requirements: []
103
+ rubyforge_project:
104
+ rubygems_version: 1.8.15
105
+ signing_key:
106
+ specification_version: 3
107
+ summary: Flurre offers, AppCircle etc
108
+ test_files:
109
+ - test/client_test.rb
110
+ - test/fixtures/data.json
111
+ - test/fixtures/data_only_one_element.json
112
+ - test/mock_client_test.rb
113
+ - test/test_helper.rb