Blux 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,76 @@
1
+ #
2
+ # Copyright 2010 Louis-Philippe Salin de l'Etoile, aka Louis Salin
3
+ # Copyright (c) 2007 John Mettraux
4
+ #
5
+ # email: louis.phil@gmail.com
6
+ #
7
+ # This file is part of Blux.
8
+ #
9
+ # Blux is free software: you can redistribute it and/or modify
10
+ # it under the terms of the GNU General Public License as published by
11
+ # the Free Software Foundation, either version 3 of the License, or
12
+ # (at your option) any later version.
13
+ #
14
+ # Blux is distributed in the hope that it will be useful,
15
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
+ # GNU General Public License for more details.
18
+ #
19
+ # You should have received a copy of the GNU General Public License
20
+ # along with Blux. If not, see <http://www.gnu.org/licenses/>.
21
+ require 'optparse'
22
+ require 'ostruct'
23
+
24
+ class WPOptionParser
25
+ def self.parse(args)
26
+ options = OpenStruct.new
27
+ options.tags = []
28
+ options.title = nil
29
+ options.type = 'html'
30
+ options.bluxrc = nil
31
+ options.command = :post
32
+ options.entry_id = nil
33
+
34
+ opts = OptionParser.new
35
+ opts.banner = "Usage: post.rb [options]"
36
+ opts.separator ""
37
+ opts.separator "options :"
38
+
39
+ opts.on("-c", "--categories {list}", "comma separated list of tags/categories") do |v|
40
+ options.tags = v.split ","
41
+ end
42
+
43
+ opts.on("-t", "--title {title}", "title for the post") do |v|
44
+ options.title = v
45
+ end
46
+
47
+ opts.on("-T", "--type {html|xhtml|text}",
48
+ "type of the content. ('html' is the default).") do |v|
49
+ options.type = v
50
+ end
51
+
52
+ opts.on("--config {config_file}", "blux config file path") do |f|
53
+ options.bluxrc = f
54
+ end
55
+
56
+ opts.on("--update {entry_id}", "update an existing post") do |id|
57
+ options.command = :put
58
+ options.entry_id = id
59
+ end
60
+
61
+ opts.on("--delete {entry_id}", "delete an existing post") do |id|
62
+ options.command = :delete
63
+ options.entry_id = id
64
+ end
65
+
66
+ opts.on("-h", "--help", "displays this help") do
67
+ puts
68
+ puts opts.to_s
69
+ puts
70
+ exit 0
71
+ end
72
+
73
+ opts.parse!(args)
74
+ yield options
75
+ end
76
+ end
@@ -0,0 +1,136 @@
1
+ #!/usr/bin/env ruby
2
+ ## Copyright (c) 2007 John Mettraux
3
+ ## Copyright 2010 Louis-Philippe Salin de l'Etoile, aka Louis Salin
4
+ ##
5
+ ## Released under the MIT license
6
+ ## http://www.opensource.org/licenses/mit-license.php
7
+ ##
8
+ ## This file is part of Blux.
9
+ ##
10
+ ## Modifications by Louis Salin, October 2010
11
+ ## => reading blog information from the configuration file
12
+ ## => splitting out option parsing and refactoring functionnality into methods
13
+ ## => added delete and put REST commands
14
+
15
+ require 'net/http'
16
+ require 'atom/entry' # sudo gem install atom-tools
17
+ require 'atom/collection'
18
+ require File.join(File.expand_path(File.dirname(__FILE__)), "..", "blux_config_reader")
19
+ require File.join(File.expand_path(File.dirname(__FILE__)), "wp_options")
20
+
21
+ # a great thanks to the devs of all the libs used here
22
+ # some info about you and your blog
23
+
24
+ def delete(options, base, username, password)
25
+ entry = Atom::Entry.new
26
+ entry.updated!
27
+ entry.edit_url = options.entry_id
28
+
29
+ h = Atom::HTTP.new
30
+ h.user = username
31
+ h.pass = password
32
+ h.always_auth = :basic
33
+
34
+ c = Atom::Collection.new(base + "/posts", h)
35
+ res = c.delete! entry
36
+
37
+ return entry, res
38
+ end
39
+
40
+ def get_content
41
+ content = ""
42
+ loop do
43
+ line = STDIN.gets
44
+ break unless line
45
+ content += line
46
+ end
47
+
48
+ content
49
+ end
50
+
51
+ def publish_or_update(command, options, username, password, base, bloguri, authorname)
52
+ raise "please specify a title for the post with the -t option" unless options.title
53
+ content = get_content
54
+
55
+ entry = Atom::Entry.new
56
+ entry.title = options.title
57
+ entry.updated!
58
+
59
+ author = Atom::Author.new
60
+ author.name = authorname
61
+ author.uri = bloguri
62
+ entry.authors << author
63
+
64
+ options.tags.each do |t|
65
+ c = Atom::Category.new
66
+ c["scheme"] = bloguri
67
+ c["term"] = t.strip
68
+ entry.categories << c
69
+ end
70
+
71
+ entry.content = content
72
+ entry.content["type"] = options.type if options.type
73
+
74
+ h = Atom::HTTP.new
75
+ h.user = username
76
+ h.pass = password
77
+ h.always_auth = :basic
78
+
79
+ c = Atom::Collection.new(base + "/posts", h)
80
+
81
+ if command == :post
82
+ res = c.post! entry
83
+ elsif command == :put
84
+ entry.edit_url = options.entry_id
85
+ res = c.put! entry
86
+ end
87
+
88
+ return entry, res
89
+ end
90
+
91
+ begin
92
+ WPOptionParser.parse(ARGV) do |options|
93
+ config = BluxConfigurationReader.new
94
+ config.load_config options.bluxrc
95
+
96
+ blog = config.blog
97
+ authorname = config.author_name
98
+ username = config.user_name
99
+ password = config.password
100
+
101
+ bloguri = "http://#{blog}"
102
+ base = "https://#{blog}/wp-app.php"
103
+
104
+ case(options.command)
105
+ when :post
106
+ entry, res = publish_or_update(:post, options, username, password,
107
+ base, bloguri, authorname)
108
+ puts "--entry--"
109
+ puts entry
110
+
111
+ puts "--response--"
112
+ puts res
113
+
114
+ puts "--url--"
115
+ puts Atom::Entry.parse(res.read_body).edit_url
116
+ when :put
117
+ entry, res = publish_or_update(:put, options, username, password,
118
+ base, bloguri, authorname)
119
+ puts "--entry--"
120
+ puts entry
121
+
122
+ puts "--response--"
123
+ puts res
124
+ when :delete
125
+ entry, res = delete(options, base, username, password)
126
+
127
+ puts "--entry--"
128
+ puts entry
129
+
130
+ puts "--response--"
131
+ puts res
132
+ end
133
+ end
134
+ rescue
135
+ STDERR << "publishing error: #{$!}\n"
136
+ end
@@ -63,24 +63,6 @@ describe BlogManager do
63
63
  it "should create a draft folder in the .blux dir if it doesn't exist" do
64
64
  File.exists?("#{@blux}/draft").should == true
65
65
  end
66
-
67
- it "should create a published post file in the .blux dir if it doesn't exist" do
68
- File.exists?("#{@blux}/.published").should == true
69
- end
70
- end
71
-
72
- context "when using the blog manager" do
73
- before :each do
74
- File.open("#{@blux}/.published", 'w') do |f|
75
- f.write('{"draft1.23":{}}')
76
- end
77
-
78
- @manager.start
79
- end
80
-
81
- it "should load the published post file" do
82
- @manager.index.key?("draft1.23").should == true
83
- end
84
66
  end
85
67
 
86
68
  context "loading the config and setting up the draft manager" do
@@ -100,12 +82,6 @@ describe BlogManager do
100
82
  before :each do
101
83
  create_config
102
84
 
103
- File.open(@manager.index_file, 'w') do |f|
104
- f.write('{"draft5.67":{"a":1,"b":2}}')
105
- end
106
-
107
- @draft_mgr.stub!(:get_attribute).and_return("title")
108
-
109
85
  @manager.load_config
110
86
  @manager.start
111
87
 
@@ -115,33 +91,68 @@ describe BlogManager do
115
91
  end
116
92
 
117
93
  it "should send the proper command" do
118
- @manager.should_receive(:system).with("blux --convert -f draft5.67 | ruby #{File.dirname(__FILE__)[0..-6]}/lib/wp_publish.rb -t title --config #{@blux_rc} | blux --set_edit_url -f draft5.67")
94
+ @draft_mgr.stub!(:get_attribute).and_return("title")
95
+ @draft_mgr.stub!(:get_attribute).with("draft5.67", "published_time").and_return(nil)
96
+ @draft_mgr.stub!(:set_attribute)
97
+
98
+ @manager.should_receive(:system).with("blux --convert -f draft5.67 | ruby #{File.dirname(__FILE__)[0..-6]}/lib/publishing/wp_publish.rb -t \"title\" --config #{@blux_rc} -c \"title\" | blux --set_edit_url -f draft5.67")
99
+ @manager.publish 'draft5.67'
100
+ end
101
+
102
+ it "should send the proper command with categories if there are any" do
103
+ @draft_mgr.stub!(:get_attribute).with("draft5.67", "title").and_return("title")
104
+ @draft_mgr.stub!(:get_attribute).with("draft5.67", "categories").and_return("tag1,tag2")
105
+ @draft_mgr.stub!(:get_attribute).with("draft5.67", "published_time").and_return(nil)
106
+ @draft_mgr.stub!(:set_attribute)
107
+
108
+ @manager.should_receive(:system).with("blux --convert -f draft5.67 | ruby #{File.dirname(__FILE__)[0..-6]}/lib/publishing/wp_publish.rb -t \"title\" --config #{@blux_rc} -c \"tag1,tag2\" | blux --set_edit_url -f draft5.67")
119
109
  @manager.publish 'draft5.67'
120
110
  end
121
111
 
122
112
  it "should send the command with the title included if it exists" do
123
113
  @draft_mgr.stub!(:get_attribute).and_return('bla')
114
+ @draft_mgr.stub!(:get_attribute).with("draft5.67", "published_time").and_return(nil)
115
+ @draft_mgr.stub!(:set_attribute)
124
116
 
125
- @manager.should_receive(:system).with("blux --convert -f draft5.67 | ruby #{File.dirname(__FILE__)[0..-6]}/lib/wp_publish.rb -t bla --config #{@blux_rc} | blux --set_edit_url -f draft5.67")
117
+ @manager.should_receive(:system).with("blux --convert -f draft5.67 | ruby #{File.dirname(__FILE__)[0..-6]}/lib/publishing/wp_publish.rb -t \"bla\" --config #{@blux_rc} -c \"bla\" | blux --set_edit_url -f draft5.67")
126
118
  @manager.publish 'draft5.67'
127
119
  end
128
120
 
129
121
  it "should save the record of published drafts to disk" do
122
+ @draft_mgr.stub!(:get_attribute).and_return("title")
123
+ @draft_mgr.stub!(:get_attribute).and_return(nil)
124
+ Time.stub!(:now).and_return('000')
125
+ @draft_mgr.should_receive(:set_attribute).with('draft5.67', "published_time", '000')
126
+
130
127
  @manager.publish 'draft5.67'
128
+ end
131
129
 
132
- lines = ''
133
- File.open(@manager.index_file, 'r') do |f|
134
- f.each_line {|l| lines += l}
135
- end
130
+ it "should not allow to publish a draft twice" do
131
+ @draft_mgr.stub!(:get_attribute).and_return('bla')
132
+ @draft_mgr.stub!(:get_attribute).with("draft5.67", "published_time").and_return('123')
133
+ @draft_mgr.stub!(:set_attribute)
136
134
 
137
- JSON.parse(lines).key?('draft5.67').should == true
135
+ lambda {@manager.publish 'draft5.67'}.should raise_error("this draft has already been published")
138
136
  end
137
+ end
139
138
 
140
- it "should add the published time to the attributes of that post" do
141
- time = Time.now.to_s
139
+ context "when deleting" do
140
+ before :each do
141
+ create_config
142
142
 
143
- @manager.publish 'draft5.67'
144
- @manager.index["draft5.67"]["published_time"].to_s.should == time
143
+ @manager.load_config
144
+ @manager.start
145
+
146
+ system "touch #{@manager.draft_dir}/draft5.67"
147
+
148
+ @manager.stub!(:system).and_return(true)
149
+ @draft_mgr.stub!(:delete_draft)
150
+ @draft_mgr.stub!(:get_attribute).and_return('http://blablabla.com/asf/1')
151
+ end
152
+
153
+ it "should send the proper command" do
154
+ @manager.should_receive(:system).with("ruby #{File.dirname(__FILE__)[0..-6]}/lib/publishing/wp_publish.rb --delete http://blablabla.com/asf/1 --config #{@blux_rc} | blux --post-cmd")
155
+ @manager.delete 'draft5.67'
145
156
  end
146
157
  end
147
158
 
@@ -22,10 +22,13 @@ describe DraftManager do
22
22
 
23
23
  context "when using a new manager" do
24
24
  it "should have an empty draft index" do
25
- @manager.index.keys.length.should == 0
25
+ index = @manager.load_index
26
+ index.keys.length.should == 0
26
27
  end
27
28
 
28
29
  it "should load the draft index from disk" do
30
+ File.stub!(:exists?).and_return(true)
31
+
29
32
  File.open("#{@draft_dir}/.draft_index", 'w') do |f|
30
33
  f.write('{"test.sh":{"a":1,"b":2}}')
31
34
  end
@@ -33,9 +36,8 @@ describe DraftManager do
33
36
  manager = DraftManager.new
34
37
  manager.setup('gedit', @temp_dir, @draft_dir)
35
38
 
36
- manager.index.key?("test.sh").should == true
37
- manager.index["test.sh"].key?("a").should == true
38
- manager.index["test.sh"].key?("b").should == true
39
+ @manager.get_attribute('test.sh', "a").should == 1
40
+ @manager.get_attribute('test.sh', "b").should == 2
39
41
  end
40
42
  end
41
43
 
@@ -78,6 +80,8 @@ describe DraftManager do
78
80
  end
79
81
 
80
82
  it "should copy the temp file in the draft folder if it has data" do
83
+ File.stub!(:exists?).and_return(true)
84
+
81
85
  class Tempfile
82
86
  def size() 123 end
83
87
  def path() 'test/test.sh' end
@@ -92,6 +96,7 @@ describe DraftManager do
92
96
  context "when saving a new draft" do
93
97
  before :each do
94
98
  @manager.stub!(:system).and_return(true)
99
+ File.stub!(:exists?).and_return(true)
95
100
 
96
101
  class Tempfile
97
102
  def size() 123 end
@@ -111,7 +116,7 @@ describe DraftManager do
111
116
  it "should add the creation time to the attributes of that draft" do
112
117
  time = Time.now.to_s
113
118
  @manager.create_draft
114
- @manager.index["test.sh"][:creation_time].to_s.should == time
119
+ @manager.get_attribute('test.sh', "creation_time").to_s.should == time
115
120
  end
116
121
  end
117
122
 
@@ -142,19 +147,19 @@ describe DraftManager do
142
147
  time = Time.now.to_s
143
148
 
144
149
  @manager.edit_draft('test.sh')
145
- @manager.index["test.sh"]["edited_time"].to_s.should == time
150
+ @manager.get_attribute('test.sh', "edited_time").to_s.should == time
146
151
  end
147
152
  end
148
153
 
149
154
  context "when listing drafts" do
150
155
  before :each do
151
- system "touch #{@draft_dir}/1"
152
- system "touch #{@draft_dir}/2"
153
- system "touch #{@draft_dir}/3"
156
+ File.open("#{@draft_dir}/.draft_index", 'w') do |f|
157
+ f.write('{"1":{},"2":{},"3":{}}')
158
+ end
154
159
  end
155
160
 
156
161
  it "should list all the draft filenames, one per line" do
157
- @manager.list.should == ["1", "2", "3"]
162
+ @manager.list.entries.should == ["1", "2", "3"]
158
163
  end
159
164
  end
160
165
 
@@ -210,7 +215,9 @@ describe DraftManager do
210
215
  context "when outputing a draft" do
211
216
  before :each do
212
217
  File.open("#{@draft_dir}/.draft_index", 'w') do |f|
213
- f.write('{"file1":{"a":1,"b":2},"file2":{"a":1,"b":2}, "file3":{}}')
218
+ f.write('{"file1":{"a":1,"b":2},
219
+ "file2":{"a":1,"deleted":"2010-10-15 00:00:00"},
220
+ "file3":{}}')
214
221
  end
215
222
 
216
223
  system "echo this is blog with lots of letters intended to go beyond the 76 chars that will be displayed by the preview functionnality of the draft manager > #{@draft_dir}/file1"
@@ -228,6 +235,10 @@ describe DraftManager do
228
235
  it "should display an empty string for an empty draft" do
229
236
  @manager.output('file3').should == ''
230
237
  end
238
+
239
+ it "should raise an exception if the draft has been deleted" do
240
+ lambda {@manager.output('file2')}.should raise_error("draft filename file2 has been deleted")
241
+ end
231
242
  end
232
243
 
233
244
  context "when adding an attribute" do
@@ -243,13 +254,13 @@ describe DraftManager do
243
254
 
244
255
  it "should add the attribute to the draft index" do
245
256
  @manager.set_attribute('draft.1', "attr", "123")
246
- @manager.index['draft.1']["attr"].should == "123"
257
+ @manager.get_attribute('draft.1', "attr").should == "123"
247
258
  end
248
259
 
249
260
  it "should overwrite the value if the attribute already exists" do
250
261
  @manager.set_attribute('draft.1', "attr", "123")
251
262
  @manager.set_attribute('draft.1', "attr", "456")
252
- @manager.index['draft.1']["attr"].should == "456"
263
+ @manager.get_attribute('draft.1', "attr").should == "456"
253
264
  end
254
265
 
255
266
  it "should output an error message if the file does not exist" do
@@ -257,6 +268,7 @@ describe DraftManager do
257
268
  end
258
269
 
259
270
  it "should save the draft index to disk" do
271
+ File.should_receive(:open).with("#{@draft_dir}/.draft_index", 'r')
260
272
  File.should_receive(:open).with("#{@draft_dir}/.draft_index", 'w')
261
273
  @manager.set_attribute('draft.1', "attr", "123")
262
274
  end
@@ -277,16 +289,77 @@ describe DraftManager do
277
289
  it "should output an warning message if the title is not unique" do
278
290
  @manager.set_attribute('draft.1', "title", 'title')
279
291
 
292
+ STDERR.stub!(:<<)
280
293
  STDERR.should_receive(:<<).with("warning: title 'title' is not unique\n")
281
294
  @manager.set_attribute('draft.2', "title", 'title')
282
295
  end
283
296
 
284
297
  it "should still change the value of the previous title" do
298
+ STDERR.stub!(:<<)
285
299
  @manager.set_attribute('draft.1', 'title', 'title')
286
300
  @manager.set_attribute('draft.2', 'title', 'title2')
287
301
  @manager.set_attribute('draft.2', 'title', 'title')
288
302
 
289
- @manager.index['draft.2']["title"].should == 'title'
303
+ @manager.get_attribute('draft.2', "title").should == "title"
304
+ end
305
+ end
306
+
307
+ context "when adding a tag" do
308
+ before :each do
309
+ File.open("#{@draft_dir}/.draft_index", 'w') do |f|
310
+ f.write('{"draft.1":{}}')
311
+ end
312
+
313
+ system "touch #{@draft_dir}/draft.1"
314
+ @manager = DraftManager.new
315
+ @manager.setup('gedit', @temp_dir, @draft_dir)
316
+ end
317
+
318
+ it "should add a new tags attribute" do
319
+ @manager.set_attribute('draft.1', 'categories', "a tag")
320
+ @manager.get_attribute('draft.1', "categories").should == "a tag"
321
+ end
322
+
323
+ it "should add to an existing tag attribute if it already exists" do
324
+ @manager.set_attribute('draft.1', 'categories', "a tag")
325
+ @manager.set_attribute('draft.1', 'categories', "another tag")
326
+ @manager.get_attribute('draft.1', "categories").should == "a tag,another tag"
327
+ end
328
+ end
329
+
330
+ context "when deleting a tag" do
331
+ before :each do
332
+ File.open("#{@draft_dir}/.draft_index", 'w') do |f|
333
+ f.write('{"draft.1":{"categories":"tag1,tag2,tag3"}}')
334
+ end
335
+
336
+ system "touch #{@draft_dir}/draft.1"
337
+ @manager = DraftManager.new
338
+ @manager.setup('gedit', @temp_dir, @draft_dir)
339
+ end
340
+
341
+ it "should remove the attribute from the draft index" do
342
+ @manager.delete_attribute('draft.1', "categories") do
343
+ 'tag2'
344
+ end
345
+
346
+ @manager.get_attribute('draft.1', "categories").should == "tag1,tag3"
347
+ end
348
+
349
+ it "should not do anything if the tag does not exist" do
350
+ @manager.delete_attribute('draft.1', "categories") do
351
+ 'tag5'
352
+ end
353
+
354
+ @manager.get_attribute('draft.1', "categories").should == "tag1,tag2,tag3"
355
+ end
356
+
357
+ it "should remove the attribute if there are no tags left" do
358
+ @manager.delete_attribute('draft.1', "categories") do
359
+ 'tag1,tag2,tag3'
360
+ end
361
+
362
+ @manager.get_attribute('draft.1', "categories").should == nil
290
363
  end
291
364
  end
292
365
 
@@ -303,7 +376,7 @@ describe DraftManager do
303
376
 
304
377
  it "should remove the attribute from the draft index" do
305
378
  @manager.delete_attribute('draft.1', :a)
306
- @manager.index['draft.1']["a"].should == nil
379
+ @manager.get_attribute('draft.1', "a").should == nil
307
380
  end
308
381
 
309
382
  it "should output an error message if the file does not exist" do
@@ -333,6 +406,20 @@ describe DraftManager do
333
406
  it "should output an error message if there are no drafts saved" do
334
407
  lambda {@manager.get_latest_created_draft}.should raise_error("there is currently no saved index")
335
408
  end
409
+
410
+ it "should not take deleted drafts into account" do
411
+ File.open("#{@draft_dir}/.draft_index", 'w') do |f|
412
+ f.write({"draft.1" => {"creation_time" => "2010-10-11 15:30:12",
413
+ "deleted" => "2010-10-15 00:00:00"},
414
+ "draft.2" => {"creation_time" => "2010-10-10 15:30:12"},
415
+ "draft.3" => {"creation_time" => "2010-10-09 15:30:12"}}.to_json)
416
+ end
417
+
418
+ @manager = DraftManager.new
419
+ @manager.setup('gedit', @temp_dir, @draft_dir)
420
+
421
+ @manager.get_latest_created_draft().should == "draft.2"
422
+ end
336
423
  end
337
424
 
338
425
  context "when requesting a draft by title" do