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.
Files changed (39) hide show
  1. data/.gitignore +6 -0
  2. data/.rspec +2 -0
  3. data/Gemfile +9 -0
  4. data/Guardfile +8 -0
  5. data/Rakefile +10 -0
  6. data/bin/file-scheduler +19 -0
  7. data/file_scheduler.gemspec +24 -0
  8. data/lib/file_scheduler.rb +21 -0
  9. data/lib/file_scheduler/attributes_parser.rb +40 -0
  10. data/lib/file_scheduler/base.rb +62 -0
  11. data/lib/file_scheduler/content.rb +19 -0
  12. data/lib/file_scheduler/core_ext.rb +37 -0
  13. data/lib/file_scheduler/file.rb +80 -0
  14. data/lib/file_scheduler/log.rb +53 -0
  15. data/lib/file_scheduler/playlist.rb +36 -0
  16. data/lib/file_scheduler/scheduling.rb +59 -0
  17. data/lib/file_scheduler/time_chain.rb +19 -0
  18. data/lib/file_scheduler/time_interval.rb +38 -0
  19. data/lib/file_scheduler/time_mark.rb +66 -0
  20. data/lib/file_scheduler/time_parser.rb +37 -0
  21. data/lib/file_scheduler/url.rb +56 -0
  22. data/lib/file_scheduler/version.rb +3 -0
  23. data/spec/lib/file_scheduler/attributes_parser_spec.rb +33 -0
  24. data/spec/lib/file_scheduler/base_spec.rb +75 -0
  25. data/spec/lib/file_scheduler/content_shared_examples.rb +12 -0
  26. data/spec/lib/file_scheduler/file_spec.rb +104 -0
  27. data/spec/lib/file_scheduler/log_spec.rb +50 -0
  28. data/spec/lib/file_scheduler/playlist_spec.rb +17 -0
  29. data/spec/lib/file_scheduler/scheduling_spec.rb +114 -0
  30. data/spec/lib/file_scheduler/time_interval_spec.rb +46 -0
  31. data/spec/lib/file_scheduler/time_mark_spec.rb +51 -0
  32. data/spec/lib/file_scheduler/time_parser_spec.rb +33 -0
  33. data/spec/lib/file_scheduler/url_spec.rb +33 -0
  34. data/spec/lib/file_scheduler_spec.rb +117 -0
  35. data/spec/spec_helper.rb +12 -0
  36. data/spec/support/pathname.rb +23 -0
  37. data/spec/support/time.rb +19 -0
  38. data/spec/support/tmpdir.rb +1 -0
  39. metadata +118 -0
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ *~
6
+ coverage/*
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format documentation
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in file_scheduler.gemspec
4
+ gemspec
5
+
6
+ gem 'guard'
7
+ gem 'rake'
8
+ gem 'rcov'
9
+ gem 'rspec'
data/Guardfile ADDED
@@ -0,0 +1,8 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard 'rspec', :version => 2 do
5
+ watch(%r{^spec/.+_spec\.rb$})
6
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
7
+ watch('spec/spec_helper.rb') { "spec" }
8
+ end
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rspec/core/rake_task'
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ RSpec::Core::RakeTask.new(:rcov) do |t|
8
+ t.rcov = true
9
+ t.rcov_opts = %w{--exclude osx\/objc,gems\/,spec\/}
10
+ end
@@ -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