drip 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|