three-sixty 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3357b6f308efd6f848d6c5cee39547780b32dc2b
4
+ data.tar.gz: 3ad95ad642210362e42b440278ec98d7d9f8b75f
5
+ SHA512:
6
+ metadata.gz: 97e80e12364c3a079c33c5f6e07abf23d2056eeacf6b60490d8341f5f339c825880779a42950024d4acc974e73aa4b90944cc9e3c18e3d0a15df1480afa8f670
7
+ data.tar.gz: 9385af23b6f6187947b989fc1e0d2feed8b99b678cf629b8f21416b3468ff08699439fa3c6c93e6d73caf408fada0c5e54d3718d9734fb257cc513d081bd8f5e
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ documentation/*
2
+ Gemfile*
3
+ .bundle/*
4
+ .ruby*
data/README.md ADDED
@@ -0,0 +1,95 @@
1
+ # 360
2
+ A Ruby gem for connecting to the 360 Dianjing AD system, using the Open API Platform
3
+
4
+ ## Install
5
+
6
+ gem 'three-sixty', github: 'forward3d/three-sixty'
7
+
8
+
9
+ ## Configuration
10
+
11
+ require 'three-sixty'
12
+ ThreeSixty.configure(logger: Logger.new($stdout))
13
+
14
+
15
+ ## Authenticate
16
+
17
+ require 'three-sixty'
18
+
19
+ client = ThreeSixty::Client.new(api_key, api_secret)
20
+ client.authenticate!(username, password)
21
+
22
+ You can specify a hash of optional parameters when initializing the client. These include
23
+
24
+ :endpoint - The 360 api endpoint (This is unlikely to change)
25
+ :version - The 360 api version number
26
+ :format - The api reponse format (Currently JSON or XML and is only useful when using the core library)
27
+ :logger - The client logger
28
+
29
+
30
+ ## Get Campaign Ids
31
+
32
+ require 'three-sixty'
33
+
34
+ client = ThreeSixty::Client.new(api_key, api_secret)
35
+ client.authenticate!(username, password)
36
+
37
+ account = ThreeSixty::Account.new(client, opts)
38
+ campaign_ids = account.download_campaign_ids
39
+
40
+ You can specify a hash of optional parameters when initializing the account. These include
41
+
42
+ :logger - Attach a local logger
43
+ :report_generating_backoff - Create a lambda for your backoff strategy when polling the report generate e.g. lambda { |retry_number| [1 30 60 300].index(retry_number) }
44
+
45
+ ## Streaming Campaign Ids to a File
46
+
47
+ File.open('campaign_ids.csv', 'w') do |file|
48
+ account.download_campaigns(campaign_ids) do |content|
49
+ file.write content
50
+ end
51
+ end
52
+
53
+ ## Download Campaign Ids to a File
54
+
55
+ file = account.download_campaigns_to_file(campaign_ids, opts)
56
+
57
+ You can specify a hash of optional parameters, which includes
58
+
59
+ :download_dir - The directory to download the file
60
+ :filename - The full path of the file you wish to create
61
+ :encoding - The encoding used when downloading the file
62
+
63
+ ## Download Campaign Info
64
+
65
+ campaign = ThreeSixty::Campaign.new(client)
66
+ campaign_info = campaign.download_all_campaigns
67
+
68
+
69
+ ## Download All Ad Groups for a Campaign
70
+
71
+ group = ThreeSixty::Group.new(client)
72
+ group_info = group.download_campaign_all_ad_groups(campaigns_id)
73
+
74
+ ## Download All Creatives for a Campaign
75
+
76
+ group = ThreeSixty::Group.new(client)
77
+ ad_group_ids = group.download_campaign_ad_group_ids(campaign_id)
78
+
79
+ creatives = []
80
+ creative = ThreeSixty::Creative.new(client)
81
+ ad_group_ids.each do |ad_group_id|
82
+ creatives += creative.download_ad_group_all_creatives(ad_group_id)
83
+ end
84
+
85
+
86
+ ## Downlod All Keywords for a Campaign
87
+
88
+ group = ThreeSixty::Group.new(client)
89
+ ad_group_ids = group.download_campaign_ad_group_ids(campaign_id)
90
+
91
+ keywords = []
92
+ keyword = ThreeSixty::Keyword.new(client)
93
+ ad_group_ids.each do |ad_group_id|
94
+ keywords += keyword.download_ad_group_all_keywords(ad_group_id)
95
+ end
@@ -0,0 +1,90 @@
1
+ require 'net/http'
2
+ require 'securerandom'
3
+
4
+ require_relative 'core/account'
5
+
6
+ module ThreeSixty
7
+ class Account < ThreeSixty::Core::Account
8
+
9
+ attr_reader :report_generating_backoff, :logger
10
+
11
+ def initialize(client, opts = {})
12
+ opts = default_options.update(opts)
13
+
14
+ @report_generating_backoff = opts[:report_generating_backoff]
15
+ @logger = opts[:logger] || client.logger
16
+
17
+ super(client)
18
+ end
19
+
20
+ def download_campaign_ids
21
+ get_campaign_id_list["campaignIdList"]
22
+ end
23
+
24
+ def download_campaigns(campaign_ids)
25
+ file_id = get_all_objects(campaign_ids)['fileId']
26
+
27
+ retry_number = 0
28
+ file_state = get_file_state(file_id)
29
+ while report_generating?(file_state["isGenerated"])
30
+ begin
31
+ @logger.debug "Waiting for report #{file_id} on retry number #{retry_number}"
32
+ sleep report_generating_backoff.call(retry_number)
33
+ retry_number += 1
34
+ file_state = get_file_state(file_id)
35
+ rescue TypeError => e
36
+ e.message = "Waiting too long to generate the report #{file_id}" # Re-raise with a better error message
37
+ raise e
38
+ end
39
+ end
40
+
41
+ @logger.info "Found file #{file_state["filePath"]} with size #{"%.2f" % (file_state["fileSize"] / 2**20)} MB"
42
+ download_filestream(file_state["filePath"]) { |chunk| yield chunk }
43
+ @logger.info "Finished downloading file #{file_state["filePath"]}"
44
+ end
45
+
46
+ def download_campaigns_to_file(campaign_ids, opts = {})
47
+ download_dir = opts[:download_dir] || "/tmp"
48
+ file = opts[:filename] || File.join(download_dir, "306_campaigns_" << SecureRandom.uuid << ".csv")
49
+
50
+ filemode = opts[:encoding].nil? ? 'w' : "w:#{opts[:encoding]}"
51
+
52
+ @logger.debug "Creating file #{file} with filemode #{filemode}"
53
+ File.open(file, filemode) do |fs|
54
+ download_campaigns(campaign_ids) do |content|
55
+ fs.write content
56
+ end
57
+ end
58
+
59
+ @logger.info "Finished downloading file to #{file}"
60
+ file
61
+ end
62
+
63
+ def exclude_ip_list
64
+ get_exclude_ip["excludeIpList"]
65
+ end
66
+
67
+
68
+ private
69
+
70
+ def report_generating?(status)
71
+ [ 'pending', 'process'].include?(status)
72
+ end
73
+
74
+ def download_filestream(file)
75
+ uri = URI(file)
76
+ Net::HTTP.start(uri.host, uri.port) do |http|
77
+ http.request_get(file) do |response|
78
+ response.read_body { |chunk| yield chunk }
79
+ end
80
+ end
81
+ end
82
+
83
+ def default_options
84
+ {
85
+ report_generating_backoff: lambda { |retry_number| retry_number < 5 ? 2**retry_number : nil }
86
+ }
87
+ end
88
+
89
+ end
90
+ end
@@ -0,0 +1,14 @@
1
+ require_relative 'core/campaign'
2
+ require_relative 'account'
3
+
4
+ module ThreeSixty
5
+ class Campaign < ThreeSixty::Core::Campaign
6
+
7
+ def download_all_campaigns
8
+ account = ThreeSixty::Account.new(@client)
9
+ campaign_ids = account.download_campaign_ids
10
+ get_info_by_id_list(campaign_ids)["campaignList"]
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,105 @@
1
+ require 'openssl'
2
+ require 'digest'
3
+ require 'base64'
4
+ require 'json'
5
+
6
+ require_relative 'core/account'
7
+
8
+ module ThreeSixty
9
+ class Client
10
+
11
+ attr_reader :api_key, :endpoint, :version, :logger
12
+ attr_reader :access_token, :session_token
13
+
14
+ def initialize(api_key, api_secret, opts = {})
15
+ opts = default_options.update(opts)
16
+
17
+ @api_key = api_key
18
+ @api_secret = api_secret
19
+ @endpoint = opts[:endpoint]
20
+ @version = opts[:version]
21
+ @format = opts[:format]
22
+ @logger = opts[:logger]
23
+ end
24
+
25
+ def authenticate!(username, password)
26
+ account = ThreeSixty::Core::Account.new(self)
27
+ tokens = account.client_login(username, generate_passwd(password))
28
+ @access_token = tokens['accessToken']
29
+ @session_token = tokens['sessionToken']
30
+ end
31
+
32
+ def request(resource_url, params = {})
33
+ params[:format] = @format
34
+ uri = URI(generate_url(resource_url, params))
35
+ @logger.debug "Resquest url #{uri.to_s}"
36
+ req = Net::HTTP::Post.new(uri, headers = generate_headers)
37
+ @logger.debug "Sending headers #{headers}"
38
+ res = Net::HTTP.start(uri.host) do |http|
39
+ http.request(req)
40
+ end
41
+ process_response(res)
42
+ end
43
+
44
+ private
45
+
46
+ def default_options
47
+ {
48
+ endpoint: ThreeSixty.configuration[:endpoint],
49
+ version: ThreeSixty.configuration[:version],
50
+ format: ThreeSixty.configuration[:format],
51
+ logger: ThreeSixty.configuration[:logger]
52
+ }
53
+ end
54
+
55
+ def serveToken
56
+ DateTime.now.strftime("%Y%m%d%H%M%S%L")
57
+ end
58
+
59
+ def process_response(res)
60
+ case res.content_type
61
+ when 'application/json'
62
+ process_json_response(res.body)
63
+ else
64
+ # raise "Unable to process response of type #{res.content_type}"
65
+ res.body
66
+ end
67
+ end
68
+
69
+ def process_json_response(content)
70
+ JSON.parse(content)
71
+ end
72
+
73
+ def generate_headers
74
+ headers = { 'serveToken' => serveToken, 'apiKey' => @api_key }
75
+ headers['accessToken'] = @access_token unless @access_token.nil?
76
+ headers['sessionToken'] = @session_token unless @session_token.nil?
77
+ headers
78
+ end
79
+
80
+ def generate_passwd(password)
81
+ encrypt(password).unpack("H2"*32).join
82
+ end
83
+
84
+ def encrypt(password)
85
+ cipher = OpenSSL::Cipher::AES128.new(:CBC)
86
+ cipher.encrypt
87
+ cipher.key, cipher.iv = generate_key_and_vector(@api_secret)
88
+ cipher.update(Digest::MD5.hexdigest(password)) + cipher.final
89
+ end
90
+
91
+ def generate_key_and_vector(secret)
92
+ secret.scan(/.{16}/m).map { |s| Base64.decode64(Base64.encode64(s)) }
93
+ end
94
+
95
+ def generate_url(resource_url, query_params = {})
96
+ endpoint = @endpoint + (resource_url.split("/")[1] == 'clientLogin' ? '/' : '/' << version) # client login doesn't contain version in the url
97
+ [endpoint, resource_url].join('/') << '?' << querystring(query_params)
98
+ end
99
+
100
+ def querystring(query_params)
101
+ query_params.map { |key, value| key.to_s << '=' << URI.escape(value.to_s) }.join('&')
102
+ end
103
+
104
+ end
105
+ end
@@ -0,0 +1,21 @@
1
+ require 'logger'
2
+
3
+ module ThreeSixty
4
+
5
+ DEFAULT_ENDPOINT = "https://api.e.360.cn"
6
+ DEFAULT_VERSION = "1.6"
7
+ DEFAULT_FORMAT = 'json'
8
+
9
+ @@configuration = {}
10
+
11
+ def self.configure(opts = {})
12
+ @@configuration[:endpoint] = opts[:endpoint] || DEFAULT_ENDPOINT
13
+ @@configuration[:version] = opts[:version] || DEFAULT_VERSION
14
+ @@configuration[:format] = opts[:format] || DEFAULT_FORMAT
15
+ @@configuration[:logger] = opts[:logger] || Logger.new(nil)
16
+ end
17
+
18
+ def self.configuration
19
+ @@configuration
20
+ end
21
+ end
@@ -0,0 +1,43 @@
1
+ module ThreeSixty
2
+ module Core
3
+ class Account
4
+
5
+ SERVICE_URL = 'account'
6
+
7
+ def initialize(client)
8
+ @client = client
9
+ end
10
+
11
+ def client_login(username, password)
12
+ @client.request(resource_url("clientLogin"), username: username, passwd: password)
13
+ end
14
+
15
+ def get_campaign_id_list
16
+ @client.request(resource_url("getCampaignIdList"))
17
+ end
18
+
19
+ def get_info
20
+ @client.request(resource_url("getInfo"))
21
+ end
22
+
23
+ def get_all_objects(campaign_ids)
24
+ @client.request(resource_url("getAllObjects"), idList: campaign_ids)
25
+ end
26
+
27
+ def get_file_state(file_id)
28
+ @client.request(resource_url("getFileState"), fileId: file_id)
29
+ end
30
+
31
+ def get_exclude_ip
32
+ @client.request(resource_url("getExcludeIp"))
33
+ end
34
+
35
+ private
36
+
37
+ def resource_url(service)
38
+ [SERVICE_URL, service].join("/")
39
+ end
40
+
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,27 @@
1
+ module ThreeSixty
2
+ module Core
3
+ class Campaign
4
+
5
+ SERVICE_URL = 'campaign'
6
+
7
+ def initialize(client)
8
+ @client = client
9
+ end
10
+
11
+ def get_info_by_id(campaign_id)
12
+ @client.request(resource_url("getInfoById"), id: campaign_id)
13
+ end
14
+
15
+ def get_info_by_id_list(campaign_ids)
16
+ @client.request(resource_url("getInfoByIdList"), idList: campaign_ids)
17
+ end
18
+
19
+ private
20
+
21
+ def resource_url(service)
22
+ [SERVICE_URL, service].join("/")
23
+ end
24
+
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,40 @@
1
+ module ThreeSixty
2
+ module Core
3
+ class Creative
4
+
5
+ SERVICE_URL = 'creative'
6
+
7
+ def initialize(client)
8
+ @client = client
9
+ end
10
+
11
+ def get_id_list_by_group_id(ad_group_id, opts = {})
12
+ opts.merge!(groupId: ad_group_id)
13
+ @client.request(resource_url("getIdListByGroupId"), opts)
14
+ end
15
+
16
+ def get_info_by_id(creative_id)
17
+ @client.request(resource_url("getInfoById"), id: creative_id)
18
+ end
19
+
20
+ def get_info_by_id_list(creative_ids)
21
+ @client.request(resource_url("getInfoByIdList"), idList: creative_ids)
22
+ end
23
+
24
+ def get_status_by_id_list(creative_ids)
25
+ @client.request(resource_url("getStatusByIdList"), idList: creative_ids)
26
+ end
27
+
28
+ def get_changed_id_list(from_time)
29
+ @client.request(resource_url("getChangedIdList"), fromTime: from_time)
30
+ end
31
+
32
+ private
33
+
34
+ def resource_url(service)
35
+ [SERVICE_URL, service].join("/")
36
+ end
37
+
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,32 @@
1
+ module ThreeSixty
2
+ module Core
3
+ class Group
4
+
5
+ SERVICE_URL = 'group'
6
+
7
+ def initialize(client)
8
+ @client = client
9
+ end
10
+
11
+ def get_id_list_by_campaign_id(campaign_id, opts = {})
12
+ opts.merge!(campaignId: campaign_id)
13
+ @client.request(resource_url("getIdListByCampaignId"), opts)
14
+ end
15
+
16
+ def get_info_by_id(adgroup_id)
17
+ @client.request(resource_url("getInfoById"), id: adgroup_id)
18
+ end
19
+
20
+ def get_info_by_id_list(adgroup_ids)
21
+ @client.request(resource_url("getInfoByIdList"), idList: adgroup_ids)
22
+ end
23
+
24
+ private
25
+
26
+ def resource_url(service)
27
+ [SERVICE_URL, service].join("/")
28
+ end
29
+
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,40 @@
1
+ module ThreeSixty
2
+ module Core
3
+ class Keyword
4
+
5
+ SERVICE_URL = 'keyword'
6
+
7
+ def initialize(client)
8
+ @client = client
9
+ end
10
+
11
+ def get_id_list_by_group_id(ad_group_id, opts = {})
12
+ opts.merge!(groupId: ad_group_id)
13
+ @client.request(resource_url("getIdListByGroupId"), opts)
14
+ end
15
+
16
+ def get_info_by_id(keyword_id)
17
+ @client.request(resource_url("getInfoById"), id: keyword_id)
18
+ end
19
+
20
+ def get_status_by_id_list(keyword_ids)
21
+ @client.request(resource_url("getStatusByIdList"), idList: keyword_ids)
22
+ end
23
+
24
+ def get_info_by_id_list(keyword_ids)
25
+ @client.request(resource_url("getInfoByIdList"), idList: keyword_ids)
26
+ end
27
+
28
+ def get_changed_id_list(from_time)
29
+ @client.request(resource_url("getChangedIdList"), fromTime: from_time)
30
+ end
31
+
32
+ private
33
+
34
+ def resource_url(service)
35
+ [SERVICE_URL, service].join("/")
36
+ end
37
+
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,49 @@
1
+ module ThreeSixty
2
+ module Core
3
+ class Report
4
+
5
+ SERVICE_URL = 'report'
6
+
7
+ def initialize(client)
8
+ @client = client
9
+ end
10
+
11
+ def keyword_count(start_date, level, opts = {})
12
+ opts.merge!({startDate: start_date, level: level})
13
+ @client.request(resource_url("keywordCount"), opts)
14
+ end
15
+
16
+ def keyword(start_date, level, opts = {})
17
+ opts.merge!({startDate: start_date, level: level})
18
+ @client.request(resource_url("keyword"), opts)
19
+ end
20
+
21
+ def creative_count(start_date, level, opts = {})
22
+ opts.merge!({startDate: start_date, level: level})
23
+ @client.request(resource_url("creativeCount"), opts)
24
+ end
25
+
26
+ def creative(start_date, level, opts = {})
27
+ opts.merge!({startDate: start_date, level: level})
28
+ @client.request(resource_url("creative"), opts)
29
+ end
30
+
31
+ def region_count(start_date, level, opts = {})
32
+ opts.merge!({startDate: start_date, level: level})
33
+ @client.request(resource_url("regionCount"), opts)
34
+ end
35
+
36
+ def region(start_date, level, opts = {})
37
+ opts.merge!({startDate: start_date, level: level})
38
+ @client.request(resource_url("region"), opts)
39
+ end
40
+
41
+ private
42
+
43
+ def resource_url(service)
44
+ [SERVICE_URL, service].join("/")
45
+ end
46
+
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,27 @@
1
+ require_relative 'core/creative'
2
+
3
+ module ThreeSixty
4
+ class Creative < ThreeSixty::Core::Creative
5
+
6
+ def download_ad_group_creative_ids(ad_group_id, opts = {})
7
+ get_id_list_by_group_id(ad_group_id, opts)["creativeIdList"]
8
+ end
9
+
10
+ def download_ad_group_all_creatives(ad_group_id, opts = {})
11
+ creative_ids = download_ad_group_creative_ids(ad_group_id, opts = {})
12
+ return [] if creative_ids.empty?
13
+ get_info_by_id_list(creative_ids)["creativeList"]
14
+ end
15
+
16
+ def download_ad_group_all_creative_statuses(ad_group_id, opts = {})
17
+ creative_ids = download_ad_group_creative_ids(ad_group_id, opts = {})
18
+ return [] if creative_ids.empty?
19
+ get_status_by_id_list(creative_ids)["creativeList"]
20
+ end
21
+
22
+ def download_changed_creative_ids(from_time)
23
+ get_changed_id_list(from_time)["creativeIdList"]
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,17 @@
1
+ require_relative 'core/group'
2
+ require_relative 'account'
3
+
4
+ module ThreeSixty
5
+ class Group < ThreeSixty::Core::Group
6
+
7
+ def download_campaign_ad_group_ids(campaign_id, opts = {})
8
+ get_id_list_by_campaign_id(campaign_id, opts)["groupIdList"]
9
+ end
10
+
11
+ def download_campaign_all_ad_groups(campaign_id, opts = {})
12
+ ad_group_ids = download_campaign_ad_group_ids(campaign_id)
13
+ get_info_by_id_list(ad_group_ids)["groupList"]
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,27 @@
1
+ require_relative 'core/keyword'
2
+
3
+ module ThreeSixty
4
+ class Keyword < ThreeSixty::Core::Keyword
5
+
6
+ def download_ad_group_keyword_ids(ad_group_id, opts = {})
7
+ get_id_list_by_group_id(ad_group_id, opts)["keywordIdList"]
8
+ end
9
+
10
+ def download_ad_group_all_keywords(ad_group_id, opts = {})
11
+ keyword_ids = download_ad_group_keyword_ids(ad_group_id)
12
+ return [] if keyword_ids.empty?
13
+ get_info_by_id_list(keyword_ids)["keywordList"]
14
+ end
15
+
16
+ def download_ad_group_all_keyword_statuses(ad_group_id, opts = {})
17
+ keyword_ids = download_ad_group_keyword_ids(ad_group_id)
18
+ return [] if keyword_ids.empty?
19
+ get_status_by_id_list(keyword_ids)["keywordList"]
20
+ end
21
+
22
+ def download_changed_keyword_ids(from_time)
23
+ get_changed_id_list(from_time)["keywordIdList"]
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,9 @@
1
+ module ThreeSixty
2
+ module Level
3
+
4
+ ACCOUNT = :account
5
+ PLAN = :plan
6
+ GROUP = :group
7
+
8
+ end
9
+ end
@@ -0,0 +1,7 @@
1
+ require_relative 'core/report'
2
+
3
+ module ThreeSixty
4
+ class Report < ThreeSixty::Core::Report
5
+
6
+ end
7
+ end
@@ -0,0 +1,3 @@
1
+ module ThreeSixty
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,13 @@
1
+ require_relative 'three-sixty/level'
2
+ require_relative 'three-sixty/configuration'
3
+ require_relative 'three-sixty/campaign'
4
+ require_relative 'three-sixty/account'
5
+ require_relative 'three-sixty/client'
6
+ require_relative 'three-sixty/group'
7
+ require_relative 'three-sixty/creative'
8
+ require_relative 'three-sixty/keyword'
9
+ require_relative 'three-sixty/report'
10
+
11
+ module ThreeSixty
12
+ ThreeSixty.configure
13
+ end
@@ -0,0 +1,17 @@
1
+ require File.join(File.expand_path('../lib', __FILE__), 'three-sixty', 'version')
2
+
3
+ Gem::Specification.new do |gem|
4
+ gem.name = "three-sixty"
5
+ gem.version = ThreeSixty::VERSION
6
+ gem.authors = ["Daniel Padden"]
7
+ gem.email = ["daniel.padden@forward3d.com"]
8
+ gem.description = %q{Connecting to the 360 api}
9
+ gem.summary = %q{Gathering data from the 360 api}
10
+ gem.homepage = ""
11
+
12
+ gem.files = `git ls-files -z`.split("\x0")
13
+ gem.executables = gem.files.grep(%r{^bin/}) { |f| File.basename(f) }
14
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
15
+ gem.require_paths = ["lib"]
16
+
17
+ end
metadata ADDED
@@ -0,0 +1,63 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: three-sixty
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Daniel Padden
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-06-17 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Connecting to the 360 api
14
+ email:
15
+ - daniel.padden@forward3d.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - .gitignore
21
+ - README.md
22
+ - lib/three-sixty.rb
23
+ - lib/three-sixty/account.rb
24
+ - lib/three-sixty/campaign.rb
25
+ - lib/three-sixty/client.rb
26
+ - lib/three-sixty/configuration.rb
27
+ - lib/three-sixty/core/account.rb
28
+ - lib/three-sixty/core/campaign.rb
29
+ - lib/three-sixty/core/creative.rb
30
+ - lib/three-sixty/core/group.rb
31
+ - lib/three-sixty/core/keyword.rb
32
+ - lib/three-sixty/core/report.rb
33
+ - lib/three-sixty/creative.rb
34
+ - lib/three-sixty/group.rb
35
+ - lib/three-sixty/keyword.rb
36
+ - lib/three-sixty/level.rb
37
+ - lib/three-sixty/report.rb
38
+ - lib/three-sixty/version.rb
39
+ - three-sixty.gemspec
40
+ homepage: ''
41
+ licenses: []
42
+ metadata: {}
43
+ post_install_message:
44
+ rdoc_options: []
45
+ require_paths:
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - '>='
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - '>='
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ requirements: []
58
+ rubyforge_project:
59
+ rubygems_version: 2.2.1
60
+ signing_key:
61
+ specification_version: 4
62
+ summary: Gathering data from the 360 api
63
+ test_files: []