twterm 1.3.0 → 2.0.0.beta1
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.
- 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
|