thimbl 0.0.1 → 0.0.2
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/Gemfile +6 -0
- data/Gemfile.lock +42 -0
- data/Manifest +10 -3
- data/README.md +43 -10
- data/Rakefile +3 -3
- data/bin/thimbl +36 -0
- data/lib/thimbl.rb +5 -123
- data/lib/thimbl/base.rb +223 -0
- data/lib/thimbl/command.rb +71 -0
- data/lib/thimbl/finger.rb +9 -0
- data/test/fixtures/finger_dk_telekommunisten_org.txt +11 -0
- data/test/fixtures/finger_dk_telekommunisten_org_two_break_lines.txt +10 -0
- data/test/test_helper.rb +5 -0
- data/test/thimbl_base_test.rb +220 -0
- data/test/thimbl_command_test.rb +106 -0
- data/thimbl.gemspec +19 -5
- metadata +83 -15
- data/lib/finger.rb +0 -5
- data/test/thimbl_test.rb +0 -89
- data/thimbl.rb +0 -1
data/Gemfile
CHANGED
data/Gemfile.lock
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
chronic (0.3.0)
|
5
|
+
columnize (0.3.2)
|
6
|
+
delorean (0.2.1)
|
7
|
+
chronic
|
8
|
+
echoe (4.3.1)
|
9
|
+
gemcutter
|
10
|
+
rubyforge
|
11
|
+
fileutils (0.6)
|
12
|
+
rmagick (>= 2.13.1)
|
13
|
+
gemcutter (0.6.1)
|
14
|
+
json (1.5.1)
|
15
|
+
json_pure (1.5.1)
|
16
|
+
linecache (0.43)
|
17
|
+
mocha (0.9.11)
|
18
|
+
rake
|
19
|
+
net-scp (1.0.4)
|
20
|
+
net-ssh (>= 1.99.1)
|
21
|
+
net-ssh (2.1.0)
|
22
|
+
rake (0.8.7)
|
23
|
+
rmagick (2.13.1)
|
24
|
+
ruby-debug (0.10.4)
|
25
|
+
columnize (>= 0.1)
|
26
|
+
ruby-debug-base (~> 0.10.4.0)
|
27
|
+
ruby-debug-base (0.10.4)
|
28
|
+
linecache (>= 0.3)
|
29
|
+
rubyforge (2.0.4)
|
30
|
+
json_pure (>= 1.1.7)
|
31
|
+
|
32
|
+
PLATFORMS
|
33
|
+
ruby
|
34
|
+
|
35
|
+
DEPENDENCIES
|
36
|
+
delorean
|
37
|
+
echoe
|
38
|
+
fileutils
|
39
|
+
json
|
40
|
+
mocha
|
41
|
+
net-scp
|
42
|
+
ruby-debug
|
data/Manifest
CHANGED
@@ -1,9 +1,16 @@
|
|
1
1
|
Gemfile
|
2
|
+
Gemfile.lock
|
2
3
|
Manifest
|
3
4
|
README.md
|
4
5
|
Rakefile
|
5
|
-
|
6
|
+
bin/thimbl
|
6
7
|
lib/thimbl.rb
|
8
|
+
lib/thimbl/base.rb
|
9
|
+
lib/thimbl/command.rb
|
10
|
+
lib/thimbl/finger.rb
|
7
11
|
test/fixtures/cache.json
|
8
|
-
test/
|
9
|
-
|
12
|
+
test/fixtures/finger_dk_telekommunisten_org.txt
|
13
|
+
test/fixtures/finger_dk_telekommunisten_org_two_break_lines.txt
|
14
|
+
test/test_helper.rb
|
15
|
+
test/thimbl_base_test.rb
|
16
|
+
test/thimbl_command_test.rb
|
data/README.md
CHANGED
@@ -1,6 +1,20 @@
|
|
1
1
|
# Thimbl Client
|
2
2
|
|
3
|
-
Is an small client for the distributed microbloging protocol: [thimbl](http://
|
3
|
+
Is an small client for the distributed microbloging protocol: [thimbl](http://thimbl.net/)
|
4
|
+
|
5
|
+
I have follow the style of the [Thimbl Python client](https://github.com/blippy/Thimbl-CLI) in many ways.
|
6
|
+
|
7
|
+
## Commands
|
8
|
+
|
9
|
+
* setup
|
10
|
+
* follow
|
11
|
+
* fetch
|
12
|
+
* post
|
13
|
+
* print
|
14
|
+
|
15
|
+
## Architecture
|
16
|
+
|
17
|
+
This client is only manipulating json files, the one with your **personal plan** and the other with the **complete cache** of the timeline of the people you are following.
|
4
18
|
|
5
19
|
## Version
|
6
20
|
|
@@ -8,21 +22,40 @@ This version is in development, not ready for any production environment.
|
|
8
22
|
|
9
23
|
## Install
|
10
24
|
|
11
|
-
|
25
|
+
gem install thimbl
|
12
26
|
|
13
27
|
## Use
|
14
28
|
|
29
|
+
require 'rubygems'
|
15
30
|
require 'thimbl'
|
16
|
-
thimbl =
|
31
|
+
thimbl =
|
32
|
+
Thimbl.new(
|
33
|
+
'plan_path' => '/tmp/plan',
|
34
|
+
'cache_path' => '/tmp/thimbl_cache',
|
35
|
+
'user' => 'fguillen@thimblserver.com'
|
36
|
+
'password' => 'my_thimblserver_password'
|
37
|
+
)
|
17
38
|
thimbl.setup(
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
39
|
+
'bio' => 'my bio',
|
40
|
+
'website' => 'my website',
|
41
|
+
'mobile' => 'my mobile',
|
42
|
+
'email' => 'my email',
|
43
|
+
'address' => 'my address',
|
44
|
+
'name' => 'my name'
|
24
45
|
)
|
25
46
|
thimbl.follow 'dk', 'dk@telekommunisten.org'
|
26
47
|
thimbl.fetch
|
27
48
|
thimbl.print
|
28
|
-
thimbl.post 'My first post'
|
49
|
+
thimbl.post 'My first post'
|
50
|
+
thimbl.push
|
51
|
+
|
52
|
+
## TODO
|
53
|
+
|
54
|
+
* Shell script /bin/thimbl
|
55
|
+
* Thinking that the *plan_path* is not needed.
|
56
|
+
* Not save thimbl password, request for it in any *thimbl.push*
|
57
|
+
* Support *simbolize* hash keys
|
58
|
+
* In the Thimbl::Command.setup ask for the rest of the configuration options *bio*, *mobile*, ...
|
59
|
+
* thimbl.unfollow
|
60
|
+
* ERROR: If finger respond empty Plan
|
61
|
+
* ERROR: the message format is without *address* key.
|
data/Rakefile
CHANGED
@@ -2,12 +2,12 @@ require 'rubygems'
|
|
2
2
|
require 'rake'
|
3
3
|
require 'echoe'
|
4
4
|
|
5
|
-
Echoe.new('thimbl', '0.0.
|
5
|
+
Echoe.new('thimbl', '0.0.2') 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"
|
9
9
|
p.email = "fguillen.mail@gmail.com"
|
10
10
|
p.ignore_pattern = ['etc/*']
|
11
|
-
p.development_dependencies = ['mocha', 'ruby-debug']
|
12
|
-
p.runtime_dependencies = []
|
11
|
+
p.development_dependencies = ['mocha', 'ruby-debug', 'delorean']
|
12
|
+
p.runtime_dependencies = ['json', 'net-scp', 'fileutils']
|
13
13
|
end
|
data/bin/thimbl
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Use:
|
4
|
+
# thimbl setup ~/thimbl_plan ~/thimbl_cache 'user@thimblserver.com', 'thimblpass'
|
5
|
+
# thimbl follow 'dk' 'dk@telekommunisten.org'
|
6
|
+
# thimbl fetch
|
7
|
+
# thimbl print
|
8
|
+
# thimbl post "My first message :)"
|
9
|
+
# thimbl push
|
10
|
+
|
11
|
+
begin
|
12
|
+
require 'thimbl'
|
13
|
+
rescue LoadError
|
14
|
+
require 'rubygems'
|
15
|
+
require 'thimbl'
|
16
|
+
end
|
17
|
+
|
18
|
+
if ARGV.empty?
|
19
|
+
puts "use: $ thimbl <command>"
|
20
|
+
exit 1
|
21
|
+
end
|
22
|
+
|
23
|
+
begin
|
24
|
+
case ARGV[0]
|
25
|
+
when 'setup'; Thimbl::Command.setup( *ARGV[1..4] ); puts "setup completed"
|
26
|
+
when 'print'; puts Thimbl::Command.print
|
27
|
+
when 'fetch'; Thimbl::Command.fetch; puts "fecth completed"
|
28
|
+
when 'post'; Thimbl::Command.post ARGV[1]; puts "post completed"
|
29
|
+
when 'push'; Thimbl::Command.push; puts "push completed"
|
30
|
+
when 'follow'; Thimbl::Command.follow( ARGV[1], ARGV[2] ); puts "follow completed"
|
31
|
+
else
|
32
|
+
puts "command not valid '#{ARGV[0]}'"
|
33
|
+
end
|
34
|
+
rescue ArgumentError => e
|
35
|
+
puts e.message
|
36
|
+
end
|
data/lib/thimbl.rb
CHANGED
@@ -1,125 +1,7 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'json'
|
3
|
-
require
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
def initialize( plan_path, cache_path )
|
9
|
-
@plan_path = plan_path
|
10
|
-
@cache_path = cache_path
|
11
|
-
@data = nil
|
12
|
-
@address = nil
|
13
|
-
end
|
14
|
-
|
15
|
-
def setup( opts = {} )
|
16
|
-
opts = {
|
17
|
-
:bio => 'bio',
|
18
|
-
:website => 'website',
|
19
|
-
:mobile => 'mobile',
|
20
|
-
:email => 'email',
|
21
|
-
:address => 'address',
|
22
|
-
:name => 'name'
|
23
|
-
}.merge( opts )
|
24
|
-
|
25
|
-
@data = {
|
26
|
-
'me' => opts[:address],
|
27
|
-
'plans' => {
|
28
|
-
opts[:address] => {
|
29
|
-
'name' => opts[:name],
|
30
|
-
'bio' => opts[:bio],
|
31
|
-
'properties' => {
|
32
|
-
'email' => opts[:email],
|
33
|
-
'mobile' => opts[:mobile],
|
34
|
-
'website' => opts[:website]
|
35
|
-
},
|
36
|
-
'following' => [],
|
37
|
-
'messages' => [],
|
38
|
-
'replies' => {}
|
39
|
-
}
|
40
|
-
}
|
41
|
-
}
|
42
|
-
|
43
|
-
@address = opts[:address]
|
44
|
-
|
45
|
-
save_data
|
46
|
-
|
47
|
-
return self
|
48
|
-
end
|
49
|
-
|
50
|
-
def post( text )
|
51
|
-
load_data
|
52
|
-
|
53
|
-
message = {
|
54
|
-
:address => address,
|
55
|
-
:time => Time.now.strftime('%Y%m%d%H%M%S'),
|
56
|
-
:text => text
|
57
|
-
}
|
58
|
-
|
59
|
-
data['plans'][address]['messages'] << message
|
60
|
-
save_data
|
61
|
-
|
62
|
-
return self
|
63
|
-
end
|
64
|
-
|
65
|
-
def follow( follow_nick, follow_address )
|
66
|
-
load_data
|
67
|
-
data['plans'][address]['following'] << { :nick => follow_nick, :address => follow_address }
|
68
|
-
save_data
|
69
|
-
|
70
|
-
return self
|
71
|
-
end
|
72
|
-
|
73
|
-
def fetch
|
74
|
-
load_data
|
75
|
-
|
76
|
-
following.map { |f| f['address'] }.each do |followed_address|
|
77
|
-
address_finger = Finger.run followed_address
|
78
|
-
address_plan = address_finger.match(/Plan:\s*(.*)/m)[1].gsub("\n",'')
|
79
|
-
data['plans'][followed_address] = JSON.load( address_plan )
|
80
|
-
end
|
81
|
-
|
82
|
-
save_data
|
83
|
-
|
84
|
-
return self
|
85
|
-
end
|
86
|
-
|
87
|
-
def print
|
88
|
-
load_data
|
89
|
-
|
90
|
-
result = ""
|
91
|
-
messages.each do |message|
|
92
|
-
result += Thimbl.parse_time( message['time'] ).strftime( '%Y-%m-%d %H:%M:%S' )
|
93
|
-
result += " #{message['address']}"
|
94
|
-
result += " > #{message['text']}"
|
95
|
-
result += "\n"
|
96
|
-
end
|
97
|
-
|
98
|
-
return result
|
99
|
-
end
|
100
|
-
|
101
|
-
def load_data
|
102
|
-
@data = JSON.load( File.read cache_path )
|
103
|
-
@address = data['me']
|
104
|
-
end
|
105
|
-
|
106
|
-
def save_data
|
107
|
-
File.open( cache_path, 'w' ) { |f| f.write data.to_json }
|
108
|
-
File.open( plan_path, 'w' ) { |f| f.write data['plans'][address].to_json }
|
109
|
-
end
|
110
|
-
|
111
|
-
def following
|
112
|
-
data['plans'][address]['following']
|
113
|
-
end
|
114
|
-
|
115
|
-
def messages
|
116
|
-
_messages = data['plans'].values.map { |e| e['messages'] }.flatten
|
117
|
-
_messages = _messages.sort { |a,b| a['time'] <=> b['time'] }
|
118
|
-
|
119
|
-
return _messages
|
120
|
-
end
|
121
|
-
|
122
|
-
def self.parse_time( time )
|
123
|
-
Time.utc( time[0,4], time[4,2], time[6,2], time[8,2], time[10,2], time[12,2] )
|
124
|
-
end
|
125
|
-
end
|
3
|
+
require 'net/scp'
|
4
|
+
require 'fileutils'
|
5
|
+
require "#{File.dirname(__FILE__)}/thimbl/base"
|
6
|
+
require "#{File.dirname(__FILE__)}/thimbl/command"
|
7
|
+
require "#{File.dirname(__FILE__)}/thimbl/finger"
|
data/lib/thimbl/base.rb
ADDED
@@ -0,0 +1,223 @@
|
|
1
|
+
# Thimbl ruby client
|
2
|
+
#
|
3
|
+
# Author: fernandoguillen.info
|
4
|
+
#
|
5
|
+
# Code: https://github.com/fguillen/ThimblClient
|
6
|
+
#
|
7
|
+
# Use:
|
8
|
+
# require 'rubygems'
|
9
|
+
# require 'thimbl'
|
10
|
+
# thimbl =
|
11
|
+
# Thimbl::Base.new(
|
12
|
+
# :plan_path => '/tmp/plan',
|
13
|
+
# :cache_path => '/tmp/thimbl_cache',
|
14
|
+
# :user => 'fguillen@thimblserver.com'
|
15
|
+
# :password => 'my_thimblserver_password'
|
16
|
+
# )
|
17
|
+
# thimbl.setup(
|
18
|
+
# :bio => 'my bio',
|
19
|
+
# :website => 'my website',
|
20
|
+
# :mobile => 'my mobile',
|
21
|
+
# :email => 'my email',
|
22
|
+
# :address => 'my address',
|
23
|
+
# :name => 'my name'
|
24
|
+
# )
|
25
|
+
# thimbl.follow 'dk', 'dk@telekommunisten.org'
|
26
|
+
# thimbl.fetch
|
27
|
+
# thimbl.print
|
28
|
+
# thimbl.post 'My first post'
|
29
|
+
#
|
30
|
+
module Thimbl
|
31
|
+
class Base
|
32
|
+
attr_reader :plan_path, :cache_path, :data, :address, :user, :password
|
33
|
+
|
34
|
+
# Initialize the thimbl client.
|
35
|
+
#
|
36
|
+
# Use:
|
37
|
+
# Thimbl.new(
|
38
|
+
# :plan_path => <path to the plan file>,
|
39
|
+
# :cache_path => <path to the cache file>
|
40
|
+
# :user => <the user@domain>,
|
41
|
+
# :password => <the user password>
|
42
|
+
# )
|
43
|
+
#
|
44
|
+
def initialize( opts = {} )
|
45
|
+
@plan_path = opts['plan_path']
|
46
|
+
@cache_path = opts['cache_path']
|
47
|
+
@user = opts['user']
|
48
|
+
@password = opts['password']
|
49
|
+
|
50
|
+
@data = nil
|
51
|
+
@address = nil
|
52
|
+
end
|
53
|
+
|
54
|
+
# Setup a new configuration, the execution of this method
|
55
|
+
# will delete any thing in the `thimbl.plan_path` file and `thimbl.cache_path` file.
|
56
|
+
#
|
57
|
+
# Use:
|
58
|
+
# thimbl.setup(
|
59
|
+
# :bio => 'bio',
|
60
|
+
# :website => 'website',
|
61
|
+
# :mobile => 'mobile',
|
62
|
+
# :email => 'email',
|
63
|
+
# :address => 'address',
|
64
|
+
# :name => 'name'
|
65
|
+
# )
|
66
|
+
#
|
67
|
+
def setup( opts = {} )
|
68
|
+
opts = {
|
69
|
+
'bio' => 'bio',
|
70
|
+
'website' => 'website',
|
71
|
+
'mobile' => 'mobile',
|
72
|
+
'email' => 'email',
|
73
|
+
'address' => 'address',
|
74
|
+
'name' => 'name'
|
75
|
+
}.merge( opts )
|
76
|
+
|
77
|
+
@data = {
|
78
|
+
'me' => opts['address'],
|
79
|
+
'plans' => {
|
80
|
+
opts['address'] => {
|
81
|
+
'name' => opts['name'],
|
82
|
+
'bio' => opts['bio'],
|
83
|
+
'properties' => {
|
84
|
+
'email' => opts['email'],
|
85
|
+
'mobile' => opts['mobile'],
|
86
|
+
'website' => opts['website']
|
87
|
+
},
|
88
|
+
'following' => [],
|
89
|
+
'messages' => [],
|
90
|
+
'replies' => {}
|
91
|
+
}
|
92
|
+
}
|
93
|
+
}
|
94
|
+
|
95
|
+
@address = opts['address']
|
96
|
+
|
97
|
+
save_data
|
98
|
+
end
|
99
|
+
|
100
|
+
# Post a new message in your time-line.
|
101
|
+
#
|
102
|
+
# Use:
|
103
|
+
# thimbl.post <message>
|
104
|
+
#
|
105
|
+
# To publish your comment you have to call:
|
106
|
+
# thimbl.push
|
107
|
+
#
|
108
|
+
def post( text )
|
109
|
+
message = {
|
110
|
+
'address' => address,
|
111
|
+
'time' => Time.now.strftime('%Y%m%d%H%M%S'),
|
112
|
+
'text' => text
|
113
|
+
}
|
114
|
+
|
115
|
+
data['plans'][address]['messages'] << message
|
116
|
+
save_data
|
117
|
+
end
|
118
|
+
|
119
|
+
# Add a new user to follow
|
120
|
+
#
|
121
|
+
# Use:
|
122
|
+
# thimbl.follow 'nick', 'address'
|
123
|
+
#
|
124
|
+
# To publish your following users you have to call:
|
125
|
+
# thimbl.push
|
126
|
+
#
|
127
|
+
def follow( follow_nick, follow_address )
|
128
|
+
data['plans'][address]['following'] << { 'nick' => follow_nick, 'address' => follow_address }
|
129
|
+
save_data
|
130
|
+
end
|
131
|
+
|
132
|
+
# Fetch all the info and timelines of all the users you are following.
|
133
|
+
#
|
134
|
+
# Use:
|
135
|
+
# thimbl.fetch
|
136
|
+
#
|
137
|
+
def fetch
|
138
|
+
following.map { |f| f['address'] }.each do |followed_address|
|
139
|
+
address_finger = Thimbl::Finger.run followed_address
|
140
|
+
address_plan = address_finger.match(/Plan:\s*(.*)/m)[1].gsub("\\\n",'')
|
141
|
+
data['plans'][followed_address] = JSON.load( address_plan )
|
142
|
+
end
|
143
|
+
|
144
|
+
save_data
|
145
|
+
end
|
146
|
+
|
147
|
+
# Print every message of you and all the users you are following.
|
148
|
+
#
|
149
|
+
# Use:
|
150
|
+
# thimbl.print
|
151
|
+
# The method doesn't print anything by it self. It just returns an string
|
152
|
+
# with all the comments.
|
153
|
+
#
|
154
|
+
def print
|
155
|
+
result = ""
|
156
|
+
messages.each do |message|
|
157
|
+
result += Thimbl::Base.parse_time( message['time'] ).strftime( '%Y-%m-%d %H:%M:%S' )
|
158
|
+
result += " #{message['address']}"
|
159
|
+
result += " > #{message['text']}"
|
160
|
+
result += "\n"
|
161
|
+
end
|
162
|
+
|
163
|
+
return result
|
164
|
+
end
|
165
|
+
|
166
|
+
# Send your actual `plan` file to your server
|
167
|
+
#
|
168
|
+
# Use:
|
169
|
+
# thimbl.push
|
170
|
+
#
|
171
|
+
def push
|
172
|
+
Net::SCP.start( user.split('@')[1], user.split('@')[0], :password => password ) do |scp|
|
173
|
+
scp.upload!( plan_path, ".plan" )
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
# Charge into the `thimbl` object all the data into your `cache` file.
|
178
|
+
# Use:
|
179
|
+
# thimbl.load_data
|
180
|
+
#
|
181
|
+
def load_data
|
182
|
+
@data = JSON.load( File.read cache_path )
|
183
|
+
@address = data['me']
|
184
|
+
end
|
185
|
+
|
186
|
+
# Save all the data into the `thimbl` objecto into your `cache` file and
|
187
|
+
# `plan` file.
|
188
|
+
#
|
189
|
+
# Use:
|
190
|
+
# thimbl.save_data
|
191
|
+
#
|
192
|
+
def save_data
|
193
|
+
File.open( cache_path, 'w' ) { |f| f.write data.to_json }
|
194
|
+
File.open( plan_path, 'w' ) { |f| f.write data['plans'][address].to_json }
|
195
|
+
end
|
196
|
+
|
197
|
+
# Returns all the info about the users you are following.
|
198
|
+
#
|
199
|
+
# Use:
|
200
|
+
# thimbl.following
|
201
|
+
#
|
202
|
+
def following
|
203
|
+
data['plans'][address]['following']
|
204
|
+
end
|
205
|
+
|
206
|
+
# Returns all the messages of you and all the users you are following
|
207
|
+
# in a chronologic order into a json format.
|
208
|
+
#
|
209
|
+
# Use:
|
210
|
+
# thimbl.messages
|
211
|
+
#
|
212
|
+
def messages
|
213
|
+
_messages = data['plans'].values.map { |e| e['messages'] }.flatten
|
214
|
+
_messages = _messages.sort { |a,b| a['time'] <=> b['time'] }
|
215
|
+
|
216
|
+
return _messages
|
217
|
+
end
|
218
|
+
|
219
|
+
def self.parse_time( time )
|
220
|
+
Time.utc( time[0,4], time[4,2], time[6,2], time[8,2], time[10,2], time[12,2] )
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|