twterm 1.3.0 → 2.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +14 -18
- data/bin/twterm +1 -1
- data/lib/twterm/app.rb +120 -30
- data/lib/twterm/client.rb +10 -13
- data/lib/twterm/completion_mamanger.rb +11 -6
- data/lib/twterm/direct_message.rb +6 -28
- data/lib/twterm/direct_message_composer.rb +10 -5
- data/lib/twterm/direct_message_manager.rb +5 -6
- data/lib/twterm/event/notification/abstract_notification.rb +27 -0
- data/lib/twterm/event/notification/error.rb +13 -0
- data/lib/twterm/event/notification/info.rb +13 -0
- data/lib/twterm/event/notification/success.rb +13 -0
- data/lib/twterm/event/notification/warning.rb +13 -0
- data/lib/twterm/event_dispatcher.rb +1 -1
- data/lib/twterm/extensions/array.rb +5 -0
- data/lib/twterm/extensions/enumerator/lazy.rb +3 -0
- data/lib/twterm/extensions/string.rb +0 -4
- data/lib/twterm/friendship.rb +1 -85
- data/lib/twterm/image/between.rb +31 -0
- data/lib/twterm/image/blank_line.rb +21 -0
- data/lib/twterm/image/bold.rb +31 -0
- data/lib/twterm/image/brackets.rb +21 -0
- data/lib/twterm/image/color.rb +45 -0
- data/lib/twterm/image/empty.rb +21 -0
- data/lib/twterm/image/horizontal_sequential_image.rb +48 -0
- data/lib/twterm/image/parens.rb +21 -0
- data/lib/twterm/image/string_image.rb +38 -0
- data/lib/twterm/image/vertical_sequential_image.rb +43 -0
- data/lib/twterm/image.rb +107 -0
- data/lib/twterm/key_mapper/abstract_key_mapper.rb +51 -0
- data/lib/twterm/key_mapper/app_key_mapper.rb +13 -0
- data/lib/twterm/key_mapper/cursor_key_mapper.rb +13 -0
- data/lib/twterm/key_mapper/general_key_mapper.rb +18 -0
- data/lib/twterm/key_mapper/no_such_command.rb +20 -0
- data/lib/twterm/key_mapper/no_such_key.rb +16 -0
- data/lib/twterm/key_mapper/status_key_mapper.rb +18 -0
- data/lib/twterm/key_mapper/tab_key_mapper.rb +31 -0
- data/lib/twterm/key_mapper.rb +127 -0
- data/lib/twterm/list.rb +0 -31
- data/lib/twterm/notifier.rb +7 -7
- data/lib/twterm/repository/abstract_entity_repository.rb +41 -0
- data/lib/twterm/repository/abstract_expirable_entity_repository.rb +35 -0
- data/lib/twterm/repository/abstract_repository.rb +64 -0
- data/lib/twterm/repository/direct_message_repository.rb +14 -0
- data/lib/twterm/repository/friendship_repository.rb +108 -0
- data/lib/twterm/repository/hashtag_repository.rb +39 -0
- data/lib/twterm/repository/list_repository.rb +14 -0
- data/lib/twterm/repository/status_repository.rb +36 -0
- data/lib/twterm/repository/user_repository.rb +22 -0
- data/lib/twterm/rest_client.rb +107 -63
- data/lib/twterm/screen.rb +21 -15
- data/lib/twterm/search_query_window.rb +139 -0
- data/lib/twterm/status.rb +14 -108
- data/lib/twterm/streaming_client.rb +13 -12
- data/lib/twterm/tab/base.rb +48 -8
- data/lib/twterm/tab/direct_message/conversation.rb +53 -52
- data/lib/twterm/tab/direct_message/conversation_list.rb +46 -45
- data/lib/twterm/tab/dumpable.rb +3 -3
- data/lib/twterm/tab/key_assignments_cheatsheet.rb +58 -57
- data/lib/twterm/tab/loadable.rb +20 -0
- data/lib/twterm/tab/new/list.rb +32 -43
- data/lib/twterm/tab/new/search.rb +31 -44
- data/lib/twterm/tab/new/start.rb +44 -55
- data/lib/twterm/tab/new/user.rb +15 -12
- data/lib/twterm/tab/rate_limit_status.rb +84 -0
- data/lib/twterm/tab/scrollable.rb +39 -19
- data/lib/twterm/tab/searchable.rb +133 -0
- data/lib/twterm/tab/statuses/base.rb +139 -129
- data/lib/twterm/tab/statuses/conversation.rb +26 -15
- data/lib/twterm/tab/statuses/favorites.rb +10 -8
- data/lib/twterm/tab/statuses/home.rb +10 -9
- data/lib/twterm/tab/statuses/list_timeline.rb +12 -8
- data/lib/twterm/tab/statuses/mentions.rb +17 -11
- data/lib/twterm/tab/statuses/search.rb +8 -5
- data/lib/twterm/tab/statuses/user_timeline.rb +11 -8
- data/lib/twterm/tab/user_list_management.rb +109 -0
- data/lib/twterm/tab/user_tab.rb +125 -126
- data/lib/twterm/tab/users/base.rb +39 -41
- data/lib/twterm/tab/users/followers.rb +9 -6
- data/lib/twterm/tab/users/friends.rb +9 -6
- data/lib/twterm/tab_manager.rb +64 -40
- data/lib/twterm/tweetbox.rb +18 -13
- data/lib/twterm/uri_opener.rb +2 -1
- data/lib/twterm/user.rb +2 -110
- data/lib/twterm/version.rb +1 -1
- data/lib/twterm/view.rb +30 -0
- data/lib/twterm.rb +3 -9
- data/spec/fixtures/status.json +107 -0
- data/spec/fixtures/user.json +102 -0
- data/spec/spec_helper.rb +7 -0
- data/spec/supports/shared_examples/abstract_key_mapper.rb +17 -0
- data/spec/twterm/extension/enumerator/lazy_spec.rb +11 -0
- data/spec/twterm/friendship_spec.rb +0 -102
- data/spec/twterm/image/blank_line_spec.rb +11 -0
- data/spec/twterm/image/brackets_spec.rb +12 -0
- data/spec/twterm/image/color_spec.rb +22 -0
- data/spec/twterm/image/empry_spec.rb +11 -0
- data/spec/twterm/image/horizontal_sequential_image_spec.rb +15 -0
- data/spec/twterm/image/parens_spec.rb +12 -0
- data/spec/twterm/image/string_image_spec.rb +12 -0
- data/spec/twterm/image/vertical_sequential_image_spec.rb +14 -0
- data/spec/twterm/image_spec.rb +65 -0
- data/spec/twterm/key_mapper/abstract_key_mapper_spec.rb +21 -0
- data/spec/twterm/key_mapper/app_key_mapper_spec.rb +7 -0
- data/spec/twterm/key_mapper/status_key_mapper_spec.rb +7 -0
- data/spec/twterm/key_mapper/tab_key_mapper_spec.rb +7 -0
- data/spec/twterm/repository/friendship_repository_spec.rb +108 -0
- data/spec/twterm/status_spec.rb +58 -0
- data/spec/twterm/user_spec.rb +94 -0
- data/twterm.gemspec +13 -10
- metadata +129 -35
- data/lib/twterm/event/notification.rb +0 -33
- data/lib/twterm/extensions/integer.rb +0 -5
- data/lib/twterm/filter_query_window.rb +0 -91
- data/lib/twterm/filterable_list.rb +0 -41
- data/lib/twterm/history/base.rb +0 -21
- data/lib/twterm/history/hashtag.rb +0 -13
- data/lib/twterm/history/savable.rb +0 -37
- data/lib/twterm/history/screen_name.rb +0 -11
- data/lib/twterm/promise.rb +0 -143
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'twterm/key_mapper/abstract_key_mapper'
|
2
|
+
|
3
|
+
module Twterm
|
4
|
+
class KeyMapper
|
5
|
+
class TabKeyMapper < Twterm::KeyMapper::AbstractKeyMapper
|
6
|
+
DEFAULT_MAPPINGS = {
|
7
|
+
:'1st' => '1',
|
8
|
+
:'2nd' => '2',
|
9
|
+
:'3rd' => '3',
|
10
|
+
:'4th' => '4',
|
11
|
+
:'5th' => '5',
|
12
|
+
:'6th' => '6',
|
13
|
+
:'7th' => '7',
|
14
|
+
:'8th' => '8',
|
15
|
+
:'9th' => '9',
|
16
|
+
close: 'w',
|
17
|
+
find_next: 'n',
|
18
|
+
find_previous: 'N',
|
19
|
+
last: '0',
|
20
|
+
new: '^T',
|
21
|
+
reload: '^R',
|
22
|
+
search_down: '/',
|
23
|
+
search_up: '?',
|
24
|
+
}.freeze
|
25
|
+
|
26
|
+
def self.category
|
27
|
+
'tab'.freeze
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
require 'toml'
|
2
|
+
require 'singleton'
|
3
|
+
|
4
|
+
require 'twterm/app'
|
5
|
+
require 'twterm/key_mapper/abstract_key_mapper'
|
6
|
+
require 'twterm/key_mapper/app_key_mapper'
|
7
|
+
require 'twterm/key_mapper/cursor_key_mapper'
|
8
|
+
require 'twterm/key_mapper/general_key_mapper.rb'
|
9
|
+
require 'twterm/key_mapper/no_such_command'
|
10
|
+
require 'twterm/key_mapper/no_such_key'
|
11
|
+
require 'twterm/key_mapper/status_key_mapper.rb'
|
12
|
+
require 'twterm/key_mapper/tab_key_mapper'
|
13
|
+
|
14
|
+
module Twterm
|
15
|
+
class KeyMapper
|
16
|
+
include Singleton
|
17
|
+
|
18
|
+
MAPPERS = {
|
19
|
+
app: AppKeyMapper,
|
20
|
+
cursor: CursorKeyMapper,
|
21
|
+
general: GeneralKeyMapper,
|
22
|
+
status: StatusKeyMapper,
|
23
|
+
tab: TabKeyMapper,
|
24
|
+
}.freeze
|
25
|
+
|
26
|
+
def initialize
|
27
|
+
create_default_dict_file! unless dict_file_exists?
|
28
|
+
load_dict_file!
|
29
|
+
end
|
30
|
+
|
31
|
+
def [](category, kind)
|
32
|
+
(@mappings[category] || {})[kind]
|
33
|
+
end
|
34
|
+
|
35
|
+
def as_string(category, kind)
|
36
|
+
key = self[category, kind]
|
37
|
+
|
38
|
+
case key
|
39
|
+
when '!'..'}' then key
|
40
|
+
when Curses::Key::F1 then 'F1'
|
41
|
+
when Curses::Key::F2 then 'F2'
|
42
|
+
when Curses::Key::F3 then 'F3'
|
43
|
+
when Curses::Key::F4 then 'F4'
|
44
|
+
when Curses::Key::F5 then 'F5'
|
45
|
+
when Curses::Key::F6 then 'F6'
|
46
|
+
when Curses::Key::F7 then 'F7'
|
47
|
+
when Curses::Key::F8 then 'F8'
|
48
|
+
when Curses::Key::F9 then 'F9'
|
49
|
+
when Curses::Key::F10 then 'F10'
|
50
|
+
when Curses::Key::F11 then 'F11'
|
51
|
+
when Curses::Key::F12 then 'F12'
|
52
|
+
when 1..26 then "^#{(key + 'A'.ord - 1).chr}"
|
53
|
+
else ''
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def assign_mappings!(dict)
|
60
|
+
@mappings = MAPPERS
|
61
|
+
.map { |c, m| { c => m.new(dict[c]) } }
|
62
|
+
.reduce({}) { |acc, x| acc.merge(x) }
|
63
|
+
rescue NoSuchCommand => e
|
64
|
+
warn "Unrecognized command detected: #{e.full_command}"
|
65
|
+
warn 'Make sure you have specified the correct command'
|
66
|
+
warn "Your key assignments are defined in #{dict_file_path}"
|
67
|
+
|
68
|
+
exit
|
69
|
+
rescue NoSuchKey => e
|
70
|
+
warn "Unrecognized key detected: #{e.key}"
|
71
|
+
warn 'Make sure you have specified the correct key'
|
72
|
+
warn "Your key assignments are defined in #{dict_file_path}"
|
73
|
+
|
74
|
+
exit
|
75
|
+
end
|
76
|
+
|
77
|
+
def create_default_dict_file!
|
78
|
+
dict = TOML.dump(default_mappings).gsub("\n[", "\n\n[")
|
79
|
+
File.open(dict_file_path, 'w', 0644) { |f| f.write(dict) }
|
80
|
+
end
|
81
|
+
|
82
|
+
def default_mappings
|
83
|
+
MAPPERS.map { |key, klass| [key, klass::DEFAULT_MAPPINGS] }.to_h
|
84
|
+
end
|
85
|
+
|
86
|
+
def dict_file_exists?
|
87
|
+
File.exist?(dict_file_path)
|
88
|
+
end
|
89
|
+
|
90
|
+
def dict_file_path
|
91
|
+
"#{App::DATA_DIR}/keys.toml".freeze
|
92
|
+
end
|
93
|
+
|
94
|
+
def getc
|
95
|
+
system('stty raw -echo')
|
96
|
+
STDIN.getc
|
97
|
+
ensure
|
98
|
+
system('stty -raw echo')
|
99
|
+
end
|
100
|
+
|
101
|
+
def load_dict_file!
|
102
|
+
dict = TOML.load_file(dict_file_path, symbolize_keys: true)
|
103
|
+
rescue TOML::ParseError, TOML::ValueOverwriteError => e
|
104
|
+
first_line =
|
105
|
+
case e
|
106
|
+
when TOML::ParseError
|
107
|
+
"Your key assignments dictionary file (#{dict_file_path}) could not be parsed"
|
108
|
+
when TOML::ValueOverwriteError
|
109
|
+
"Command `#{e.key}` is declared more than once"
|
110
|
+
end
|
111
|
+
|
112
|
+
warn <<-EOS
|
113
|
+
#{first_line}
|
114
|
+
Falling back to the default key assignments
|
115
|
+
|
116
|
+
Check the syntax and edit the file manually,
|
117
|
+
or remove it and launch twterm again to restore
|
118
|
+
|
119
|
+
Press any key to continue
|
120
|
+
EOS
|
121
|
+
|
122
|
+
getc
|
123
|
+
ensure
|
124
|
+
assign_mappings!(dict || default_mappings)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
data/lib/twterm/list.rb
CHANGED
@@ -2,8 +2,6 @@ module Twterm
|
|
2
2
|
class List
|
3
3
|
attr_reader :id, :name, :slug, :full_name, :mode, :description, :member_count, :subscriber_count
|
4
4
|
|
5
|
-
@@instances = {}
|
6
|
-
|
7
5
|
def ==(other)
|
8
6
|
other.is_a?(self.class) && id == other.id
|
9
7
|
end
|
@@ -11,13 +9,6 @@ module Twterm
|
|
11
9
|
def initialize(list)
|
12
10
|
@id = list.id
|
13
11
|
update!(list)
|
14
|
-
|
15
|
-
@@instances[@id] = self
|
16
|
-
self
|
17
|
-
end
|
18
|
-
|
19
|
-
def matches?(query)
|
20
|
-
[full_name, description].any? { |x| x.downcase.include?(query.downcase) }
|
21
12
|
end
|
22
13
|
|
23
14
|
def update!(list)
|
@@ -31,27 +22,5 @@ module Twterm
|
|
31
22
|
|
32
23
|
self
|
33
24
|
end
|
34
|
-
|
35
|
-
def self.all
|
36
|
-
@@instances.values
|
37
|
-
end
|
38
|
-
|
39
|
-
def self.find(id)
|
40
|
-
@@instances[id]
|
41
|
-
end
|
42
|
-
|
43
|
-
def self.find_or_fetch(id)
|
44
|
-
Promise.new do |resolve, reject|
|
45
|
-
instance = find(id)
|
46
|
-
(resolve.(instance) && next) if instance
|
47
|
-
|
48
|
-
Client.current.list(id).then { |list| resolve.(list) }
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
def self.new(list)
|
53
|
-
instance = find(list.id)
|
54
|
-
instance.nil? ? super : instance.update!(list)
|
55
|
-
end
|
56
25
|
end
|
57
26
|
end
|
data/lib/twterm/notifier.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'twterm/subscriber'
|
2
2
|
require 'twterm/event/favorite'
|
3
|
-
require 'twterm/event/notification'
|
3
|
+
require 'twterm/event/notification/abstract_notification'
|
4
4
|
require 'twterm/event/screen/resize'
|
5
5
|
|
6
6
|
module Twterm
|
@@ -14,15 +14,15 @@ module Twterm
|
|
14
14
|
@queue = Queue.new
|
15
15
|
|
16
16
|
subscribe(Event::Favorite) do |e|
|
17
|
-
|
17
|
+
next if e.source.id == e.authenticating_user.user_id
|
18
18
|
|
19
19
|
msg = '@%s has favorited your tweet: %s' % [
|
20
20
|
e.source.screen_name, e.target.text
|
21
21
|
]
|
22
|
-
|
22
|
+
show_info(msg)
|
23
23
|
end
|
24
24
|
|
25
|
-
subscribe(Event::Notification) do |e|
|
25
|
+
subscribe(Event::Notification::AbstractNotification) do |e|
|
26
26
|
queue(e)
|
27
27
|
end
|
28
28
|
|
@@ -37,8 +37,8 @@ module Twterm
|
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
40
|
-
def
|
41
|
-
notification = Event::Notification.new(
|
40
|
+
def show_info(message)
|
41
|
+
notification = Event::Notification::Info.new(message)
|
42
42
|
@queue.push(notification)
|
43
43
|
self
|
44
44
|
end
|
@@ -51,7 +51,7 @@ module Twterm
|
|
51
51
|
|
52
52
|
@window.clear
|
53
53
|
|
54
|
-
if notification.is_a?(Event::Notification)
|
54
|
+
if notification.is_a?(Event::Notification::AbstractNotification)
|
55
55
|
fg_color, bg_color = notification.color
|
56
56
|
|
57
57
|
@window.with_color(fg_color, bg_color) do
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'concurrent'
|
2
|
+
|
3
|
+
require 'twterm/repository/abstract_repository'
|
4
|
+
|
5
|
+
module Twterm
|
6
|
+
module Repository
|
7
|
+
class AbstractEntityRepository < AbstractRepository
|
8
|
+
def create(*args)
|
9
|
+
invoke_callbacks(:before_create, *args)
|
10
|
+
|
11
|
+
existing_instance = find(extract_key(args))
|
12
|
+
|
13
|
+
instance = existing_instance.nil? ? type.new(*args) : existing_instance.update!(*args)
|
14
|
+
|
15
|
+
store(instance)
|
16
|
+
|
17
|
+
invoke_callbacks(:after_create, instance)
|
18
|
+
|
19
|
+
instance
|
20
|
+
end
|
21
|
+
|
22
|
+
def find(key)
|
23
|
+
repository[key]
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def empty_repository
|
29
|
+
Concurrent::Hash.new
|
30
|
+
end
|
31
|
+
|
32
|
+
def extract_key(args)
|
33
|
+
args[0].id
|
34
|
+
end
|
35
|
+
|
36
|
+
def store(instance)
|
37
|
+
repository[instance.id] = instance
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'concurrent'
|
2
|
+
|
3
|
+
require 'twterm/repository/abstract_entity_repository'
|
4
|
+
|
5
|
+
module Twterm
|
6
|
+
module Repository
|
7
|
+
class AbstractExpirableEntityRepository < AbstractEntityRepository
|
8
|
+
def initialize
|
9
|
+
super
|
10
|
+
@touched_at = Concurrent::Hash.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def create(args, *)
|
14
|
+
touch(args.id)
|
15
|
+
super
|
16
|
+
end
|
17
|
+
|
18
|
+
def find(id)
|
19
|
+
touch(id)
|
20
|
+
super
|
21
|
+
end
|
22
|
+
|
23
|
+
def expire(threshold)
|
24
|
+
now = Time.now
|
25
|
+
repository.delete_if { |id, _| !@touched_at[id] || @touched_at[id] + threshold < now }
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def touch(id)
|
31
|
+
@touched_at[id] = Time.now
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Twterm
|
2
|
+
module Repository
|
3
|
+
class AbstractRepository
|
4
|
+
def initialize
|
5
|
+
@repository = empty_repository
|
6
|
+
|
7
|
+
@callbacks = {
|
8
|
+
after_create: [],
|
9
|
+
before_create: [],
|
10
|
+
}
|
11
|
+
end
|
12
|
+
|
13
|
+
def after_create(&block)
|
14
|
+
@callbacks[:after_create] << block
|
15
|
+
end
|
16
|
+
|
17
|
+
def before_create(&block)
|
18
|
+
@callbacks[:before_create] << block
|
19
|
+
end
|
20
|
+
|
21
|
+
def create(*args)
|
22
|
+
invoke_callbacks(:before_create, *args)
|
23
|
+
|
24
|
+
existing_instance = find(extract_key(args))
|
25
|
+
|
26
|
+
instance = existing_instance.nil? ? type.new(*args) : existing_instance
|
27
|
+
|
28
|
+
store(instance) if existing_instance.nil?
|
29
|
+
|
30
|
+
invoke_callbacks(:after_create, instance)
|
31
|
+
|
32
|
+
instance
|
33
|
+
end
|
34
|
+
|
35
|
+
def find(_key)
|
36
|
+
raise NotImplementedError, '`find` must be implemented'
|
37
|
+
end
|
38
|
+
|
39
|
+
def type
|
40
|
+
raise NotImplementedError, '`type` must be implemented'
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
attr_reader :repository
|
46
|
+
|
47
|
+
def empty_repository
|
48
|
+
raise NotImplementedError, '`empty_repository` must be implemented'
|
49
|
+
end
|
50
|
+
|
51
|
+
def extract_key(_args)
|
52
|
+
raise NotImplementedError, '`extract_key` must be implemented'
|
53
|
+
end
|
54
|
+
|
55
|
+
def invoke_callbacks(type, *args)
|
56
|
+
@callbacks[type].each { |cb| cb.call(*args) }
|
57
|
+
end
|
58
|
+
|
59
|
+
def store(_instance)
|
60
|
+
raise NotImplementedError, '`store` must be implemented'
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'twterm/direct_message'
|
2
|
+
require 'twterm/repository/abstract_entity_repository'
|
3
|
+
|
4
|
+
module Twterm
|
5
|
+
module Repository
|
6
|
+
class DirectMessageRepository < AbstractEntityRepository
|
7
|
+
private
|
8
|
+
|
9
|
+
def type
|
10
|
+
DirectMessage
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'concurrent'
|
2
|
+
|
3
|
+
require 'twterm/friendship'
|
4
|
+
require 'twterm/repository//abstract_repository'
|
5
|
+
|
6
|
+
module Twterm
|
7
|
+
module Repository
|
8
|
+
class FriendshipRepository < AbstractRepository
|
9
|
+
private :create
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
super
|
13
|
+
|
14
|
+
@user_ids = Set.new
|
15
|
+
@m = Mutex.new
|
16
|
+
end
|
17
|
+
|
18
|
+
def already_looked_up?(user_id)
|
19
|
+
@m.synchronize { @user_ids.include?(user_id) }
|
20
|
+
end
|
21
|
+
|
22
|
+
def block(from, to)
|
23
|
+
create(:blocking, from, to)
|
24
|
+
end
|
25
|
+
|
26
|
+
def blocking?(from, to)
|
27
|
+
!find([:blocking, from, to]).nil?
|
28
|
+
end
|
29
|
+
|
30
|
+
def cancel_follow_request(from, to)
|
31
|
+
create(:following_requested, from, to)
|
32
|
+
end
|
33
|
+
|
34
|
+
def delete(status, from, to)
|
35
|
+
repository[status].delete_if { |f| f.status == status && f.from == from && f.to == to }
|
36
|
+
end
|
37
|
+
|
38
|
+
def follow(from, to)
|
39
|
+
create(:following, from, to)
|
40
|
+
end
|
41
|
+
|
42
|
+
def following?(from, to)
|
43
|
+
!find([:following, from, to]).nil?
|
44
|
+
end
|
45
|
+
|
46
|
+
def following_not_requested(from, to)
|
47
|
+
delete(:following_requested, from, to)
|
48
|
+
end
|
49
|
+
|
50
|
+
def following_requested(from, to)
|
51
|
+
create(:following_requested, from, to)
|
52
|
+
end
|
53
|
+
|
54
|
+
def following_requested?(from, to)
|
55
|
+
!find([:following_requested, from, to]).nil?
|
56
|
+
end
|
57
|
+
|
58
|
+
def looked_up!(user_id)
|
59
|
+
@m.synchronize { @user_ids << user_id }
|
60
|
+
user_id
|
61
|
+
end
|
62
|
+
|
63
|
+
def mute(from, to)
|
64
|
+
create(:muting, from, to)
|
65
|
+
end
|
66
|
+
|
67
|
+
def muting?(from, to)
|
68
|
+
!find([:muting, from, to]).nil?
|
69
|
+
end
|
70
|
+
|
71
|
+
def unblock(from, to)
|
72
|
+
delete(:blocking, from, to)
|
73
|
+
end
|
74
|
+
|
75
|
+
def unfollow(from, to)
|
76
|
+
delete(:following, from, to)
|
77
|
+
end
|
78
|
+
|
79
|
+
def unmute(from, to)
|
80
|
+
delete(:muting, from, to)
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def find(key)
|
86
|
+
status, from, to = key
|
87
|
+
|
88
|
+
repository[status].find { |f| f.from == from && f.to == to }
|
89
|
+
end
|
90
|
+
|
91
|
+
def extract_key(args)
|
92
|
+
args
|
93
|
+
end
|
94
|
+
|
95
|
+
def empty_repository
|
96
|
+
Friendship::STATUSES.map { |s| [s, Concurrent::Array.new] }.to_h
|
97
|
+
end
|
98
|
+
|
99
|
+
def store(instance)
|
100
|
+
repository[instance.status] << instance
|
101
|
+
end
|
102
|
+
|
103
|
+
def type
|
104
|
+
Friendship
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'twterm/repository/abstract_repository'
|
2
|
+
|
3
|
+
module Twterm
|
4
|
+
module Repository
|
5
|
+
class HashtagRepository < AbstractRepository
|
6
|
+
def initialize
|
7
|
+
@m = Mutex.new
|
8
|
+
|
9
|
+
super
|
10
|
+
end
|
11
|
+
|
12
|
+
def all
|
13
|
+
repository.to_a
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def empty_repository
|
19
|
+
Set.new
|
20
|
+
end
|
21
|
+
|
22
|
+
def extract_key(args)
|
23
|
+
args[0]
|
24
|
+
end
|
25
|
+
|
26
|
+
def find(key)
|
27
|
+
repository.include?(key) ? key : nil
|
28
|
+
end
|
29
|
+
|
30
|
+
def store(hashtag)
|
31
|
+
@m.synchronize { repository << hashtag }
|
32
|
+
end
|
33
|
+
|
34
|
+
def type
|
35
|
+
String
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'twterm/repository/abstract_expirable_entity_repository'
|
2
|
+
require 'twterm/status'
|
3
|
+
|
4
|
+
module Twterm
|
5
|
+
module Repository
|
6
|
+
class StatusRepository < AbstractExpirableEntityRepository
|
7
|
+
def all
|
8
|
+
repository.values
|
9
|
+
end
|
10
|
+
|
11
|
+
def create(tweet, is_retweeted_status = false)
|
12
|
+
create(tweet.retweeted_status, true) unless tweet.retweeted_status.is_a?(Twitter::NullObject)
|
13
|
+
super
|
14
|
+
end
|
15
|
+
|
16
|
+
def delete(id)
|
17
|
+
@touched_at.delete(id)
|
18
|
+
repository.delete(id)
|
19
|
+
end
|
20
|
+
|
21
|
+
def find_replies_for(id)
|
22
|
+
repository.values.select { |s| s.in_reply_to_status_id == id }
|
23
|
+
end
|
24
|
+
|
25
|
+
def ids
|
26
|
+
repository.keys
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def type
|
32
|
+
Status
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'twterm/repository/abstract_expirable_entity_repository'
|
2
|
+
require 'twterm/user'
|
3
|
+
|
4
|
+
module Twterm
|
5
|
+
module Repository
|
6
|
+
class UserRepository < AbstractExpirableEntityRepository
|
7
|
+
def all
|
8
|
+
repository.values
|
9
|
+
end
|
10
|
+
|
11
|
+
def ids
|
12
|
+
repository.keys
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def type
|
18
|
+
User
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|