tweetwine 0.2.5 → 0.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|