melon 0.1.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.
@@ -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