melon 0.1.0 → 0.2.0
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/History.txt +7 -0
- data/features/add.feature +57 -0
- data/features/check.feature +27 -0
- data/features/edges.feature +8 -1
- data/features/list.feature +18 -0
- data/features/show.feature +28 -0
- data/lib/melon/cli.rb +11 -31
- data/lib/melon/commands.rb +125 -13
- data/lib/melon/helpers.rb +43 -0
- data/lib/melon/version.rb +1 -1
- data/script/console +8 -5
- metadata +13 -9
- data/features/basic.feature +0 -33
- data/lib/melon/commands/add.rb +0 -10
- data/lib/melon/commands/basic_command.rb +0 -19
- data/lib/melon/commands/help.rb +0 -51
    
        data/History.txt
    CHANGED
    
    
| @@ -0,0 +1,57 @@ | |
| 1 | 
            +
            Feature: Adding files to the database
         | 
| 2 | 
            +
              In order to have something to compare new files to
         | 
| 3 | 
            +
              As a user
         | 
| 4 | 
            +
              I should be able to add files to the database
         | 
| 5 | 
            +
             | 
| 6 | 
            +
             | 
| 7 | 
            +
              Background:
         | 
| 8 | 
            +
                Given a file named "test_file" with:
         | 
| 9 | 
            +
                """
         | 
| 10 | 
            +
                This file is a test file
         | 
| 11 | 
            +
                """
         | 
| 12 | 
            +
             | 
| 13 | 
            +
              Scenario: Adding files to a melon database
         | 
| 14 | 
            +
                When I run "melon -d test.db add test_file"
         | 
| 15 | 
            +
                Then the output should contain a hash
         | 
| 16 | 
            +
                And the output should contain "test_file"
         | 
| 17 | 
            +
             | 
| 18 | 
            +
              Scenario: Adding a file that already exists
         | 
| 19 | 
            +
                When I run "melon -d test.db add test_file"
         | 
| 20 | 
            +
                And I run "melon -d test.db add test_file"
         | 
| 21 | 
            +
                Then it should fail with:
         | 
| 22 | 
            +
                """
         | 
| 23 | 
            +
                melon: path already present in database
         | 
| 24 | 
            +
                """
         | 
| 25 | 
            +
             | 
| 26 | 
            +
              Scenario: Adding a directory itself
         | 
| 27 | 
            +
                Given a directory named "testo"
         | 
| 28 | 
            +
                When I run "melon -d test.db add testo"
         | 
| 29 | 
            +
                Then it should fail with:
         | 
| 30 | 
            +
                """
         | 
| 31 | 
            +
                directory
         | 
| 32 | 
            +
                """
         | 
| 33 | 
            +
             | 
| 34 | 
            +
              Scenario: Adding a file that doesn't exist
         | 
| 35 | 
            +
                When I run "melon -d test.db add nonexistant_file"
         | 
| 36 | 
            +
                Then it should fail with:
         | 
| 37 | 
            +
                """
         | 
| 38 | 
            +
                melon: no such file: nonexistant_file
         | 
| 39 | 
            +
                """
         | 
| 40 | 
            +
             | 
| 41 | 
            +
              Scenario: Adding a directory recursively
         | 
| 42 | 
            +
                Given a file named "dir/test1" with:
         | 
| 43 | 
            +
                """
         | 
| 44 | 
            +
                First test file
         | 
| 45 | 
            +
                """
         | 
| 46 | 
            +
                And a file named "dir/test2" with:
         | 
| 47 | 
            +
                """
         | 
| 48 | 
            +
                Second test file
         | 
| 49 | 
            +
                """
         | 
| 50 | 
            +
                And a file named "dir/test/test3" with:
         | 
| 51 | 
            +
                """
         | 
| 52 | 
            +
                Third test file
         | 
| 53 | 
            +
                """
         | 
| 54 | 
            +
                When I run "melon -d test.db add -r dir"
         | 
| 55 | 
            +
                Then the output should contain "dir/test1"
         | 
| 56 | 
            +
                And the output should contain "dir/test2"
         | 
| 57 | 
            +
                And the output should contain "dir/test/test3"
         | 
| @@ -0,0 +1,27 @@ | |
| 1 | 
            +
            Feature: Check
         | 
| 2 | 
            +
              In order to see if a given file is tracked by the database
         | 
| 3 | 
            +
              As a user
         | 
| 4 | 
            +
              I should be able to query the database with a file
         | 
| 5 | 
            +
             | 
| 6 | 
            +
              Background:
         | 
| 7 | 
            +
                Given a file named "test_file" with:
         | 
| 8 | 
            +
                """
         | 
| 9 | 
            +
                This file is a test file
         | 
| 10 | 
            +
                """
         | 
| 11 | 
            +
             | 
| 12 | 
            +
              Scenario: Checking a file that is not in the database
         | 
| 13 | 
            +
                When I run "melon -d test.db check test_file"
         | 
| 14 | 
            +
                Then the output should contain "test_file"
         | 
| 15 | 
            +
                And the output should start with "/"
         | 
| 16 | 
            +
             | 
| 17 | 
            +
              Scenario: Checking a file that is in the database:
         | 
| 18 | 
            +
                When I run "melon -d test.db add -q test_file"
         | 
| 19 | 
            +
                And I run "melon -d test.db check test_file"
         | 
| 20 | 
            +
                Then the output should be empty
         | 
| 21 | 
            +
             | 
| 22 | 
            +
              Scenario: Checking a file that doesn't exist
         | 
| 23 | 
            +
                When I run "melon -d test.db check nonexistant_file"
         | 
| 24 | 
            +
                Then it should fail with:
         | 
| 25 | 
            +
                """
         | 
| 26 | 
            +
                melon: no such file: nonexistant_file
         | 
| 27 | 
            +
                """
         | 
    
        data/features/edges.feature
    CHANGED
    
    | @@ -7,7 +7,14 @@ Feature: Edge cases | |
| 7 7 | 
             
                """
         | 
| 8 8 | 
             
                When I run "cp test_file test_file_2"
         | 
| 9 9 | 
             
                And I run "melon -d test.db add test_file test_file_2"
         | 
| 10 | 
            -
                Then  | 
| 10 | 
            +
                Then it should fail with:
         | 
| 11 11 | 
             
                """
         | 
| 12 12 | 
             
                melon: file exists elsewhere in the database
         | 
| 13 13 | 
             
                """
         | 
| 14 | 
            +
             | 
| 15 | 
            +
              Scenario: Unrecognized command
         | 
| 16 | 
            +
                When I run "melon whizzle bang"
         | 
| 17 | 
            +
                Then it should fail with:
         | 
| 18 | 
            +
                """
         | 
| 19 | 
            +
                melon: unrecognized command: whizzle
         | 
| 20 | 
            +
                """
         | 
| @@ -0,0 +1,18 @@ | |
| 1 | 
            +
            Feature: List
         | 
| 2 | 
            +
              In order to see what's tracked by the database
         | 
| 3 | 
            +
              As a user
         | 
| 4 | 
            +
              I should be able to get a list of tracked files
         | 
| 5 | 
            +
             | 
| 6 | 
            +
              Scenario: Listing files
         | 
| 7 | 
            +
                Given a file named "test_file" with:
         | 
| 8 | 
            +
                """
         | 
| 9 | 
            +
                Test file 1
         | 
| 10 | 
            +
                """
         | 
| 11 | 
            +
                And a file named "file_test" with:
         | 
| 12 | 
            +
                """
         | 
| 13 | 
            +
                Test file 2
         | 
| 14 | 
            +
                """
         | 
| 15 | 
            +
                And I run "melon -d test.db add -q test_file file_test"
         | 
| 16 | 
            +
                When I run "melon -d test.db list"
         | 
| 17 | 
            +
                Then the output should contain "test_file"
         | 
| 18 | 
            +
                And the output should contain "file_test"
         | 
| @@ -0,0 +1,28 @@ | |
| 1 | 
            +
            Feature: Show
         | 
| 2 | 
            +
              In order to see where a file is stored (according the database)
         | 
| 3 | 
            +
              As a user
         | 
| 4 | 
            +
              I should be able to query the database with a file
         | 
| 5 | 
            +
             | 
| 6 | 
            +
              Background:
         | 
| 7 | 
            +
                Given a file named "dir/test_file" with:
         | 
| 8 | 
            +
                """
         | 
| 9 | 
            +
                This file is a test file
         | 
| 10 | 
            +
                """
         | 
| 11 | 
            +
                And I run "cp dir/test_file ."
         | 
| 12 | 
            +
             | 
| 13 | 
            +
              Scenario: Showing a file that is not in the database
         | 
| 14 | 
            +
                When I run "melon -d test.db show test_file"
         | 
| 15 | 
            +
                Then the output should be empty
         | 
| 16 | 
            +
             | 
| 17 | 
            +
              Scenario: Showing a file that is in the database:
         | 
| 18 | 
            +
                When I run "melon -d test.db add -q dir/test_file"
         | 
| 19 | 
            +
                And I run "melon -d test.db show test_file"
         | 
| 20 | 
            +
                Then the output should contain "dir/test_file"
         | 
| 21 | 
            +
                And the output should start with "/"
         | 
| 22 | 
            +
             | 
| 23 | 
            +
              Scenario: Showing a file that doesn't exist
         | 
| 24 | 
            +
                When I run "melon -d test.db show nonexistant_file"
         | 
| 25 | 
            +
                Then it should fail with:
         | 
| 26 | 
            +
                """
         | 
| 27 | 
            +
                melon: no such file: nonexistant_file
         | 
| 28 | 
            +
                """
         | 
    
        data/lib/melon/cli.rb
    CHANGED
    
    | @@ -1,11 +1,14 @@ | |
| 1 1 | 
             
            require 'ostruct'
         | 
| 2 | 
            +
            require 'pstore'
         | 
| 2 3 | 
             
            require 'optparse'
         | 
| 3 4 |  | 
| 4 5 | 
             
            require 'melon/version'
         | 
| 5 6 | 
             
            require 'melon/commands'
         | 
| 7 | 
            +
            require 'melon/helpers'
         | 
| 6 8 |  | 
| 7 9 | 
             
            module Melon
         | 
| 8 10 | 
             
              class CLI
         | 
| 11 | 
            +
                include Helpers
         | 
| 9 12 |  | 
| 10 13 | 
             
                def self.execute(arguments=[])
         | 
| 11 14 | 
             
                  new(arguments).run
         | 
| @@ -42,15 +45,17 @@ module Melon | |
| 42 45 | 
             
                  # look for command class in args.shift
         | 
| 43 46 | 
             
                  command_name = arguments.shift
         | 
| 44 47 | 
             
                  begin
         | 
| 45 | 
            -
                     | 
| 46 | 
            -
             | 
| 47 | 
            -
             | 
| 48 | 
            -
             | 
| 48 | 
            +
                    c = Commands[command_name.capitalize]
         | 
| 49 | 
            +
                  rescue NameError => e
         | 
| 50 | 
            +
                    # don't swallow NoMethodErrors
         | 
| 51 | 
            +
                    raise e unless e.instance_of?(NameError)
         | 
| 52 | 
            +
                    error "unrecognized command: #{command_name}"
         | 
| 49 53 | 
             
                  end
         | 
| 54 | 
            +
                  c.new(arguments, options).run
         | 
| 50 55 | 
             
                end
         | 
| 51 56 |  | 
| 52 57 | 
             
                def parse_options
         | 
| 53 | 
            -
                  options =  | 
| 58 | 
            +
                  options = self.class.default_options
         | 
| 54 59 |  | 
| 55 60 | 
             
                  parser = OptionParser.new do |p|
         | 
| 56 61 | 
             
                    p.banner = "Usage: melon [options] COMMAND [command-options] [ARGS]"
         | 
| @@ -82,40 +87,15 @@ module Melon | |
| 82 87 | 
             
                      puts p
         | 
| 83 88 | 
             
                      exit 0
         | 
| 84 89 | 
             
                    end
         | 
| 85 | 
            -
             | 
| 86 90 | 
             
                  end
         | 
| 87 91 |  | 
| 88 92 | 
             
                  begin
         | 
| 89 93 | 
             
                    parser.order!(arguments)
         | 
| 90 94 | 
             
                  rescue OptionParser::ParseError => e
         | 
| 91 | 
            -
                     | 
| 95 | 
            +
                    error e
         | 
| 92 96 | 
             
                  end
         | 
| 93 97 |  | 
| 94 98 | 
             
                  options
         | 
| 95 99 | 
             
                end
         | 
| 96 | 
            -
             | 
| 97 | 
            -
                def format_command(name, desc, margin = 4, width = 22, wrapdesc = 80)
         | 
| 98 | 
            -
                  pad = "\n" + ' ' * width
         | 
| 99 | 
            -
                  desc = wrap_text(desc, wrapdesc - width).split("\n").join(pad)
         | 
| 100 | 
            -
             | 
| 101 | 
            -
                  ' ' * margin + "#{name.ljust(width-margin)}#{desc}"
         | 
| 102 | 
            -
                end
         | 
| 103 | 
            -
             | 
| 104 | 
            -
                def wrap_text(txt, col = 80)
         | 
| 105 | 
            -
                  txt.gsub(/(.{1,#{col}})( +|$\n?)|(.{1,#{col}})/,
         | 
| 106 | 
            -
                           "\\1\\3\n") 
         | 
| 107 | 
            -
                end
         | 
| 108 | 
            -
                
         | 
| 109 | 
            -
                
         | 
| 110 | 
            -
                def self.error(error_obj_or_str, code = 1)
         | 
| 111 | 
            -
                  if error_obj_or_str.respond_to?('to_s')
         | 
| 112 | 
            -
                    error_str = error_obj_or_str.to_s
         | 
| 113 | 
            -
                  else
         | 
| 114 | 
            -
                    error_str = error_obj_or_str.inspect
         | 
| 115 | 
            -
                  end
         | 
| 116 | 
            -
             | 
| 117 | 
            -
                  $stderr.puts "melon: #{error_str}"
         | 
| 118 | 
            -
                  exit code
         | 
| 119 | 
            -
                end
         | 
| 120 100 | 
             
              end
         | 
| 121 101 | 
             
            end
         | 
    
        data/lib/melon/commands.rb
    CHANGED
    
    | @@ -1,16 +1,32 @@ | |
| 1 | 
            -
            require 'pstore'
         | 
| 2 | 
            -
            require 'ftools'
         | 
| 3 | 
            -
             | 
| 4 1 | 
             
            require 'melon/hasher'
         | 
| 5 | 
            -
            require 'melon/ | 
| 6 | 
            -
             | 
| 7 | 
            -
            # TODO: in commands, parse arguments with parse!  in CLI, parse with order!
         | 
| 2 | 
            +
            require 'melon/helpers'
         | 
| 8 3 |  | 
| 9 4 | 
             
            module Melon
         | 
| 10 5 | 
             
              module Commands
         | 
| 6 | 
            +
                def self.each
         | 
| 7 | 
            +
                  consts = []
         | 
| 8 | 
            +
                  base = self.const_get('Base')
         | 
| 9 | 
            +
                  self.constants.each do |c|
         | 
| 10 | 
            +
                    const = self.const_get(c)
         | 
| 11 | 
            +
                    if const.superclass == base
         | 
| 12 | 
            +
                      consts << const
         | 
| 13 | 
            +
                      yield const 
         | 
| 14 | 
            +
                    end
         | 
| 15 | 
            +
                  end
         | 
| 16 | 
            +
                  consts
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                class << self
         | 
| 20 | 
            +
                  alias :[] :const_get
         | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
             | 
| 11 23 | 
             
                # needs a 'verify' command to check integrity of database
         | 
| 24 | 
            +
                #   both internal 2-hash consistency (consistency) and db<->filesystem
         | 
| 25 | 
            +
                #   matching up (integrity) [file exists, hashes match]
         | 
| 12 26 | 
             
                # needs a 'remove' command, or some way to deal with deletes/renames
         | 
| 27 | 
            +
                # needs a 'list' command
         | 
| 13 28 | 
             
                class Base
         | 
| 29 | 
            +
                  include Helpers
         | 
| 14 30 | 
             
                  attr_accessor :args, :options
         | 
| 15 31 | 
             
                  attr_reader :description
         | 
| 16 32 |  | 
| @@ -31,10 +47,18 @@ module Melon | |
| 31 47 | 
             
                    begin
         | 
| 32 48 | 
             
                      parser.parse!(args)
         | 
| 33 49 | 
             
                    rescue OptionParser::ParseError => e
         | 
| 34 | 
            -
                       | 
| 50 | 
            +
                      error "#{self.class.to_s.split("::").last.downcase}: #{e}"
         | 
| 35 51 | 
             
                    end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                    # verify remaining args are files - overrideable
         | 
| 54 | 
            +
                    verify_args
         | 
| 36 55 | 
             
                  end
         | 
| 37 56 |  | 
| 57 | 
            +
                  def verify_args
         | 
| 58 | 
            +
                    args.each do |arg|
         | 
| 59 | 
            +
                      error "no such file: #{arg}" unless File.exists?(arg)
         | 
| 60 | 
            +
                    end
         | 
| 61 | 
            +
                  end
         | 
| 38 62 | 
             
                end
         | 
| 39 63 |  | 
| 40 64 | 
             
                class Add < Base
         | 
| @@ -47,12 +71,20 @@ module Melon | |
| 47 71 | 
             
                    @parser ||= OptionParser.new do |p|
         | 
| 48 72 | 
             
                      p.banner = "Usage: melon add [options] file [file [file ...]]"
         | 
| 49 73 | 
             
                      p.separator ""
         | 
| 50 | 
            -
                      p.separator Add.description
         | 
| 74 | 
            +
                      p.separator blockquote(Add.description + ".")
         | 
| 51 75 |  | 
| 52 76 | 
             
                      p.separator ""
         | 
| 53 77 | 
             
                      p.separator "Options:"
         | 
| 54 78 | 
             
                      p.separator ""
         | 
| 55 79 |  | 
| 80 | 
            +
                      p.on("-q", "--quiet", "Suppress printing of hash and path") do
         | 
| 81 | 
            +
                        options.quiet = true
         | 
| 82 | 
            +
                      end
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                      p.on("-r", "--recursive", "Recursively add directory contents") do
         | 
| 85 | 
            +
                        options.recursive = true
         | 
| 86 | 
            +
                      end
         | 
| 87 | 
            +
             | 
| 56 88 | 
             
                      # p.on("-f", "--force",
         | 
| 57 89 | 
             
                      #      "Force the recalculation of the path that",
         | 
| 58 90 | 
             
                      #      " already exists in the database") do
         | 
| @@ -64,29 +96,73 @@ module Melon | |
| 64 96 | 
             
                  def run
         | 
| 65 97 | 
             
                    parse_options!
         | 
| 66 98 |  | 
| 99 | 
            +
                    if options.recursive
         | 
| 100 | 
            +
                      self.args = args.collect do |arg|
         | 
| 101 | 
            +
                        if File.directory?(arg)
         | 
| 102 | 
            +
                          Dir["#{arg}/**/*"]
         | 
| 103 | 
            +
                        else
         | 
| 104 | 
            +
                          arg
         | 
| 105 | 
            +
                        end
         | 
| 106 | 
            +
                      end.flatten.reject { |arg| File.directory?(arg) }
         | 
| 107 | 
            +
                    end
         | 
| 108 | 
            +
             | 
| 67 109 | 
             
                    options.database.transaction do
         | 
| 68 110 | 
             
                      args.each do |arg|
         | 
| 69 111 | 
             
                        filename = File.expand_path(arg)
         | 
| 70 112 |  | 
| 71 113 | 
             
                        if File.directory?(filename)
         | 
| 72 | 
            -
                           | 
| 114 | 
            +
                          error "argument is a directory: #{arg}"
         | 
| 73 115 | 
             
                        end
         | 
| 74 116 |  | 
| 75 117 | 
             
                        if options.database[:by_path][filename]# and !options.force
         | 
| 76 | 
            -
                           | 
| 118 | 
            +
                          error "path already present in database: #{arg}"
         | 
| 77 119 | 
             
                        end
         | 
| 78 120 |  | 
| 79 121 | 
             
                        # hash strategy should be encapsulated, ergo indirection here
         | 
| 80 122 | 
             
                        hash = Hasher.digest(filename)
         | 
| 81 123 |  | 
| 82 124 | 
             
                        if options.database[:by_hash][hash]
         | 
| 83 | 
            -
                           | 
| 125 | 
            +
                          error "file exists elsewhere in the database: #{arg}"
         | 
| 84 126 | 
             
                        end
         | 
| 85 127 |  | 
| 86 128 |  | 
| 87 129 | 
             
                        options.database[:by_hash][hash] = filename
         | 
| 88 130 | 
             
                        options.database[:by_path][filename] = hash
         | 
| 89 | 
            -
                        puts "#{hash}:#{filename}"
         | 
| 131 | 
            +
                        puts "#{hash}:#{filename}" unless options.quiet
         | 
| 132 | 
            +
                      end
         | 
| 133 | 
            +
                    end
         | 
| 134 | 
            +
                  end
         | 
| 135 | 
            +
                end
         | 
| 136 | 
            +
             | 
| 137 | 
            +
                class Show < Base
         | 
| 138 | 
            +
                  def self.description
         | 
| 139 | 
            +
                    "Show where the database thinks a file is located"
         | 
| 140 | 
            +
                  end
         | 
| 141 | 
            +
             | 
| 142 | 
            +
                  def parser
         | 
| 143 | 
            +
                    @parser ||= OptionParser.new do |p|
         | 
| 144 | 
            +
                      p.banner = "Usage: melon show file [file [file ...]]"
         | 
| 145 | 
            +
                      p.separator ""
         | 
| 146 | 
            +
                      p.separator blockquote(self.class.description + <<EOS
         | 
| 147 | 
            +
            .  If the file's hash matches a hash in the database, then
         | 
| 148 | 
            +
            the associated path in the database is printed.  Otherwise,
         | 
| 149 | 
            +
            nothing is printed.
         | 
| 150 | 
            +
            EOS
         | 
| 151 | 
            +
                                            )
         | 
| 152 | 
            +
                      p.separator ""
         | 
| 153 | 
            +
                      
         | 
| 154 | 
            +
                    end
         | 
| 155 | 
            +
                  end
         | 
| 156 | 
            +
             | 
| 157 | 
            +
                  def run
         | 
| 158 | 
            +
                    parse_options!
         | 
| 159 | 
            +
             | 
| 160 | 
            +
                    options.database.transaction do
         | 
| 161 | 
            +
                      args.each do |filename|
         | 
| 162 | 
            +
                        hash = Hasher.digest(filename)
         | 
| 163 | 
            +
                        if path = options.database[:by_hash][hash]
         | 
| 164 | 
            +
                          puts path
         | 
| 165 | 
            +
                        end
         | 
| 90 166 | 
             
                      end
         | 
| 91 167 | 
             
                    end
         | 
| 92 168 | 
             
                  end
         | 
| @@ -101,7 +177,13 @@ module Melon | |
| 101 177 | 
             
                    @parser ||= OptionParser.new do |p|
         | 
| 102 178 | 
             
                      p.banner = "Usage: melon check file [file [file ...]]"
         | 
| 103 179 | 
             
                      p.separator ""
         | 
| 104 | 
            -
                      p.separator  | 
| 180 | 
            +
                      p.separator blockquote(self.class.description + <<EOS
         | 
| 181 | 
            +
            .  If the file's hash matches a hash in the database, nothing is
         | 
| 182 | 
            +
            printed.  Otherwise, the full path to the file is printed.
         | 
| 183 | 
            +
            EOS
         | 
| 184 | 
            +
                                            )
         | 
| 185 | 
            +
                      p.separator ""
         | 
| 186 | 
            +
                      
         | 
| 105 187 | 
             
                    end
         | 
| 106 188 | 
             
                  end
         | 
| 107 189 |  | 
| @@ -118,5 +200,35 @@ module Melon | |
| 118 200 | 
             
                    end
         | 
| 119 201 | 
             
                  end
         | 
| 120 202 | 
             
                end
         | 
| 203 | 
            +
             | 
| 204 | 
            +
                class List < Base
         | 
| 205 | 
            +
                  def self.description
         | 
| 206 | 
            +
                    "List the files tracked by the database"
         | 
| 207 | 
            +
                  end
         | 
| 208 | 
            +
             | 
| 209 | 
            +
                  def parser
         | 
| 210 | 
            +
                    @parser ||= OptionParser.new do |p|
         | 
| 211 | 
            +
                      p.banner = "Usage: melon list"
         | 
| 212 | 
            +
                      p.separator ""
         | 
| 213 | 
            +
                      p.separator blockquote(self.class.description)
         | 
| 214 | 
            +
                      p.separator ""
         | 
| 215 | 
            +
                      
         | 
| 216 | 
            +
                    end
         | 
| 217 | 
            +
                  end
         | 
| 218 | 
            +
             | 
| 219 | 
            +
                  def verify_args
         | 
| 220 | 
            +
                    error "invalid argument: #{args.shift}" unless args.empty?
         | 
| 221 | 
            +
                  end
         | 
| 222 | 
            +
             | 
| 223 | 
            +
                  def run
         | 
| 224 | 
            +
                    parse_options!
         | 
| 225 | 
            +
             | 
| 226 | 
            +
                    options.database.transaction do
         | 
| 227 | 
            +
                      options.database[:by_hash].each_pair do |hash, path|
         | 
| 228 | 
            +
                        puts "#{path}:#{hash}"
         | 
| 229 | 
            +
                      end
         | 
| 230 | 
            +
                    end
         | 
| 231 | 
            +
                  end
         | 
| 232 | 
            +
                end
         | 
| 121 233 | 
             
              end
         | 
| 122 234 | 
             
            end
         | 
| @@ -0,0 +1,43 @@ | |
| 1 | 
            +
            module Melon
         | 
| 2 | 
            +
              module Helpers
         | 
| 3 | 
            +
                def format_command(name, desc, options = {})
         | 
| 4 | 
            +
                  options = {
         | 
| 5 | 
            +
                    :margin => 4,
         | 
| 6 | 
            +
                    :width => 22,
         | 
| 7 | 
            +
                    :wrap => 80
         | 
| 8 | 
            +
                  }.update(options)
         | 
| 9 | 
            +
                  
         | 
| 10 | 
            +
                  pad = "\n" + ' ' * options[:width]
         | 
| 11 | 
            +
                  desc = self.wrap_text(desc, options[:wrap] - options[:width])
         | 
| 12 | 
            +
                  desc = desc.split("\n").join(pad)
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                  ' ' * options[:margin] +
         | 
| 15 | 
            +
                    "#{name.ljust(options[:width] - options[:margin])}#{desc}"
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                def wrap_text(txt, col = 80)
         | 
| 19 | 
            +
                  txt.gsub(/(.{1,#{col}})( +|$\n?)|(.{1,#{col}})/,
         | 
| 20 | 
            +
                           "\\1\\3\n") 
         | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                def blockquote(string, options = {})
         | 
| 24 | 
            +
                  options = {
         | 
| 25 | 
            +
                    :margin => 4,
         | 
| 26 | 
            +
                    :wrap => 70
         | 
| 27 | 
            +
                  }.update(options)
         | 
| 28 | 
            +
                  options[:width] = options.delete(:margin)
         | 
| 29 | 
            +
                  format_command('', string.gsub(/\s+/,' ').gsub('\. ', '.  '), options)
         | 
| 30 | 
            +
                end
         | 
| 31 | 
            +
                
         | 
| 32 | 
            +
                def error(error_obj_or_str, code = 1)
         | 
| 33 | 
            +
                  if error_obj_or_str.respond_to?('to_s')
         | 
| 34 | 
            +
                    error_str = error_obj_or_str.to_s
         | 
| 35 | 
            +
                  else
         | 
| 36 | 
            +
                    error_str = error_obj_or_str.inspect
         | 
| 37 | 
            +
                  end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                  $stderr.puts "melon: #{error_str}"
         | 
| 40 | 
            +
                  exit code
         | 
| 41 | 
            +
                end
         | 
| 42 | 
            +
              end
         | 
| 43 | 
            +
            end
         | 
    
        data/lib/melon/version.rb
    CHANGED
    
    
    
        data/script/console
    CHANGED
    
    | @@ -1,12 +1,15 @@ | |
| 1 1 | 
             
            #!/usr/bin/env ruby
         | 
| 2 | 
            -
             | 
| 2 | 
            +
             | 
| 3 3 | 
             
            irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
         | 
| 4 4 |  | 
| 5 | 
            -
             | 
| 6 | 
            -
             | 
| 5 | 
            +
            lib_dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            libs = []
         | 
| 8 | 
            +
            libs << " -I #{lib_dir}"
         | 
| 9 | 
            +
            libs << " -r irb/completion"
         | 
| 10 | 
            +
            libs << " -r irb/ext/save-history"
         | 
| 7 11 |  | 
| 8 | 
            -
            # Perhaps use a console_lib to store any extra methods I may want available in the cosole
         | 
| 9 | 
            -
            # libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
         | 
| 10 12 | 
             
            libs <<  " -r #{File.dirname(__FILE__) + '/../lib/melon.rb'}"
         | 
| 11 13 | 
             
            puts "Loading melon gem"
         | 
| 14 | 
            +
             | 
| 12 15 | 
             
            exec "#{irb} #{libs} --simple-prompt"
         | 
    
        metadata
    CHANGED
    
    | @@ -1,13 +1,13 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification 
         | 
| 2 2 | 
             
            name: melon
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version 
         | 
| 4 | 
            -
              hash:  | 
| 4 | 
            +
              hash: 23
         | 
| 5 5 | 
             
              prerelease: false
         | 
| 6 6 | 
             
              segments: 
         | 
| 7 7 | 
             
              - 0
         | 
| 8 | 
            -
              -  | 
| 8 | 
            +
              - 2
         | 
| 9 9 | 
             
              - 0
         | 
| 10 | 
            -
              version: 0. | 
| 10 | 
            +
              version: 0.2.0
         | 
| 11 11 | 
             
            platform: ruby
         | 
| 12 12 | 
             
            authors: 
         | 
| 13 13 | 
             
            - Andrew Roberts
         | 
| @@ -15,7 +15,7 @@ autorequire: | |
| 15 15 | 
             
            bindir: bin
         | 
| 16 16 | 
             
            cert_chain: []
         | 
| 17 17 |  | 
| 18 | 
            -
            date: 2011-01- | 
| 18 | 
            +
            date: 2011-01-26 00:00:00 -05:00
         | 
| 19 19 | 
             
            default_executable: melon
         | 
| 20 20 | 
             
            dependencies: 
         | 
| 21 21 | 
             
            - !ruby/object:Gem::Dependency 
         | 
| @@ -78,18 +78,19 @@ files: | |
| 78 78 | 
             
            - Rakefile
         | 
| 79 79 | 
             
            - TODO
         | 
| 80 80 | 
             
            - bin/melon
         | 
| 81 | 
            -
            - features/ | 
| 81 | 
            +
            - features/add.feature
         | 
| 82 | 
            +
            - features/check.feature
         | 
| 82 83 | 
             
            - features/edges.feature
         | 
| 84 | 
            +
            - features/list.feature
         | 
| 85 | 
            +
            - features/show.feature
         | 
| 83 86 | 
             
            - features/step_definitions/basic_steps.rb
         | 
| 84 87 | 
             
            - features/support/env.rb
         | 
| 85 88 | 
             
            - lib/melon.rb
         | 
| 86 89 | 
             
            - lib/melon/cli.rb
         | 
| 87 90 | 
             
            - lib/melon/commands.rb
         | 
| 88 | 
            -
            - lib/melon/commands/add.rb
         | 
| 89 | 
            -
            - lib/melon/commands/basic_command.rb
         | 
| 90 | 
            -
            - lib/melon/commands/help.rb
         | 
| 91 91 | 
             
            - lib/melon/example.rb
         | 
| 92 92 | 
             
            - lib/melon/hasher.rb
         | 
| 93 | 
            +
            - lib/melon/helpers.rb
         | 
| 93 94 | 
             
            - lib/melon/version.rb
         | 
| 94 95 | 
             
            - melon.gemspec
         | 
| 95 96 | 
             
            - script/console
         | 
| @@ -133,8 +134,11 @@ signing_key: | |
| 133 134 | 
             
            specification_version: 3
         | 
| 134 135 | 
             
            summary: A media catalog
         | 
| 135 136 | 
             
            test_files: 
         | 
| 136 | 
            -
            - features/ | 
| 137 | 
            +
            - features/add.feature
         | 
| 138 | 
            +
            - features/check.feature
         | 
| 137 139 | 
             
            - features/edges.feature
         | 
| 140 | 
            +
            - features/list.feature
         | 
| 141 | 
            +
            - features/show.feature
         | 
| 138 142 | 
             
            - features/step_definitions/basic_steps.rb
         | 
| 139 143 | 
             
            - features/support/env.rb
         | 
| 140 144 | 
             
            - spec/melon/cli_spec.rb
         | 
    
        data/features/basic.feature
    DELETED
    
    | @@ -1,33 +0,0 @@ | |
| 1 | 
            -
            Feature: Basic usage
         | 
| 2 | 
            -
             | 
| 3 | 
            -
              Background:
         | 
| 4 | 
            -
                Given a file named "test_file" with:
         | 
| 5 | 
            -
                """
         | 
| 6 | 
            -
                This file is a test file
         | 
| 7 | 
            -
                """
         | 
| 8 | 
            -
             | 
| 9 | 
            -
              Scenario: Adding files to a melon database
         | 
| 10 | 
            -
                When I run "melon -d test.db add test_file"
         | 
| 11 | 
            -
                Then the output should contain a hash
         | 
| 12 | 
            -
                And the output should contain "test_file"
         | 
| 13 | 
            -
             | 
| 14 | 
            -
              Scenario: Adding a file that already exists
         | 
| 15 | 
            -
                When I run "melon -d test.db add test_file"
         | 
| 16 | 
            -
                And I run "melon -d test.db add test_file"
         | 
| 17 | 
            -
                Then the output should contain:
         | 
| 18 | 
            -
                """
         | 
| 19 | 
            -
                melon: path already present in database
         | 
| 20 | 
            -
                """
         | 
| 21 | 
            -
                And the exit status should not be 0
         | 
| 22 | 
            -
             | 
| 23 | 
            -
              Scenario: Checking a file that is not in the database
         | 
| 24 | 
            -
                When I run "melon -d test.db check test_file"
         | 
| 25 | 
            -
                Then the output should contain "test_file"
         | 
| 26 | 
            -
                And the output should start with "/"
         | 
| 27 | 
            -
             | 
| 28 | 
            -
              Scenario: Adding a directory
         | 
| 29 | 
            -
                Given a directory named "testo"
         | 
| 30 | 
            -
                When I run "melon -d test.db add testo"
         | 
| 31 | 
            -
                Then the output should contain "directory"
         | 
| 32 | 
            -
                And the exit status should not be 0
         | 
| 33 | 
            -
             | 
    
        data/lib/melon/commands/add.rb
    DELETED
    
    
| @@ -1,19 +0,0 @@ | |
| 1 | 
            -
            module Melon
         | 
| 2 | 
            -
              module Commands
         | 
| 3 | 
            -
                module BasicCommand
         | 
| 4 | 
            -
             | 
| 5 | 
            -
                  def execute(arguments, options)
         | 
| 6 | 
            -
                    new(arguments, options).run
         | 
| 7 | 
            -
                  end
         | 
| 8 | 
            -
             | 
| 9 | 
            -
                  # returns name {padding} description, for usage"
         | 
| 10 | 
            -
                  def short_usage(padding="   ")
         | 
| 11 | 
            -
                    name = Melon::Commands.translate_command(self)
         | 
| 12 | 
            -
                    extra_spaces = Melon::Commands.commands.collect do |c|
         | 
| 13 | 
            -
                      c.length
         | 
| 14 | 
            -
                    end.max - name.length
         | 
| 15 | 
            -
                    "#{name}#{padding}#{' ' * extra_spaces}#{self.description}"
         | 
| 16 | 
            -
                  end
         | 
| 17 | 
            -
                end
         | 
| 18 | 
            -
              end
         | 
| 19 | 
            -
            end
         | 
    
        data/lib/melon/commands/help.rb
    DELETED
    
    | @@ -1,51 +0,0 @@ | |
| 1 | 
            -
            require 'melon/commands/basic_command'
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            module Melon
         | 
| 4 | 
            -
              module Commands
         | 
| 5 | 
            -
                class Help
         | 
| 6 | 
            -
                  # Help is a basic command that also ties itself in to cli#usage, so
         | 
| 7 | 
            -
                  # it's a little bit unconventional.  Retrospectively, I should not
         | 
| 8 | 
            -
                  # have written it first.  
         | 
| 9 | 
            -
                  extend BasicCommand
         | 
| 10 | 
            -
             | 
| 11 | 
            -
                  def self.description
         | 
| 12 | 
            -
                    "Get help with a specific command, or with Melon in general"
         | 
| 13 | 
            -
                  end
         | 
| 14 | 
            -
             | 
| 15 | 
            -
                  attr_accessor :arguments
         | 
| 16 | 
            -
             | 
| 17 | 
            -
                  def initialize(arguments, options)
         | 
| 18 | 
            -
                    self.arguments = arguments
         | 
| 19 | 
            -
                  end
         | 
| 20 | 
            -
                  
         | 
| 21 | 
            -
                  def parser
         | 
| 22 | 
            -
                    @parser ||= OptionParser.new do |opts|
         | 
| 23 | 
            -
                      Melon::Commands.command_hash.each do |name, command|
         | 
| 24 | 
            -
                        next if command == self.class
         | 
| 25 | 
            -
                        # TODO help banner: gem help help
         | 
| 26 | 
            -
                        # TODO flesh out parsing - give short_usage for each command
         | 
| 27 | 
            -
                        opts.on(name) { command.parser }
         | 
| 28 | 
            -
                      end
         | 
| 29 | 
            -
                    end
         | 
| 30 | 
            -
                  end
         | 
| 31 | 
            -
             | 
| 32 | 
            -
                  def run
         | 
| 33 | 
            -
                    begin
         | 
| 34 | 
            -
                      parser.parse!(arguments)
         | 
| 35 | 
            -
                    rescue OptionParser::InvalidOption => e
         | 
| 36 | 
            -
                      puts "melon: #{e.to_s}"
         | 
| 37 | 
            -
                      exit 1
         | 
| 38 | 
            -
                    end
         | 
| 39 | 
            -
             | 
| 40 | 
            -
                    if arguments == ['help']
         | 
| 41 | 
            -
                      puts parser
         | 
| 42 | 
            -
                      exit
         | 
| 43 | 
            -
                    end
         | 
| 44 | 
            -
                    
         | 
| 45 | 
            -
                    # if arguments are empty, we handled it in CLI
         | 
| 46 | 
            -
                    puts "melon: '#{arguments.join(' ')}' is not a recognized command."
         | 
| 47 | 
            -
                    exit 1
         | 
| 48 | 
            -
                  end
         | 
| 49 | 
            -
                end
         | 
| 50 | 
            -
              end
         | 
| 51 | 
            -
            end
         |