twoffein-client 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +7 -0
- data/.rspec +2 -0
- data/.rvmrc +1 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +207 -0
- data/README.md.erb +52 -0
- data/Rakefile +67 -0
- data/VERSION +1 -0
- data/bin/twoffein-client +84 -0
- data/features/step_definitions/twoffein-client_steps.rb +1 -0
- data/features/support/env.rb +16 -0
- data/features/twoffein-client.feature +13 -0
- data/fixtures/vcr_cassettes/cookie.yml +234 -0
- data/fixtures/vcr_cassettes/drink.yml +60 -0
- data/fixtures/vcr_cassettes/drinks.yml +461 -0
- data/fixtures/vcr_cassettes/profile.yml +69 -0
- data/fixtures/vcr_cassettes/tweet.yml +103 -0
- data/lib/twoffein-client.rb +12 -0
- data/lib/twoffein-client/constants.rb +9 -0
- data/lib/twoffein-client/cookie.rb +23 -0
- data/lib/twoffein-client/credentials.rb +5 -0
- data/lib/twoffein-client/drink.rb +43 -0
- data/lib/twoffein-client/drinks.rb +96 -0
- data/lib/twoffein-client/exceptions.rb +15 -0
- data/lib/twoffein-client/http.rb +96 -0
- data/lib/twoffein-client/profile.rb +61 -0
- data/lib/twoffein-client/tweet.rb +31 -0
- data/lib/twoffein-client/util.rb +7 -0
- data/lib/twoffein-client/version.rb +7 -0
- data/spec/constants_spec.rb +11 -0
- data/spec/cookie_spec.rb +25 -0
- data/spec/drink_spec.rb +49 -0
- data/spec/drinks_spec.rb +154 -0
- data/spec/exceptions_spec.rb +14 -0
- data/spec/http_spec.rb +94 -0
- data/spec/profile_spec.rb +58 -0
- data/spec/spec_helper.rb +25 -0
- data/spec/tweet_spec.rb +41 -0
- data/spec/util_spec.rb +20 -0
- data/twoffein-client.gemspec +28 -0
- metadata +245 -0
@@ -0,0 +1,69 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: get
|
5
|
+
uri: http://twoffein.com/api/get/profile/?api_key=1CBA7V9XFKHJ&screen_name=DSIW
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
Accept:
|
11
|
+
- ! '*/*'
|
12
|
+
User-Agent:
|
13
|
+
- Ruby
|
14
|
+
response:
|
15
|
+
status:
|
16
|
+
code: 200
|
17
|
+
message: OK
|
18
|
+
headers:
|
19
|
+
Date:
|
20
|
+
- Wed, 22 Aug 2012 14:26:16 GMT
|
21
|
+
Server:
|
22
|
+
- Apache/2.2.15 (CentOS)
|
23
|
+
X-Powered-By:
|
24
|
+
- PHP/5.4.4
|
25
|
+
Connection:
|
26
|
+
- close
|
27
|
+
Transfer-Encoding:
|
28
|
+
- chunked
|
29
|
+
Content-Type:
|
30
|
+
- application/json; charset=utf-8
|
31
|
+
body:
|
32
|
+
encoding: US-ASCII
|
33
|
+
string: ! '{"quest":"Blitzlicht","drink":"Club-Mate","rank":74,"rank_title":"Kaffeek\u00e4nnchen","drunken":"15","bluttwoffeinkonzentration":"1%","first_login":"1341747375","screen_name":"DSIW"}'
|
34
|
+
http_version:
|
35
|
+
recorded_at: Wed, 22 Aug 2012 14:26:16 GMT
|
36
|
+
- request:
|
37
|
+
method: get
|
38
|
+
uri: http://twoffein.com/api/get/profile/?api_key=1CBA7V9XFKHJ&profile=BakeRolls&screen_name=DSIW
|
39
|
+
body:
|
40
|
+
encoding: US-ASCII
|
41
|
+
string: ''
|
42
|
+
headers:
|
43
|
+
Accept:
|
44
|
+
- ! '*/*'
|
45
|
+
User-Agent:
|
46
|
+
- Ruby
|
47
|
+
response:
|
48
|
+
status:
|
49
|
+
code: 200
|
50
|
+
message: OK
|
51
|
+
headers:
|
52
|
+
Date:
|
53
|
+
- Wed, 22 Aug 2012 14:27:12 GMT
|
54
|
+
Server:
|
55
|
+
- Apache/2.2.15 (CentOS)
|
56
|
+
X-Powered-By:
|
57
|
+
- PHP/5.4.4
|
58
|
+
Connection:
|
59
|
+
- close
|
60
|
+
Transfer-Encoding:
|
61
|
+
- chunked
|
62
|
+
Content-Type:
|
63
|
+
- application/json; charset=utf-8
|
64
|
+
body:
|
65
|
+
encoding: US-ASCII
|
66
|
+
string: ! '{"quest":"Blitzlicht","drink":"Club-Mate","rank":74,"rank_title":"Kaffeek\u00e4nnchen","drunken":"15","bluttwoffeinkonzentration":"1%","first_login":"1341747375","screen_name":"DSIW"}'
|
67
|
+
http_version:
|
68
|
+
recorded_at: Wed, 22 Aug 2012 14:27:12 GMT
|
69
|
+
recorded_with: VCR 2.2.4
|
@@ -0,0 +1,103 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: get
|
5
|
+
uri: http://twoffein.com/api/post/tweet/?api_key=1CBA7V9XFKHJ&drink=clubmate&screen_name=DSIW&target_screen_name=UserDoesNotExistTest
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
Accept:
|
11
|
+
- ! '*/*'
|
12
|
+
User-Agent:
|
13
|
+
- Ruby
|
14
|
+
response:
|
15
|
+
status:
|
16
|
+
code: 200
|
17
|
+
message: OK
|
18
|
+
headers:
|
19
|
+
Date:
|
20
|
+
- Wed, 22 Aug 2012 13:46:08 GMT
|
21
|
+
Server:
|
22
|
+
- Apache/2.2.15 (CentOS)
|
23
|
+
X-Powered-By:
|
24
|
+
- PHP/5.4.4
|
25
|
+
Connection:
|
26
|
+
- close
|
27
|
+
Transfer-Encoding:
|
28
|
+
- chunked
|
29
|
+
Content-Type:
|
30
|
+
- application/json; charset=utf-8
|
31
|
+
body:
|
32
|
+
encoding: US-ASCII
|
33
|
+
string: ! '{"code":"luna","info":"Youre Tweet has been tweeted. Thanks.","tweets":1}'
|
34
|
+
http_version:
|
35
|
+
recorded_at: Wed, 22 Aug 2012 13:46:08 GMT
|
36
|
+
- request:
|
37
|
+
method: get
|
38
|
+
uri: http://twoffein.com/api/post/tweet/?api_key=1CBA7V9XFKHJ&drink=clubmate&screen_name=DSIW&target_screen_name=BakeRolls
|
39
|
+
body:
|
40
|
+
encoding: US-ASCII
|
41
|
+
string: ''
|
42
|
+
headers:
|
43
|
+
Accept:
|
44
|
+
- ! '*/*'
|
45
|
+
User-Agent:
|
46
|
+
- Ruby
|
47
|
+
response:
|
48
|
+
status:
|
49
|
+
code: 200
|
50
|
+
message: OK
|
51
|
+
headers:
|
52
|
+
Date:
|
53
|
+
- Wed, 22 Aug 2012 13:51:28 GMT
|
54
|
+
Server:
|
55
|
+
- Apache/2.2.15 (CentOS)
|
56
|
+
X-Powered-By:
|
57
|
+
- PHP/5.4.4
|
58
|
+
Connection:
|
59
|
+
- close
|
60
|
+
Transfer-Encoding:
|
61
|
+
- chunked
|
62
|
+
Content-Type:
|
63
|
+
- application/json; charset=utf-8
|
64
|
+
body:
|
65
|
+
encoding: US-ASCII
|
66
|
+
string: ! '{"code":"pinkiepie","error":"You cant drink more than one drink in
|
67
|
+
15 minutes.","sleep":9}'
|
68
|
+
http_version:
|
69
|
+
recorded_at: Wed, 22 Aug 2012 13:51:28 GMT
|
70
|
+
- request:
|
71
|
+
method: get
|
72
|
+
uri: http://twoffein.com/api/post/tweet/?api_key=1CBA7V9XFKHJ&drink=clubmate&screen_name=DSIW
|
73
|
+
body:
|
74
|
+
encoding: US-ASCII
|
75
|
+
string: ''
|
76
|
+
headers:
|
77
|
+
Accept:
|
78
|
+
- ! '*/*'
|
79
|
+
User-Agent:
|
80
|
+
- Ruby
|
81
|
+
response:
|
82
|
+
status:
|
83
|
+
code: 200
|
84
|
+
message: OK
|
85
|
+
headers:
|
86
|
+
Date:
|
87
|
+
- Wed, 22 Aug 2012 14:07:21 GMT
|
88
|
+
Server:
|
89
|
+
- Apache/2.2.15 (CentOS)
|
90
|
+
X-Powered-By:
|
91
|
+
- PHP/5.4.4
|
92
|
+
Connection:
|
93
|
+
- close
|
94
|
+
Transfer-Encoding:
|
95
|
+
- chunked
|
96
|
+
Content-Type:
|
97
|
+
- application/json; charset=utf-8
|
98
|
+
body:
|
99
|
+
encoding: US-ASCII
|
100
|
+
string: ! '{"code":"luna","info":"Youre Tweet has been tweeted. Thanks.","tweets":2}'
|
101
|
+
http_version:
|
102
|
+
recorded_at: Wed, 22 Aug 2012 14:07:22 GMT
|
103
|
+
recorded_with: VCR 2.2.4
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require "twoffein-client/version"
|
2
|
+
require "twoffein-client/credentials"
|
3
|
+
|
4
|
+
require "twoffein-client/http"
|
5
|
+
require "twoffein-client/util"
|
6
|
+
require "twoffein-client/exceptions"
|
7
|
+
|
8
|
+
require "twoffein-client/drink"
|
9
|
+
require "twoffein-client/drinks"
|
10
|
+
require "twoffein-client/profile"
|
11
|
+
require "twoffein-client/tweet"
|
12
|
+
require "twoffein-client/cookie"
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require_relative "exceptions"
|
2
|
+
require_relative "http"
|
3
|
+
|
4
|
+
module Twoffein
|
5
|
+
class Cookie
|
6
|
+
attr_accessor :target_screen_name
|
7
|
+
|
8
|
+
def initialize target_screen_name
|
9
|
+
@target_screen_name = target_screen_name
|
10
|
+
end
|
11
|
+
|
12
|
+
def post
|
13
|
+
info = HTTP.post("cookie", :target_screen_name => @target_screen_name)
|
14
|
+
raise Server::Error.new(info[:code], info[:error]) if info.has_key? :error
|
15
|
+
info
|
16
|
+
end
|
17
|
+
alias send post
|
18
|
+
|
19
|
+
def to_s
|
20
|
+
"Ich gebe #@target_screen_name einen Keks!"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Twoffein
|
4
|
+
class Drink
|
5
|
+
attr_reader :name, :key, :brand
|
6
|
+
|
7
|
+
def initialize(name, key=nil, brand=false)
|
8
|
+
@name = name.to_s
|
9
|
+
@key = (key ? key : key_of(name)).to_sym
|
10
|
+
@brand = !!brand
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_s
|
14
|
+
"#@name (#@key)"
|
15
|
+
end
|
16
|
+
|
17
|
+
def ==(drink)
|
18
|
+
name == drink.name &&
|
19
|
+
key == drink.key &&
|
20
|
+
brand == drink.brand
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def key_of(name)
|
26
|
+
key = name.dup
|
27
|
+
changes = {
|
28
|
+
'ü' => 'ue',
|
29
|
+
'Ü' => 'Ue',
|
30
|
+
'ö' => 'oe',
|
31
|
+
'Ö' => 'Oe',
|
32
|
+
'ä' => 'ae',
|
33
|
+
'Ä' => 'Ae',
|
34
|
+
}
|
35
|
+
changes.each { |orig, trans| key.gsub!(orig, trans) }
|
36
|
+
|
37
|
+
removeables = [/\W/]
|
38
|
+
removeables.each { |r| key.gsub!(r, '') }
|
39
|
+
|
40
|
+
key.downcase.to_sym
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require_relative "drink"
|
2
|
+
require_relative "http"
|
3
|
+
|
4
|
+
module Twoffein
|
5
|
+
class Drinks
|
6
|
+
attr_reader :all
|
7
|
+
|
8
|
+
def initialize(subset=nil)
|
9
|
+
unless subset
|
10
|
+
@all ||= Drinks.get
|
11
|
+
return @all.map! do |drink|
|
12
|
+
name = drink[:drink]
|
13
|
+
key = drink[:key].to_sym
|
14
|
+
brand = (drink[:brand] == "0" ? false : true)
|
15
|
+
Drink.new(name, key, brand)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
if subset.is_a? Array
|
20
|
+
@all = subset
|
21
|
+
else
|
22
|
+
@all = [subset]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class << self
|
27
|
+
def all; new.all; end
|
28
|
+
def names; all.map { |d| d.name }; end
|
29
|
+
def keys; all.map { |d| d.key }; end
|
30
|
+
def brands; all.map { |d| d.brand }; end
|
31
|
+
|
32
|
+
def get
|
33
|
+
HTTP.get("drinks", :drink => :all)
|
34
|
+
end
|
35
|
+
|
36
|
+
def find(key)
|
37
|
+
key = key.to_sym
|
38
|
+
all.each { |d| return d if d.key == key }
|
39
|
+
nil
|
40
|
+
end
|
41
|
+
alias :[] :find
|
42
|
+
|
43
|
+
def search search
|
44
|
+
search = /#{search}/i unless search.is_a? Regexp
|
45
|
+
selected = all.select { |drink| drink.to_s =~ search }
|
46
|
+
new(selected)
|
47
|
+
#all.grep(search) # TODO see :===
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def add(drink)
|
52
|
+
raise ArgumentError, "drink has to be of type Drink" unless drink.is_a? Drink
|
53
|
+
@all << drink
|
54
|
+
end
|
55
|
+
|
56
|
+
def to_s
|
57
|
+
name_max_length = @all.map { |d| d.name.length }.max
|
58
|
+
pre_key = " ("
|
59
|
+
post_key = ")"
|
60
|
+
|
61
|
+
# Calc max length of line
|
62
|
+
length = lambda do |drink|
|
63
|
+
key = drink.key
|
64
|
+
[pre_key, key, post_key].map { |cont|
|
65
|
+
cont.length
|
66
|
+
}.push(name_max_length).reduce(&:+)
|
67
|
+
end
|
68
|
+
max_line = @all.map { |d| length.call(d) }.max
|
69
|
+
|
70
|
+
# Header
|
71
|
+
header = ["Drink".ljust(name_max_length), pre_key, "key", post_key].join
|
72
|
+
line = "-"*max_line
|
73
|
+
|
74
|
+
# Drinks
|
75
|
+
drinks = @all.map { |d|
|
76
|
+
name = d.name
|
77
|
+
key = d.key
|
78
|
+
[name.ljust(name_max_length), pre_key, key, post_key].join
|
79
|
+
}.join("\n")
|
80
|
+
|
81
|
+
# All
|
82
|
+
[header, line, drinks].join("\n")
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
# Delegates method call to @all if it has defined this method
|
88
|
+
def method_missing(method, *args, &block)
|
89
|
+
if @all.respond_to? method
|
90
|
+
@all.send(method, *args, &block)
|
91
|
+
else
|
92
|
+
super(method, *args, &block)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'net/http'
|
3
|
+
require 'uri'
|
4
|
+
#require 'rexml/document'
|
5
|
+
|
6
|
+
require_relative "util"
|
7
|
+
require_relative "constants"
|
8
|
+
|
9
|
+
module Twoffein
|
10
|
+
class HTTP
|
11
|
+
def self.create_url(path, params={})
|
12
|
+
query = URI.encode_www_form(params)
|
13
|
+
# TODO: Change API-URI: Add controller in MVC –> rest_client (https://github.com/adamwiggins/rest-client)
|
14
|
+
URI.join(BASE_URL, path+'/').to_s + '?' + query
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.fetch(uri_str, limit = 10)
|
18
|
+
# You should choose a better exception.
|
19
|
+
raise ArgumentError, 'too many HTTP redirects' if limit == 0
|
20
|
+
|
21
|
+
response = Net::HTTP.get_response(URI(uri_str))
|
22
|
+
|
23
|
+
case response
|
24
|
+
when Net::HTTPSuccess then
|
25
|
+
response
|
26
|
+
when Net::HTTPRedirection then
|
27
|
+
location = response['location']
|
28
|
+
#warn "redirected to #{location}"
|
29
|
+
fetch(location, limit - 1)
|
30
|
+
else
|
31
|
+
response.value
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.request(verb, path, params={})
|
36
|
+
Util.compact! params
|
37
|
+
case verb
|
38
|
+
when :get
|
39
|
+
res = fetch(create_url([verb, path].join('/'), PARAMS.merge(params)))
|
40
|
+
case content_type(res)
|
41
|
+
when /xml/
|
42
|
+
puts "XML isn't supported."
|
43
|
+
exit 1
|
44
|
+
#xml = REXML::Document.new(res.body)
|
45
|
+
when /json/
|
46
|
+
return JSON.parse(res.body, :symbolize_names => true)
|
47
|
+
end
|
48
|
+
when :post # TODO
|
49
|
+
res = fetch(create_url([:post, path].join('/'), PARAMS.merge(params)))
|
50
|
+
end
|
51
|
+
JSON.parse(res.body, :symbolize_names => true)
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.post_data(path, data, params={})
|
55
|
+
#self.request(:post, path, params)
|
56
|
+
uri = URI(path)
|
57
|
+
req = Net::HTTP::Post.new(uri.path)
|
58
|
+
req.set_form_data(data)
|
59
|
+
#req.body = multipart_data
|
60
|
+
#req.content_type = 'multipart/form-data'
|
61
|
+
|
62
|
+
res = Net::HTTP.start(uri.hostname, uri.port) do |http|
|
63
|
+
http.request(req)
|
64
|
+
end
|
65
|
+
|
66
|
+
case res
|
67
|
+
when Net::HTTPSuccess, Net::HTTPRedirection
|
68
|
+
# OK
|
69
|
+
else
|
70
|
+
raise res.value
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.get(path, params={})
|
75
|
+
request(:get, path, params)
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.post(path, params={})
|
79
|
+
request(:post, path, params)
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def self.content_type(res)
|
85
|
+
res["Content-Type"].split(';').first
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.xml?(res)
|
89
|
+
content_type(res) == "application/xml"
|
90
|
+
end
|
91
|
+
|
92
|
+
def self.json?(res)
|
93
|
+
content_type(res) == "application/json"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|