thimbl 0.1.2 → 0.2.0
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/Manifest +5 -2
- data/README.md +21 -18
- data/Rakefile +1 -1
- data/bin/thimblr +4 -7
- data/lib/thimbl.rb +1 -0
- data/lib/thimbl/base.rb +118 -103
- data/lib/thimbl/command.rb +68 -53
- data/test/fixtures/messages_1.json +22 -0
- data/test/fixtures/messages_2.json +12 -0
- data/test/fixtures/messages_3.json +12 -0
- data/test/fixtures/official_plan.json +69 -0
- data/test/fixtures/print.txt +6 -0
- data/test/test_helper.rb +3 -1
- data/test/thimbl_base_test.rb +120 -103
- data/test/thimbl_command_test.rb +58 -56
- data/test/thimbl_utils_test.rb +1 -1
- data/thimbl.gemspec +3 -3
- metadata +9 -6
- data/test/fixtures/cache.json +0 -146
- data/test/fixtures/cache_error_nil.json +0 -487
data/Manifest
CHANGED
@@ -9,10 +9,13 @@ lib/thimbl/base.rb
|
|
9
9
|
lib/thimbl/command.rb
|
10
10
|
lib/thimbl/finger.rb
|
11
11
|
lib/thimbl/utils.rb
|
12
|
-
test/fixtures/cache.json
|
13
|
-
test/fixtures/cache_error_nil.json
|
14
12
|
test/fixtures/finger_dk_telekommunisten_org.txt
|
15
13
|
test/fixtures/finger_dk_telekommunisten_org_two_break_lines.txt
|
14
|
+
test/fixtures/messages_1.json
|
15
|
+
test/fixtures/messages_2.json
|
16
|
+
test/fixtures/messages_3.json
|
17
|
+
test/fixtures/official_plan.json
|
18
|
+
test/fixtures/print.txt
|
16
19
|
test/test_helper.rb
|
17
20
|
test/thimbl_base_test.rb
|
18
21
|
test/thimbl_command_test.rb
|
data/README.md
CHANGED
@@ -6,15 +6,20 @@ I have follow the style of the [Thimbl Python client](https://github.com/blippy/
|
|
6
6
|
|
7
7
|
## Commands
|
8
8
|
|
9
|
-
* follow
|
10
9
|
* fetch
|
10
|
+
* follow
|
11
11
|
* post
|
12
|
-
* print
|
13
12
|
* push
|
14
13
|
|
14
|
+
## Attributes
|
15
|
+
|
16
|
+
* messages
|
17
|
+
* following
|
18
|
+
* properties
|
19
|
+
|
15
20
|
## Version
|
16
21
|
|
17
|
-
This version is in development,
|
22
|
+
This version is in development, use it in production environment under your own responsability.
|
18
23
|
|
19
24
|
## Install
|
20
25
|
|
@@ -24,18 +29,20 @@ This version is in development, not ready for any production environment.
|
|
24
29
|
|
25
30
|
require 'rubygems'
|
26
31
|
require 'thimbl'
|
27
|
-
thimbl =
|
32
|
+
thimbl =
|
28
33
|
Thimbl::Base.new(
|
29
|
-
'
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
34
|
+
'user@thimbl.net',
|
35
|
+
{
|
36
|
+
:bio => 'my bio',
|
37
|
+
:website => 'my website',
|
38
|
+
:mobile => 'my mobile',
|
39
|
+
:email => 'my email',
|
40
|
+
:name => 'my name'
|
41
|
+
}
|
35
42
|
)
|
36
43
|
thimbl.follow 'dk', 'dk@telekommunisten.org'
|
37
44
|
thimbl.fetch
|
38
|
-
thimbl.
|
45
|
+
thimbl.messages
|
39
46
|
thimbl.post 'My first post'
|
40
47
|
thimbl.push 'password'
|
41
48
|
|
@@ -44,15 +51,11 @@ This version is in development, not ready for any production environment.
|
|
44
51
|
The gem comes with a *shell command*, you can use it like this:
|
45
52
|
|
46
53
|
thimblr setup 'user@thimblrserver.com'
|
47
|
-
thimblr follow 'dk' 'dk@telekommunisten.org'
|
48
|
-
thimblr fetch
|
54
|
+
thimblr follow 'dk' 'dk@telekommunisten.org' 'my password'
|
49
55
|
thimblr print
|
50
|
-
thimblr post
|
51
|
-
thimblr push <password>
|
56
|
+
thimblr post 'My first message :)' 'my password'
|
52
57
|
|
53
58
|
## TODO
|
54
59
|
|
55
60
|
* Support *simbolize* hash keys
|
56
|
-
*
|
57
|
-
* thimbl.unfollow
|
58
|
-
* ERROR: If finger respond empty Plan
|
61
|
+
* Reply to another message support
|
data/Rakefile
CHANGED
@@ -2,7 +2,7 @@ require 'rubygems'
|
|
2
2
|
require 'rake'
|
3
3
|
require 'echoe'
|
4
4
|
|
5
|
-
Echoe.new('thimbl', '0.
|
5
|
+
Echoe.new('thimbl', '0.2.0') do |p|
|
6
6
|
p.description = "Small client for the distributed microbloging protocol: [thimbl](http://www.thimbl.net/)"
|
7
7
|
p.url = "http://github.com/fguillen/ThimblClient"
|
8
8
|
p.author = "Fernando Guillen"
|
data/bin/thimblr
CHANGED
@@ -3,10 +3,8 @@
|
|
3
3
|
# Use:
|
4
4
|
# thimblr setup 'user@thimblrserver.com'
|
5
5
|
# thimblr follow 'dk' 'dk@telekommunisten.org'
|
6
|
-
# thimblr fetch
|
7
6
|
# thimblr print
|
8
7
|
# thimblr post "My first message :)"
|
9
|
-
# thimblr push
|
10
8
|
|
11
9
|
begin
|
12
10
|
require 'thimbl'
|
@@ -16,18 +14,17 @@ rescue LoadError
|
|
16
14
|
end
|
17
15
|
|
18
16
|
if ARGV.empty?
|
19
|
-
puts "use: $
|
17
|
+
puts "use: $ thimblr <command>"
|
20
18
|
exit 1
|
21
19
|
end
|
22
20
|
|
23
21
|
begin
|
24
22
|
case ARGV[0]
|
25
23
|
when 'setup'; Thimbl::Command.setup( *ARGV[1..4] ); puts "setup completed"
|
24
|
+
when 'version'; puts Thimbl::Command.version
|
26
25
|
when 'print'; puts Thimbl::Command.print
|
27
|
-
when '
|
28
|
-
when '
|
29
|
-
when 'push'; Thimbl::Command.push ARGV[1]; puts "push completed"
|
30
|
-
when 'follow'; Thimbl::Command.follow( ARGV[1], ARGV[2] ); puts "follow completed"
|
26
|
+
when 'post'; Thimbl::Command.post( ARGV[1], ARGV[2] ); puts "post completed"
|
27
|
+
when 'follow'; Thimbl::Command.follow( ARGV[1], ARGV[2], ARGV[3] ); puts "follow completed"
|
31
28
|
else
|
32
29
|
puts "command not valid '#{ARGV[0]}'"
|
33
30
|
exit 1
|
data/lib/thimbl.rb
CHANGED
data/lib/thimbl/base.rb
CHANGED
@@ -1,78 +1,67 @@
|
|
1
1
|
# Thimbl ruby client
|
2
2
|
#
|
3
3
|
# Author: fernandoguillen.info
|
4
|
-
#
|
5
4
|
# Code: https://github.com/fguillen/ThimblClient
|
6
|
-
#
|
7
5
|
# Use:
|
8
6
|
# require 'rubygems'
|
9
7
|
# require 'thimbl'
|
10
8
|
# thimbl =
|
11
9
|
# Thimbl::Base.new(
|
12
|
-
# '
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
10
|
+
# 'user@thimbl.net',
|
11
|
+
# {
|
12
|
+
# :bio => 'my bio',
|
13
|
+
# :website => 'my website',
|
14
|
+
# :mobile => 'my mobile',
|
15
|
+
# :email => 'my email',
|
16
|
+
# :name => 'my name'
|
17
|
+
# }
|
18
18
|
# )
|
19
19
|
# thimbl.follow 'dk', 'dk@telekommunisten.org'
|
20
20
|
# thimbl.fetch
|
21
|
-
# thimbl.
|
21
|
+
# thimbl.messages
|
22
22
|
# thimbl.post 'My first post'
|
23
|
-
# thimbl.push
|
23
|
+
# thimbl.push 'password'
|
24
24
|
#
|
25
25
|
module Thimbl
|
26
|
+
class NoPlanException < Exception; end
|
26
27
|
class Base
|
27
|
-
attr_accessor :data
|
28
|
+
attr_accessor :data, :address
|
28
29
|
|
29
|
-
# Initialize a new configuration
|
30
|
-
# will delete any thing in the `thimbl.plan_path` file and `thimbl.cache_path` file.
|
30
|
+
# Initialize a new configuration
|
31
31
|
#
|
32
32
|
# Use:
|
33
33
|
# thimbl =
|
34
34
|
# Thimbl::Base.new(
|
35
|
-
#
|
36
|
-
#
|
37
|
-
#
|
38
|
-
#
|
39
|
-
#
|
40
|
-
#
|
41
|
-
#
|
35
|
+
# 'user@thimbl.net',
|
36
|
+
# {
|
37
|
+
# :bio => 'bio',
|
38
|
+
# :website => 'website',
|
39
|
+
# :mobile => 'mobile',
|
40
|
+
# :email => 'email',
|
41
|
+
# :name => 'name'
|
42
|
+
# }
|
43
|
+
# )
|
42
44
|
#
|
43
45
|
# or just:
|
44
46
|
#
|
45
|
-
# thimbl = Thimbl::Base.new
|
46
|
-
def initialize( opts = {} )
|
47
|
-
|
48
|
-
'bio' => 'bio',
|
49
|
-
'website' => 'website',
|
50
|
-
'mobile' => 'mobile',
|
51
|
-
'email' => 'email',
|
52
|
-
'address' => 'address',
|
53
|
-
'name' => 'name'
|
54
|
-
}.merge( opts )
|
55
|
-
|
47
|
+
# thimbl = Thimbl::Base.new( 'user@thimbl.net' )
|
48
|
+
def initialize( address, opts = {} )
|
49
|
+
@address = address
|
56
50
|
@data = {
|
57
|
-
'
|
58
|
-
'
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
'following' => [],
|
68
|
-
'messages' => [],
|
69
|
-
'replies' => {}
|
70
|
-
}
|
71
|
-
}
|
51
|
+
'name' => opts[:name],
|
52
|
+
'bio' => opts[:bio],
|
53
|
+
'properties' => {
|
54
|
+
'email' => opts[:email],
|
55
|
+
'mobile' => opts[:mobile],
|
56
|
+
'website' => opts[:website]
|
57
|
+
},
|
58
|
+
'following' => [],
|
59
|
+
'messages' => [],
|
60
|
+
'replies' => {}
|
72
61
|
}
|
73
62
|
end
|
74
63
|
|
75
|
-
# Post a new message in
|
64
|
+
# Post a new message in user's time-line.
|
76
65
|
# _This method doesn't push the modifications to de server._
|
77
66
|
def post( text )
|
78
67
|
message = {
|
@@ -80,99 +69,125 @@ module Thimbl
|
|
80
69
|
'text' => text
|
81
70
|
}
|
82
71
|
|
83
|
-
data['
|
72
|
+
data['messages'] << message
|
84
73
|
end
|
85
74
|
|
86
|
-
# Post a new message in
|
75
|
+
# Post a new message in user's time-line and push the modifications to the server.
|
87
76
|
def post!( text, password )
|
88
77
|
post text
|
89
78
|
push password
|
90
79
|
end
|
91
80
|
|
92
|
-
# Add a new user to
|
81
|
+
# Add a new user to user's following
|
93
82
|
# _This method doesn't push the modifications to de server._
|
94
83
|
def follow( follow_nick, follow_address )
|
95
|
-
return if
|
96
|
-
data['
|
84
|
+
return if following.count { |e| e.address == follow_address } != 0
|
85
|
+
data['following'] << { 'nick' => follow_nick, 'address' => follow_address }
|
97
86
|
end
|
98
87
|
|
99
|
-
# Add a new user to
|
88
|
+
# Add a new user to user's following and push the modifications to the server.
|
100
89
|
def follow!( follow_nick, follow_address, password )
|
101
90
|
follow follow_nick, follow_address
|
102
91
|
push password
|
103
92
|
end
|
104
93
|
|
105
|
-
#
|
106
|
-
#
|
94
|
+
# Remove a user from the user's following
|
95
|
+
# _This method doesn't push the modifications to de server._
|
96
|
+
def unfollow( follow_address )
|
97
|
+
data['following'].delete_if { |e| e['address'] == follow_address }
|
98
|
+
end
|
99
|
+
|
100
|
+
# Remove a new from user's following and push the modifications to the server.
|
101
|
+
def unfollow!( follow_address, password )
|
102
|
+
unfollow follow_address
|
103
|
+
push password
|
104
|
+
end
|
105
|
+
|
106
|
+
# Updating cached .plan
|
107
|
+
# Any not pushed modification will be deleted
|
107
108
|
def fetch
|
108
|
-
|
109
|
-
following_and_me.uniq.each do |address|
|
110
|
-
address_finger = Thimbl::Finger.run address
|
111
|
-
next if address_finger.nil? || address_finger.match(/Plan:\s*(.*)/m).nil?
|
112
|
-
address_plan = address_finger.match(/Plan:\s*(.*)/m)[1].gsub("\\\n",'')
|
113
|
-
data['plans'][address] = JSON.load( address_plan )
|
114
|
-
end
|
109
|
+
@data = JSON.load( fetch_plan )
|
115
110
|
end
|
116
111
|
|
117
|
-
# Send
|
118
|
-
# It requires the password of
|
112
|
+
# Send user's cached .plan to user's server
|
113
|
+
# It requires the password of user's thimbl user
|
119
114
|
def push( password )
|
120
|
-
tmp_path = Thimbl::Utils.to_file
|
121
|
-
Net::SCP.start(
|
115
|
+
tmp_path = Thimbl::Utils.to_file( data.to_json )
|
116
|
+
Net::SCP.start( address.split('@')[1], address.split('@')[0], :password => password ) do |scp|
|
122
117
|
scp.upload!( tmp_path, ".plan" )
|
123
118
|
end
|
124
119
|
end
|
125
120
|
|
126
|
-
#
|
127
|
-
#
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
result =
|
132
|
-
|
133
|
-
|
134
|
-
result
|
135
|
-
|
136
|
-
|
121
|
+
# Returns all this user's messages
|
122
|
+
# in a chronologic order.
|
123
|
+
def messages
|
124
|
+
return [] if data['messages'].nil?
|
125
|
+
|
126
|
+
result = []
|
127
|
+
|
128
|
+
data['messages'].each do |message|
|
129
|
+
result << OpenStruct.new({
|
130
|
+
:address => address,
|
131
|
+
:time => Thimbl::Utils.parse_time( message['time'] ),
|
132
|
+
:text => message['text']
|
133
|
+
})
|
137
134
|
end
|
135
|
+
|
136
|
+
result = result.sort { |a,b| a.time <=> b.time }
|
138
137
|
|
139
138
|
return result
|
140
139
|
end
|
141
|
-
|
142
|
-
# Returns all the
|
143
|
-
|
144
|
-
|
140
|
+
|
141
|
+
# Returns all the info about the users this user is following.
|
142
|
+
def following
|
143
|
+
return [] if data['following'].nil?
|
144
|
+
|
145
145
|
result = []
|
146
146
|
|
147
|
-
data['
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
'time' => Thimbl::Utils.parse_time( message['time'] ),
|
153
|
-
'text' => message['text']
|
154
|
-
}
|
155
|
-
end
|
147
|
+
data['following'].each do |chased|
|
148
|
+
result << OpenStruct.new({
|
149
|
+
:nick => chased['nick'],
|
150
|
+
:address => chased['address']
|
151
|
+
})
|
156
152
|
end
|
157
153
|
|
158
|
-
result = result.sort { |a,b| a['time'] <=> b['time'] }
|
159
|
-
|
160
154
|
return result
|
161
155
|
end
|
162
|
-
|
163
|
-
# Returns the actual thimbl user account
|
164
|
-
def me
|
165
|
-
data['me']
|
166
|
-
end
|
167
156
|
|
168
|
-
# Returns all the
|
169
|
-
def
|
170
|
-
|
157
|
+
# Returns all the user properties
|
158
|
+
def properties
|
159
|
+
OpenStruct.new({
|
160
|
+
:bio => data['bio'],
|
161
|
+
:name => data['name'],
|
162
|
+
:email => data['properties']['email'],
|
163
|
+
:mobile => data['properties']['mobile'],
|
164
|
+
:website => data['properties']['website']
|
165
|
+
})
|
171
166
|
end
|
172
167
|
|
173
|
-
#
|
174
|
-
|
175
|
-
|
168
|
+
# Update all the user properties
|
169
|
+
# _This method doesn't push the modifications to de server._
|
170
|
+
def properties=( opts = {} )
|
171
|
+
data['bio'] = opts[:bio] unless opts[:bio].nil?
|
172
|
+
data['name'] = opts[:name] unless opts[:name].nil?
|
173
|
+
data['properties']['email'] = opts[:email] unless opts[:email].nil?
|
174
|
+
data['properties']['mobile'] = opts[:mobile] unless opts[:mobile].nil?
|
175
|
+
data['properties']['website'] = opts[:website] unless opts[:website].nil?
|
176
176
|
end
|
177
|
+
|
178
|
+
private
|
179
|
+
|
180
|
+
def fetch_plan
|
181
|
+
finger_response = Thimbl::Finger.run address
|
182
|
+
|
183
|
+
if( finger_response.nil? || finger_response.match(/Plan:\s*(.*)/m).nil? )
|
184
|
+
raise NoPlanException, "Not Thimbl Plan in this address: '#{address}'"
|
185
|
+
end
|
186
|
+
|
187
|
+
finger_plan = finger_response.match(/Plan:\s*(.*)/m)[1].gsub("\\\n",'')
|
188
|
+
|
189
|
+
return finger_plan
|
190
|
+
end
|
191
|
+
|
177
192
|
end
|
178
193
|
end
|
data/lib/thimbl/command.rb
CHANGED
@@ -1,76 +1,91 @@
|
|
1
1
|
module Thimbl
|
2
2
|
class Command
|
3
|
-
|
3
|
+
THIMBL_FOLDER = File.expand_path( "~#{ENV['USER']}/.thimbl" )
|
4
4
|
|
5
|
-
def self.
|
6
|
-
|
7
|
-
raise ArgumentError, "use: $ thimbl setup <thimbl_user>"
|
8
|
-
end
|
9
|
-
|
10
|
-
thimbl = Thimbl::Base.new( 'address' => args[0] )
|
11
|
-
save_cache thimbl.data
|
5
|
+
def self.version
|
6
|
+
"0.2.0"
|
12
7
|
end
|
13
8
|
|
14
|
-
def self.
|
15
|
-
if
|
16
|
-
|
9
|
+
def self.setup( *args )
|
10
|
+
if( args.size != 1 )
|
11
|
+
raise ArgumentError, "use: $ thimblr setup <thimbl_user>"
|
17
12
|
end
|
18
|
-
|
19
|
-
File.
|
20
|
-
|
13
|
+
|
14
|
+
if !File.exists? File.dirname( thimbl_folder )
|
15
|
+
FileUtils.mkdir_p File.dirname( thimbl_folder )
|
21
16
|
end
|
17
|
+
|
18
|
+
save_actual args[0]
|
22
19
|
end
|
23
20
|
|
24
21
|
def self.print
|
25
|
-
thimbl =
|
26
|
-
return thimbl.print
|
27
|
-
end
|
28
|
-
|
29
|
-
def self.fetch
|
30
|
-
thimbl = Thimbl::Command.load
|
22
|
+
thimbl = get_actual
|
31
23
|
thimbl.fetch
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
24
|
+
|
25
|
+
# user's messages
|
26
|
+
messages = thimbl.messages
|
27
|
+
|
28
|
+
# user's following's messages
|
29
|
+
thimbl.following.each do |followed|
|
30
|
+
thimbl = Thimbl::Base.new followed.address
|
31
|
+
begin
|
32
|
+
thimbl.fetch
|
33
|
+
messages += thimbl.messages
|
34
|
+
rescue Thimbl::NoPlanException
|
35
|
+
puts "Error fetching #{followed.address} messages"
|
36
|
+
end
|
38
37
|
end
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
38
|
+
|
39
|
+
messages = messages.sort { |a,b| a.time <=> b.time }
|
40
|
+
|
41
|
+
result = ""
|
42
|
+
messages.each do |message|
|
43
|
+
result += message.time.strftime( '%Y-%m-%d %H:%M:%S' )
|
44
|
+
result += " #{message.address}"
|
45
|
+
result += " > #{message.text}"
|
46
|
+
result += "\n"
|
47
47
|
end
|
48
|
-
|
49
|
-
|
48
|
+
|
49
|
+
return result
|
50
50
|
end
|
51
51
|
|
52
|
-
def self.
|
53
|
-
if(
|
54
|
-
raise ArgumentError, "use: $
|
52
|
+
def self.post( text = nil, password = nil)
|
53
|
+
if( text.nil? || text.empty? || password.nil? )
|
54
|
+
raise ArgumentError, "use: $ thimblr post <message> <password>"
|
55
55
|
end
|
56
|
-
|
57
|
-
thimbl
|
58
|
-
|
56
|
+
|
57
|
+
thimbl = get_actual
|
58
|
+
thimbl.post! text, password
|
59
59
|
end
|
60
60
|
|
61
|
-
def self.
|
62
|
-
if(
|
63
|
-
raise ArgumentError, "
|
61
|
+
def self.follow( nick = nil, address = nil, password = nil )
|
62
|
+
if( nick.nil? || nick.empty? || address.nil? || address.empty? || password.nil? )
|
63
|
+
raise ArgumentError, "use: $ thimblr follow <nick> <address> <password>"
|
64
64
|
end
|
65
|
-
|
66
|
-
thimbl =
|
67
|
-
thimbl.
|
68
|
-
|
69
|
-
return thimbl
|
65
|
+
|
66
|
+
thimbl = get_actual
|
67
|
+
thimbl.follow! nick, address, password
|
70
68
|
end
|
71
69
|
|
72
|
-
|
73
|
-
|
74
|
-
|
70
|
+
private
|
71
|
+
|
72
|
+
def self.save_actual( address )
|
73
|
+
File.open( "#{thimbl_folder}/actual", 'w' ) { |f| f.write address }
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.get_actual
|
77
|
+
if( !File.exists? "#{thimbl_folder}/actual" )
|
78
|
+
raise ArgumentError, "Thimbl need to setup, use: $ thimblr setup <thimbl_user>"
|
79
|
+
end
|
80
|
+
|
81
|
+
thimbl = Thimbl::Base.new File.read( "#{thimbl_folder}/actual" )
|
82
|
+
thimbl.fetch
|
83
|
+
|
84
|
+
return thimbl
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.thimbl_folder
|
88
|
+
THIMBL_FOLDER
|
89
|
+
end
|
75
90
|
end
|
76
91
|
end
|