torganiser 0.0.2

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 95da8d1d2cb1e354a078274b45135d20fabf096c
4
+ data.tar.gz: 4e131a6537e63b4443f5c882ab70ef83ec7eaf22
5
+ SHA512:
6
+ metadata.gz: 079a6dcc32b5176016b5b302038c79c33ad9f80c0870472786b5982429cbcdb4fb5c0c847146c50bbe8e38627980e802fdce1ea747bb5ce6602dbb1397158547
7
+ data.tar.gz: 32eabd36aaaad7201a196149ddedfd99f4173dc76b8020edebfdc17a907edd617f80f6db2f38af4e383820c5c4d34d096fb638ad0e7ec5c9b04134df5ec97177
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.0
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.0
4
+ - 2.0.0
5
+ addons:
6
+ code_climate:
7
+ repo_token: 655cc3541dfabfbe6fa6aa98f5aef299a4ddbdca29c9e6ae4838a7a7225661d2
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in torganiser.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Sergei Matheson
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,34 @@
1
+ # Torganiser
2
+
3
+ Simple utility that organises media files according to filename
4
+
5
+
6
+ [![Build Status](https://travis-ci.org/sergei-matheson/torganiser.svg?branch=master)](https://travis-ci.org/sergei-matheson/torganiser)
7
+ [![Code Climate](https://codeclimate.com/github/sergei-matheson/torganiser/badges/gpa.svg)](https://codeclimate.com/github/sergei-matheson/torganiser)
8
+ [![Test Coverage](https://codeclimate.com/github/sergei-matheson/torganiser/badges/coverage.svg)](https://codeclimate.com/github/sergei-matheson/torganiser)
9
+
10
+ ## Installation
11
+
12
+ Add this line to your application's Gemfile:
13
+
14
+ gem 'torganiser'
15
+
16
+ And then execute:
17
+
18
+ $ bundle
19
+
20
+ Or install it yourself as:
21
+
22
+ $ gem install torganiser
23
+
24
+ ## Usage
25
+
26
+ TODO: Write usage instructions here
27
+
28
+ ## Contributing
29
+
30
+ 1. Fork it ( http://github.com/<my-github-username>/torganiser/fork )
31
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
32
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
33
+ 4. Push to the branch (`git push origin my-new-feature`)
34
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,22 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+ require 'reek/rake/task'
4
+
5
+ Reek::Rake::Task.new
6
+
7
+ begin
8
+ require 'cane/rake_task'
9
+
10
+ desc "Run cane to check quality metrics"
11
+ Cane::RakeTask.new(:quality) do |cane|
12
+ cane.abc_max = 10
13
+ cane.add_threshold 'coverage/.last_run.json', :>=, 100
14
+ end
15
+
16
+ rescue LoadError
17
+ warn "cane not available, quality task not provided."
18
+ end
19
+
20
+ RSpec::Core::RakeTask.new(:spec)
21
+
22
+ task :default => [ :spec, :quality, :reek ]
data/bin/torganiser ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'torganiser'
4
+
5
+ Clamp do
6
+
7
+ option ["--version", "-v"], :flag, "Show version" do
8
+ puts Torganiser::VERSION
9
+ exit(0)
10
+ end
11
+
12
+ option ["--collection", "-c"], "DIRECTORY",
13
+ "Root of the collection into which to organise the files",
14
+ environment_variable: "TORGANISER_COLLECTION",
15
+ required: true
16
+
17
+ option ["--extension", "-e"], "EXTENSION", "Extension to include eg. mp4. May be specifed multiple times", multivalued: true, attribute_name: :extensions
18
+
19
+ option "--dry-run", :flag, "If specified, no files will be moved"
20
+
21
+ parameter "FILES ...", "Files or directories to organise", attribute_name: :files
22
+
23
+ def execute
24
+ Torganiser::Runner.new(collection, files: files, extensions: extensions, dry_run: dry_run?).run
25
+ end
26
+
27
+ end
@@ -0,0 +1,61 @@
1
+ require 'fileutils'
2
+
3
+ module Torganiser
4
+ # Handles arranging episode files into a collection
5
+ class Arranger
6
+
7
+ attr_reader :collection, :dry_run
8
+
9
+ def initialize collection, dry_run: false
10
+ @collection = collection
11
+ @dry_run = dry_run
12
+ end
13
+
14
+ def arrange file
15
+ episode = EpisodeFile.new(file)
16
+ move(episode, Destination.new(collection, episode))
17
+ end
18
+
19
+ private
20
+
21
+ def move episode, destination
22
+ directory = destination.directory
23
+
24
+ file_utils.mkdir_p directory unless File.exists? directory
25
+ file_utils.mv episode.file, directory
26
+ end
27
+
28
+ def file_utils
29
+ @file_utils ||= dry_run ? FileUtils::DryRun : FileUtils
30
+ end
31
+
32
+ # Models a destination for an episode file in a collection
33
+ class Destination
34
+
35
+ attr_reader :collection, :episode_file
36
+
37
+ def initialize collection, episode_file
38
+ @collection = collection
39
+ @episode_file = episode_file
40
+ end
41
+
42
+ def directory
43
+ @directory ||= File.join(collection, series_dir, season_dir)
44
+ end
45
+
46
+ private
47
+ def season_dir
48
+ "Season #{episode_file.season}"
49
+ end
50
+
51
+ def series_dir
52
+ series.display_name
53
+ end
54
+
55
+ def series
56
+ episode_file.series
57
+ end
58
+
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,48 @@
1
+ module Torganiser
2
+ # Models a file that contains a single episode of a TV series.
3
+ # Attempts to extract episode data, based on the filename.
4
+ class EpisodeFile
5
+
6
+ attr_reader :file
7
+
8
+ EPISODE_INFO_MATCHER = %r{^
9
+ (?<series>.+) # Series name, and possibly year
10
+ .s(?<season>\d+) # season number
11
+ e(?<episode>\d+) # episode number
12
+ \..*$ # everything else
13
+ }ix
14
+
15
+ YEAR_MATCHER = /^\d{4}$/
16
+
17
+ def initialize(file)
18
+ @file = file
19
+ end
20
+
21
+ def basename
22
+ @basename ||= File.basename(@file)
23
+ end
24
+
25
+ def season
26
+ @season ||= episode_info[:season].to_i
27
+ end
28
+
29
+ def episode
30
+ @episode ||= episode_info[:episode].to_i
31
+ end
32
+
33
+ def series
34
+ @series ||= begin
35
+ parts = episode_info[:series].split('.')
36
+ year = YEAR_MATCHER.match(parts.last) ? parts.pop.to_i : nil
37
+ Series.new(parts.join(' '), year:year)
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ def episode_info
44
+ @episode_info ||= EPISODE_INFO_MATCHER.match(basename)
45
+ end
46
+
47
+ end
48
+ end
@@ -0,0 +1,59 @@
1
+ module Torganiser
2
+ # A file query is specified by adding allowed directories,
3
+ # and optionally, extensions.
4
+ # The 'pattern' method returns a Dir.glob style pattern
5
+ # that can be used to match a set of files.
6
+ class FileQuery
7
+
8
+ attr_reader :directories, :extensions
9
+
10
+ def initialize(directories: nil, extensions: nil)
11
+ @directories = []
12
+ @extensions = []
13
+ add_directory(directories) if directories
14
+ add_extension(extensions) if extensions
15
+ end
16
+
17
+ def empty?
18
+ @directories.empty?
19
+ end
20
+
21
+ def add_extension extensions
22
+ @extensions.concat([*extensions])
23
+ end
24
+
25
+ def add_directory directories
26
+ @directories.concat([*directories])
27
+ end
28
+
29
+ def pattern
30
+ directory_pattern + "/**/" + extension_pattern
31
+ end
32
+
33
+ private
34
+
35
+ def directory_pattern
36
+ ItemsPattern.new(directories).to_s
37
+ end
38
+
39
+ def extension_pattern
40
+ "*" + (extensions.count > 0 ? ItemsPattern.new(extensions).to_s : '')
41
+ end
42
+
43
+ # Models a pattern that matchers a series of one or more string items.
44
+ class ItemsPattern
45
+
46
+ attr_reader :items
47
+
48
+ def initialize items
49
+ @items = items
50
+ end
51
+
52
+ def to_s
53
+ items.count > 1 ? "{#{items.join(',')}}" : items.first
54
+ end
55
+
56
+ end
57
+
58
+ end
59
+ end
@@ -0,0 +1,24 @@
1
+ module Torganiser
2
+ # Runs the organisation process for a given array of
3
+ # files and extensions
4
+ class Runner
5
+
6
+ def initialize(collection, files: [], extensions: [], dry_run: false)
7
+ @scanner = Scanner.new(files, extensions)
8
+ @arranger = Arranger.new(collection, dry_run: dry_run)
9
+ end
10
+
11
+ def run
12
+ @scanner.each do |episode_file|
13
+ arrange episode_file
14
+ end
15
+ end
16
+
17
+ private
18
+ def arrange episode_file
19
+ @arranger.arrange episode_file
20
+ end
21
+
22
+ end
23
+
24
+ end
@@ -0,0 +1,55 @@
1
+ module Torganiser
2
+ # Handles scanning a set of directories and files
3
+ # and returning any found episode files.
4
+ class Scanner
5
+
6
+ include Enumerable
7
+
8
+ def initialize(files, extensions)
9
+ file_query.add_extension extensions
10
+ add_files files
11
+ end
12
+
13
+ def each
14
+ ordinary_files.each do |file|
15
+ yield file
16
+ end
17
+ directory_files.each do |file|
18
+ yield file
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def directory_files
25
+ if file_query.empty?
26
+ []
27
+ else
28
+ Dir[file_query.pattern].select { |file| File.file?(file) }
29
+ end
30
+ end
31
+
32
+ def add_files files
33
+ files.each do |file|
34
+ add_file file
35
+ end
36
+ end
37
+
38
+ def add_file file
39
+ if File.file?(file)
40
+ ordinary_files << file
41
+ else
42
+ file_query.add_directory file
43
+ end
44
+ end
45
+
46
+ def ordinary_files
47
+ @ordinary_files ||= []
48
+ end
49
+
50
+ def file_query
51
+ @file_query ||= FileQuery.new
52
+ end
53
+
54
+ end
55
+ end
@@ -0,0 +1,19 @@
1
+ module Torganiser
2
+
3
+ # Models the series information from an episode file
4
+ class Series
5
+
6
+ attr_reader :name, :year
7
+
8
+ def initialize name, year: nil
9
+ @name = name
10
+ @year = year
11
+ end
12
+
13
+ def display_name
14
+ @display_name = year ? "#{name} (#{year})" : name
15
+ end
16
+
17
+ end
18
+
19
+ end
@@ -0,0 +1,3 @@
1
+ module Torganiser
2
+ VERSION = "0.0.2"
3
+ end
data/lib/torganiser.rb ADDED
@@ -0,0 +1,12 @@
1
+ require "torganiser/version"
2
+ require "torganiser/file_query"
3
+ require "torganiser/series"
4
+ require "torganiser/episode_file"
5
+ require "torganiser/scanner"
6
+ require "torganiser/arranger"
7
+ require "torganiser/runner"
8
+
9
+ require "clamp"
10
+
11
+ module Torganiser
12
+ end
@@ -0,0 +1,9 @@
1
+ require "codeclimate-test-reporter"
2
+ CodeClimate::TestReporter.start
3
+ # At the top because simplecov needs to watch files being loaded
4
+ require 'simplecov'
5
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
6
+ SimpleCov.start do
7
+ add_filter 'spec/'
8
+ end
9
+ require 'torganiser'
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ module Torganiser
4
+
5
+ describe Arranger do
6
+
7
+ describe "for a collection" do
8
+ subject { Arranger.new("/my/media") }
9
+
10
+ context "when arranging a file" do
11
+
12
+ before do
13
+ allow(FileUtils).to receive(:mkdir_p)
14
+ allow(FileUtils).to receive(:mv)
15
+ end
16
+
17
+ let(:file) { "/tmp/stuff/Waffle.Cone.2007.S01E02.HDTV.x264-LOL.mp4" }
18
+
19
+ it "creates a destination by series and season" do
20
+ expect(FileUtils).to receive(:mkdir_p).with(
21
+ "/my/media/Waffle Cone (2007)/Season 1"
22
+ )
23
+ subject.arrange file
24
+ end
25
+
26
+ it "moves the file to the destination" do
27
+ expect(FileUtils).to receive(:mv).with(
28
+ file,
29
+ "/my/media/Waffle Cone (2007)/Season 1"
30
+ )
31
+ subject.arrange file
32
+ end
33
+
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,63 @@
1
+ require 'spec_helper'
2
+
3
+ module Torganiser
4
+
5
+ describe EpisodeFile do
6
+
7
+ context "when initialized with an informative filename" do
8
+
9
+ let(:file) { "file/path/Hello.S02E01.mp4"}
10
+
11
+ subject { EpisodeFile.new(file) }
12
+
13
+ it 'extracts the base file name' do
14
+ expect(subject.basename).to eq 'Hello.S02E01.mp4'
15
+ end
16
+
17
+ it 'extracts season number' do
18
+ expect(subject.season).to eq 2
19
+ end
20
+
21
+ it 'extracts episode number' do
22
+ expect(subject.episode).to eq 1
23
+ end
24
+
25
+ it 'creates a series with a name and no year' do
26
+ expect(Series).to receive(:new).with(
27
+ "Hello", year: nil
28
+ )
29
+ subject.series
30
+ end
31
+
32
+ context "that contains year information" do
33
+
34
+ let(:file) { "file/path/Hello.2008.S02E01.mp4"}
35
+
36
+ it 'creates a series with a name and year' do
37
+ expect(Series).to receive(:new).with(
38
+ "Hello", year: 2008
39
+ )
40
+ subject.series
41
+ end
42
+
43
+ end
44
+
45
+ context "that has a series name in dot-format" do
46
+
47
+ let(:file) { "file/path/Goodbye.Hello.Hamburger.2008.S02E01.mp4"}
48
+
49
+ it 'creates a series with a name with spaces' do
50
+ expect(Series).to receive(:new).with(
51
+ "Goodbye Hello Hamburger",
52
+ anything
53
+ )
54
+ subject.series
55
+ end
56
+
57
+ end
58
+
59
+ end
60
+
61
+ end
62
+
63
+ end
@@ -0,0 +1,117 @@
1
+ require 'spec_helper'
2
+
3
+ module Torganiser
4
+
5
+ describe FileQuery do
6
+
7
+ context "when initialised" do
8
+
9
+ context "with no arguments" do
10
+
11
+ it "has no directories" do
12
+ expect(subject.directories).to be_empty
13
+ end
14
+
15
+ it "has no media extensions" do
16
+ expect(subject.extensions).to be_empty
17
+ end
18
+
19
+ it "is empty" do
20
+ expect(subject).to be_empty
21
+ end
22
+
23
+ end
24
+
25
+ context "with a directory" do
26
+ subject { FileQuery.new(directories: "woot/waffle") }
27
+
28
+ it "is not empty" do
29
+ expect(subject).not_to be_empty
30
+ end
31
+
32
+ it "adds that directory to the list of search directories" do
33
+ expect(subject.directories).to eq ['woot/waffle']
34
+ end
35
+
36
+ end
37
+
38
+ context "with a directory and a media extension" do
39
+
40
+ subject { FileQuery.new(directories: 'mydir', extensions: 'mp4') }
41
+
42
+ it "adds that directory to the list of search directories" do
43
+ expect(subject.directories).to eq ['mydir']
44
+ end
45
+
46
+ it "adds that extension to the list of media extensions" do
47
+ expect(subject.extensions).to eq ['mp4']
48
+ end
49
+
50
+ end
51
+
52
+ end
53
+
54
+ describe "search pattern" do
55
+
56
+ let(:extensions) { nil }
57
+
58
+ let(:directories) { 'the-dir' }
59
+
60
+ subject do
61
+ FileQuery.new(
62
+ directories: directories,
63
+ extensions: extensions
64
+ )
65
+ end
66
+
67
+ context "with one directory" do
68
+
69
+ let(:directories) { "stuff" }
70
+
71
+ it "includes only that directory" do
72
+ expect(subject.pattern).to match(/^stuff\/\*\*/)
73
+ end
74
+
75
+ end
76
+
77
+ context "with multiple directories" do
78
+
79
+ let(:directories) { ["one","two"] }
80
+
81
+ it "includes all specified directories" do
82
+ expect(subject.pattern).to match(/{one,two}\/\*\*/)
83
+ end
84
+
85
+ end
86
+
87
+ context "with no extensions" do
88
+
89
+ it "matches all files" do
90
+ expect(subject.pattern).to match(/\*\*\/\*/)
91
+ end
92
+
93
+ end
94
+
95
+ context "with one extension" do
96
+
97
+ let(:extensions) { "mp4" }
98
+
99
+ it "includes only that extension" do
100
+ expect(subject.pattern).to match(/\*\*\/\*mp4/)
101
+ end
102
+
103
+ end
104
+
105
+ context "with multiple extensions" do
106
+
107
+ let(:extensions) { ["mp4", "mov", "qt"] }
108
+
109
+ it "includes all specified extensions" do
110
+ expect(subject.pattern).to match(/\*\*\/\*{mp4,mov,qt}/)
111
+ end
112
+
113
+ end
114
+ end
115
+
116
+ end
117
+ end
@@ -0,0 +1,76 @@
1
+ require 'spec_helper'
2
+
3
+ module Torganiser
4
+
5
+ describe Runner do
6
+
7
+ context "when initialised with a collection, files and extensions" do
8
+
9
+ let(:collection) { double("collection") }
10
+ let(:files) { double("files") }
11
+ let(:extensions) { double("extensions") }
12
+
13
+ subject do
14
+ Runner.new(
15
+ collection,
16
+ files: files, extensions: extensions, dry_run: true
17
+ )
18
+ end
19
+
20
+ let(:scanner) do
21
+ instance_double("Torganiser::Scanner", each: nil)
22
+ end
23
+
24
+ before do
25
+ allow(Scanner).to receive(:new).and_return scanner
26
+ end
27
+
28
+ describe "a scanner" do
29
+
30
+ it "is created for the files and extensions" do
31
+ expect(Scanner).to receive(:new).with(files, extensions)
32
+ subject.run
33
+ end
34
+
35
+ it "is used to retrieve episode files" do
36
+ expect(scanner).to receive(:each)
37
+ subject.run
38
+ end
39
+
40
+ end
41
+
42
+ context "if any episode files are found" do
43
+
44
+ let(:episode_file) { instance_double("Torganiser::EpisodeFile") }
45
+
46
+ before do
47
+ allow(scanner).to receive(:each).and_yield episode_file
48
+ end
49
+
50
+ describe "an arranger" do
51
+
52
+ let(:arranger) do
53
+ instance_double("Torganiser::Arranger", arrange: nil)
54
+ end
55
+
56
+ before do
57
+ allow(Arranger).to receive(:new).and_return arranger
58
+ end
59
+
60
+ it "is created for the collection, and dry run status" do
61
+ expect(Arranger).to receive(:new).with(collection, dry_run: true)
62
+ subject.run
63
+ end
64
+
65
+ it "is used to arrange episode files found by the scanner" do
66
+ expect(arranger).to receive(:arrange).with(episode_file)
67
+ subject.run
68
+ end
69
+
70
+ end
71
+ end
72
+
73
+ end
74
+
75
+ end
76
+ end
@@ -0,0 +1,91 @@
1
+ require 'spec_helper'
2
+
3
+ module Torganiser
4
+
5
+ describe Scanner do
6
+
7
+ context "when initialised with an array of files" do
8
+
9
+ let(:files) { ["/tmp/file1", "/tmp/dir1", "/tmp/file2", "/tmp/dir2"] }
10
+
11
+ let(:extensions) { double("extensions") }
12
+
13
+ subject { Scanner.new(files, extensions) }
14
+
15
+ let(:query_pattern) { double("query pattern") }
16
+
17
+ let(:file_query) do
18
+ instance_double(
19
+ "Torganiser::FileQuery",
20
+ pattern: query_pattern,
21
+ add_directory: nil,
22
+ add_extension: nil,
23
+ empty?: false
24
+ )
25
+ end
26
+
27
+ let(:query_results) do
28
+ ["/tmp/dir1", "/tmp/dir1/file3", "/tmp/dir2", "/tmp/dir2/file4"]
29
+ end
30
+
31
+ before do
32
+ allow(File).to receive(:file?) { |path| path.match(/file/) }
33
+ allow(FileQuery).to receive(:new).and_return file_query
34
+ allow(Dir).to receive(:[]).and_return query_results
35
+ end
36
+
37
+ describe "a file query" do
38
+ it "is created for any non-ordinary files" do
39
+ expect(file_query).to receive(:add_directory).with("/tmp/dir1")
40
+ expect(file_query).to receive(:add_directory).with("/tmp/dir2")
41
+ subject
42
+ end
43
+
44
+ it "is given any extensions" do
45
+ expect(file_query).to receive(:add_extension).with extensions
46
+ subject
47
+ end
48
+
49
+ context "when enumerating" do
50
+
51
+ context "when given some directories" do
52
+ it "is used do a directory search" do
53
+ expect(Dir).to receive(:[]).with query_pattern
54
+ subject.each { |_| }
55
+ end
56
+ end
57
+
58
+ context "when empty" do
59
+
60
+ let(:files) { ["/tmp/file1""/tmp/file2"] }
61
+
62
+ let(:file_query) do
63
+ instance_double(
64
+ "Torganiser::FileQuery",
65
+ add_extension: nil,
66
+ empty?: true
67
+ )
68
+ end
69
+
70
+ it "is not used" do
71
+ expect(Dir).not_to receive(:[]).with query_pattern
72
+ subject.each { |_| }
73
+ end
74
+ end
75
+
76
+ end
77
+ end
78
+
79
+ it "enumerates the ordinary files, and files found in the directories" do
80
+ expect { |b| subject.each(&b) }.to yield_successive_args(
81
+ "/tmp/file1",
82
+ "/tmp/file2",
83
+ "/tmp/dir1/file3",
84
+ "/tmp/dir2/file4"
85
+ )
86
+ end
87
+
88
+
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ module Torganiser
4
+
5
+ describe Series do
6
+
7
+ context "when initialized with a name" do
8
+
9
+ subject { Series.new("Pear Tree") }
10
+
11
+ it 'has a display name' do
12
+ expect(subject.display_name).to eq 'Pear Tree'
13
+ end
14
+
15
+ context "and year" do
16
+
17
+ subject { Series.new("Pear Tree", year: 2009) }
18
+
19
+ it 'has a display name that includes year' do
20
+ expect(subject.display_name).to eq 'Pear Tree (2009)'
21
+ end
22
+
23
+ end
24
+
25
+ end
26
+
27
+ end
28
+
29
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ describe Torganiser do
4
+
5
+ it 'should have a version number' do
6
+ expect(Torganiser::VERSION).not_to be_nil
7
+ end
8
+
9
+ end
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'torganiser/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "torganiser"
8
+ spec.version = Torganiser::VERSION
9
+ spec.authors = [""]
10
+ spec.email = [""]
11
+ spec.summary = %q{Organises episode files according to filename.}
12
+ spec.description = %q{Organises episode files according to filename.}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "clamp"
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.5"
24
+ spec.add_development_dependency "rake"
25
+ spec.add_development_dependency "reek"
26
+ spec.add_development_dependency "rspec"
27
+ spec.add_development_dependency "simplecov"
28
+ spec.add_development_dependency "cane"
29
+
30
+ spec.add_development_dependency "pry"
31
+ spec.add_development_dependency "codeclimate-test-reporter"
32
+ end
metadata ADDED
@@ -0,0 +1,205 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: torganiser
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - ''
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-10-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: clamp
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '1.5'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '1.5'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: reek
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: simplecov
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: cane
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: pry
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - '>='
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: codeclimate-test-reporter
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - '>='
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - '>='
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ description: Organises episode files according to filename.
140
+ email:
141
+ - ''
142
+ executables:
143
+ - torganiser
144
+ extensions: []
145
+ extra_rdoc_files: []
146
+ files:
147
+ - .gitignore
148
+ - .rspec
149
+ - .ruby-version
150
+ - .travis.yml
151
+ - Gemfile
152
+ - LICENSE.txt
153
+ - README.md
154
+ - Rakefile
155
+ - bin/torganiser
156
+ - lib/torganiser.rb
157
+ - lib/torganiser/arranger.rb
158
+ - lib/torganiser/episode_file.rb
159
+ - lib/torganiser/file_query.rb
160
+ - lib/torganiser/runner.rb
161
+ - lib/torganiser/scanner.rb
162
+ - lib/torganiser/series.rb
163
+ - lib/torganiser/version.rb
164
+ - spec/spec_helper.rb
165
+ - spec/torganiser/arranger_spec.rb
166
+ - spec/torganiser/episode_file_spec.rb
167
+ - spec/torganiser/file_query_spec.rb
168
+ - spec/torganiser/runner_spec.rb
169
+ - spec/torganiser/scanner_spec.rb
170
+ - spec/torganiser/series_spec.rb
171
+ - spec/torganiser_spec.rb
172
+ - torganiser.gemspec
173
+ homepage: ''
174
+ licenses:
175
+ - MIT
176
+ metadata: {}
177
+ post_install_message:
178
+ rdoc_options: []
179
+ require_paths:
180
+ - lib
181
+ required_ruby_version: !ruby/object:Gem::Requirement
182
+ requirements:
183
+ - - '>='
184
+ - !ruby/object:Gem::Version
185
+ version: '0'
186
+ required_rubygems_version: !ruby/object:Gem::Requirement
187
+ requirements:
188
+ - - '>='
189
+ - !ruby/object:Gem::Version
190
+ version: '0'
191
+ requirements: []
192
+ rubyforge_project:
193
+ rubygems_version: 2.0.14
194
+ signing_key:
195
+ specification_version: 4
196
+ summary: Organises episode files according to filename.
197
+ test_files:
198
+ - spec/spec_helper.rb
199
+ - spec/torganiser/arranger_spec.rb
200
+ - spec/torganiser/episode_file_spec.rb
201
+ - spec/torganiser/file_query_spec.rb
202
+ - spec/torganiser/runner_spec.rb
203
+ - spec/torganiser/scanner_spec.rb
204
+ - spec/torganiser/series_spec.rb
205
+ - spec/torganiser_spec.rb