akane 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.
@@ -0,0 +1,51 @@
1
+ require 'akane/storages/abstract_storage'
2
+
3
+ module Akane
4
+ module Storages
5
+ class Mock < AbstractStorage
6
+ class << self
7
+ def recorded_tweets
8
+ @recorded_tweets ||= []
9
+ end
10
+
11
+ def deletion_marks
12
+ @deletion_marks ||= []
13
+ end
14
+
15
+ def recorded_events
16
+ @recorded_events ||= []
17
+ end
18
+
19
+ def recorded_messages
20
+ @recorded_messages ||= []
21
+ end
22
+
23
+ def reset!
24
+ [recorded_tweets, deletion_marks,
25
+ recorded_events, recorded_messages].each(&:clear)
26
+ self
27
+ end
28
+ end
29
+
30
+ def record_tweet(account, tweet)
31
+ self.class.recorded_tweets << [account, tweet]
32
+ self
33
+ end
34
+
35
+ def mark_as_deleted(account, user_id, tweet_id)
36
+ self.class.deletion_marks << [account, user_id, tweet_id]
37
+ self
38
+ end
39
+
40
+ def record_event(account, event)
41
+ self.class.recorded_events << [account, event]
42
+ self
43
+ end
44
+
45
+ def record_message(account, message)
46
+ self.class.recorded_messages << [account, message]
47
+ self
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,23 @@
1
+ require 'akane/storages/abstract_storage'
2
+
3
+ module Akane
4
+ module Storages
5
+ class Stdout < AbstractStorage
6
+ def record_tweet(account, tweet)
7
+ $stdout.puts "[#{account}] #{tweet["user"]["screen_name"]}: #{tweet["text"]}"
8
+ end
9
+
10
+ def mark_as_deleted(account, user_id, tweet_id)
11
+ $stdout.puts "[#{account}](DELETION) #{user_id}/#{tweet_id}"
12
+ end
13
+
14
+ def record_event(account, event)
15
+ $stdout.puts "[#{account}](EVENT) #{event["event"]}: #{event["source"]["screen_name"]}-> #{event["target"]["screen_name"]}"
16
+ end
17
+
18
+ def record_message(account, message)
19
+ $stdout.puts "[#{account}](DM) #{message["user"]["screen_name"]}: #{message["text"]}"
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,3 @@
1
+ module Akane
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+ require 'logger'
3
+ require 'akane/config'
4
+
5
+ describe Akane::Config do
6
+ subject { described_class.new("log" => File::NULL, "foo" => "bar") }
7
+
8
+ describe "#logger" do
9
+ it "returns logger" do
10
+ expect(subject.logger).to be_a_kind_of(Logger)
11
+ end
12
+ end
13
+
14
+ describe "#[]" do
15
+ it "returns from config hash" do
16
+ expect(subject["foo"]).to eq "bar"
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,107 @@
1
+ require 'spec_helper'
2
+ require 'eventmachine'
3
+ require 'akane/manager'
4
+ require 'akane/receivers/stream'
5
+ require 'akane/storages/mock'
6
+ require 'akane/config'
7
+
8
+ describe Akane::Manager do
9
+ let(:conf_accounts) do
10
+ {
11
+ "a" => {"token" => "a-access-token", "secret" => "a-access-secret"},
12
+ }
13
+ end
14
+ let(:conf_storages) do
15
+ [
16
+ { "mock" => {"a" => "b"} }
17
+ ]
18
+ end
19
+
20
+ let(:config) do
21
+ Akane::Config.new(
22
+ "consumer" => {
23
+ "token" => "consumer-token", "secret" => "consumer-secret"
24
+ },
25
+ "accounts" => conf_accounts,
26
+ "storages" => conf_storages
27
+ ).tap { |_| _.stub(logger: Logger.new(nil)) }
28
+ end
29
+
30
+ subject { Akane::Manager.new(config) }
31
+
32
+ before do
33
+ EM.stub(:run) { |&block| block.call }
34
+ end
35
+
36
+ # receivers -> manager -> recorder -> storage
37
+
38
+ describe "#prepare" do
39
+ it "creates receivers" do
40
+ Akane::Receivers::Stream.should_receive(:new) \
41
+ .with(consumer: {token: 'consumer-token', secret: 'consumer-secret'},
42
+ account: {token: 'a-access-token', secret: 'a-access-secret'},
43
+ logger: config.logger) \
44
+ .and_return(double("a").as_null_object)
45
+
46
+ subject.prepare
47
+ end
48
+
49
+ it "instantiates storages" do
50
+ Akane::Storages::Mock.should_receive(:new).with(config: {"a" => "b"}, logger: config.logger)
51
+ subject.prepare
52
+ end
53
+
54
+ it "creates recorder with storages" do
55
+ storage = double("storage")
56
+ Akane::Storages::Mock.stub(new: storage)
57
+ Akane::Recorder.should_receive(:new).with([storage], logger: config.logger).and_call_original
58
+
59
+ subject.prepare
60
+ end
61
+ end
62
+
63
+ describe "#start" do
64
+ let(:receiver) { double("a").as_null_object }
65
+ let(:recorder) { double("recorder").as_null_object }
66
+ before do
67
+ Akane::Receivers::Stream.stub(new: receiver)
68
+ Akane::Recorder.stub(new: recorder)
69
+
70
+ @on_event, @on_tweet, @on_delete, @on_message = nil
71
+ receiver.stub(:on_event) { |&block| @on_event = block }
72
+ receiver.stub(:on_tweet) { |&block| @on_tweet = block }
73
+ receiver.stub(:on_delete) { |&block| @on_delete = block }
74
+ receiver.stub(:on_message) { |&block| @on_message = block }
75
+
76
+ subject.prepare
77
+ end
78
+
79
+ it "starts all receivers" do
80
+ receiver.should_receive(:start)
81
+ subject.start
82
+ end
83
+
84
+ it "starts all receivers" do
85
+ recorder.should_receive(:run)
86
+ subject.start
87
+ end
88
+
89
+ it "sends all to recorder" do
90
+ subject.start
91
+
92
+ recorder.should_receive(:record_tweet).with('a', "id" => 42)
93
+ recorder.should_receive(:record_event).with('a', "event" => 'favorite')
94
+ recorder.should_receive(:mark_as_deleted).with('a', 5, 420)
95
+ recorder.should_receive(:record_message).with('a', 'mes' => 'sage')
96
+
97
+ @on_tweet.call("id" => 42)
98
+ @on_event.call("event" => 'favorite')
99
+ @on_delete.call(5, 420)
100
+ @on_message.call('mes' => 'sage')
101
+ end
102
+ end
103
+
104
+ describe "#run" do
105
+ it "calls setup then start"
106
+ end
107
+ end
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+ require 'akane/receivers/abstract_receiver'
3
+
4
+ describe Akane::Receivers::AbstractReceiver do
5
+ =begin
6
+ subject { described_class.new(consumer: {token: 'consumer-token', secret: 'consumer-secret'}, account: {token: '42-access-token', secret: 'access-secret'}) }
7
+ describe "#start" do
8
+ it "starts listening" do
9
+ subject.start
10
+ end
11
+
12
+ it "starts returning running? true" do
13
+ expect { subject.start } \
14
+ .to change { subject.running? } \
15
+ .from(false).to(true)
16
+ end
17
+ end
18
+
19
+ describe "#stop" do
20
+ it "stops listening" do
21
+ subject.stop
22
+ end
23
+
24
+ it "stops returning running? true" do
25
+ subject.start
26
+
27
+ expect { subject.stop } \
28
+ .to change { subject.running? } \
29
+ .from(true).to(false)
30
+ end
31
+ end
32
+
33
+ describe "when received tweet" do
34
+ it "calls on_tweet hook" do
35
+ end
36
+ end
37
+
38
+ describe "when received event" do
39
+ it "calls on_event hook" do
40
+ end
41
+ end
42
+
43
+ describe "when error occured" do
44
+ it "calls on_error hook" do
45
+ end
46
+ end
47
+ =end
48
+ end
@@ -0,0 +1,105 @@
1
+ require 'spec_helper'
2
+ require 'logger'
3
+ require 'akane/receivers/stream'
4
+
5
+ describe Akane::Receivers::Stream do
6
+ let(:config) { {} }
7
+ subject { described_class.new(consumer: {token: 'consumer-token', secret: 'consumer-secret'}, account: {token: '42-access-token', secret: 'access-secret'}, config: config, logger: Logger.new(nil)) }
8
+
9
+ after(:each) do
10
+ TweetStream::MockClient.clients.clear
11
+ end
12
+
13
+ describe "#start" do
14
+ it "starts listening" do
15
+ expect { subject.start } \
16
+ .to change { TweetStream::MockClient.clients.size } \
17
+ .by(1)
18
+ end
19
+
20
+ it "starts returning running? true" do
21
+ expect { subject.start } \
22
+ .to change { subject.running? } \
23
+ .from(false).to(true)
24
+ end
25
+ end
26
+
27
+ describe "#stop" do
28
+ before do
29
+ subject.start
30
+ end
31
+
32
+ it "stops listening" do
33
+ expect { subject.stop } \
34
+ .to change { TweetStream::MockClient.clients.size } \
35
+ .by(-1)
36
+ end
37
+
38
+ it "stops returning running? true" do
39
+ expect { subject.stop } \
40
+ .to change { subject.running? } \
41
+ .from(true).to(false)
42
+ end
43
+ end
44
+
45
+ describe "when received tweet" do
46
+ before do
47
+ subject.start
48
+ end
49
+
50
+ it "calls on_tweet hook" do
51
+ called = nil
52
+ subject.on_tweet do |tweet|
53
+ called = tweet
54
+ end
55
+ TweetStream::MockClient.invoke('timeline_status', foo: :bar)
56
+ expect(called).to eq(foo: :bar)
57
+ end
58
+ end
59
+
60
+ describe "when received message" do
61
+ before do
62
+ subject.start
63
+ end
64
+
65
+ it "calls on_message hook" do
66
+ called = nil
67
+ subject.on_message do |message|
68
+ called = message
69
+ end
70
+ TweetStream::MockClient.invoke('direct_message', foo: :bar)
71
+ expect(called).to eq(foo: :bar)
72
+ end
73
+ end
74
+
75
+ describe "when received deletion" do
76
+ before do
77
+ subject.start
78
+ end
79
+
80
+ it "calls on_delete hook" do
81
+ called = nil
82
+ subject.on_delete do |u,t|
83
+ called = [u,t]
84
+ end
85
+ TweetStream::MockClient.invoke('delete', 424242, 42)
86
+ expect(called).to eq([42,424242])
87
+ end
88
+ end
89
+
90
+ describe "when received event" do
91
+ before do
92
+ subject.start
93
+ end
94
+
95
+ it "calls on_event hook" do
96
+ called = nil
97
+ subject.on_event do |h|
98
+ called = h
99
+ end
100
+ TweetStream::MockClient.invoke('anything', "event" => "favorite")
101
+ TweetStream::MockClient.invoke('anything', "something" => "else")
102
+ expect(called).to eq("event" => "favorite")
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,86 @@
1
+ require 'spec_helper'
2
+ require 'akane/recorder'
3
+
4
+ describe Akane::Recorder do
5
+ let(:storages) do
6
+ [
7
+ double("storage0")
8
+ ]
9
+ end
10
+ subject { described_class.new(storages) }
11
+
12
+ describe "recording tweets" do
13
+ it "records tweet" do
14
+ storages[0].should_receive(:record_tweet).with('a', {"id" => 42})
15
+ subject.record_tweet('a', "id" => 42)
16
+ subject.dequeue(true)
17
+ end
18
+
19
+ it "doesn't record tweets which already recorded recently" do
20
+ storages[0].should_receive(:record_tweet).with('a', {"id" => 40})
21
+ storages[0].should_receive(:record_tweet).with('a', {"id" => 42})
22
+
23
+ subject.record_tweet('a', "id" => 40)
24
+ subject.record_tweet('a', "id" => 42)
25
+ subject.dequeue(true)
26
+ subject.dequeue(true)
27
+ subject.record_tweet('a', "id" => 42)
28
+ subject.dequeue(true)
29
+ end
30
+
31
+ it "records retweeted tweet" do
32
+ tweet = {"id" => 42, "text" => "foo", "user" => {"id" => 1, "screen_name" => "a"}}
33
+ retweet = {"id" => 43, "text" => "RT @a: foo", "user" => {"id" => 2, "screen_name" => "b"}, "retweeted_status" => tweet}
34
+ storages[0].should_receive(:record_tweet).with('a', retweet)
35
+ storages[0].should_receive(:record_tweet).with('a', tweet)
36
+ subject.record_tweet('a', retweet)
37
+ subject.dequeue(true)
38
+ end
39
+ end
40
+
41
+ describe "marking deletion" do
42
+ it "marks as deleted" do
43
+ subject.mark_as_deleted('foo', 1, 42)
44
+
45
+ storages[0].should_receive(:mark_as_deleted).with('foo', 1, 42)
46
+ subject.dequeue(true)
47
+ end
48
+ end
49
+
50
+ describe "recording messages" do
51
+ it "records message" do
52
+ subject.record_message('foo', {"id" => 42})
53
+
54
+ storages[0].should_receive(:record_message).with('foo', "id" => 42)
55
+ subject.dequeue(true)
56
+ end
57
+ end
58
+
59
+ describe "recording event" do
60
+ it "records event" do
61
+ subject.record_event('foo', {"event" => "favorite"})
62
+
63
+ storages[0].should_receive(:record_event).with('foo', "event" => "favorite")
64
+ subject.dequeue(true)
65
+ end
66
+ end
67
+
68
+ describe "#run" do
69
+ before do
70
+ @th = Thread.new { subject.run(true) }
71
+ @th.abort_on_exception = true
72
+ end
73
+
74
+ it "continues dequeuing the queue" do
75
+ storages[0].should_receive(:record_tweet).with('a', {"id" => 42})
76
+ storages[0].should_receive(:record_tweet).with('b', {"id" => 43})
77
+ subject.record_tweet('a', "id" => 42)
78
+ subject.record_tweet('b', "id" => 43)
79
+ 10.times { break if subject.queue_length.zero?; sleep 0.1 }
80
+ end
81
+
82
+ after do
83
+ @th.kill if @th && @th.alive?
84
+ end
85
+ end
86
+ end