tumblr-rb 1.3.0 → 2.0.0.alpha

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. data/.travis.yml +7 -0
  2. data/Gemfile +15 -8
  3. data/Gemfile.lock +65 -65
  4. data/LICENSE +4 -2
  5. data/README.md +31 -84
  6. data/Rakefile +8 -68
  7. data/bin/tumblr +3 -133
  8. data/lib/tumblr.rb +7 -184
  9. data/lib/tumblr/authentication.rb +71 -0
  10. data/lib/tumblr/client.rb +148 -0
  11. data/lib/tumblr/command_line_interface.rb +222 -0
  12. data/lib/tumblr/credentials.rb +31 -0
  13. data/lib/tumblr/post.rb +253 -171
  14. data/lib/tumblr/post/answer.rb +17 -0
  15. data/lib/tumblr/post/audio.rb +22 -10
  16. data/lib/tumblr/post/chat.rb +25 -0
  17. data/lib/tumblr/post/link.rb +22 -9
  18. data/lib/tumblr/post/photo.rb +29 -10
  19. data/lib/tumblr/post/quote.rb +17 -10
  20. data/lib/tumblr/post/text.rb +18 -0
  21. data/lib/tumblr/post/video.rb +26 -11
  22. data/lib/tumblr/version.rb +3 -0
  23. data/lib/tumblr/views/error.erb +6 -0
  24. data/lib/tumblr/views/form.erb +11 -0
  25. data/lib/tumblr/views/layout.erb +41 -0
  26. data/lib/tumblr/views/success.erb +6 -0
  27. data/man/tumblr.1 +67 -65
  28. data/man/tumblr.1.html +131 -108
  29. data/man/tumblr.1.ronn +76 -57
  30. data/man/tumblr.5 +48 -68
  31. data/man/tumblr.5.html +106 -114
  32. data/man/tumblr.5.ronn +38 -51
  33. data/spec/fixtures/posts.json +10 -0
  34. data/spec/fixtures/typical_animated_gif.gif +0 -0
  35. data/spec/spec_helper.rb +12 -0
  36. data/spec/tumblr/authentication_spec.rb +57 -0
  37. data/spec/tumblr/client_spec.rb +223 -0
  38. data/spec/tumblr/credentials_spec.rb +63 -0
  39. data/spec/tumblr/post_spec.rb +125 -0
  40. data/tumblr-rb.gemspec +16 -89
  41. metadata +101 -102
  42. data/lib/tumblr/authenticator.rb +0 -18
  43. data/lib/tumblr/post/conversation.rb +0 -15
  44. data/lib/tumblr/post/regular.rb +0 -14
  45. data/lib/tumblr/reader.rb +0 -191
  46. data/lib/tumblr/writer.rb +0 -39
  47. data/test/fixtures/vcr_cassettes/authenticate/authenticate.yml +0 -39
  48. data/test/fixtures/vcr_cassettes/read/all_pages.yml +0 -34
  49. data/test/fixtures/vcr_cassettes/read/authenticated.yml +0 -40
  50. data/test/fixtures/vcr_cassettes/read/authentication_failure.yml +0 -33
  51. data/test/fixtures/vcr_cassettes/read/like.yml +0 -31
  52. data/test/fixtures/vcr_cassettes/read/mwunsch.yml +0 -101
  53. data/test/fixtures/vcr_cassettes/read/optional.yml +0 -48
  54. data/test/fixtures/vcr_cassettes/read/pages.yml +0 -36
  55. data/test/fixtures/vcr_cassettes/read/tumblrgemtest.yml +0 -42
  56. data/test/fixtures/vcr_cassettes/read/unlike.yml +0 -31
  57. data/test/fixtures/vcr_cassettes/write/delete.yml +0 -31
  58. data/test/fixtures/vcr_cassettes/write/edit.yml +0 -31
  59. data/test/fixtures/vcr_cassettes/write/reblog.yml +0 -31
  60. data/test/fixtures/vcr_cassettes/write/write.yml +0 -31
  61. data/test/helper.rb +0 -44
  62. data/test/test_tumblr.rb +0 -710
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ bundler_args: --without development
3
+ rvm:
4
+ - 1.8.7
5
+ - 1.9.3
6
+ - jruby-18mode
7
+ - jruby-19mode
data/Gemfile CHANGED
@@ -1,12 +1,19 @@
1
1
  source 'http://rubygems.org'
2
2
 
3
- gem 'weary','>= 0.7.1'
4
- gem 'highline', '>= 1.5.2'
3
+ gemspec
4
+
5
+ gem "rake", "~> 0.9.2"
5
6
 
6
7
  group :test do
7
- gem 'rake'
8
- gem 'redgreen'
9
- gem 'contest'
10
- gem 'vcr', '>= 0.3.1'
11
- gem 'jeweler'
12
- end
8
+ gem "rspec", "~> 2.11.0"
9
+ gem "webmock", "~> 1.8.5"
10
+ gem "rack-test", "~> 0.6.1"
11
+ end
12
+
13
+ group :development do
14
+ gem "ronn"
15
+ end
16
+
17
+ platforms :jruby do
18
+ gem "jruby-openssl"
19
+ end
@@ -1,65 +1,65 @@
1
- ---
2
- dependencies:
3
- contest:
4
- group:
5
- - :test
6
- version: ">= 0"
7
- rake:
8
- group:
9
- - :test
10
- version: ">= 0"
11
- highline:
12
- group:
13
- - :default
14
- version: ">= 1.5.2"
15
- weary:
16
- group:
17
- - :default
18
- version: ">= 0.7.1"
19
- vcr:
20
- group:
21
- - :test
22
- version: ">= 0.3.1"
23
- jeweler:
24
- group:
25
- - :test
26
- version: ">= 0"
27
- redgreen:
28
- group:
29
- - :test
30
- version: ">= 0"
31
- specs:
32
- - rake:
33
- version: 0.8.7
34
- - contest:
35
- version: 0.1.2
36
- - crack:
37
- version: 0.1.7
38
- - fakeweb:
39
- version: 1.2.8
40
- - json_pure:
41
- version: 1.2.4
42
- - gemcutter:
43
- version: 0.5.0
44
- - git:
45
- version: 1.2.5
46
- - highline:
47
- version: 1.5.2
48
- - rubyforge:
49
- version: 2.0.4
50
- - jeweler:
51
- version: 1.4.0
52
- - ruby-hmac:
53
- version: 0.4.0
54
- - oauth:
55
- version: 0.3.6
56
- - redgreen:
57
- version: 1.2.2
58
- - vcr:
59
- version: 0.3.1
60
- - weary:
61
- version: 0.7.2
62
- hash: 494102bf0992e5bb83118c12065b38b342f1de6a
63
- sources:
64
- - Rubygems:
65
- uri: http://rubygems.org
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ tumblr-rb (2.0.0.alpha)
5
+ sinatra (~> 1.3.2)
6
+ thor (~> 0.16.0)
7
+ weary (~> 1.1.0)
8
+
9
+ GEM
10
+ remote: http://rubygems.org/
11
+ specs:
12
+ addressable (2.3.2)
13
+ crack (0.3.1)
14
+ diff-lcs (1.1.3)
15
+ hpricot (0.8.6)
16
+ multi_json (1.3.6)
17
+ mustache (0.99.4)
18
+ promise (0.3.0)
19
+ rack (1.4.1)
20
+ rack-protection (1.2.0)
21
+ rack
22
+ rack-test (0.6.2)
23
+ rack (>= 1.0)
24
+ rake (0.9.2.2)
25
+ rdiscount (1.6.8)
26
+ ronn (0.7.3)
27
+ hpricot (>= 0.8.2)
28
+ mustache (>= 0.7.0)
29
+ rdiscount (>= 1.5.8)
30
+ rspec (2.11.0)
31
+ rspec-core (~> 2.11.0)
32
+ rspec-expectations (~> 2.11.0)
33
+ rspec-mocks (~> 2.11.0)
34
+ rspec-core (2.11.1)
35
+ rspec-expectations (2.11.3)
36
+ diff-lcs (~> 1.1.3)
37
+ rspec-mocks (2.11.3)
38
+ simple_oauth (0.1.9)
39
+ sinatra (1.3.3)
40
+ rack (~> 1.3, >= 1.3.6)
41
+ rack-protection (~> 1.2)
42
+ tilt (~> 1.3, >= 1.3.3)
43
+ thor (0.16.0)
44
+ tilt (1.3.3)
45
+ weary (1.1.1)
46
+ addressable (~> 2.3.2)
47
+ multi_json (~> 1.3.0)
48
+ promise (~> 0.3.0)
49
+ rack (~> 1.4.0)
50
+ simple_oauth (~> 0.1.5)
51
+ webmock (1.8.11)
52
+ addressable (>= 2.2.7)
53
+ crack (>= 0.1.7)
54
+
55
+ PLATFORMS
56
+ ruby
57
+
58
+ DEPENDENCIES
59
+ jruby-openssl
60
+ rack-test (~> 0.6.1)
61
+ rake (~> 0.9.2)
62
+ ronn
63
+ rspec (~> 2.11.0)
64
+ tumblr-rb!
65
+ webmock (~> 1.8.5)
data/LICENSE CHANGED
@@ -1,4 +1,6 @@
1
- Copyright (c) 2010 Mark Wunsch
1
+ Copyright (c) 2010-2012 Mark Wunsch
2
+
3
+ MIT License
2
4
 
3
5
  Permission is hereby granted, free of charge, to any person obtaining
4
6
  a copy of this software and associated documentation files (the
@@ -17,4 +19,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
19
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
20
  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
21
  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md CHANGED
@@ -1,33 +1,28 @@
1
1
  # tumblr
2
2
 
3
- Ruby wrapper and command line tool for the [Tumblr API](http://www.tumblr.com/docs/en/api). In case there weren't enough of those already. This one is powered by the [Weary](http://github.com/mwunsch/weary) gem.
3
+ Command line interface and Ruby client for the [Tumblr API](http://www.tumblr.com/docs/en/api/v2)
4
4
 
5
- [RDoc](http://rdoc.info/projects/mwunsch/tumblr) | [Gem](http://rubygems.org/gems/tumblr-rb) | [Metrics](http://getcaliper.com/caliper/project?repo=git%3A%2F%2Fgithub.com%2Fmwunsch%2Ftumblr.git)
5
+ It's being rewritten from the ground up to support v2 of the api.
6
+
7
+ **Check out [tag v1.3.0](https://github.com/mwunsch/tumblr/tree/v1.3.0) if you are interested in v1.** The master branch is now dedicated to v2, and is not in steady state.
8
+
9
+ Like the previous version, the current version reads files with a special front-matter block, like [Jekyll](http://tom.preston-werner.com/jekyll/). In addition, this new version offers the ability to post photos, videos, and audio.
10
+
11
+ Unlike the previous version, this new command line utility uses OAuth to authenticate and authorize the user.
12
+
13
+ ## TODO
14
+
15
+ + Task to build a homebrew formula
6
16
 
7
17
  ## Installation
8
18
 
9
- gem install tumblr-rb
10
-
11
- ## Usage
12
-
13
- $: tumblr path/to/a_post.markdown
14
- Email Address: tumblr-user@foobarmail.com
15
- Password:
16
- Published to Tumblr. The ID for this post is: 123456789
17
-
18
- You can pass `tumblr` something from standard input, but you have to set your email and password as arguments:
19
-
20
- $: echo 'Hello world.' | tumblr -a user@tumblr.com:supers3cretp4ssw0rd
21
- Published to Tumblr. The ID for this post is: 123456790
22
-
23
- Or the credentials can come from a YAML file:
24
-
25
- $ cat ~/.tumblrlogin
26
- email: tumblruser@generic-email.com
27
- password: myvoiceismypassport
28
- $ echo 'Hello world. | tumblr --credentials ~/.tumblrlogin
29
-
30
- Try `tumblr --help` if you are in need of guidance. Read [tumblr(1)](http://mwunsch.github.com/tumblr/tumblr.1.html) for more information.
19
+ Until the gem is published, you'll just need to clone this repository. Run `bundle install` and `bundle exec bin/tumblr`.
20
+
21
+ Alternatively, you can clone the repo, and run `rake install` -- this will build the gem, place it in the `pkg` directory, and install the gem to your system. You should then be able to `require 'tumblr'` and/or run `tumblr` from the command line.
22
+
23
+ ## Authorization
24
+
25
+ Run `tumblr authorize` to boot up a small application to manage the fancy OAuth handshake with tumblr. You'll be prompted for a consumer key and secret you get from [registering an app](http://www.tumblr.com/oauth/apps).
31
26
 
32
27
  ## Getting Started
33
28
 
@@ -42,76 +37,28 @@ YAML frontmatter beings with `---` on a single line, followed by YAML, ending wi
42
37
  tags: hamlet, shakespeare
43
38
  ---
44
39
  "To be or not to be."
45
-
46
- Understood YAML parameters are taken from the Tumblr API: http://www.tumblr.com/docs/en/api#api_write
47
40
 
48
- Read [tumblr(5)](http://mwunsch.github.com/tumblr/tumblr.5.html) for more info.
41
+ Understood YAML parameters are taken from the Tumblr API: http://www.tumblr.com/docs/en/api/v2#posting
49
42
 
50
- #### All Posts
43
+ ### All Posts
51
44
 
52
- type regular, photo, link, quote, conversation, video, audio
45
+ type text, photo, link, quote, chat, video, audio
53
46
  will take a guess if ommitted.
54
-
55
- state published, queue, draft, submission
56
-
47
+
48
+ state published, queue, draft, private
49
+
57
50
  format html or markdown
58
-
51
+
59
52
  tags comma-separated list of tags
60
-
53
+
61
54
  date post date
62
-
63
- private true if the post is private
64
-
55
+
65
56
  slug A custom string to appear in the post's URL
66
-
67
- group id for a secondary blog
68
-
69
- generator description of the publishing application
70
-
71
- send-to-twitter Twitter status update if the tumblelog has enabled it
72
-
73
- publish-on if the post state is 'queue', publish on this date
74
-
75
- #### Additional parameters for specific Post Types
76
-
77
- regular title
78
-
79
- photo caption, click-through-url
80
-
81
- quote source
82
-
83
- link name, description
84
-
85
- conversation title
86
-
87
- video title, caption
88
-
89
- audio caption
90
-
91
- To publish to Tumblr, do this:
92
-
93
- request = Tumblr.new(username, password).post(document)
94
- request.perform do |response|
95
- if response.success?
96
- puts response.body # Returns the new post's id.
97
- else
98
- puts "Something went wrong: #{response.code} #{response.message}"
99
- end
100
- end
101
-
102
- ## Goals
103
-
104
- + Full API coverage. Leave no method behind.
105
- + Well tested. Like a good Rubyist.
106
- + Obnoxiously simple CLI. *nix idioms are wonderful.
107
- + Kind-of-sort-of proof-of-concept for [Weary](http://github.com/mwunsch/weary).
108
-
109
- ## TODO:
110
-
111
- + File-uploading for Photos, Videos, Audio (needs to get into Weary)
57
+
58
+ tweet Manages the autotweet (if enabled) for this post
112
59
 
113
60
  ## Copyright
114
61
 
115
- The Tumblr gem is Copyright (c) 2010 Mark Wunsch and is licensed under the [MIT License](http://creativecommons.org/licenses/MIT/).
62
+ The Tumblr gem is Copyright (c) 2010 - 2012 Mark Wunsch and is licensed under the [MIT License](http://creativecommons.org/licenses/MIT/).
116
63
 
117
64
  Tumblr is Copyright (c) Tumblr, Inc. The Tumblr gem is NOT affiliated with Tumblr, Inc.
data/Rakefile CHANGED
@@ -1,79 +1,19 @@
1
- begin
2
- # Try to require the preresolved locked set of gems.
3
- require File.expand_path('../.bundle/environment', __FILE__)
4
- rescue LoadError
5
- # Fall back on doing an unlocked resolve at runtime.
6
- require "rubygems"
7
- require "bundler"
8
- Bundler.setup
9
- end
10
- $LOAD_PATH.unshift File.join(File.dirname(__FILE__), 'lib')
11
- require 'tumblr'
12
- require 'rake'
13
-
14
- begin
15
- require 'jeweler'
16
- Jeweler::Tasks.new do |gem|
17
- gem.name = "tumblr-rb"
18
- gem.summary = "Ruby wrapper and command line interface to the Tumblr API."
19
- gem.description = "Ruby library and command line utility to work with the Tumblr Blogging Platform, powered by Weary."
20
- gem.version = Tumblr::VERSION
21
- gem.email = "mark@markwunsch.com"
22
- gem.homepage = "http://github.com/mwunsch/tumblr"
23
- gem.authors = ["Mark Wunsch"]
24
- gem.add_dependency 'weary', '>= 0.7.1'
25
- gem.add_dependency 'highline', '>= 1.5.2'
26
- gem.add_development_dependency "bundler", ">= 0.9.19"
27
- # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
28
- end
29
- Jeweler::GemcutterTasks.new
30
- rescue LoadError
31
- puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
32
- end
33
-
34
- require 'rake/testtask'
35
- Rake::TestTask.new(:test) do |test|
36
- test.libs << 'lib' << 'test'
37
- test.pattern = 'test/**/test_*.rb'
38
- test.verbose = true
39
- end
40
-
41
- begin
42
- require 'rcov/rcovtask'
43
- Rcov::RcovTask.new do |test|
44
- test.libs << 'test'
45
- test.pattern = 'test/**/test_*.rb'
46
- test.verbose = true
47
- end
48
- rescue LoadError
49
- task :rcov do
50
- abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
51
- end
52
- end
53
-
54
- task :default => :test
55
-
56
- require 'rake/rdoctask'
57
- Rake::RDocTask.new do |rdoc|
58
- version = File.exist?('VERSION') ? File.read('VERSION') : ""
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require 'rspec/core/rake_task'
59
4
 
60
- rdoc.rdoc_dir = 'rdoc'
61
- rdoc.title = "tumblr #{version}"
62
- rdoc.rdoc_files.include('README*')
63
- rdoc.rdoc_files.include('lib/**/*.rb')
64
- end
5
+ task :default => :spec
65
6
 
66
- desc "Open an irb session preloaded with this library"
67
- task :console do
68
- sh "irb -rubygems -I lib -r tumblr"
7
+ RSpec::Core::RakeTask.new(:spec) do |t|
8
+ t.rspec_opts = ["--color", "--format=documentation"]
69
9
  end
70
10
 
71
11
  desc "Build the manual"
72
12
  task :build_man do
73
13
  sh "ronn -br5 --organization='Mark Wunsch' --manual='Tumblr Manual' man/*.ronn"
74
14
  end
75
-
15
+
76
16
  desc "Show the manual"
77
17
  task :man => :build_man do
78
18
  exec "man man/tumblr.1"
79
- end
19
+ end
data/bin/tumblr CHANGED
@@ -1,135 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
- ## Tumblr is a command line interface for publishing to Tumblr.com
3
- ##
4
- ## Usage: tumblr [ OPTIONS ] [ FILE or URL ]
5
- ##
6
- ## Publish to Tumblr. If a URL is given, it will create a link post.
7
- ## If URL is a YouTube or Vimeo URL, it will create a video post.
8
- ## If a FILE is given, it will publish its contents.
9
- ## See tumblr(5) for formatting instructions for FILE.
10
- ## If a URL or FILE is not given, tumblr will read from the Standard Input.
11
- ## If tumblr reads from the Standard Input, you must authenticate with -a.
12
- ##
13
- ##
2
+ require File.expand_path File.join(File.dirname(__FILE__), "/../lib/tumblr")
3
+ require 'tumblr/command_line_interface'
14
4
 
15
- require 'optparse'
16
-
17
- def usage
18
- File.readlines(__FILE__).
19
- grep(/^##.*/).
20
- map { |line| line.chomp[3..-1] }.
21
- join("\n")
22
- end
23
-
24
- begin
25
- require 'tumblr'
26
- rescue LoadError
27
- raise if $!.to_s !~ /tumblr/
28
- libdir = File.expand_path("../../lib", __FILE__).sub(/^#{Dir.pwd}/, '.')
29
- if !$:.include?(libdir)
30
- $:.unshift libdir
31
- require File.expand_path('../../.bundle/environment', __FILE__)
32
- retry
33
- end
34
- raise
35
- end
36
-
37
- publisher = {}
38
- publish_state = nil
39
- group = nil
40
-
41
- ARGV.options do |option|
42
- option.banner = usage
43
- option.separator "Options"
44
-
45
- auth_text = 'Email Address and Password, separated by a colon'
46
- option.on('-a EMAIL:PASSWORD', '--auth EMAIL:PASSWORD', auth_text) do |auth|
47
- publisher[:email],publisher[:password] = auth.split(':')
48
- end
49
-
50
- address_text = 'Email Address (will prompt for password)'
51
- option.on('-e EMAIL', '--email EMAIL', address_text) do |email|
52
- publisher[:email] = email
53
- end
54
-
55
- option.on('--credentials FILE', 'A YAML file with "email" and "password" keys for authentication') do |file|
56
- credentials = YAML.load(File.read(file))
57
- publisher[:email] = credentials['email']
58
- publisher[:password] = credentials['password']
59
- end
60
-
61
- option.on('-p','--publish', 'Publish the post immediately (ignores "state" parameter)') { publish_state = :published }
62
- option.on('-q','--queue', 'Add the post to the queue') { publish_state = :queue }
63
- option.on('-d','--draft', 'Save the post as a draft') { publish_state = :draft }
64
-
65
- option.on('--group=GROUP','Publish to a group blog') {|value| group = value }
66
-
67
- option.separator ""
68
-
69
- option.on('-h','--help', 'Show this help message') { puts option ; exit }
70
- option.on('-v','--version', 'Show version number') do
71
- puts Tumblr::VERSION
72
- exit
73
- end
74
-
75
- option.separator ""
76
-
77
- option.parse!
78
- end
79
-
80
- if ARGV.empty? && STDIN.tty?
81
- puts usage
82
- puts "See 'tumblr --help' for more information."
83
- exit
84
- end
85
-
86
- input = if !ARGV.empty?
87
- path = ARGV.first
88
- (path =~ /^https?:\/\/\S+$/i) ? path : File.read(path)
89
- else
90
- STDIN.read.chomp!
91
- end
92
-
93
- if !STDIN.tty? && (!publisher[:email] || !publisher[:password])
94
- bad_auth = %q(Error: An email address and password are required. Use 'tumblr -a' to authenticate. See 'tumblr --help' for details.)
95
- abort bad_auth
96
- end
97
-
98
- if !publisher[:email] || !publisher[:password]
99
- begin
100
- require 'highline/import'
101
- rescue LoadError
102
- require 'rubygems'
103
- retry
104
- end
105
-
106
- def get_email(pub)
107
- pub[:email] = ask("Email Address: ")
108
- get_email(pub) if pub[:email].empty?
109
- end
110
-
111
- def get_password(pub)
112
- pub[:password] = ask("Password: ") { |q| q.echo = false }
113
- get_password(pub) if pub[:password].empty?
114
- end
115
-
116
- if !publisher[:email]
117
- get_email(publisher)
118
- end
119
-
120
- if !publisher[:password]
121
- get_password(publisher)
122
- end
123
- end
124
-
125
- post = Tumblr.parse(input)
126
- post.state = publish_state if publish_state
127
- post.group = group if group
128
-
129
- response = Tumblr.execute(publisher, post)
130
- if response.success?
131
- puts "Published to Tumblr. The ID for this post is: #{response.body}"
132
- exit
133
- else
134
- abort %Q(Oh no! Something went wrong. Tumblr says, "#{response.body}")
135
- end
5
+ Tumblr::CommandLineInterface.start