file_scheduler 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.
- data/.gitignore +6 -0
- data/.rspec +2 -0
- data/Gemfile +9 -0
- data/Guardfile +8 -0
- data/Rakefile +10 -0
- data/bin/file-scheduler +19 -0
- data/file_scheduler.gemspec +24 -0
- data/lib/file_scheduler.rb +21 -0
- data/lib/file_scheduler/attributes_parser.rb +40 -0
- data/lib/file_scheduler/base.rb +62 -0
- data/lib/file_scheduler/content.rb +19 -0
- data/lib/file_scheduler/core_ext.rb +37 -0
- data/lib/file_scheduler/file.rb +80 -0
- data/lib/file_scheduler/log.rb +53 -0
- data/lib/file_scheduler/playlist.rb +36 -0
- data/lib/file_scheduler/scheduling.rb +59 -0
- data/lib/file_scheduler/time_chain.rb +19 -0
- data/lib/file_scheduler/time_interval.rb +38 -0
- data/lib/file_scheduler/time_mark.rb +66 -0
- data/lib/file_scheduler/time_parser.rb +37 -0
- data/lib/file_scheduler/url.rb +56 -0
- data/lib/file_scheduler/version.rb +3 -0
- data/spec/lib/file_scheduler/attributes_parser_spec.rb +33 -0
- data/spec/lib/file_scheduler/base_spec.rb +75 -0
- data/spec/lib/file_scheduler/content_shared_examples.rb +12 -0
- data/spec/lib/file_scheduler/file_spec.rb +104 -0
- data/spec/lib/file_scheduler/log_spec.rb +50 -0
- data/spec/lib/file_scheduler/playlist_spec.rb +17 -0
- data/spec/lib/file_scheduler/scheduling_spec.rb +114 -0
- data/spec/lib/file_scheduler/time_interval_spec.rb +46 -0
- data/spec/lib/file_scheduler/time_mark_spec.rb +51 -0
- data/spec/lib/file_scheduler/time_parser_spec.rb +33 -0
- data/spec/lib/file_scheduler/url_spec.rb +33 -0
- data/spec/lib/file_scheduler_spec.rb +117 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/support/pathname.rb +23 -0
- data/spec/support/time.rb +19 -0
- data/spec/support/tmpdir.rb +1 -0
- metadata +118 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
data/Rakefile
ADDED
data/bin/file-scheduler
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$: << File.expand_path('../../lib', __FILE__)
|
4
|
+
|
5
|
+
require 'file_scheduler'
|
6
|
+
|
7
|
+
root = ARGV.shift
|
8
|
+
command = ARGV.shift
|
9
|
+
|
10
|
+
scheduler = FileScheduler::Base.new(root)
|
11
|
+
|
12
|
+
case command
|
13
|
+
when "contents"
|
14
|
+
scheduler.contents.each do |content|
|
15
|
+
puts "#{content} #{content.time_constraints}"
|
16
|
+
end
|
17
|
+
else
|
18
|
+
puts scheduler.send(command)
|
19
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "file_scheduler/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "file_scheduler"
|
7
|
+
s.version = FileScheduler::VERSION
|
8
|
+
s.authors = ["Alban Peignier"]
|
9
|
+
s.email = ["alban@tryphon.eu"]
|
10
|
+
s.homepage = "http://projects.tryphon.eu/filescheduler"
|
11
|
+
s.summary = %q{Schedule contents from files}
|
12
|
+
s.description = %q{Manage a audio/video program from a simple directory}
|
13
|
+
|
14
|
+
s.rubyforge_project = "file_scheduler"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
# specify any dependencies here; for example:
|
22
|
+
# s.add_development_dependency "rspec"
|
23
|
+
# s.add_runtime_dependency "rest-client"
|
24
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require "file_scheduler/version"
|
2
|
+
|
3
|
+
module FileScheduler
|
4
|
+
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'pathname'
|
8
|
+
|
9
|
+
require "file_scheduler/core_ext"
|
10
|
+
require "file_scheduler/time_mark"
|
11
|
+
require "file_scheduler/time_interval"
|
12
|
+
require "file_scheduler/time_parser"
|
13
|
+
require "file_scheduler/time_chain"
|
14
|
+
require "file_scheduler/attributes_parser"
|
15
|
+
require "file_scheduler/content"
|
16
|
+
require "file_scheduler/file"
|
17
|
+
require "file_scheduler/url"
|
18
|
+
require "file_scheduler/playlist"
|
19
|
+
require "file_scheduler/log"
|
20
|
+
require "file_scheduler/scheduling"
|
21
|
+
require "file_scheduler/base"
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module FileScheduler
|
2
|
+
class AttributesParser
|
3
|
+
|
4
|
+
def parse(string)
|
5
|
+
Parsing.new(string).attributes
|
6
|
+
end
|
7
|
+
|
8
|
+
class Parsing
|
9
|
+
|
10
|
+
attr_reader :string
|
11
|
+
|
12
|
+
def initialize(string)
|
13
|
+
@string = string
|
14
|
+
end
|
15
|
+
|
16
|
+
def definition
|
17
|
+
# Matches {...}$ or {...}.ext$
|
18
|
+
if string =~ /\{([^\}]+)\}($|\.[^.]+$)/
|
19
|
+
$1
|
20
|
+
else
|
21
|
+
""
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def pairs
|
26
|
+
definition.split(",")
|
27
|
+
end
|
28
|
+
|
29
|
+
def attributes
|
30
|
+
pairs.inject({}) do |attributes, pair|
|
31
|
+
key, value = pair.split("=")
|
32
|
+
attributes[key.strip.to_sym] = value.strip
|
33
|
+
attributes
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module FileScheduler
|
2
|
+
class Base
|
3
|
+
|
4
|
+
attr_accessor :root, :log, :status_file
|
5
|
+
|
6
|
+
def initialize(attributes = {})
|
7
|
+
case attributes
|
8
|
+
when String
|
9
|
+
attributes =
|
10
|
+
{ (attributes.url? ? :playlist : :directory) => attributes }
|
11
|
+
when Pathname
|
12
|
+
attributes = { :directory => attributes }
|
13
|
+
end
|
14
|
+
|
15
|
+
@root =
|
16
|
+
if attributes.has_key?(:directory)
|
17
|
+
FileScheduler::File.new(attributes[:directory])
|
18
|
+
elsif attributes.has_key?(:playlist)
|
19
|
+
FileScheduler::Playlist.new(attributes[:playlist])
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def log
|
24
|
+
@log ||= Log.new.tap do |log|
|
25
|
+
log.load(status_file) if status_file
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def contents
|
30
|
+
root.contents
|
31
|
+
end
|
32
|
+
|
33
|
+
def scheduling(time = Time.now)
|
34
|
+
Scheduling.new(root, time).tap do |scheduling|
|
35
|
+
scheduling.log = log
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def next(time = Time.now)
|
40
|
+
scheduling(time).next
|
41
|
+
end
|
42
|
+
|
43
|
+
def forced_next(time = Time.now)
|
44
|
+
scheduling(time).forced_next
|
45
|
+
end
|
46
|
+
|
47
|
+
def after_next(content)
|
48
|
+
log.save(status_file) if status_file
|
49
|
+
end
|
50
|
+
|
51
|
+
[:next, :forced_next].each do |method|
|
52
|
+
alias_method :"#{method}_without_callback", method
|
53
|
+
define_method("#{method}_with_callback") do |*arguments|
|
54
|
+
content = send "#{method}_without_callback", *arguments
|
55
|
+
after_next content
|
56
|
+
content
|
57
|
+
end
|
58
|
+
alias_method method, :"#{method}_with_callback"
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module FileScheduler
|
2
|
+
module Content
|
3
|
+
def forced_started_time?
|
4
|
+
name.start_with?("T")
|
5
|
+
end
|
6
|
+
|
7
|
+
def parser
|
8
|
+
@parser ||= TimeParser.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def attributes_parser
|
12
|
+
@attributes_parser ||= AttributesParser.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def repeat_constraints
|
16
|
+
@repeat_constraints ||= attributes[:repeat].try(:to_i)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
class String
|
2
|
+
|
3
|
+
def to_url
|
4
|
+
URI::parse self
|
5
|
+
rescue URI::InvalidURIError
|
6
|
+
nil
|
7
|
+
end
|
8
|
+
|
9
|
+
def url?
|
10
|
+
start_with? "http://"
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
class Object
|
16
|
+
|
17
|
+
def try(method, *arguments)
|
18
|
+
send method, *arguments
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
class Nil
|
24
|
+
|
25
|
+
def try(method, *arguments)
|
26
|
+
nil
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
class Hash
|
32
|
+
|
33
|
+
def set_attributes(object, defaults = {})
|
34
|
+
defaults.merge(self).each { |k,v| object.send "#{k}=", v }
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module FileScheduler
|
2
|
+
class File
|
3
|
+
|
4
|
+
attr_accessor :path, :parent
|
5
|
+
|
6
|
+
def initialize(path, parent = nil)
|
7
|
+
@parent = parent
|
8
|
+
@path = Pathname.new(path)
|
9
|
+
end
|
10
|
+
|
11
|
+
def name
|
12
|
+
@name ||= path.basename.to_s
|
13
|
+
end
|
14
|
+
|
15
|
+
def hidden?
|
16
|
+
name.start_with?("_")
|
17
|
+
end
|
18
|
+
|
19
|
+
def local_time_constraints
|
20
|
+
parser.parse(name) if parent
|
21
|
+
end
|
22
|
+
|
23
|
+
def time_constraints
|
24
|
+
@time_constraints ||=
|
25
|
+
if parent and parent.time_constraints
|
26
|
+
if local_time_constraints
|
27
|
+
TimeChain.new parent.time_constraints, local_time_constraints
|
28
|
+
else
|
29
|
+
parent.time_constraints
|
30
|
+
end
|
31
|
+
else
|
32
|
+
local_time_constraints
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def file_system_children
|
37
|
+
@file_system_children ||= path.children
|
38
|
+
end
|
39
|
+
|
40
|
+
include FileScheduler::Content
|
41
|
+
|
42
|
+
def children
|
43
|
+
@children ||= file_system_children.collect do |file|
|
44
|
+
File.new(file, self)
|
45
|
+
end.delete_if(&:hidden?)
|
46
|
+
end
|
47
|
+
|
48
|
+
def contents
|
49
|
+
if content?
|
50
|
+
[self]
|
51
|
+
else
|
52
|
+
children.collect(&:contents).flatten
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def content?
|
57
|
+
path.file?
|
58
|
+
end
|
59
|
+
|
60
|
+
def ==(other)
|
61
|
+
[:parent, :path].all? do |attribute|
|
62
|
+
other.respond_to?(attribute) and send(attribute) == other.send(attribute)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def to_s
|
67
|
+
path.to_s
|
68
|
+
end
|
69
|
+
|
70
|
+
def local_attributes
|
71
|
+
@local_attributes ||= attributes_parser.parse(name)
|
72
|
+
end
|
73
|
+
|
74
|
+
def attributes
|
75
|
+
@attributes ||=
|
76
|
+
(parent ? parent.attributes : {}).merge(local_attributes)
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module FileScheduler
|
2
|
+
class Log
|
3
|
+
|
4
|
+
attr_accessor :max_size
|
5
|
+
|
6
|
+
def initialize(options = {})
|
7
|
+
options.set_attributes self, :max_size => 100
|
8
|
+
clear
|
9
|
+
end
|
10
|
+
|
11
|
+
def distance(content)
|
12
|
+
@contents.index content
|
13
|
+
end
|
14
|
+
|
15
|
+
def log(content)
|
16
|
+
@contents.unshift content
|
17
|
+
@contents = @contents.first(max_size)
|
18
|
+
content
|
19
|
+
end
|
20
|
+
|
21
|
+
def clear
|
22
|
+
@contents = []
|
23
|
+
end
|
24
|
+
|
25
|
+
include Enumerable
|
26
|
+
def each(&block)
|
27
|
+
@contents.each(&block)
|
28
|
+
end
|
29
|
+
|
30
|
+
def load(data)
|
31
|
+
return unless data
|
32
|
+
|
33
|
+
# Marshalled data starts with \004
|
34
|
+
unless data.start_with?("\004")
|
35
|
+
data = IO.read(file) if File.exists?(data)
|
36
|
+
end
|
37
|
+
|
38
|
+
@contents = Marshal.load data
|
39
|
+
self
|
40
|
+
end
|
41
|
+
|
42
|
+
def dump
|
43
|
+
Marshal.dump(@contents)
|
44
|
+
end
|
45
|
+
|
46
|
+
def save(file)
|
47
|
+
File.open(file, "w") do |f|
|
48
|
+
f.write dump
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
|
3
|
+
module FileScheduler
|
4
|
+
class Playlist
|
5
|
+
|
6
|
+
attr_accessor :url, :content
|
7
|
+
|
8
|
+
def initialize(attributes = {})
|
9
|
+
if String === attributes
|
10
|
+
attributes =
|
11
|
+
{ (attributes.url? ? :url : :content) => attributes }
|
12
|
+
end
|
13
|
+
|
14
|
+
attributes.each { |k,v| send "#{k}=", v }
|
15
|
+
end
|
16
|
+
|
17
|
+
def content
|
18
|
+
@content ||= open(url, &:read)
|
19
|
+
end
|
20
|
+
|
21
|
+
def lines
|
22
|
+
@lines ||= content.split
|
23
|
+
end
|
24
|
+
|
25
|
+
def contents
|
26
|
+
lines.collect do |path|
|
27
|
+
FileScheduler::URL.new :path => path, :url => full_url(path)
|
28
|
+
end.delete_if(&:hidden?)
|
29
|
+
end
|
30
|
+
|
31
|
+
def full_url(path)
|
32
|
+
URI.join url, path if url
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|