alx 0.0.1
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/.gitignore +23 -0
- data/.simplecov +5 -0
- data/COPYING +20 -0
- data/Gemfile +4 -0
- data/LICENSE +674 -0
- data/README.md +5 -0
- data/Rakefile +16 -0
- data/alx.gemspec +24 -0
- data/bin/alx +14 -0
- data/features/add_books.feature +15 -0
- data/features/create_default_configuration.feature +8 -0
- data/features/give_meaningful_help.feature +8 -0
- data/features/remove_book.feature +17 -0
- data/features/step_definitions/alx_base_steps.rb +36 -0
- data/features/step_definitions/alx_configuration_steps.rb +19 -0
- data/features/step_definitions/alx_help_steps.rb +7 -0
- data/features/support/alx_extensions.rb +67 -0
- data/features/support/env.rb +27 -0
- data/lib/alx/add_handler.rb +25 -0
- data/lib/alx/book.rb +20 -0
- data/lib/alx/configuration.rb +51 -0
- data/lib/alx/controller.rb +43 -0
- data/lib/alx/editor.rb +33 -0
- data/lib/alx/executer.rb +13 -0
- data/lib/alx/handler.rb +62 -0
- data/lib/alx/help_handler.rb +39 -0
- data/lib/alx/library.rb +27 -0
- data/lib/alx/list_handler.rb +22 -0
- data/lib/alx/remove_handler.rb +36 -0
- data/lib/alx/renderer.rb +39 -0
- data/lib/alx/show_handler.rb +32 -0
- data/lib/alx/version.rb +3 -0
- data/lib/alx/viewer.rb +32 -0
- data/test/tc_book.rb +23 -0
- data/test/tc_controller.rb +37 -0
- data/test/tc_editor.rb +42 -0
- data/test/tc_library.rb +33 -0
- data/test/tc_list_handler.rb +48 -0
- data/test/tc_renderer.rb +41 -0
- data/test/tc_viewer.rb +58 -0
- data/test/test_helper.rb +69 -0
- metadata +168 -0
data/lib/alx/executer.rb
ADDED
data/lib/alx/handler.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'alx/library'
|
2
|
+
require 'optparse'
|
3
|
+
require 'tempfile'
|
4
|
+
|
5
|
+
module Alx
|
6
|
+
|
7
|
+
class Handler
|
8
|
+
attr_reader :command_name
|
9
|
+
|
10
|
+
def initialize( details )
|
11
|
+
@conf = details[ :configuration ]
|
12
|
+
@command_name = details[ :command ]
|
13
|
+
|
14
|
+
@opts = OptionParser.new
|
15
|
+
banner_text = @command_name
|
16
|
+
banner_text += ' [options]' if details[ :options ]
|
17
|
+
banner_text += " <#{details[ :rest_description ]}>" if details[ :rest_description ]
|
18
|
+
banner_text += "\n #{details[ :description ]}"
|
19
|
+
add_options( details[ :options ] ) if details[ :options ]
|
20
|
+
@opts.banner = banner_text
|
21
|
+
end
|
22
|
+
|
23
|
+
def help
|
24
|
+
@opts.help
|
25
|
+
end
|
26
|
+
|
27
|
+
protected
|
28
|
+
def add_options( opts )
|
29
|
+
opts.each { | o | add_option( o ) }
|
30
|
+
end
|
31
|
+
|
32
|
+
def add_option( opt )
|
33
|
+
case opt
|
34
|
+
when :editor
|
35
|
+
@opts.on( '-e EDITOR', '--editor EDITOR', 'Use EDITOR instead of predefined editor.' ) { | editor | @conf[ :editor ] = editor }
|
36
|
+
when :stdin
|
37
|
+
@opts.on( '-i', '--stdin', 'Use stdin instead of an editor.' ) { @conf.delete( :editor ) }
|
38
|
+
when :stdout
|
39
|
+
@opts.on( '-o', '--stdout', 'Print to stdout instead of starting a viewer.' ) { @conf[ :stdout ] = true }
|
40
|
+
when :html
|
41
|
+
@opts.on( '-h', '--html', 'Generate html from the original text file.' ) { @conf[ :html ] = true }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def parse_options
|
46
|
+
@conf[ :rest ] = @opts.parse( @conf[ :rest ] ).join( ' ' )
|
47
|
+
end
|
48
|
+
|
49
|
+
def options_error( message )
|
50
|
+
puts message
|
51
|
+
puts help
|
52
|
+
raise ""
|
53
|
+
end
|
54
|
+
|
55
|
+
def load_library
|
56
|
+
@lib = Alx::Library.new
|
57
|
+
@lib.load_library( @conf[ :default_shelf_dir ] )
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'alx/handler'
|
2
|
+
|
3
|
+
module Alx
|
4
|
+
|
5
|
+
class HelpHandler < Handler
|
6
|
+
def initialize( configuration, controller )
|
7
|
+
super(
|
8
|
+
:configuration => configuration,
|
9
|
+
:command => 'help',
|
10
|
+
:rest_description => 'command',
|
11
|
+
:description => "Gives more information on defined command." )
|
12
|
+
@controller = controller
|
13
|
+
end
|
14
|
+
|
15
|
+
def handle
|
16
|
+
parse_options
|
17
|
+
handler = @controller.handlers[ @conf[ :rest ] ]
|
18
|
+
if handler
|
19
|
+
puts handler.help
|
20
|
+
return;
|
21
|
+
end
|
22
|
+
|
23
|
+
default_help
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
def default_help
|
28
|
+
executable_name = File.basename( $PROGRAM_NAME )
|
29
|
+
text = "Usage: #{executable_name} command [options]\n"
|
30
|
+
text += "An easy to use text document manager.\n\n"
|
31
|
+
text += "Commands:\n"
|
32
|
+
@controller.handlers.keys.each { | command | text+=" " + command + "\n" }
|
33
|
+
text += "\nFor more information type: #{executable_name} help <command>"
|
34
|
+
puts text
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
data/lib/alx/library.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'alx/book'
|
2
|
+
|
3
|
+
module Alx
|
4
|
+
|
5
|
+
class Library
|
6
|
+
def initialize
|
7
|
+
@books = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def load_library( directory )
|
11
|
+
Dir.open( directory ) do | dir |
|
12
|
+
files = dir.entries.keep_if { | i | i !~ /^\./ }
|
13
|
+
files.each do | file |
|
14
|
+
@books << Book.new( file.gsub( /_/, " " ), "#{directory}/#{file}" )
|
15
|
+
end
|
16
|
+
end
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
def list( pattern )
|
21
|
+
@books.select { | book | book.title=~/#{pattern}/ }
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'alx/handler'
|
2
|
+
|
3
|
+
module Alx
|
4
|
+
|
5
|
+
class ListHandler < Handler
|
6
|
+
def initialize( configuration )
|
7
|
+
super(
|
8
|
+
:configuration => configuration,
|
9
|
+
:command => 'list',
|
10
|
+
:rest_description => 'pattern',
|
11
|
+
:description => "Lists matching books in the library. Lists all if there's no pattern given." )
|
12
|
+
end
|
13
|
+
|
14
|
+
def handle
|
15
|
+
parse_options
|
16
|
+
load_library.list( @conf[ :rest ] ).each { | book | puts book.title }
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'alx/handler'
|
2
|
+
|
3
|
+
module Alx
|
4
|
+
|
5
|
+
class RemoveHandler < Handler
|
6
|
+
def initialize( configuration )
|
7
|
+
super(
|
8
|
+
:configuration => configuration,
|
9
|
+
:command => 'remove',
|
10
|
+
:rest_description => 'pattern',
|
11
|
+
:description => "Removes matching books from the library." )
|
12
|
+
end
|
13
|
+
|
14
|
+
def handle
|
15
|
+
parse_options
|
16
|
+
options_error( "No pattern given. Please provide one." ) if @conf[ :rest ].empty?
|
17
|
+
matching_books = load_library.list( @conf[ :rest ] )
|
18
|
+
return remove_book( matching_books.pop ) if matching_books.size == 1
|
19
|
+
matching_books.each { | book | remove_book( book ) if ask?( "Do you really want to remove #{book.title}?" ) }
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
def ask?( message )
|
24
|
+
printf message + " (yes/no) "
|
25
|
+
answer = $stdin.gets.chomp
|
26
|
+
return answer == "yes"
|
27
|
+
end
|
28
|
+
|
29
|
+
def remove_book( book )
|
30
|
+
puts "Removing #{book.title}. A backup is saved to /tmp just in case."
|
31
|
+
FileUtils.mv( book.file_name, "/tmp" )
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
data/lib/alx/renderer.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'redcarpet'
|
2
|
+
|
3
|
+
module Alx
|
4
|
+
|
5
|
+
class Renderer
|
6
|
+
def initialize( conf )
|
7
|
+
@conf = conf
|
8
|
+
end
|
9
|
+
|
10
|
+
def render_to_file( book, destination = nil )
|
11
|
+
on_file( destination ) { | file | file.puts( render( book ) ) }
|
12
|
+
end
|
13
|
+
|
14
|
+
def render( book )
|
15
|
+
if @conf[ :html ]
|
16
|
+
markdown = Redcarpet::Markdown.new( Redcarpet::Render::HTML.new, :fenced_code_blocks => true, :tables => true )
|
17
|
+
return markdown.render( book.content )
|
18
|
+
end
|
19
|
+
book.content
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
def on_file( destination )
|
24
|
+
file_name = destination ? destination : temp_file
|
25
|
+
File.open( file_name, "w" ) do | file |
|
26
|
+
yield file
|
27
|
+
end
|
28
|
+
file_name
|
29
|
+
end
|
30
|
+
|
31
|
+
def temp_file
|
32
|
+
extension = @conf[ :html ] ? ".html" : ".md"
|
33
|
+
file_name = "/tmp/" + Time.now.to_i.to_s + extension
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'alx/handler'
|
2
|
+
require 'alx/renderer'
|
3
|
+
require 'alx/viewer'
|
4
|
+
|
5
|
+
module Alx
|
6
|
+
|
7
|
+
class ShowHandler < Handler
|
8
|
+
def initialize( configuration )
|
9
|
+
super(
|
10
|
+
:configuration => configuration,
|
11
|
+
:command => 'show',
|
12
|
+
:rest_description => 'pattern',
|
13
|
+
:description => "Shows book matching the given pattern.",
|
14
|
+
:options => [ :html, :stdout ] )
|
15
|
+
end
|
16
|
+
|
17
|
+
def handle
|
18
|
+
parse_options
|
19
|
+
options_error( "No pattern given. Please provide one." ) if @conf[ :rest ].empty?
|
20
|
+
load_library.list( @conf[ :rest ] ).each { | book | show_book( book ) }
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
def show_book( book )
|
25
|
+
file_name = Renderer.new( @conf ).render_to_file( book )
|
26
|
+
Viewer.new( @conf ).view( file_name )
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
data/lib/alx/version.rb
ADDED
data/lib/alx/viewer.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
module Alx
|
2
|
+
|
3
|
+
class Viewer
|
4
|
+
include Executer
|
5
|
+
|
6
|
+
def initialize( conf )
|
7
|
+
@conf = conf
|
8
|
+
end
|
9
|
+
|
10
|
+
def view( file_name )
|
11
|
+
if @conf[ :stdout ]
|
12
|
+
File.open( file_name ) do | file |
|
13
|
+
while line = file.gets
|
14
|
+
$stdout.puts line
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
return true
|
19
|
+
end
|
20
|
+
|
21
|
+
if @conf[ :html ]
|
22
|
+
start( @conf[ :html_viewer ] + " " + file_name )
|
23
|
+
return true
|
24
|
+
end
|
25
|
+
|
26
|
+
start( @conf[ :terminal_viewer ] + " " + file_name )
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
data/test/tc_book.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'alx/book'
|
4
|
+
require 'stringio'
|
5
|
+
|
6
|
+
class BookTest < Test::Unit::TestCase
|
7
|
+
|
8
|
+
def setup
|
9
|
+
@text = 'something something dark\nside\n'
|
10
|
+
File.stubs( :open ).yields( StringIO.new( @text ) )
|
11
|
+
@book = Alx::Book.new( 'title', 'filename' )
|
12
|
+
end
|
13
|
+
|
14
|
+
def teardown
|
15
|
+
File.unstub( :open )
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_show_content
|
19
|
+
@book.content.should == @text
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'alx/controller'
|
4
|
+
require 'rspec'
|
5
|
+
|
6
|
+
class ControllerTest < Test::Unit::TestCase
|
7
|
+
|
8
|
+
def setup
|
9
|
+
@testconf = {}
|
10
|
+
@controller = Alx::Controller.new( @testconf )
|
11
|
+
@handlers = @controller.handlers
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_constructor_creates_handlers
|
15
|
+
@controller.handlers.should have_key( 'add' )
|
16
|
+
@controller.handlers.should have_key( 'list' )
|
17
|
+
@controller.handlers.should have_key( 'show' )
|
18
|
+
@controller.handlers.should have_key( 'remove' )
|
19
|
+
@controller.handlers.should have_key( 'help' )
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_command_handler_should_be_called_if_exists
|
23
|
+
@testconf[ :command ] = 'help'
|
24
|
+
@handlers[ 'help' ] = HandlerStub.new( @testconf )
|
25
|
+
@controller.run()
|
26
|
+
@handlers[ 'help' ].handle_called.should == true
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_help_handler_should_be_called_if_command_does_not_exist
|
30
|
+
@testconf[ :command ] = 'somethingsomethingdarkside'
|
31
|
+
@handlers[ 'help' ] = HandlerStub.new( @testconf )
|
32
|
+
lambda { @controller.run() }.should raise_error
|
33
|
+
@handlers[ 'help' ].handle_called.should == true
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
data/test/tc_editor.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'alx/editor'
|
4
|
+
|
5
|
+
class EditorTest < Test::Unit::TestCase
|
6
|
+
|
7
|
+
def setup
|
8
|
+
@conf = Hash.new
|
9
|
+
@editor = Alx::Editor.new( @conf )
|
10
|
+
@file_output = StringIO.new
|
11
|
+
File.stubs( :open ).yields( @file_output )
|
12
|
+
end
|
13
|
+
|
14
|
+
def teardown
|
15
|
+
File.unstub( :open )
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_editor_error
|
19
|
+
@conf[ :editor ] = DUMMY_EXEC
|
20
|
+
lambda { @editor.edit( '/tmp/some_file' ) }.should raise_error
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_editor_returns_false
|
24
|
+
@conf[ :editor ] = FALSE_EXEC
|
25
|
+
lambda { @editor.edit( '/tmp/some_file' ) }.should raise_error
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_editor_success
|
29
|
+
@conf[ :editor ] = TRUE_EXEC
|
30
|
+
lambda { @editor.edit( '/tmp/some_file' ) }.should_not raise_error
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_stdin
|
34
|
+
text_to_write = "some string"
|
35
|
+
stub_in_and_out( text_to_write ) do
|
36
|
+
lambda { @editor.edit( '/tmp/some_file' ) }.should_not raise_error
|
37
|
+
end.should =~ /Reading.*from.*standard.*input/
|
38
|
+
@file_output.string.chomp.should == text_to_write
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
data/test/tc_library.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'alx/library'
|
4
|
+
require 'alx/book'
|
5
|
+
|
6
|
+
class LibraryTest < Test::Unit::TestCase
|
7
|
+
|
8
|
+
def setup_dir( dir )
|
9
|
+
dir.entries << "first_file"
|
10
|
+
dir.entries << "almost_first_file"
|
11
|
+
dir.entries << "second_file"
|
12
|
+
end
|
13
|
+
|
14
|
+
def setup
|
15
|
+
@directory = 'something'
|
16
|
+
@dir_stub = DirStub.new
|
17
|
+
setup_dir( @dir_stub )
|
18
|
+
Dir.stubs( :open ).yields( @dir_stub )
|
19
|
+
@library = Alx::Library.new.load_library( @directory )
|
20
|
+
end
|
21
|
+
|
22
|
+
def teardown
|
23
|
+
Dir.unstub( :open )
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_list_pattern
|
27
|
+
books = @library.list( 'first' )
|
28
|
+
books.size.should == 2
|
29
|
+
books[ 0 ].title.should == @dir_stub.entries[ 0 ].gsub( /_/, ' ' )
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|