thimbl 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -1,5 +1,11 @@
1
1
  source 'http://rubygems.org'
2
2
 
3
+ gem 'json'
4
+ gem 'net-scp'
5
+
3
6
  group :test do
7
+ gem 'echoe'
4
8
  gem 'mocha'
9
+ gem 'delorean'
10
+ gem 'ruby-debug'
5
11
  end
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
- lib/finger.rb
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/thimbl_test.rb
9
- thimbl.rb
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://www.thimbl.net/)
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
- gem install thimbl
25
+ gem install thimbl
12
26
 
13
27
  ## Use
14
28
 
29
+ require 'rubygems'
15
30
  require 'thimbl'
16
- thimbl = Thimbl.new( '/tmp/plan', '/tmp/thimbl_cache' )
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
- :bio => 'my bio',
19
- :website => 'my website',
20
- :mobile => 'my mobile',
21
- :email => 'my email',
22
- :address => 'my address',
23
- :name => 'my name'
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.1') do |p|
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 "#{File.dirname(__FILE__)}/finger"
4
-
5
- class Thimbl
6
- attr_reader :plan_path, :cache_path, :data, :address
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"
@@ -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