melon 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,8 @@
1
+ tmp
2
+ *.swo
3
+ *~
4
+ *.swp
5
+ pkg
6
+ coverage
7
+ *.db
8
+ Gemfile.lock
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
@@ -0,0 +1,6 @@
1
+ === 0.1.0 2011-01-25
2
+
3
+ * Initial release, basic functionality present:
4
+ * Add files to database (add)
5
+ * query database about file (check)
6
+
@@ -0,0 +1,29 @@
1
+ h1. Melon
2
+
3
+ A media file cataloging tool.
4
+
5
+ h2. Synopsis
6
+
7
+ Melon is a tool for tracking files based on content, aiming
8
+ to scale to arbitrarily large files without slowing down. It uses
9
+ a sparse hashing algorithm to generate a digest without having to
10
+ read the whole file, allowing it to go much faster than disk would
11
+ ordinarily allow.
12
+
13
+ h2. Installation
14
+
15
+ <pre>
16
+ gem install melon
17
+ </pre>
18
+
19
+ h2. Usage
20
+
21
+ TODO
22
+
23
+ h2. Motivation
24
+
25
+ "Is this file copied onto my media drive yet?"
26
+
27
+ h2. Author
28
+
29
+ Copyright 2010 Andrew Roberts
@@ -0,0 +1,18 @@
1
+ require 'rubygems'
2
+ require 'cucumber'
3
+ require 'cucumber/rake/task'
4
+ require 'rake/gempackagetask'
5
+
6
+ desc 'Default: run the cucumber features.'
7
+ task :default => :features
8
+
9
+ Cucumber::Rake::Task.new(:features) do |t|
10
+ t.cucumber_opts = "features --format pretty"
11
+ end
12
+
13
+ eval("$specification = begin; #{IO.read('melon.gemspec')}; end")
14
+ Rake::GemPackageTask.new($specification) do |package|
15
+ package.need_zip = true
16
+ package.need_tar = true
17
+ end
18
+
data/TODO ADDED
@@ -0,0 +1,15 @@
1
+ Command line parsing:
2
+ * melon [<opts>] <cmd> <cmd-params> [<cmd-opts>]
3
+ * pivot on <cmd>, melon opts come before cmd, cmd-opts come after
4
+ * this may make it hard to guess at unrecognized commands
5
+ * commands don't have a dash
6
+
7
+ Testing:
8
+ * fix rcov rake world
9
+
10
+ Commands:
11
+ * a command helper that
12
+ * dynamically makes the collection "commands" from files in commands/*
13
+ * figures out the padding for the banner
14
+
15
+ * google "ruby get all classes declared in a directory"
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ lib_dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
4
+ $LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir)
5
+
6
+ require 'melon'
7
+ Melon::CLI.execute(ARGV)
8
+
@@ -0,0 +1,33 @@
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
+
@@ -0,0 +1,13 @@
1
+ Feature: Edge cases
2
+
3
+ Scenario: Adding two files with the identical contents
4
+ Given a file named "test_file" with:
5
+ """
6
+ This file is a test file
7
+ """
8
+ When I run "cp test_file test_file_2"
9
+ And I run "melon -d test.db add test_file test_file_2"
10
+ Then the stderr should contain:
11
+ """
12
+ melon: file exists elsewhere in the database
13
+ """
@@ -0,0 +1,15 @@
1
+ require 'aruba/api'
2
+
3
+ Then /^the output should be empty$/ do
4
+ all_output.should match(/^$/)
5
+ end
6
+
7
+ Then /^the output should start with "([^"]*)"$/ do |arg1|
8
+ all_output.should match(/^#{arg1}/)
9
+ end
10
+
11
+ Then /^the output should contain a hash$/ do
12
+ all_output.should match(/[a-fA-F0-9]{20}/)
13
+ end
14
+
15
+
@@ -0,0 +1,7 @@
1
+ require 'rubygems'
2
+ require 'aruba/cucumber'
3
+
4
+ Before do
5
+ @aruba_timeout_seconds = 2
6
+ end
7
+
@@ -0,0 +1 @@
1
+ require 'melon/cli'
@@ -0,0 +1,121 @@
1
+ require 'ostruct'
2
+ require 'optparse'
3
+
4
+ require 'melon/version'
5
+ require 'melon/commands'
6
+
7
+ module Melon
8
+ class CLI
9
+
10
+ def self.execute(arguments=[])
11
+ new(arguments).run
12
+ end
13
+
14
+ attr_accessor :arguments
15
+
16
+ def initialize(arguments)
17
+ self.arguments = arguments
18
+ end
19
+
20
+ def self.default_options
21
+ options = OpenStruct.new
22
+ options.database_path = "#{ENV['HOME']}/.melondb"
23
+ options
24
+ end
25
+
26
+ def run
27
+ options = parse_options
28
+ options.database = PStore.new(File.expand_path(options.database_path))
29
+
30
+ # prepare db
31
+ options.database.transaction do
32
+ options.database[:by_hash] ||= {}
33
+ options.database[:by_path] ||= {}
34
+ end
35
+
36
+ unless arguments.empty?
37
+ run_command(options)
38
+ end
39
+ end
40
+
41
+ def run_command(options)
42
+ # look for command class in args.shift
43
+ command_name = arguments.shift
44
+ begin
45
+ command = Commands.const_get(command_name.capitalize)
46
+ command.new(arguments, options).run
47
+ # rescue NameError
48
+ # CLI.error "unrecognized command: #{command_name}"
49
+ end
50
+ end
51
+
52
+ def parse_options
53
+ options = CLI.default_options
54
+
55
+ parser = OptionParser.new do |p|
56
+ p.banner = "Usage: melon [options] COMMAND [command-options] [ARGS]"
57
+
58
+ p.separator ""
59
+ p.separator "Commands:"
60
+ p.separator ""
61
+
62
+ %w(add check).each do |command|
63
+ cls = Commands.const_get(command.capitalize)
64
+ p.separator format_command(command, cls.description)
65
+ end
66
+
67
+ p.separator ""
68
+ p.separator "Options:"
69
+ p.separator ""
70
+
71
+ p.on("-d", "--database PATH",
72
+ "Path to database file (#{options.database_path})") do |database|
73
+ options.database_path = database
74
+ end
75
+
76
+ p.on_tail("-v", "--version", "Show version") do
77
+ puts Melon.version_string
78
+ exit 0
79
+ end
80
+
81
+ p.on_tail("-h", "--help", "This is it") do
82
+ puts p
83
+ exit 0
84
+ end
85
+
86
+ end
87
+
88
+ begin
89
+ parser.order!(arguments)
90
+ rescue OptionParser::ParseError => e
91
+ CLI.error e
92
+ end
93
+
94
+ options
95
+ 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
+ end
121
+ end
@@ -0,0 +1,122 @@
1
+ require 'pstore'
2
+ require 'ftools'
3
+
4
+ require 'melon/hasher'
5
+ require 'melon/cli'
6
+
7
+ # TODO: in commands, parse arguments with parse! in CLI, parse with order!
8
+
9
+ module Melon
10
+ module Commands
11
+ # needs a 'verify' command to check integrity of database
12
+ # needs a 'remove' command, or some way to deal with deletes/renames
13
+ class Base
14
+ attr_accessor :args, :options
15
+ attr_reader :description
16
+
17
+ def initialize(args, options)
18
+ self.args = args
19
+ self.options = options
20
+ end
21
+
22
+ def parser
23
+ raise
24
+ end
25
+
26
+ def self.description
27
+ raise
28
+ end
29
+
30
+ def parse_options!
31
+ begin
32
+ parser.parse!(args)
33
+ rescue OptionParser::ParseError => e
34
+ CLI.error "#{self.class.to_s.split("::").last.downcase}: #{e}"
35
+ end
36
+ end
37
+
38
+ end
39
+
40
+ class Add < Base
41
+
42
+ def self.description
43
+ "Add files to the melon database"
44
+ end
45
+
46
+ def parser
47
+ @parser ||= OptionParser.new do |p|
48
+ p.banner = "Usage: melon add [options] file [file [file ...]]"
49
+ p.separator ""
50
+ p.separator Add.description
51
+
52
+ p.separator ""
53
+ p.separator "Options:"
54
+ p.separator ""
55
+
56
+ # p.on("-f", "--force",
57
+ # "Force the recalculation of the path that",
58
+ # " already exists in the database") do
59
+ # options.force = true
60
+ # end
61
+ end
62
+ end
63
+
64
+ def run
65
+ parse_options!
66
+
67
+ options.database.transaction do
68
+ args.each do |arg|
69
+ filename = File.expand_path(arg)
70
+
71
+ if File.directory?(filename)
72
+ CLI.error "argument is a directory: #{arg}"
73
+ end
74
+
75
+ if options.database[:by_path][filename]# and !options.force
76
+ CLI.error "path already present in database: #{arg}"
77
+ end
78
+
79
+ # hash strategy should be encapsulated, ergo indirection here
80
+ hash = Hasher.digest(filename)
81
+
82
+ if options.database[:by_hash][hash]
83
+ CLI.error "file exists elsewhere in the database: #{arg}"
84
+ end
85
+
86
+
87
+ options.database[:by_hash][hash] = filename
88
+ options.database[:by_path][filename] = hash
89
+ puts "#{hash}:#{filename}"
90
+ end
91
+ end
92
+ end
93
+ end
94
+
95
+ class Check < Base
96
+ def self.description
97
+ "Determine whether or not a copy of a file resides in the database"
98
+ end
99
+
100
+ def parser
101
+ @parser ||= OptionParser.new do |p|
102
+ p.banner = "Usage: melon check file [file [file ...]]"
103
+ p.separator ""
104
+ p.separator Check.description
105
+ end
106
+ end
107
+
108
+ def run
109
+ parse_options!
110
+
111
+ options.database.transaction do
112
+ args.each do |filename|
113
+ hash = Hasher.digest(filename)
114
+ unless options.database[:by_hash][hash]
115
+ puts File.expand_path(filename)
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,10 @@
1
+ module Melon
2
+ module Commands
3
+ class Add
4
+ extend BasicCommand
5
+
6
+
7
+ end
8
+ end
9
+ end
10
+
@@ -0,0 +1,19 @@
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
@@ -0,0 +1,51 @@
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
@@ -0,0 +1,68 @@
1
+ require 'rubygems'
2
+ require 'subcommand'
3
+
4
+ require 'melon/version'
5
+
6
+ module Melon
7
+ class CLI
8
+ def self.execute(arguments=[])
9
+ new(arguments).run
10
+ end
11
+
12
+ attr_accessor :arguments
13
+
14
+ def initialize(arguments)
15
+ self.arguments = arguments
16
+ end
17
+
18
+ def run
19
+ # command may be nil, if no command is given
20
+ command = parser.parse!(arguments)
21
+ end
22
+
23
+ def parser
24
+ @parser ||= OptionParser.new do |opts|
25
+ opts.banner = "Example of what option parsing should look like"
26
+
27
+ opts.on("-d", "--database=PATH", String,
28
+ "Path to Melon's sqlite database.",
29
+ " (default ~/.melon/melon.db)") do |arg|
30
+ self.options[:database] = arg
31
+ end
32
+
33
+ opts.on("-v", "--version",
34
+ "Display the program version.") { puts "0.0.0" }
35
+ opts.on("-h", "--help",
36
+ "Show this help message.") { puts parser }
37
+ opts.separator ""
38
+
39
+ opts.command :create, "description text" do |subopts|
40
+ subopts.banner "create: create a directory, somewhere"
41
+
42
+ subopts.on("-f" "--force",
43
+ "Force creation.") { self.options[:force] = true }
44
+ end
45
+
46
+
47
+ opts.separator "To see a list of commands, use 'melon help commands'."
48
+ opts.separator "To get help with a specific command," +
49
+ " use 'melon help COMMAND'."
50
+ end
51
+
52
+ # OptionParser#command adds lazy option parsers to an array
53
+ # of subcommands
54
+ #
55
+ # melon help COMMAND prints the appropriate option
56
+ #
57
+ # PARSING:
58
+ # the option parser knows what the commands are at parse time
59
+ # so, scan the parse!() argument for the first command and pivot on
60
+ # it
61
+ #
62
+ # pre-cmd goes to the main option_parser.parse
63
+ # cmd goes into a string, is returned
64
+ # post-cmd goes to cmd parser.parse!()
65
+ #
66
+ # possibly implement that behavior for order! as well
67
+ end
68
+ end
@@ -0,0 +1,13 @@
1
+ require 'rubygems'
2
+ require 'digestif/hasher'
3
+
4
+ module Melon
5
+ class Hasher
6
+ def self.digest(filename)
7
+ hasher = Digestif::Hasher.new(filename)
8
+ hasher.options.read_size = 40000
9
+ hasher.options.seek_size = 80000000
10
+ hasher.digest
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,10 @@
1
+ module Melon
2
+ def self.version
3
+ "0.1.0"
4
+ end
5
+
6
+ def self.version_string
7
+ "melon version #{self.version}"
8
+ end
9
+ end
10
+
@@ -0,0 +1,32 @@
1
+ lib = File.expand_path('../lib/', __FILE__)
2
+ $:.unshift lib unless $:.include?(lib)
3
+ require 'melon/version'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = %q{melon}
7
+ s.version = Melon.version
8
+ s.summary = %q{A media catalog}
9
+ s.description = %q{A tool for tracking files based on content, aiming to scale to arbitrarily large files without slowing down}
10
+
11
+ s.files = `git ls-files`.split("\n")
12
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
13
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
14
+ s.default_executable = 'melon'
15
+
16
+ s.require_path = 'lib'
17
+
18
+ s.has_rdoc = false
19
+
20
+ s.authors = ["Andrew Roberts"]
21
+ s.email = %q{adroberts@gmail.com}
22
+ s.homepage = "http://github.com/aroberts/melon"
23
+
24
+ s.add_dependency("digestif", ">=1.0.4")
25
+
26
+ s.add_development_dependency('cucumber')
27
+ s.add_development_dependency('aruba')
28
+
29
+ s.platform = Gem::Platform::RUBY
30
+ s.rubygems_version = %q{1.2.0}
31
+ end
32
+
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+ # File: script/console
3
+ irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
4
+
5
+ libs = " -r irb/completion"
6
+ libs << " -r irb/ext/save-history"
7
+
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
+ libs << " -r #{File.dirname(__FILE__) + '/../lib/melon.rb'}"
11
+ puts "Loading melon gem"
12
+ exec "#{irb} #{libs} --simple-prompt"
@@ -0,0 +1,8 @@
1
+ require 'melon/cli'
2
+
3
+ describe Melon::CLI do
4
+ before do
5
+ @cli = Melon::CLI
6
+ end
7
+
8
+ end
@@ -0,0 +1,32 @@
1
+ require 'melon/database'
2
+
3
+ describe Melon::Database, "when passing a file that doesn't exist" do
4
+ before do
5
+ @database = Melon::Database.new(nonexistant_database_file)
6
+ end
7
+
8
+ after do
9
+ FileUtils.rm_rf(@database.path)
10
+ end
11
+
12
+ it "should be created" do
13
+ File.exist?(@database.path).should be_true
14
+ end
15
+
16
+ end
17
+
18
+ describe Melon::Database, "when passing a file that is not a database" do
19
+ before do
20
+ @file = non_database_file
21
+ end
22
+
23
+ after do
24
+ FileUtils.rm_rf(@file)
25
+ end
26
+
27
+ it "should throw an exception" do
28
+ lambda {
29
+ @database = Melon::Database.new(non_database_file)
30
+ }.should raise_error(ActiveRecord::StatementInvalid)
31
+ end
32
+ end
@@ -0,0 +1,30 @@
1
+ require 'melon/fingerprint'
2
+ require 'melon/database' # may need to reverse the order of these
3
+
4
+
5
+ describe Fingerprint, "when creating with just a file" do
6
+ before do
7
+ File.stub!(:exist?) { true }
8
+
9
+ @database = Database.new(nonexistant_database_file)
10
+ @fingerprint = Fingerprint.new(:file => small_media_file)
11
+ @fingerprint.save!
12
+ end
13
+
14
+ after do
15
+ FileUtils.rm_rf(@database.path)
16
+ end
17
+
18
+ it "should resolve the path" do
19
+ @fingerprint.file.should match(/^\//)
20
+ end
21
+
22
+ it "should be valid" do
23
+ @fingerprint.should be_valid
24
+ end
25
+
26
+ it "should set the digest field" do
27
+ @fingerprint.digest.should_not be_nil
28
+ end
29
+
30
+ end
@@ -0,0 +1,77 @@
1
+ require 'melon/parsed_arguments'
2
+
3
+ describe Melon::ParsedArguments, "when splitting valid arguments" do
4
+ before do
5
+ @args = Melon::ParsedArguments.new(%w(-t -b help plate /path/to/file -t 5))
6
+ end
7
+
8
+ it "should correctly find the program arguments" do
9
+ @args.program_arguments.should include("-t", "-b")
10
+ @args.program_arguments.length.should == 2
11
+ end
12
+
13
+ it "should correctly find the command" do
14
+ @args.command.should == "help"
15
+ end
16
+
17
+ it "should correctly find the command arguments" do
18
+ @args.command_arguments.should include("plate", "/path/to/file", "-t", "5")
19
+ @args.command_arguments.length.should == 4
20
+ end
21
+ end
22
+
23
+ describe Melon::ParsedArguments, "when splitting argument-less commands" do
24
+ before do
25
+ @args = Melon::ParsedArguments.new(%w(help))
26
+ end
27
+
28
+ it "should correctly find the program arguments" do
29
+ @args.program_arguments.should be_empty
30
+ end
31
+
32
+ it "should correctly find the command" do
33
+ @args.command.should == "help"
34
+ end
35
+
36
+ it "should correctly find the command arguments" do
37
+ @args.command_arguments.should be_empty
38
+ end
39
+ end
40
+
41
+ describe Melon::ParsedArguments, "when splitting command-less arguments" do
42
+ before do
43
+ @args = Melon::ParsedArguments.new(%w(--help))
44
+ end
45
+
46
+ it "should set the program arguments" do
47
+ @args.program_arguments.should include("--help")
48
+ @args.program_arguments.length.should == 1
49
+ end
50
+
51
+ it "shound not set a command" do
52
+ @args.command.should be_nil
53
+ end
54
+
55
+ it "should not set any command arguments" do
56
+ @args.command_arguments.should be_nil
57
+ end
58
+ end
59
+
60
+ describe Melon::ParsedArguments, "when splitting invalid commands" do
61
+ before do
62
+ @args = Melon::ParsedArguments.new(%w(fizzle))
63
+ end
64
+
65
+ it "should not set a command" do
66
+ @args.command.should be_nil
67
+ end
68
+
69
+ it "should treat the invalid command as a program argument" do
70
+ @args.program_arguments.should include("fizzle")
71
+ @args.program_arguments.length.should == 1
72
+ end
73
+
74
+ it "should not set command arguments" do
75
+ @args.command_arguments.should be_nil
76
+ end
77
+ end
@@ -0,0 +1,61 @@
1
+ begin
2
+ require 'rspec'
3
+ rescue LoadError
4
+ require 'rubygems' unless ENV['NO_RUBYGEMS']
5
+ gem 'rspec'
6
+ require 'rspec'
7
+ end
8
+
9
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
10
+ require 'melon'
11
+ include Melon
12
+
13
+
14
+ def nonexistant_database_file
15
+ 'tmp/' + (0..8).map{ ('a'..'z').to_a[rand(26)] }.join + '.db'
16
+ end
17
+
18
+ def non_database_file
19
+ name = nonexistant_database_file
20
+ File.open(name, 'w') {|f| f.write("bad") }
21
+ name
22
+ end
23
+
24
+ def small_media_file
25
+ name = nonexistant_database_file
26
+ File.open(name, 'w') do |f|
27
+ f.write(<<EOS
28
+ <p>
29
+ It is included in the Ruby standard library.
30
+ </p>
31
+ <h2>Description</h2>
32
+ <p>
33
+ ftools adds several (class, not instance) methods to the <a
34
+ href="File.html">File</a> class, for copying, moving, deleting, installing,
35
+ and comparing files, as well as creating a directory <a
36
+ href="File.html#M002554">path</a>. See the <a href="File.html">File</a>
37
+ class for details.
38
+ </p>
39
+ <p>
40
+ <a href="FileUtils.html">FileUtils</a> contains all or nearly all the same
41
+ functionality and more, and is a recommended option over ftools
42
+ </p>
43
+ <p>
44
+ When you
45
+ </p>
46
+ <pre>
47
+ require 'ftools'
48
+ </pre>
49
+ <p>
50
+ then the <a href="File.html">File</a> class aquires some utility methods
51
+ for copying, moving, and deleting files, and more.
52
+ </p>
53
+ <p>
54
+ See the method descriptions below, and consider using <a
55
+ href="FileUtils.html">FileUtils</a> as it is more comprehensive.
56
+ </p>
57
+ EOS
58
+ )
59
+ end
60
+ name
61
+ end
metadata ADDED
@@ -0,0 +1,144 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: melon
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Andrew Roberts
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-01-25 00:00:00 -05:00
19
+ default_executable: melon
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: digestif
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 31
30
+ segments:
31
+ - 1
32
+ - 0
33
+ - 4
34
+ version: 1.0.4
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: cucumber
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ hash: 3
46
+ segments:
47
+ - 0
48
+ version: "0"
49
+ type: :development
50
+ version_requirements: *id002
51
+ - !ruby/object:Gem::Dependency
52
+ name: aruba
53
+ prerelease: false
54
+ requirement: &id003 !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ hash: 3
60
+ segments:
61
+ - 0
62
+ version: "0"
63
+ type: :development
64
+ version_requirements: *id003
65
+ description: A tool for tracking files based on content, aiming to scale to arbitrarily large files without slowing down
66
+ email: adroberts@gmail.com
67
+ executables:
68
+ - melon
69
+ extensions: []
70
+
71
+ extra_rdoc_files: []
72
+
73
+ files:
74
+ - .gitignore
75
+ - Gemfile
76
+ - History.txt
77
+ - README.textile
78
+ - Rakefile
79
+ - TODO
80
+ - bin/melon
81
+ - features/basic.feature
82
+ - features/edges.feature
83
+ - features/step_definitions/basic_steps.rb
84
+ - features/support/env.rb
85
+ - lib/melon.rb
86
+ - lib/melon/cli.rb
87
+ - lib/melon/commands.rb
88
+ - lib/melon/commands/add.rb
89
+ - lib/melon/commands/basic_command.rb
90
+ - lib/melon/commands/help.rb
91
+ - lib/melon/example.rb
92
+ - lib/melon/hasher.rb
93
+ - lib/melon/version.rb
94
+ - melon.gemspec
95
+ - script/console
96
+ - spec/melon/cli_spec.rb
97
+ - spec/melon/database_spec.rb
98
+ - spec/melon/fingerprint_spec.rb
99
+ - spec/melon/parsed_arguments_spec.rb
100
+ - spec/spec_helper.rb
101
+ has_rdoc: true
102
+ homepage: http://github.com/aroberts/melon
103
+ licenses: []
104
+
105
+ post_install_message:
106
+ rdoc_options: []
107
+
108
+ require_paths:
109
+ - lib
110
+ required_ruby_version: !ruby/object:Gem::Requirement
111
+ none: false
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ hash: 3
116
+ segments:
117
+ - 0
118
+ version: "0"
119
+ required_rubygems_version: !ruby/object:Gem::Requirement
120
+ none: false
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ hash: 3
125
+ segments:
126
+ - 0
127
+ version: "0"
128
+ requirements: []
129
+
130
+ rubyforge_project:
131
+ rubygems_version: 1.3.7
132
+ signing_key:
133
+ specification_version: 3
134
+ summary: A media catalog
135
+ test_files:
136
+ - features/basic.feature
137
+ - features/edges.feature
138
+ - features/step_definitions/basic_steps.rb
139
+ - features/support/env.rb
140
+ - spec/melon/cli_spec.rb
141
+ - spec/melon/database_spec.rb
142
+ - spec/melon/fingerprint_spec.rb
143
+ - spec/melon/parsed_arguments_spec.rb
144
+ - spec/spec_helper.rb