torganiser 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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