tweetwine 0.2.5 → 0.2.7
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.rdoc +30 -17
- data/README.rdoc +16 -17
- data/Rakefile +2 -0
- data/bin/tweetwine +3 -68
- data/example/example_helper.rb +31 -41
- data/example/fixtures/{statuses.json → home.json} +0 -0
- data/example/fixtures/mentions.json +1 -0
- data/example/fixtures/search.json +1 -0
- data/example/fixtures/update.json +1 -0
- data/example/fixtures/user.json +1 -0
- data/example/fixtures/users.json +1 -0
- data/example/search_statuses_example.rb +36 -0
- data/example/show_followers_example.rb +23 -0
- data/example/show_friends_example.rb +23 -0
- data/example/show_home_example.rb +51 -0
- data/example/show_mentions_example.rb +23 -0
- data/example/show_metadata_example.rb +54 -7
- data/example/show_user_example.rb +37 -0
- data/example/update_status_example.rb +65 -0
- data/lib/tweetwine/cli.rb +241 -0
- data/lib/tweetwine/client.rb +94 -57
- data/lib/tweetwine/io.rb +39 -28
- data/lib/tweetwine/meta.rb +1 -1
- data/lib/tweetwine/retrying_http.rb +93 -0
- data/lib/tweetwine/startup_config.rb +14 -15
- data/lib/tweetwine/url_shortener.rb +13 -8
- data/lib/tweetwine/util.rb +14 -0
- data/lib/tweetwine.rb +2 -1
- data/test/cli_test.rb +16 -0
- data/test/client_test.rb +275 -205
- data/test/fixtures/test_config.yaml +2 -1
- data/test/io_test.rb +89 -62
- data/test/retrying_http_test.rb +127 -0
- data/test/startup_config_test.rb +52 -27
- data/test/test_helper.rb +32 -17
- data/test/url_shortener_test.rb +18 -18
- data/test/util_test.rb +145 -47
- metadata +20 -7
- data/example/show_latest_statuses_example.rb +0 -45
- data/lib/tweetwine/rest_client_wrapper.rb +0 -37
- data/test/rest_client_wrapper_test.rb +0 -68
data/test/url_shortener_test.rb
CHANGED
@@ -5,14 +5,14 @@ module Tweetwine
|
|
5
5
|
class UrlShortenerTest < Test::Unit::TestCase
|
6
6
|
context "An UrlShortener instance" do
|
7
7
|
setup do
|
8
|
-
@
|
8
|
+
@http_client = mock()
|
9
9
|
end
|
10
10
|
|
11
11
|
context "upon initialization" do
|
12
12
|
should "raise exception if service URL is not given" do
|
13
13
|
assert_raise(ArgumentError) do
|
14
14
|
UrlShortener.new(
|
15
|
-
@
|
15
|
+
@http_client,
|
16
16
|
{
|
17
17
|
:service_url => nil,
|
18
18
|
:url_param_name => "url",
|
@@ -25,7 +25,7 @@ class UrlShortenerTest < Test::Unit::TestCase
|
|
25
25
|
should "raise exception if URL parameter name is not given" do
|
26
26
|
assert_raise(ArgumentError) do
|
27
27
|
UrlShortener.new(
|
28
|
-
@
|
28
|
+
@http_client,
|
29
29
|
{
|
30
30
|
:service_url => "http://shorten.it/create",
|
31
31
|
:url_param_name => nil,
|
@@ -38,7 +38,7 @@ class UrlShortenerTest < Test::Unit::TestCase
|
|
38
38
|
should "raise exception if XPath selector is not given" do
|
39
39
|
assert_raise(ArgumentError) do
|
40
40
|
UrlShortener.new(
|
41
|
-
@
|
41
|
+
@http_client,
|
42
42
|
{
|
43
43
|
:service_url => "http://shorten.it/create",
|
44
44
|
:url_param_name => "url",
|
@@ -50,7 +50,7 @@ class UrlShortenerTest < Test::Unit::TestCase
|
|
50
50
|
|
51
51
|
should "fallback to use GET method if not given explicitly" do
|
52
52
|
url_shortener = UrlShortener.new(
|
53
|
-
@
|
53
|
+
@http_client,
|
54
54
|
{
|
55
55
|
:service_url => "http://shorten.it/create",
|
56
56
|
:url_param_name => "url",
|
@@ -65,7 +65,7 @@ class UrlShortenerTest < Test::Unit::TestCase
|
|
65
65
|
context "configured for HTTP GET" do
|
66
66
|
should "use parameters as URL query parameters, with just the URL parameter" do
|
67
67
|
url_shortener = UrlShortener.new(
|
68
|
-
@
|
68
|
+
@http_client,
|
69
69
|
{
|
70
70
|
:method => "get",
|
71
71
|
:service_url => "http://shorten.it/create",
|
@@ -73,14 +73,14 @@ class UrlShortenerTest < Test::Unit::TestCase
|
|
73
73
|
:xpath_selector => "//input[@id='short_url']/@value"
|
74
74
|
}
|
75
75
|
)
|
76
|
-
@
|
76
|
+
@http_client.expects(:get) \
|
77
77
|
.with("http://shorten.it/create?url=http://www.ruby-doc.org/core/")
|
78
78
|
url_shortener.shorten("http://www.ruby-doc.org/core/")
|
79
79
|
end
|
80
80
|
|
81
81
|
should "use parameters as URL query parameters, with additional extra parameters" do
|
82
82
|
url_shortener = UrlShortener.new(
|
83
|
-
@
|
83
|
+
@http_client,
|
84
84
|
{
|
85
85
|
:method => "get",
|
86
86
|
:service_url => "http://shorten.it/create",
|
@@ -91,7 +91,7 @@ class UrlShortenerTest < Test::Unit::TestCase
|
|
91
91
|
:xpath_selector => "//input[@id='short_url']/@value"
|
92
92
|
}
|
93
93
|
)
|
94
|
-
@
|
94
|
+
@http_client.expects(:get) \
|
95
95
|
.with("http://shorten.it/create?token=xyz&url=http://www.ruby-doc.org/core/")
|
96
96
|
url_shortener.shorten("http://www.ruby-doc.org/core/")
|
97
97
|
end
|
@@ -100,7 +100,7 @@ class UrlShortenerTest < Test::Unit::TestCase
|
|
100
100
|
context "configured for HTTP POST" do
|
101
101
|
should "use parameters as payload, with just the URL parameter" do
|
102
102
|
url_shortener = UrlShortener.new(
|
103
|
-
@
|
103
|
+
@http_client,
|
104
104
|
{
|
105
105
|
:method => "post",
|
106
106
|
:service_url => "http://shorten.it/create",
|
@@ -108,14 +108,14 @@ class UrlShortenerTest < Test::Unit::TestCase
|
|
108
108
|
:xpath_selector => "//input[@id='short_url']/@value"
|
109
109
|
}
|
110
110
|
)
|
111
|
-
@
|
111
|
+
@http_client.expects(:post) \
|
112
112
|
.with("http://shorten.it/create", {:url => "http://www.ruby-doc.org/core/"})
|
113
113
|
url_shortener.shorten("http://www.ruby-doc.org/core/")
|
114
114
|
end
|
115
115
|
|
116
116
|
should "use parameters as payload, with additional extra parameters" do
|
117
117
|
url_shortener = UrlShortener.new(
|
118
|
-
@
|
118
|
+
@http_client,
|
119
119
|
{
|
120
120
|
:method => "post",
|
121
121
|
:service_url => "http://shorten.it/create",
|
@@ -126,7 +126,7 @@ class UrlShortenerTest < Test::Unit::TestCase
|
|
126
126
|
:xpath_selector => "//input[@id='short_url']/@value"
|
127
127
|
}
|
128
128
|
)
|
129
|
-
@
|
129
|
+
@http_client.expects(:post) \
|
130
130
|
.with("http://shorten.it/create", {
|
131
131
|
:token => "xyz",
|
132
132
|
:url => "http://www.ruby-doc.org/core/"
|
@@ -136,9 +136,9 @@ class UrlShortenerTest < Test::Unit::TestCase
|
|
136
136
|
end
|
137
137
|
|
138
138
|
context "in erroenous situations" do
|
139
|
-
should "raise
|
139
|
+
should "raise HttpError upon connection error" do
|
140
140
|
url_shortener = UrlShortener.new(
|
141
|
-
@
|
141
|
+
@http_client,
|
142
142
|
{
|
143
143
|
:method => "post",
|
144
144
|
:service_url => "http://shorten.it/create",
|
@@ -146,12 +146,12 @@ class UrlShortenerTest < Test::Unit::TestCase
|
|
146
146
|
:xpath_selector => "//input[@id='short_url']/@value"
|
147
147
|
}
|
148
148
|
)
|
149
|
-
@
|
149
|
+
@http_client.expects(:post) \
|
150
150
|
.with("http://shorten.it/create", {
|
151
151
|
:url => "http://www.ruby-doc.org/core/"
|
152
152
|
}) \
|
153
|
-
.raises(
|
154
|
-
assert_raise(
|
153
|
+
.raises(HttpError, "connection error")
|
154
|
+
assert_raise(HttpError) { url_shortener.shorten("http://www.ruby-doc.org/core/") }
|
155
155
|
end
|
156
156
|
end
|
157
157
|
end
|
data/test/util_test.rb
CHANGED
@@ -4,76 +4,104 @@ require "time"
|
|
4
4
|
module Tweetwine
|
5
5
|
|
6
6
|
class UtilTest < Test::Unit::TestCase
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
7
|
+
context "for humanizing time differences" do
|
8
|
+
should "use second granularity for time differences smaller than a minute" do
|
9
|
+
assert_equal [1, "sec"], Util.humanize_time_diff(Time.parse("2009-01-01 00:00:59").to_s, Time.parse("2009-01-01 00:01:00"))
|
10
|
+
assert_equal [0, "sec"], Util.humanize_time_diff(Time.parse("2009-01-01 01:00:00").to_s, Time.parse("2009-01-01 01:00:00"))
|
11
|
+
assert_equal [1, "sec"], Util.humanize_time_diff(Time.parse("2009-01-01 01:00:00").to_s, Time.parse("2009-01-01 01:00:01"))
|
12
|
+
assert_equal [59, "sec"], Util.humanize_time_diff(Time.parse("2009-01-01 01:00:00").to_s, Time.parse("2009-01-01 01:00:59"))
|
13
|
+
end
|
14
|
+
|
15
|
+
should "use minute granularity for time differences greater than a minute but smaller than an hour" do
|
16
|
+
assert_equal [59, "min"], Util.humanize_time_diff(Time.parse("2009-01-01 01:00").to_s, Time.parse("2009-01-01 01:59"))
|
17
|
+
assert_equal [59, "min"], Util.humanize_time_diff(Time.parse("2009-01-01 01:00:30").to_s, Time.parse("2009-01-01 01:59:00"))
|
18
|
+
assert_equal [57, "min"], Util.humanize_time_diff(Time.parse("2009-01-01 01:01:00").to_s, Time.parse("2009-01-01 01:58:00"))
|
19
|
+
assert_equal [56, "min"], Util.humanize_time_diff(Time.parse("2009-01-01 01:01:31").to_s, Time.parse("2009-01-01 01:58:00"))
|
20
|
+
assert_equal [57, "min"], Util.humanize_time_diff(Time.parse("2009-01-01 01:01:00").to_s, Time.parse("2009-01-01 01:58:29"))
|
21
|
+
assert_equal [58, "min"], Util.humanize_time_diff(Time.parse("2009-01-01 01:01:00").to_s, Time.parse("2009-01-01 01:58:30"))
|
22
|
+
end
|
23
|
+
|
24
|
+
should "use hour granularity for time differences greater than an hour but smaller than a day" do
|
25
|
+
assert_equal [1, "hour"], Util.humanize_time_diff(Time.parse("2009-01-01 01:00").to_s, Time.parse("2009-01-01 02:00"))
|
26
|
+
assert_equal [1, "hour"], Util.humanize_time_diff(Time.parse("2009-01-01 02:00").to_s, Time.parse("2009-01-01 01:00"))
|
27
|
+
assert_equal [2, "hours"], Util.humanize_time_diff(Time.parse("2009-01-01 01:00").to_s, Time.parse("2009-01-01 03:00"))
|
28
|
+
end
|
29
|
+
|
30
|
+
should "use day granularity for time differences greater than a day" do
|
31
|
+
assert_equal [1, "day"], Util.humanize_time_diff(Time.parse("2009-01-01 01:00").to_s, Time.parse("2009-01-02 03:00"))
|
32
|
+
assert_equal [2, "days"], Util.humanize_time_diff(Time.parse("2009-01-01 01:00").to_s, Time.parse("2009-01-03 03:00"))
|
33
|
+
end
|
23
34
|
end
|
24
35
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
"
|
32
|
-
|
36
|
+
context "for recursively symbolizing keys in a hash" do
|
37
|
+
should "symbolize hash keys correctly" do
|
38
|
+
given = {
|
39
|
+
"alpha" => "A",
|
40
|
+
:beta => "B",
|
41
|
+
"charlie" => "C",
|
42
|
+
"delta" => {
|
43
|
+
"echelon" => "E",
|
44
|
+
"fox" => "F"
|
45
|
+
}
|
33
46
|
}
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
47
|
+
expected = {
|
48
|
+
:alpha => "A",
|
49
|
+
:beta => "B",
|
50
|
+
:charlie => "C",
|
51
|
+
:delta => {
|
52
|
+
:echelon => "E",
|
53
|
+
:fox => "F"
|
54
|
+
}
|
42
55
|
}
|
43
|
-
|
44
|
-
|
56
|
+
assert_equal expected, Util.symbolize_hash_keys(given)
|
57
|
+
end
|
45
58
|
end
|
46
59
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
60
|
+
context "for parsing integers from strings, with minimum and default values, and naming parameter" do
|
61
|
+
should "return an integer from its string presentation" do
|
62
|
+
assert_equal 6, Util.parse_int_gt("6", 8, 4, "ethical working hours per day")
|
63
|
+
end
|
64
|
+
|
65
|
+
should "return default value if the string parameter is falsy" do
|
66
|
+
assert_equal 8, Util.parse_int_gt(nil, 8, 4, "ethical working hours per day")
|
67
|
+
assert_equal 8, Util.parse_int_gt(false, 8, 4, "ethical working hours per day")
|
68
|
+
end
|
69
|
+
|
70
|
+
should "raise an error if the parsed value is less than the minimum parameter" do
|
71
|
+
assert_raise(ArgumentError) { Util.parse_int_gt(3, 8, 4, "ethical working hours per day") }
|
72
|
+
assert_raise(ArgumentError) { Util.parse_int_gt("3", 8, 4, "ethical working hours per day") }
|
73
|
+
end
|
52
74
|
end
|
53
75
|
|
54
76
|
context "for replacing the contents of a string with a regexp that uses group syntax" do
|
55
|
-
should "replace the contents by using
|
56
|
-
assert_equal "hello", Util.str_gsub_by_group("hello", /he(f)/) { |s| s.upcase }
|
57
|
-
assert_equal "", Util.str_gsub_by_group("", /.+([ai])/) { |s| s.upcase }
|
58
|
-
assert_equal "hello", Util.str_gsub_by_group("hello", /.+([ai])/) { |s| s.upcase }
|
77
|
+
should "replace the contents of the string by using a single matching group of the regexp" do
|
59
78
|
assert_equal "hEllo", Util.str_gsub_by_group("hello", /.+(e)/) { |s| s.upcase }
|
60
79
|
assert_equal "hEllO", Util.str_gsub_by_group("hello", /([aeio])/) { |s| s.upcase }
|
61
80
|
assert_equal "hEEllOO", Util.str_gsub_by_group("hello", /([aeio])/) { |s| s.upcase * 2 }
|
81
|
+
assert_equal "hll", Util.str_gsub_by_group("hello", /([aeio])/) { |s| "" }
|
82
|
+
assert_equal "hell", Util.str_gsub_by_group("hello", /.+([io])/) { |s| "" }
|
83
|
+
end
|
84
|
+
|
85
|
+
should "replace the contents of the string by using multiple matching groups of the regexp" do
|
62
86
|
assert_equal "hEllO", Util.str_gsub_by_group("hello", /([ae]).+([io])/) { |s| s.upcase }
|
63
87
|
assert_equal "hXEXllXOX", Util.str_gsub_by_group("hello", /([ae]).+([io])/) { |s| "X" + s.upcase + "X" }
|
64
88
|
assert_equal "hll", Util.str_gsub_by_group("hello", /.+([ae]).+([io])/) { |s| "" }
|
65
89
|
assert_equal "hll", Util.str_gsub_by_group("hello", /([ae]).+([io])/) { |s| "" }
|
66
|
-
assert_equal "hll", Util.str_gsub_by_group("hello", /([aeio])/) { |s| "" }
|
67
|
-
assert_equal "hell", Util.str_gsub_by_group("hello", /.+([io])/) { |s| "" }
|
68
90
|
assert_equal "hEllo", Util.str_gsub_by_group("hello", /^(a)|.+(e)/) { |s| s.upcase }
|
69
91
|
end
|
70
92
|
|
71
|
-
should "replace the contents by using the whole
|
93
|
+
should "replace the contents of the string by using the whole regexp if there are no groups in the regexp an the regexp matches" do
|
72
94
|
assert_equal "", Util.str_gsub_by_group("", /el/) { |s| s.upcase }
|
73
95
|
assert_equal "hELlo", Util.str_gsub_by_group("hello", /el/) { |s| s.upcase }
|
74
96
|
end
|
75
97
|
|
76
|
-
should "
|
98
|
+
should "not change the contents of the string if the regexp does not match" do
|
99
|
+
assert_equal "", Util.str_gsub_by_group("", /.+([ai])/) { |s| s.upcase }
|
100
|
+
assert_equal "hello", Util.str_gsub_by_group("hello", /.+([ai])/) { |s| s.upcase }
|
101
|
+
assert_equal "hello", Util.str_gsub_by_group("hello", /he(f)/) { |s| s.upcase }
|
102
|
+
end
|
103
|
+
|
104
|
+
should "return a new string as the result, leaving the original string unmodified" do
|
77
105
|
org_str = "hello"
|
78
106
|
new_str = Util.str_gsub_by_group(org_str, /e/) { |s| s.upcase }
|
79
107
|
assert_not_same new_str, org_str
|
@@ -81,6 +109,76 @@ class UtilTest < Test::Unit::TestCase
|
|
81
109
|
assert_equal "hEllo", new_str
|
82
110
|
end
|
83
111
|
end
|
112
|
+
|
113
|
+
context "for percent-encoding strings" do
|
114
|
+
should "not encode safe characters" do
|
115
|
+
assert_equal "a", Util.percent_encode("a")
|
116
|
+
assert_equal "B", Util.percent_encode("B")
|
117
|
+
assert_equal "3", Util.percent_encode("3")
|
118
|
+
assert_equal ".", Util.percent_encode(".")
|
119
|
+
assert_equal "-", Util.percent_encode("-")
|
120
|
+
assert_equal "_", Util.percent_encode("_")
|
121
|
+
end
|
122
|
+
|
123
|
+
should "encode space character with precent-encoding, not with '+' character" do
|
124
|
+
assert_equal "%20", Util.percent_encode(" ")
|
125
|
+
end
|
126
|
+
|
127
|
+
should "encode unsafe characters that URI.encode leaves by default unencoded" do
|
128
|
+
assert_equal "&", URI.encode("&")
|
129
|
+
assert_equal "%26", Util.percent_encode("&")
|
130
|
+
assert_equal "?", URI.encode("?")
|
131
|
+
assert_equal "%3F", Util.percent_encode("?")
|
132
|
+
assert_equal "/", URI.encode("/")
|
133
|
+
assert_equal "%2F", Util.percent_encode("/")
|
134
|
+
assert_equal ":", URI.encode(":")
|
135
|
+
assert_equal "%3A", Util.percent_encode(":")
|
136
|
+
assert_equal ",", URI.encode(",")
|
137
|
+
assert_equal "%2C", Util.percent_encode(",")
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
context "for traversing a hash with a path expression for finding a value" do
|
142
|
+
setup do
|
143
|
+
@inner_hash = {
|
144
|
+
:salmon => "slick"
|
145
|
+
}
|
146
|
+
@inner_hash.default = "no such element in inner hash"
|
147
|
+
@outer_hash = {
|
148
|
+
:simple => "beautiful",
|
149
|
+
:inner => @inner_hash,
|
150
|
+
:fishy => nil
|
151
|
+
}
|
152
|
+
@outer_hash.default = "no such element in outer hash"
|
153
|
+
end
|
154
|
+
|
155
|
+
should "support both a non-array and a single element array path for finding the value" do
|
156
|
+
assert_equal "beautiful", Util.find_hash_path(@outer_hash, :simple)
|
157
|
+
assert_equal "beautiful", Util.find_hash_path(@outer_hash, [:simple])
|
158
|
+
end
|
159
|
+
|
160
|
+
should "find a nested value with an array path" do
|
161
|
+
assert_equal "slick", Util.find_hash_path(@outer_hash, [:inner, :salmon])
|
162
|
+
end
|
163
|
+
|
164
|
+
should "return the default value of the hash if the value cannot be found" do
|
165
|
+
assert_equal @outer_hash.default, Util.find_hash_path(@outer_hash, :difficult)
|
166
|
+
assert_equal @inner_hash.default, Util.find_hash_path(@outer_hash, [:inner, :cucumber])
|
167
|
+
assert_equal @outer_hash.default, Util.find_hash_path(@outer_hash, [:fishy, :no_such])
|
168
|
+
end
|
169
|
+
|
170
|
+
should "return the default value of the hash if invalid path value" do
|
171
|
+
assert_equal @outer_hash.default, Util.find_hash_path(@outer_hash, nil)
|
172
|
+
assert_equal @outer_hash.default, Util.find_hash_path(@outer_hash, [:no_such, nil])
|
173
|
+
assert_equal @outer_hash.default, Util.find_hash_path(@outer_hash, [:simple, nil])
|
174
|
+
assert_equal @outer_hash.default, Util.find_hash_path(@outer_hash, [:inner, nil])
|
175
|
+
assert_equal @outer_hash.default, Util.find_hash_path(@outer_hash, [:inner, :salmon, nil])
|
176
|
+
end
|
177
|
+
|
178
|
+
should "return nil if nil hash value" do
|
179
|
+
assert_equal nil, Util.find_hash_path(nil, nil)
|
180
|
+
end
|
181
|
+
end
|
84
182
|
end
|
85
183
|
|
86
184
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tweetwine
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tuomas Kareinen
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-12-22 00:00:00 +02:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -39,23 +39,36 @@ files:
|
|
39
39
|
- README.rdoc
|
40
40
|
- bin/tweetwine
|
41
41
|
- example/example_helper.rb
|
42
|
-
- example/fixtures/
|
43
|
-
- example/
|
42
|
+
- example/fixtures/home.json
|
43
|
+
- example/fixtures/mentions.json
|
44
|
+
- example/fixtures/search.json
|
45
|
+
- example/fixtures/update.json
|
46
|
+
- example/fixtures/user.json
|
47
|
+
- example/fixtures/users.json
|
48
|
+
- example/search_statuses_example.rb
|
49
|
+
- example/show_followers_example.rb
|
50
|
+
- example/show_friends_example.rb
|
51
|
+
- example/show_home_example.rb
|
52
|
+
- example/show_mentions_example.rb
|
44
53
|
- example/show_metadata_example.rb
|
54
|
+
- example/show_user_example.rb
|
55
|
+
- example/update_status_example.rb
|
56
|
+
- lib/tweetwine/cli.rb
|
45
57
|
- lib/tweetwine/client.rb
|
46
58
|
- lib/tweetwine/io.rb
|
47
59
|
- lib/tweetwine/meta.rb
|
48
60
|
- lib/tweetwine/options.rb
|
49
|
-
- lib/tweetwine/
|
61
|
+
- lib/tweetwine/retrying_http.rb
|
50
62
|
- lib/tweetwine/startup_config.rb
|
51
63
|
- lib/tweetwine/url_shortener.rb
|
52
64
|
- lib/tweetwine/util.rb
|
53
65
|
- lib/tweetwine.rb
|
66
|
+
- test/cli_test.rb
|
54
67
|
- test/client_test.rb
|
55
68
|
- test/fixtures/test_config.yaml
|
56
69
|
- test/io_test.rb
|
57
70
|
- test/options_test.rb
|
58
|
-
- test/
|
71
|
+
- test/retrying_http_test.rb
|
59
72
|
- test/startup_config_test.rb
|
60
73
|
- test/test_helper.rb
|
61
74
|
- test/url_shortener_test.rb
|
@@ -67,7 +80,7 @@ licenses: []
|
|
67
80
|
post_install_message:
|
68
81
|
rdoc_options:
|
69
82
|
- --title
|
70
|
-
- tweetwine 0.2.
|
83
|
+
- tweetwine 0.2.7
|
71
84
|
- --main
|
72
85
|
- README.rdoc
|
73
86
|
- --exclude
|
@@ -1,45 +0,0 @@
|
|
1
|
-
require "example_helper"
|
2
|
-
|
3
|
-
Feature "show the latest statuses" do
|
4
|
-
in_order_to "stay up-to-date"
|
5
|
-
as_a "user"
|
6
|
-
i_want_to "see the latest statuses"
|
7
|
-
|
8
|
-
INJECTION = 'FakeWeb.register_uri(:get, "https://foouser:barpwd@twitter.com/statuses/friends_timeline.json?count=20&page=1", :body => fixture("statuses.json"))'
|
9
|
-
|
10
|
-
Scenario "see the latest statuses with colorization disabled" do
|
11
|
-
When "application is launched with command 'home'" do
|
12
|
-
@status = launch_app("-a foouser:barpwd --no-colorize", INJECTION) do |pid, stdin, stdout|
|
13
|
-
@output = stdout.readlines
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
Then "the latest statuses are shown" do
|
18
|
-
@output[0].should == "pelit, 11 days ago:\n"
|
19
|
-
@output[1].should == "F1-kausi alkaa marraskuussa http://bit.ly/1qQwjQ\n"
|
20
|
-
@output[2].should == "\n"
|
21
|
-
@output[58].should == "radar, 15 days ago:\n"
|
22
|
-
@output[59].should == "Four short links: 29 September 2009 http://bit.ly/dYxay\n"
|
23
|
-
@output[60].should == "\n"
|
24
|
-
@status.exitstatus.should == 0
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
Scenario "see the latest statuses with colorization enabled" do
|
29
|
-
When "application is launched with command 'home'" do
|
30
|
-
@status = launch_app("-a foouser:barpwd --colorize", INJECTION) do |pid, stdin, stdout|
|
31
|
-
@output = stdout.readlines
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
Then "the latest statuses are shown" do
|
36
|
-
@output[0].should == "\e[32mpelit\e[0m, 11 days ago:\n"
|
37
|
-
@output[1].should == "F1-kausi alkaa marraskuussa \e[36mhttp://bit.ly/1qQwjQ\e[0m\n"
|
38
|
-
@output[2].should == "\n"
|
39
|
-
@output[58].should == "\e[32mradar\e[0m, 15 days ago:\n"
|
40
|
-
@output[59].should == "Four short links: 29 September 2009 \e[36mhttp://bit.ly/dYxay\e[0m\n"
|
41
|
-
@output[60].should == "\n"
|
42
|
-
@status.exitstatus.should == 0
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
@@ -1,37 +0,0 @@
|
|
1
|
-
require "rest_client"
|
2
|
-
|
3
|
-
module Tweetwine
|
4
|
-
class ClientError < RuntimeError; end
|
5
|
-
|
6
|
-
class RestClientWrapper
|
7
|
-
instance_methods.each { |m| undef_method m unless m =~ /(^__|^send$|^object_id$)/ }
|
8
|
-
|
9
|
-
MAX_RETRIES = 3
|
10
|
-
RETRY_BASE_WAIT_TIMEOUT = 4
|
11
|
-
|
12
|
-
def initialize(io)
|
13
|
-
@io = io
|
14
|
-
end
|
15
|
-
|
16
|
-
protected
|
17
|
-
|
18
|
-
def method_missing(name, *args, &block)
|
19
|
-
tries = 0
|
20
|
-
begin
|
21
|
-
tries += 1
|
22
|
-
RestClient.send(name, *args, &block)
|
23
|
-
rescue Errno::ECONNRESET => e
|
24
|
-
if tries < MAX_RETRIES
|
25
|
-
timeout = RETRY_BASE_WAIT_TIMEOUT**tries
|
26
|
-
@io.warn("Could not connect -- retrying in #{timeout} seconds")
|
27
|
-
sleep timeout
|
28
|
-
retry
|
29
|
-
else
|
30
|
-
raise ClientError, e
|
31
|
-
end
|
32
|
-
rescue RestClient::Exception, SocketError, SystemCallError => e
|
33
|
-
raise ClientError, e
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
@@ -1,68 +0,0 @@
|
|
1
|
-
require "test_helper"
|
2
|
-
require "rest_client"
|
3
|
-
|
4
|
-
class Object
|
5
|
-
def sleep(timeout); end # speed up tests
|
6
|
-
end
|
7
|
-
|
8
|
-
module Tweetwine
|
9
|
-
|
10
|
-
class RestClientWrapperTest < Test::Unit::TestCase
|
11
|
-
context "A RestClientWrapper instance" do
|
12
|
-
setup do
|
13
|
-
@io = mock()
|
14
|
-
@rest_client = RestClientWrapper.new(@io)
|
15
|
-
end
|
16
|
-
|
17
|
-
should "raise ClientError for an invalid request" do
|
18
|
-
RestClient.expects(:get) \
|
19
|
-
.with("https://secret:agent@hushhush.net") \
|
20
|
-
.raises(RestClient::Unauthorized)
|
21
|
-
assert_raise(ClientError) { @rest_client.get("https://secret:agent@hushhush.net") }
|
22
|
-
end
|
23
|
-
|
24
|
-
should "raise ClientError when connection cannot be established" do
|
25
|
-
RestClient.expects(:get) \
|
26
|
-
.with("http://www.invalid.net") \
|
27
|
-
.raises(Errno::ECONNABORTED)
|
28
|
-
assert_raise(ClientError) { @rest_client.get("http://www.invalid.net") }
|
29
|
-
end
|
30
|
-
|
31
|
-
should "raise ClientError when host cannot be resolved" do
|
32
|
-
RestClient.expects(:get) \
|
33
|
-
.with("http://unknown.net") \
|
34
|
-
.raises(SocketError)
|
35
|
-
assert_raise(ClientError) { @rest_client.get("http://unknown.net") }
|
36
|
-
end
|
37
|
-
|
38
|
-
should "retry connection upon connection reset" do
|
39
|
-
rest_client_calls = sequence("RestClient")
|
40
|
-
RestClient.expects(:get) \
|
41
|
-
.with("http://www.heavilyloaded.net") \
|
42
|
-
.in_sequence(rest_client_calls) \
|
43
|
-
.raises(Errno::ECONNRESET)
|
44
|
-
RestClient.expects(:get) \
|
45
|
-
.with("http://www.heavilyloaded.net") \
|
46
|
-
.in_sequence(rest_client_calls)
|
47
|
-
@io.expects(:warn).with("Could not connect -- retrying in 4 seconds")
|
48
|
-
@rest_client.get("http://www.heavilyloaded.net")
|
49
|
-
end
|
50
|
-
|
51
|
-
should "retry connection a maximum of certain number of times" do
|
52
|
-
rest_client_calls = sequence("RestClient")
|
53
|
-
io_calls = sequence("IO")
|
54
|
-
RestClientWrapper::MAX_RETRIES.times do
|
55
|
-
RestClient.expects(:get) \
|
56
|
-
.with("http://www.heavilyloaded.net") \
|
57
|
-
.in_sequence(rest_client_calls) \
|
58
|
-
.raises(Errno::ECONNRESET)
|
59
|
-
end
|
60
|
-
(RestClientWrapper::MAX_RETRIES - 1).times do
|
61
|
-
@io.expects(:warn).in_sequence(io_calls)
|
62
|
-
end
|
63
|
-
assert_raise(ClientError) { @rest_client.get("http://www.heavilyloaded.net") }
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
end
|