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
         |