drip 0.0.1
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/.gitignore +4 -0
- data/Gemfile +6 -0
- data/README +6 -0
- data/Rakefile +2 -0
- data/drip.gemspec +20 -0
- data/drip.txt +380 -0
- data/install.rb +10 -0
- data/lib/drip.rb +294 -0
- data/lib/drip/version.rb +3 -0
- data/lib/my_drip.rb +64 -0
- data/sample/copocopo.rb +82 -0
- data/sample/drip_tw.rb +255 -0
- data/sample/gca.rb +70 -0
- data/sample/hello_tw.rb +5 -0
- data/sample/my_status.rb +52 -0
- data/sample/simple-oauth.rb +140 -0
- data/sample/tw_markov.rb +154 -0
- data/test/basic.rb +156 -0
- metadata +71 -0
data/sample/hello_tw.rb
ADDED
data/sample/my_status.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'drip_tw'
|
3
|
+
|
4
|
+
def last_tweet_id
|
5
|
+
key = nil
|
6
|
+
while kv = MyDrip.older(key, 'DripDemo MyStatus')
|
7
|
+
key, value = kv
|
8
|
+
return value['id'] if value['id']
|
9
|
+
end
|
10
|
+
nil
|
11
|
+
end
|
12
|
+
|
13
|
+
def get_user_timeline(app, since_id, max_id)
|
14
|
+
count = 3
|
15
|
+
begin
|
16
|
+
ary = app.user_timeline(since_id, max_id)
|
17
|
+
rescue
|
18
|
+
p $!
|
19
|
+
count -= 1
|
20
|
+
return nil, [] if count <= 0
|
21
|
+
sleep 5
|
22
|
+
retry
|
23
|
+
end
|
24
|
+
max_id = nil
|
25
|
+
ary.reverse_each do |event|
|
26
|
+
next unless event['id']
|
27
|
+
max_id = event['id'] - 1
|
28
|
+
break
|
29
|
+
end
|
30
|
+
return max_id, ary
|
31
|
+
end
|
32
|
+
|
33
|
+
app = DripDemo.new
|
34
|
+
|
35
|
+
since_id = last_tweet_id
|
36
|
+
|
37
|
+
max_id = nil
|
38
|
+
timeline = []
|
39
|
+
while true
|
40
|
+
max_id, ary = get_user_timeline(app, since_id, max_id)
|
41
|
+
break if ary.empty?
|
42
|
+
pp [ary[0]['text'], ary[-1]['text']] rescue nil
|
43
|
+
sleep 5
|
44
|
+
timeline += ary
|
45
|
+
end
|
46
|
+
|
47
|
+
p [timeline.size, 'continue?']
|
48
|
+
gets
|
49
|
+
|
50
|
+
timeline.reverse_each do |event|
|
51
|
+
MyDrip.write(event, 'DripDemo MyStatus')
|
52
|
+
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
#
|
2
|
+
# import from https://github.com/shibason/rb-simple-oauth
|
3
|
+
# see: http://d.hatena.ne.jp/shibason/20090809/1249764381
|
4
|
+
require 'uri'
|
5
|
+
require 'net/http'
|
6
|
+
require 'openssl'
|
7
|
+
|
8
|
+
class SimpleOAuth
|
9
|
+
def initialize(consumer_key, consumer_secret, token, token_secret)
|
10
|
+
@consumer_key = consumer_key
|
11
|
+
@consumer_secret = consumer_secret
|
12
|
+
@token = token
|
13
|
+
@token_secret = token_secret
|
14
|
+
# This class supports only 'HMAC-SHA1' as signature method at present.
|
15
|
+
@signature_method = 'HMAC-SHA1'
|
16
|
+
end
|
17
|
+
|
18
|
+
def get(url, headers = {})
|
19
|
+
request(:GET, url, nil, headers)
|
20
|
+
end
|
21
|
+
|
22
|
+
def head(url, headers = {})
|
23
|
+
request(:HEAD, url, nil, headers)
|
24
|
+
end
|
25
|
+
|
26
|
+
def post(url, body = nil, headers = {})
|
27
|
+
request(:POST, url, body, headers)
|
28
|
+
end
|
29
|
+
|
30
|
+
def put(url, body = nil, headers = {})
|
31
|
+
request(:PUT, url, body, headers)
|
32
|
+
end
|
33
|
+
|
34
|
+
def delete(url, headers = {})
|
35
|
+
request(:DELETE, url, nil, headers)
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
def request(method, url, body = nil, headers = {})
|
40
|
+
method = method.to_s
|
41
|
+
url = URI.parse(url)
|
42
|
+
request = create_http_request(method, url.request_uri, body, headers)
|
43
|
+
request['Authorization'] = auth_header(method, url, request.body)
|
44
|
+
Net::HTTP.new(url.host, url.port).request(request)
|
45
|
+
end
|
46
|
+
|
47
|
+
RESERVED_CHARACTERS = /[^a-zA-Z0-9\-\.\_\~]/
|
48
|
+
|
49
|
+
def escape(value)
|
50
|
+
URI.escape(value.to_s, RESERVED_CHARACTERS)
|
51
|
+
end
|
52
|
+
|
53
|
+
def encode_parameters(params, delimiter = '&', quote = nil)
|
54
|
+
if params.is_a?(Hash)
|
55
|
+
params = params.map do |key, value|
|
56
|
+
"#{escape(key)}=#{quote}#{escape(value)}#{quote}"
|
57
|
+
end
|
58
|
+
else
|
59
|
+
params = params.map { |value| escape(value) }
|
60
|
+
end
|
61
|
+
delimiter ? params.join(delimiter) : params
|
62
|
+
end
|
63
|
+
|
64
|
+
VERSION = '0.1'
|
65
|
+
USER_AGENT = "SimpleOAuth/#{VERSION}"
|
66
|
+
|
67
|
+
def create_http_request(method, path, body, headers)
|
68
|
+
method = method.capitalize.to_sym
|
69
|
+
request = Net::HTTP.const_get(method).new(path, headers)
|
70
|
+
request['User-Agent'] = USER_AGENT
|
71
|
+
if method == :Post || method == :Put
|
72
|
+
request.body = body.is_a?(Hash) ? encode_parameters(body) : body.to_s
|
73
|
+
request.content_type = 'application/x-www-form-urlencoded'
|
74
|
+
request.content_length = (request.body || '').length
|
75
|
+
end
|
76
|
+
request
|
77
|
+
end
|
78
|
+
|
79
|
+
def auth_header(method, url, body)
|
80
|
+
parameters = oauth_parameters
|
81
|
+
parameters[:oauth_signature] = signature(method, url, body, parameters)
|
82
|
+
'OAuth ' + encode_parameters(parameters, ', ', '"')
|
83
|
+
end
|
84
|
+
|
85
|
+
OAUTH_VERSION = '1.0'
|
86
|
+
|
87
|
+
def oauth_parameters
|
88
|
+
{
|
89
|
+
:oauth_consumer_key => @consumer_key,
|
90
|
+
:oauth_token => @token,
|
91
|
+
:oauth_signature_method => @signature_method,
|
92
|
+
:oauth_timestamp => timestamp,
|
93
|
+
:oauth_nonce => nonce,
|
94
|
+
:oauth_version => OAUTH_VERSION
|
95
|
+
}
|
96
|
+
end
|
97
|
+
|
98
|
+
def timestamp
|
99
|
+
Time.now.to_i.to_s
|
100
|
+
end
|
101
|
+
|
102
|
+
def nonce
|
103
|
+
OpenSSL::Digest::Digest.hexdigest('MD5', "#{Time.now.to_f}#{rand}")
|
104
|
+
end
|
105
|
+
|
106
|
+
def signature(*args)
|
107
|
+
base64(digest_hmac_sha1(signature_base_string(*args)))
|
108
|
+
end
|
109
|
+
|
110
|
+
def base64(value)
|
111
|
+
[ value ].pack('m').gsub(/\n/, '')
|
112
|
+
end
|
113
|
+
|
114
|
+
def digest_hmac_sha1(value)
|
115
|
+
OpenSSL::HMAC.digest(OpenSSL::Digest::SHA1.new, secret, value)
|
116
|
+
end
|
117
|
+
|
118
|
+
def secret
|
119
|
+
escape(@consumer_secret) + '&' + escape(@token_secret)
|
120
|
+
end
|
121
|
+
|
122
|
+
def signature_base_string(method, url, body, parameters)
|
123
|
+
method = method.upcase
|
124
|
+
base_url = signature_base_url(url)
|
125
|
+
parameters = normalize_parameters(parameters, body, url.query)
|
126
|
+
encode_parameters([ method, base_url, parameters ])
|
127
|
+
end
|
128
|
+
|
129
|
+
def signature_base_url(url)
|
130
|
+
URI::HTTP.new(url.scheme, url.userinfo, url.host, nil, nil, url.path,
|
131
|
+
nil, nil, nil)
|
132
|
+
end
|
133
|
+
|
134
|
+
def normalize_parameters(parameters, body, query)
|
135
|
+
parameters = encode_parameters(parameters, nil)
|
136
|
+
parameters += body.split('&') if body
|
137
|
+
parameters += query.split('&') if query
|
138
|
+
parameters.sort.join('&')
|
139
|
+
end
|
140
|
+
end
|
data/sample/tw_markov.rb
ADDED
@@ -0,0 +1,154 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'my_drip'
|
3
|
+
require 'MeCab'
|
4
|
+
require 'pp'
|
5
|
+
|
6
|
+
class Bag
|
7
|
+
def initialize
|
8
|
+
@bag = Hash.new(0)
|
9
|
+
@size = 0
|
10
|
+
@cache = nil
|
11
|
+
end
|
12
|
+
attr_reader :size
|
13
|
+
|
14
|
+
def push(obj)
|
15
|
+
@cache = nil
|
16
|
+
@bag[obj] += 1
|
17
|
+
@size += 1
|
18
|
+
obj
|
19
|
+
end
|
20
|
+
|
21
|
+
def sample
|
22
|
+
@cache = @bag.sort_by {|k, v| v} unless @cache
|
23
|
+
prob = rand(@size)
|
24
|
+
@cache.each do |k, v|
|
25
|
+
prob -= v
|
26
|
+
return k if prob < 0
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class BagInBag < Bag
|
32
|
+
def initialize(&blk)
|
33
|
+
super
|
34
|
+
@bag_in_bag = Hash.new {|h, k| h[k] = blk.call}
|
35
|
+
end
|
36
|
+
|
37
|
+
def push(obj, *rest)
|
38
|
+
super(obj)
|
39
|
+
@bag_in_bag[obj].push(*rest)
|
40
|
+
end
|
41
|
+
|
42
|
+
def [](key, *rest)
|
43
|
+
if rest.empty?
|
44
|
+
@bag_in_bag[key]
|
45
|
+
else
|
46
|
+
@bag_in_bag[key][*rest]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class EntityEnum
|
52
|
+
def initialize(str, entity)
|
53
|
+
@str = str || ''
|
54
|
+
@entity = []
|
55
|
+
(entity || []).each do |k, v|
|
56
|
+
@entity += v
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def each
|
61
|
+
unless @entity
|
62
|
+
yield(@str, nil)
|
63
|
+
return
|
64
|
+
end
|
65
|
+
ary = @entity.sort_by {|e| e['indices'][0]}
|
66
|
+
cursor = 0
|
67
|
+
while head = ary.shift
|
68
|
+
left, right = head['indices']
|
69
|
+
if left > cursor
|
70
|
+
yield(@str[cursor ... left], nil)
|
71
|
+
end
|
72
|
+
yield(@str[left .. right], head)
|
73
|
+
cursor = right + 1
|
74
|
+
end
|
75
|
+
if @str.length > cursor
|
76
|
+
yield(@str[cursor ... @str.length], nil)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
class Markov
|
82
|
+
def initialize
|
83
|
+
@bag = BagInBag.new { BagInBag.new { Bag.new } }
|
84
|
+
@start = Bag.new
|
85
|
+
@intern = Hash.new
|
86
|
+
end
|
87
|
+
|
88
|
+
def intern(str)
|
89
|
+
@intern[str] ||= str
|
90
|
+
@intern[str]
|
91
|
+
end
|
92
|
+
|
93
|
+
def add(ary)
|
94
|
+
return if ary.size <= 3
|
95
|
+
ary = ary.collect {|s| intern(s)}
|
96
|
+
@start.push(ary[0])
|
97
|
+
ary.each_cons(3) do |p0, p1, suffix|
|
98
|
+
@bag.push(p0, p1, suffix)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def generate(max_len=30)
|
103
|
+
s = @start.sample
|
104
|
+
ary = [s, @bag[s].sample]
|
105
|
+
max_len.times do
|
106
|
+
it = @bag[*ary[-2, 2]].sample
|
107
|
+
break if it == :eos
|
108
|
+
ary << it
|
109
|
+
end
|
110
|
+
ary
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
$screen_name = {}
|
115
|
+
|
116
|
+
def mention_user
|
117
|
+
" @#{$screen_name.keys.sample} "
|
118
|
+
end
|
119
|
+
|
120
|
+
def as_str(ary)
|
121
|
+
ary = ary.map {|x| x == :name ? mention_user : x}
|
122
|
+
ary.inject{|s,c|s+=(s+"\00"+c=~/\w\00\w/?" ":"")+c}
|
123
|
+
end
|
124
|
+
|
125
|
+
markov = Markov.new
|
126
|
+
m = MeCab::Tagger.new('-Owakati')
|
127
|
+
|
128
|
+
MyDrip.head(4000, 'DripDemo MyStatus').each do |k, v|
|
129
|
+
next if v['retweeted_status']
|
130
|
+
entity = v['entities']
|
131
|
+
str = v['text']
|
132
|
+
ary = []
|
133
|
+
EntityEnum.new(str, entity).each do |s, kind|
|
134
|
+
next if s == 'RT'
|
135
|
+
unless kind
|
136
|
+
ary += m.parse(s).split(' ')
|
137
|
+
else
|
138
|
+
ary <<:name if kind['screen_name']
|
139
|
+
if kind['screen_name']
|
140
|
+
$screen_name[kind['screen_name']] = true
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
markov.add(ary + [:eos])
|
145
|
+
end
|
146
|
+
|
147
|
+
while gets
|
148
|
+
10.times do
|
149
|
+
ary = markov.generate
|
150
|
+
next if ary.size < 10
|
151
|
+
puts as_str(ary)
|
152
|
+
puts
|
153
|
+
end
|
154
|
+
end
|
data/test/basic.rb
ADDED
@@ -0,0 +1,156 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require '../lib/drip.rb'
|
3
|
+
require 'fileutils'
|
4
|
+
|
5
|
+
class TestDrip < Test::Unit::TestCase
|
6
|
+
def setup
|
7
|
+
@drip = Drip.new(nil)
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_time_travel
|
11
|
+
@drip.write('age' => 1)
|
12
|
+
@drip.write('age' => 2)
|
13
|
+
@drip.write('age' => 3)
|
14
|
+
oid, value = @drip.older(nil)
|
15
|
+
assert_equal(value, 'age' => 3)
|
16
|
+
oid, value = @drip.older(oid)
|
17
|
+
assert_equal(value, 'age' => 2)
|
18
|
+
oid, value = @drip.older(oid)
|
19
|
+
assert_equal(value, 'age' => 1)
|
20
|
+
oid, value = @drip.older(oid)
|
21
|
+
assert_equal(oid, nil)
|
22
|
+
assert_equal(value, nil)
|
23
|
+
|
24
|
+
oid, value = @drip.newer(0)
|
25
|
+
assert_equal(value, 'age' => 1)
|
26
|
+
oid, value = @drip.newer(oid)
|
27
|
+
assert_equal(value, 'age' => 2)
|
28
|
+
oid, value = @drip.newer(oid)
|
29
|
+
assert_equal(value, 'age' => 3)
|
30
|
+
oid, value = @drip.newer(oid)
|
31
|
+
assert_equal(oid, nil)
|
32
|
+
assert_equal(value, nil)
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_read
|
36
|
+
11.times do |n|
|
37
|
+
@drip.write("n=#{n}" => 'x' * n, n => n, "n" => n, :symbol => n)
|
38
|
+
end
|
39
|
+
ary = @drip.read(0, 3)
|
40
|
+
assert_equal(ary.size, 3)
|
41
|
+
assert_equal(ary[0][1]['n'], 0)
|
42
|
+
assert_equal(ary[1][1]['n'], 1)
|
43
|
+
assert_equal(ary[2][1]['n'], 2)
|
44
|
+
ary = @drip.read(ary[2][0], 3)
|
45
|
+
assert_equal(ary.size, 3)
|
46
|
+
ary = @drip.read(ary[2][0], 3)
|
47
|
+
assert_equal(ary.size, 3)
|
48
|
+
ary = @drip.read(ary[2][0], 3)
|
49
|
+
assert_equal(ary.size, 2)
|
50
|
+
|
51
|
+
oid = @drip.write('latest', 'tag1', 'tag2')
|
52
|
+
oid, value, *tags = @drip.newer(oid - 1)
|
53
|
+
assert_equal(value, 'latest')
|
54
|
+
assert_equal(tags, ['tag1', 'tag2'])
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_symbol_is_not_tag
|
58
|
+
@drip.write({:symbol => :symbol, 'string' => :string}, :symbol, 'string')
|
59
|
+
assert_raise(ArgumentError) {@drip.read_tag(0, :symbol, 1, 0)}
|
60
|
+
oid, value = @drip.older(@drip.time_to_key(Time.now))
|
61
|
+
assert_equal(value, {:symbol => :symbol, 'string' => :string})
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_number_is_not_tag
|
65
|
+
@drip.write({5 => :five, 'string' => :string}, 5, 'string')
|
66
|
+
assert_equal(@drip.read_tag(0, 'string', 1, 0).size, 1)
|
67
|
+
assert_raise(ArgumentError) {@drip.read_tag(0, 5, 1, 0)}
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_older_now_is_newest
|
71
|
+
@drip.write('age' => 1)
|
72
|
+
@drip.write('age' => 2)
|
73
|
+
@drip.write('age' => 3)
|
74
|
+
oid, value = @drip.older(nil)
|
75
|
+
assert_equal(value, 'age' => 3)
|
76
|
+
oid, value = @drip.older(@drip.time_to_key(Time.now))
|
77
|
+
assert_equal(value, 'age' => 3)
|
78
|
+
|
79
|
+
# newer(past)
|
80
|
+
assert_equal(@drip.newer(0)[1], 'age' => 1)
|
81
|
+
assert_equal(@drip.newer(0)[1], 'age' => 1)
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_read_tag
|
85
|
+
3.times do |n|
|
86
|
+
@drip.write({'n' => n}, 'n')
|
87
|
+
@drip.write({'n' => n, '2' => n * 2}, 'n', '2')
|
88
|
+
@drip.write({'n' => n, '2' => n * 2, '3' => n * 3}, 'n', '2', '3')
|
89
|
+
end
|
90
|
+
|
91
|
+
ary = @drip.read_tag(0, 'n', 10)
|
92
|
+
assert_equal(ary.size, 9)
|
93
|
+
assert_equal(ary[0][1]['n'], 0)
|
94
|
+
assert_equal(ary[0][2], 'n')
|
95
|
+
assert_equal(ary[2][4], '3')
|
96
|
+
end
|
97
|
+
|
98
|
+
def test_head
|
99
|
+
10.times do |n|
|
100
|
+
@drip.write(n)
|
101
|
+
end
|
102
|
+
|
103
|
+
ary = @drip.head(3)
|
104
|
+
assert_equal(ary.size, 3)
|
105
|
+
assert_equal(ary[0][1], 7)
|
106
|
+
assert_equal(ary[2][1], 9)
|
107
|
+
end
|
108
|
+
|
109
|
+
def test_after
|
110
|
+
assert_equal(@drip.write_after(Time.at(1), 1), 1000000)
|
111
|
+
assert_equal(@drip.write_after(Time.at(2), 2), 2000000)
|
112
|
+
assert_equal(@drip.write_after(Time.at(3), 3), 3000000)
|
113
|
+
assert_equal(@drip.write_after(Time.at(4), 4), 4000000)
|
114
|
+
assert_equal(@drip.write_after(Time.at(4), 5), 4000001)
|
115
|
+
assert_equal(@drip.write_after(Time.at(2), 6), 4000002)
|
116
|
+
assert_equal(@drip.write_after(Time.at(5), 6), 5000000)
|
117
|
+
assert_equal(@drip.write_after(Time.at(5), 7), 5000001)
|
118
|
+
assert_equal(@drip.write_at(Time.at(6), 8), 6000000)
|
119
|
+
assert_raise(RuntimeError) {@drip.write_at(Time.at(6), 8)}
|
120
|
+
assert_equal(@drip.write_after(Time.at(5), 8), 6000001)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
class TestDripUsingStorage < TestDrip
|
125
|
+
def remove_drip(dir='test_db')
|
126
|
+
FileUtils.rm_r(dir, :force => true)
|
127
|
+
end
|
128
|
+
|
129
|
+
def setup
|
130
|
+
remove_drip
|
131
|
+
@drip = Drip.new('test_db')
|
132
|
+
end
|
133
|
+
|
134
|
+
def teardown
|
135
|
+
remove_drip
|
136
|
+
end
|
137
|
+
|
138
|
+
def test_twice
|
139
|
+
11.times do |n|
|
140
|
+
@drip.write("n=#{n}" => 'x' * n, n => n, "n" => n, :symbol => n)
|
141
|
+
end
|
142
|
+
|
143
|
+
drip = Drip.new('test_db')
|
144
|
+
ary = drip.read(0, 3)
|
145
|
+
assert_equal(ary.size, 3)
|
146
|
+
assert_equal(ary[0][1]['n'], 0)
|
147
|
+
assert_equal(ary[1][1]['n'], 1)
|
148
|
+
assert_equal(ary[2][1]['n'], 2)
|
149
|
+
ary = drip.read(ary[2][0], 3)
|
150
|
+
assert_equal(ary.size, 3)
|
151
|
+
ary = drip.read(ary[2][0], 3)
|
152
|
+
assert_equal(ary.size, 3)
|
153
|
+
ary = drip.read(ary[2][0], 3)
|
154
|
+
assert_equal(ary.size, 2)
|
155
|
+
end
|
156
|
+
end
|