rivendell-import 0.0.1 → 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/Gemfile CHANGED
@@ -2,7 +2,7 @@ source "http://rubygems.org"
2
2
 
3
3
  gemspec
4
4
 
5
- # gem "rivendell-api", :git => "git://projects.tryphon.priv/rivendell-api", :path => "~/Projects/RivendellApi"
5
+ gem "rivendell-api" # , :git => "git://projects.tryphon.priv/rivendell-api", :ref => "b98a46fbb0c31d965853206a56500cea4a340044" # , :path => "~/Projects/RivendellApi"
6
6
 
7
7
  if RUBY_PLATFORM =~ /linux/
8
8
  gem 'libnotify'
@@ -0,0 +1,9 @@
1
+ class AddDeleteFileToTask < ActiveRecord::Migration
2
+ def self.up
3
+ add_column :tasks, :delete_file, :boolean
4
+ end
5
+
6
+ def self.down
7
+ drop_column :tasks, :delete_file
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ class AddXportOptionsToTask < ActiveRecord::Migration
2
+ def self.up
3
+ add_column :tasks, :xport_options, :string
4
+ end
5
+
6
+ def self.down
7
+ drop_column :tasks, :xport_options
8
+ end
9
+ end
data/examples/config.rb CHANGED
@@ -15,17 +15,26 @@ end
15
15
  Rivendell::Import::Notifier::Mail.from = "root@tryphon.eu"
16
16
 
17
17
  Rivendell::Import.config do |config|
18
+
19
+ config.rivendell.host = "localhost"
20
+ config.rivendell.login_name = "user"
21
+ config.rivendell.password = ""
22
+
18
23
  config.to_prepare do |file|
19
24
  file.in("music") do
20
25
  cart.group = "MUSIC"
21
26
  end
22
27
 
23
28
  file.in("pad") do
29
+ cart.clear_cuts!
24
30
  cart.find_by_title file.basename
25
31
  end
26
32
 
27
33
  cart.group ||= "TEST"
28
34
 
35
+ # To delete file when task is completed
36
+ #task.delete_file!
37
+
29
38
  notify 'alban@tryphon.eu', :by => :email
30
39
  end
31
40
  end
@@ -34,6 +34,8 @@ ActiveRecord::Base.include_root_in_json = false
34
34
 
35
35
  require "rivendell/api"
36
36
 
37
+ require "rivendell/import/config"
38
+ require "rivendell/import/config_loader"
37
39
  require "rivendell/import/worker"
38
40
  require "rivendell/import/task"
39
41
  require "rivendell/import/tasks"
@@ -0,0 +1,53 @@
1
+ require 'sinatra'
2
+
3
+ require 'will_paginate'
4
+ require 'will_paginate/active_record'
5
+
6
+ module Rivendell::Import
7
+ class Application < Sinatra::Application
8
+
9
+ set :public_folder, ::File.expand_path('static', ::File.dirname(__FILE__))
10
+ # set :static_cache_control, [:public, :max_age => 3600]
11
+
12
+ get '/' do
13
+ redirect "/tasks", 302
14
+ end
15
+
16
+ get '/tasks' do
17
+ erb :index, :locals => { :tasks => tasks.paginate(:page => params[:page], :per_page => 15) }
18
+ end
19
+
20
+ get '/tasks.json' do
21
+ tasks.to_json
22
+ end
23
+
24
+ def tasks
25
+ Task.order("updated_at DESC")
26
+ end
27
+
28
+ helpers do
29
+ def distance_of_time_in_words_from_now(time)
30
+ distance = Time.now - time
31
+
32
+ if distance < 43200 # less than 12 hours
33
+ time.strftime("%H:%M")
34
+ else
35
+ time.strftime("%d/%m")
36
+ end
37
+ end
38
+
39
+ def truncate_filename(path, length)
40
+ path = path.to_s
41
+
42
+ if path.size < length
43
+ path
44
+ else
45
+ path[0..length] + "..."
46
+ end
47
+ end
48
+
49
+ end
50
+
51
+ end
52
+ end
53
+
@@ -10,7 +10,13 @@ module Rivendell::Import
10
10
  @workers = []
11
11
  end
12
12
 
13
+ @@default_to_prepare = nil
14
+ cattr_accessor :default_to_prepare
15
+
13
16
  attr_accessor :to_prepare
17
+ def to_prepare
18
+ @to_prepare or default_to_prepare
19
+ end
14
20
 
15
21
  def listen(directory, options = {})
16
22
  workers << Worker.new(self).start unless options[:dry_run]
@@ -5,7 +5,7 @@ module Rivendell::Import
5
5
  include ActiveModel::Serializers::JSON
6
6
 
7
7
  def attributes
8
- %w{number group}.inject({}) do |map, attribute|
8
+ %w{number group clear_cuts?}.inject({}) do |map, attribute|
9
9
  value = send attribute
10
10
  map[attribute] = value if value
11
11
  map
@@ -48,6 +48,7 @@ module Rivendell::Import
48
48
  raise "File #{file.path} not found" unless file.exists?
49
49
 
50
50
  cut.create
51
+ xport.clear_cuts number if clear_cuts?
51
52
  xport.import number, cut.number, file.path
52
53
  cut.update
53
54
  end
@@ -58,6 +59,13 @@ module Rivendell::Import
58
59
  end
59
60
  end
60
61
 
62
+ attr_accessor :clear_cuts
63
+ alias_method :clear_cuts?, :clear_cuts
64
+
65
+ def clear_cuts!
66
+ self.clear_cuts = true
67
+ end
68
+
61
69
  def carts_cache
62
70
  @carts_cache ||= Rivendell::Import::CartsCache.new(xport)
63
71
  end
@@ -1,5 +1,6 @@
1
1
  require 'trollop'
2
2
  require 'logger'
3
+ require 'syslog/logger'
3
4
 
4
5
  module Rivendell::Import
5
6
  class CLI
@@ -27,6 +28,10 @@ module Rivendell::Import
27
28
  options[:debug]
28
29
  end
29
30
 
31
+ def syslog?
32
+ options[:syslog]
33
+ end
34
+
30
35
  def database
31
36
  options[:database]
32
37
  end
@@ -37,6 +42,7 @@ module Rivendell::Import
37
42
  opt :listen, "Wait for files in given directory"
38
43
  opt :dry_run, "Just create tasks without executing them"
39
44
  opt :debug, "Enable debug messages (in stderr)"
45
+ opt :syslog, "Log messages to syslog"
40
46
  opt :database, "The database file used to store tasks", :type => String
41
47
  end
42
48
  end
@@ -62,8 +68,39 @@ module Rivendell::Import
62
68
  parsed_parser.leftovers
63
69
  end
64
70
 
71
+ def setup_logger
72
+ new_logger =
73
+ if syslog?
74
+ Syslog::Logger.new('rivendell-import').tap do |syslog|
75
+ syslog.level = debug? ? Logger::DEBUG : Logger::INFO
76
+ end
77
+ elsif debug?
78
+ Logger.new($stderr)
79
+ end
80
+
81
+ Rivendell::Import.logger = new_logger if new_logger
82
+ end
83
+
84
+ def start_webserver
85
+ require 'rivendell/import/application'
86
+
87
+ Rivendell::Import.logger.debug "Start webserver"
88
+
89
+ Thread.new do
90
+ Rivendell::Import::Application.run!
91
+
92
+ # FIXME we don't see difference between normal quit and start error (EADDRINUSE, ...)
93
+ Rivendell::Import.logger.debug "Webserver is stopped"
94
+ exit 0
95
+ end
96
+ end
97
+
98
+ def config_loader
99
+ ConfigLoader.new(config_file, listen_mode?)
100
+ end
101
+
65
102
  def run
66
- Rivendell::Import.logger = Logger.new($stderr) if debug?
103
+ setup_logger
67
104
 
68
105
  if database
69
106
  Rivendell::Import.establish_connection database
@@ -71,12 +108,11 @@ module Rivendell::Import
71
108
  Rivendell::Import.establish_connection
72
109
  end
73
110
 
74
- if config_file
75
- load config_file
76
- import.to_prepare = Rivendell::Import.config.to_prepare
77
- end
111
+ config_loader.load
78
112
 
79
113
  if listen_mode?
114
+ start_webserver
115
+
80
116
  listen_options = {}
81
117
  listen_options[:dry_run] = true if dry_run?
82
118
 
@@ -2,11 +2,29 @@ module Rivendell::Import
2
2
  class Config
3
3
 
4
4
  def to_prepare(&block)
5
- if block_given?
6
- @prepare = block
7
- else
8
- @prepare
5
+ Base.default_to_prepare = block
6
+ end
7
+
8
+ def rivendell
9
+ @rivendell ||= Rivendell.new
10
+ end
11
+ alias_method :xport_options, :rivendell
12
+
13
+ class Rivendell
14
+
15
+ def host=(host)
16
+ Task.default_xport_options[:host] = host
9
17
  end
18
+
19
+ def login_name=(login_name)
20
+ Task.default_xport_options[:login_name] = login_name
21
+ end
22
+ alias_method :user=, :login_name=
23
+
24
+ def password=(password)
25
+ Task.default_xport_options[:password] = password
26
+ end
27
+
10
28
  end
11
29
 
12
30
  end
@@ -0,0 +1,43 @@
1
+ module Rivendell::Import
2
+ class ConfigLoader
3
+
4
+ attr_accessor :file, :auto_reload
5
+ alias_method :auto_reload?, :auto_reload
6
+
7
+ def initialize(file, auto_reload = false)
8
+ self.file = file
9
+ self.auto_reload = auto_reload
10
+
11
+ listen_file if auto_reload?
12
+ end
13
+
14
+ def load
15
+ Kernel.load file
16
+ end
17
+
18
+ def listen_file
19
+ callback = Proc.new do |modified, added, removed|
20
+ if modified.include? absolute_path
21
+ Rivendell::Import.logger.info "Configuration changed, reload it"
22
+ load
23
+ end
24
+ end
25
+
26
+ Rivendell::Import.logger.info "Listen to config file changes (#{file})"
27
+ Listen.to(directory).filter(/^#{basename}$/).change(&callback).start(false)
28
+ end
29
+
30
+ def absolute_path
31
+ ::File.expand_path(file)
32
+ end
33
+
34
+ def basename
35
+ ::File.basename(file)
36
+ end
37
+
38
+ def directory
39
+ ::File.dirname(file)
40
+ end
41
+
42
+ end
43
+ end
@@ -53,5 +53,10 @@ module Rivendell::Import
53
53
  ::File.exists? path
54
54
  end
55
55
 
56
+ def destroy!
57
+ Rivendell::Import.logger.debug "Delete file #{path}"
58
+ ::File.delete(path) if exists?
59
+ end
60
+
56
61
  end
57
62
  end
@@ -58,10 +58,15 @@ module Rivendell::Import::Notifier
58
58
 
59
59
  after_initialize :read_parameters
60
60
 
61
+ def parameters_hash
62
+ parameters_as_string = parameters.sort_by { |p| p.first.to_s }.flatten.join('-')
63
+ Digest::SHA256.hexdigest parameters_as_string
64
+ end
65
+
61
66
  def write_parameters
62
67
  if parameters.present?
63
68
  write_attribute :parameters, parameters.to_json
64
- write_attribute :key, parameters.hash
69
+ write_attribute :key, parameters_hash
65
70
  else
66
71
  write_attribute :parameters, nil
67
72
  write_attribute :key, nil
@@ -76,7 +81,7 @@ module Rivendell::Import::Notifier
76
81
  Mail.new options.merge(:to => target)
77
82
  end
78
83
 
79
- key = new_notifier.parameters.hash
84
+ key = new_notifier.parameters_hash
80
85
  if existing_notifier = where(:type => new_notifier.type, :key => key).first
81
86
  existing_notifier
82
87
  else
@@ -0,0 +1,137 @@
1
+ html {color:#000;}
2
+ body,div,ul,li,p,th,td{margin:0;padding:0;}
3
+ table {border-collapse:collapse;border-spacing:0;}
4
+ img {border:0;}
5
+ strong,b,i,em,th {font-style:normal;font-weight:normal;}
6
+ li {list-style:none;}
7
+ th {text-align:left;}
8
+ body {
9
+ margin: 0;
10
+ padding: 0;
11
+ font-size: 14px;
12
+ font-family: Trebuchet MS, Verdana, sans-serif;
13
+ }
14
+ a {
15
+ color: #5884a6;
16
+ text-decoration: none;
17
+ }
18
+ a:hover { text-decoration: underline; }
19
+ hr {
20
+ display: none;
21
+ clear: both;
22
+ }
23
+ img {
24
+ border: none;
25
+ vertical-align: middle;
26
+ }
27
+ strong { font-weight: bold; }
28
+ em { font-style: italic; }
29
+ #access { display: none; }
30
+ #header {
31
+ border-bottom: 4px solid #cccccc;
32
+ background: #333333;
33
+ margin-bottom: 20px;
34
+ }
35
+ #header div {
36
+ position: relative;
37
+ height: 100px;
38
+ width: 750px;
39
+ margin: 0 auto;
40
+ background: url("rivendell-import.png") no-repeat 0px 30px;
41
+ }
42
+ #menu {
43
+ position: absolute;
44
+ bottom: 0;
45
+ right: 0;
46
+ }
47
+ #menu li {
48
+ float: left;
49
+ margin: 0 0 0 8px;
50
+ background-color: #7196c4;
51
+ }
52
+ #menu li a {
53
+ display: block;
54
+ padding: 6px 10px;
55
+ font-size: 105%;
56
+ color: #ffffff;
57
+ }
58
+ #menu li a:hover {
59
+ color: #333333;
60
+ text-decoration: none;
61
+ }
62
+ #frame {
63
+ width: 750px;
64
+ margin: 0 auto;
65
+ }
66
+ #frame:after {
67
+ content: ".";
68
+ display: block;
69
+ height: 0;
70
+ clear: both;
71
+ visibility: hidden;
72
+ }
73
+ #content {
74
+ padding-top: 5px;
75
+ }
76
+ #content .empty {
77
+ margin-top: 10px;
78
+ font-style: italic;
79
+ }
80
+ #content .pagination {
81
+ clear: both;
82
+ padding: 4px 0;
83
+ font-size: 90%;
84
+ text-align: right;
85
+ }
86
+ #content .pagination .disabled { display: none; }
87
+ #flash div {
88
+ margin: 6px 0;
89
+ padding: 12px 0;
90
+ font-size: 115%;
91
+ font-weight: bold;
92
+ text-indent: 54px;
93
+ border-top: 1px solid #979797;
94
+ border-bottom: 1px solid #979797;
95
+ background: #e7e7e7 url("../images/ui/notice.png") no-repeat 28px;
96
+ }
97
+ #flash .success {
98
+ color: #1e7a1b;
99
+ border-color: #4bb426;
100
+ background: #b7e1af url("../images/ui/success.png") no-repeat 28px;
101
+ }
102
+ #flash .failure {
103
+ color: #c01100;
104
+ border-color: #ca4f35;
105
+ background: #ffbdbd url("../images/ui/failure.png") no-repeat 28px;
106
+ }
107
+ #flash .warning {
108
+ color: #c67109;
109
+ border-color: #c67109;
110
+ background: #f8e694 url("../images/ui/warning.png") no-repeat 28px;
111
+ }
112
+ #footer {
113
+ display: none;
114
+ clear: both;
115
+ color: #333333;
116
+ line-height: 18px;
117
+ margin: 0;
118
+ padding: 24px 0;
119
+ background: #e7db9f;
120
+ }
121
+ #footer div {
122
+ width: 760px;
123
+ margin: 0 auto;
124
+ text-align: left;
125
+ }
126
+ #content table {
127
+ width: 100%;
128
+ }
129
+ #content table th {
130
+ font-weight: bold;
131
+ }
132
+ #content table td, #content table th {
133
+ padding: 5px 2px;
134
+ }
135
+ #content table tr.odd {
136
+ background-color: #EAEAEA;
137
+ }
@@ -24,8 +24,24 @@ module Rivendell::Import
24
24
  status.completed? or status.failed?
25
25
  end
26
26
 
27
+ @@default_xport_options = {}
28
+ cattr_accessor :default_xport_options
29
+
30
+ def raw_xport_options
31
+ read_attribute :xport_options
32
+ end
33
+
34
+ def xport_options
35
+ @xport_options ||= (raw_xport_options ? JSON.parse(raw_xport_options).with_indifferent_access : default_xport_options.dup)
36
+ end
37
+
38
+ def write_xport_options
39
+ write_attribute :xport_options, (xport_options.present? ? xport_options.to_json : nil)
40
+ end
41
+ before_save :write_xport_options
42
+
27
43
  def xport
28
- @xport ||= Rivendell::API::Xport.new
44
+ @xport ||= Rivendell::API::Xport.new(xport_options)
29
45
  end
30
46
 
31
47
  def prepare(&block)
@@ -53,6 +69,11 @@ module Rivendell::Import
53
69
  notifier.notify
54
70
  end
55
71
  end
72
+ after_status_changed :notify!, :on => [:completed, :failed]
73
+
74
+ def destroy_file!
75
+ file.destroy! if delete_file?
76
+ end
56
77
 
57
78
  def run
58
79
  logger.debug "Run #{self.inspect}"
@@ -64,16 +85,18 @@ module Rivendell::Import
64
85
  cart.import file
65
86
  cart.update
66
87
 
67
- save!
88
+ destroy_file!
68
89
  change_status! :completed
69
90
 
70
91
  logger.info "Imported Cart #{cart.number}"
71
92
  rescue Exception => e
72
93
  logger.error "Task failed : #{e}"
94
+ logger.debug e.backtrace.join("\n")
73
95
  ensure
74
96
  unless status.completed?
75
97
  change_status! :failed
76
98
  end
99
+ save!
77
100
  end
78
101
 
79
102
  end
@@ -1,5 +1,6 @@
1
1
  module Rivendell::Import::Tasking
2
2
  module File
3
+
3
4
  def file=(file)
4
5
  if file
5
6
  @file = file
@@ -16,5 +17,10 @@ module Rivendell::Import::Tasking
16
17
  def file
17
18
  @file ||= Rivendell::Import::File.new(file_name, :path => file_path)
18
19
  end
20
+
21
+ def delete_file!
22
+ self.delete_file = true
23
+ end
24
+
19
25
  end
20
26
  end
@@ -9,6 +9,7 @@ module Rivendell::Import::Tasking
9
9
  base.class_eval do
10
10
  after_initialize :define_default_status
11
11
  end
12
+ base.extend ClassMethods
12
13
  end
13
14
 
14
15
  def raw_status
@@ -22,7 +23,27 @@ module Rivendell::Import::Tasking
22
23
  def change_status!(status)
23
24
  logger.debug "Change status to #{status}"
24
25
  update_attribute :status, status.to_s
25
- notify! if ran?
26
+ # notify! if ran?
27
+ invoke_status_changed_callbacks
28
+ end
29
+
30
+ def invoke_status_changed_callbacks
31
+ callbacks = self.class.status_changed_callbacks.values_at(:all, status.to_sym).flatten
32
+ callbacks.each { |method| send method }
33
+ end
34
+
35
+ module ClassMethods
36
+
37
+ def status_changed_callbacks
38
+ @status_changed_callbacks ||= Hash.new { |h,k| h[k] = [] }
39
+ end
40
+
41
+ def after_status_changed(method, options = {})
42
+ Array(options[:on] || :all).each do |status|
43
+ status_changed_callbacks[status] << method
44
+ end
45
+ end
46
+
26
47
  end
27
48
 
28
49
  end
@@ -1,10 +1,6 @@
1
1
  module Rivendell::Import
2
2
  class Tasks
3
3
 
4
- def pending?
5
- not Task.pending.empty?
6
- end
7
-
8
4
  def pop
9
5
  Task.pending.first
10
6
  end
@@ -1,5 +1,5 @@
1
1
  module Rivendell
2
2
  module Import
3
- VERSION = "0.0.1"
3
+ VERSION = "0.0.2"
4
4
  end
5
5
  end
@@ -0,0 +1,56 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
2
+ <html xmlns="http://www.w3.org/1999/xhtml" lang="fr">
3
+ <head>
4
+ <title>Rivendell Import</title>
5
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
6
+ <link rel="shortcut icon" href="/favicon.ico" />
7
+ <link href="/screen.css" media="screen" rel="stylesheet" type="text/css" />
8
+ </head>
9
+ <body class="tasks index">
10
+ <ul id="access">
11
+ <li><a href="#content">Aller au contenu</a></li>
12
+ <li><a href="#sidebar">Aller au menu</a></li>
13
+ </ul>
14
+ <hr/>
15
+ <div id="header">
16
+ <div>
17
+ <ul id="menu">
18
+ <li><a href="http://wiki.tryphon.eu/rivendell-import/">Help</a></li>
19
+ </ul>
20
+ </div>
21
+ </div>
22
+ <hr/>
23
+ <div id="frame">
24
+ <div id="content">
25
+ <%= will_paginate tasks %>
26
+
27
+ <table>
28
+ <tr>
29
+ <th>#</th>
30
+ <th>Status</th>
31
+ <th>Since</th>
32
+ <th>File</th>
33
+ <th>Destination</th>
34
+ <th>Priority</th>
35
+ <tr/>
36
+ <% tasks.each_with_index do |task, index| %>
37
+ <tr class="<%= 'odd' if index.odd? %>">
38
+ <td><%= task.id %></td>
39
+ <td><%= task.status %></td>
40
+ <td><%= distance_of_time_in_words_from_now(task.updated_at) %></td>
41
+ <td><%= truncate_filename(task.file, 60) %></td>
42
+ <td><%= task.destination %></td>
43
+ <td>default</td>
44
+ </tr>
45
+ <% end %>
46
+ </table>
47
+ </div>
48
+ </div>
49
+ <hr/>
50
+ <div id="footer">
51
+ <div>
52
+ &nbsp;
53
+ </div>
54
+ </div>
55
+ </body>
56
+ </html>
@@ -17,12 +17,16 @@ Gem::Specification.new do |gem|
17
17
 
18
18
  gem.add_runtime_dependency 'listen'
19
19
  gem.add_runtime_dependency 'httmultiparty'
20
- gem.add_runtime_dependency 'rivendell-api', '~> 0.0.2'
20
+ gem.add_runtime_dependency 'rivendell-api', '~> 0.0.3'
21
21
  gem.add_runtime_dependency 'trollop'
22
22
  gem.add_runtime_dependency 'activerecord', '~> 3.2.8'
23
23
  gem.add_runtime_dependency 'activesupport', '~> 3.2.8'
24
24
  gem.add_runtime_dependency 'mail'
25
25
  gem.add_runtime_dependency 'sqlite3'
26
+ gem.add_runtime_dependency 'SyslogLogger', '~> 2.0'
27
+
28
+ gem.add_runtime_dependency 'sinatra'
29
+ gem.add_runtime_dependency 'will_paginate', '~> 3.0.0'
26
30
 
27
31
  gem.add_development_dependency "simplecov"
28
32
  gem.add_development_dependency "rspec"
@@ -14,6 +14,17 @@ describe Rivendell::Import::Base do
14
14
 
15
15
  end
16
16
 
17
+ describe "#to_prepare" do
18
+
19
+ let(:block) { mock }
20
+
21
+ it "should use default_to_prepare if not defined" do
22
+ subject.stub :default_to_prepare => block
23
+ subject.to_prepare.should == block
24
+ end
25
+
26
+ end
27
+
17
28
  describe "#create_task" do
18
29
 
19
30
  let(:file) { Rivendell::Import::File.new "dummy.wav" }
@@ -83,8 +83,7 @@ describe Rivendell::Import::Cart do
83
83
 
84
84
  before(:each) do
85
85
  subject.number = 123
86
- subject.stub :xport => mock(:import => true)
87
- subject.cut.stub :create => true, :number => 1, :update => true
86
+ subject.stub :cut => mock.as_null_object, :xport => mock.as_null_object
88
87
  end
89
88
 
90
89
  it "should create Cut" do
@@ -102,6 +101,25 @@ describe Rivendell::Import::Cart do
102
101
  subject.import file
103
102
  end
104
103
 
104
+ context "clear_cuts has been defined" do
105
+ before do
106
+ subject.clear_cuts!
107
+ end
108
+
109
+ it "should invoke Xport#clear_cuts before create a new cut" do
110
+ subject.xport.should_receive(:clear_cuts).ordered.with(subject.number)
111
+ subject.xport.should_receive(:import).ordered
112
+ subject.import file
113
+ end
114
+ end
115
+
116
+ context "clear_cuts hasn't been defined" do
117
+ it "should not invoke Xport#clear_cuts" do
118
+ subject.xport.should_not_receive(:create_cuts).with(subject.number)
119
+ subject.import file
120
+ end
121
+ end
122
+
105
123
  end
106
124
 
107
125
  describe "#find_by_title" do
@@ -129,4 +147,17 @@ describe Rivendell::Import::Cart do
129
147
 
130
148
  end
131
149
 
150
+ describe "#clear_cuts!" do
151
+
152
+ before do
153
+ subject.number = 123
154
+ end
155
+
156
+ it "should set flag clear_cuts" do
157
+ subject.clear_cuts!
158
+ subject.clear_cuts.should be_true
159
+ end
160
+
161
+ end
162
+
132
163
  end
@@ -40,6 +40,15 @@ describe Rivendell::Import::CLI do
40
40
 
41
41
  end
42
42
 
43
+ describe "syslog?" do
44
+
45
+ it "should return true when --syslog is specified" do
46
+ subject.arguments << "--syslog"
47
+ subject.should be_syslog
48
+ end
49
+
50
+ end
51
+
43
52
  describe "#import" do
44
53
 
45
54
  it "should return a Rivendell::Import::Base instance" do
@@ -71,13 +80,18 @@ describe Rivendell::Import::CLI do
71
80
 
72
81
  before(:each) do
73
82
  subject.stub :paths => %w{file1 file2}
83
+ subject.stub :start_webserver
84
+ subject.stub :config_loader => mock(:load => true)
74
85
  subject.import.tasks.stub :run => true
75
86
  end
76
87
 
77
- it "should load config_file" do
78
- subject.stub :config_file => "dummy.rb"
79
- subject.should_receive(:load).with(subject.config_file)
88
+ it "should setup logger" do
89
+ subject.should_receive(:setup_logger)
90
+ subject.run
91
+ end
80
92
 
93
+ it "should load config_file" do
94
+ subject.config_loader.should_receive(:load)
81
95
  subject.run
82
96
  end
83
97
 
@@ -146,4 +160,34 @@ describe Rivendell::Import::CLI do
146
160
 
147
161
  end
148
162
 
163
+ describe "#setup_logger" do
164
+
165
+ context " with syslog option" do
166
+
167
+ before do
168
+ subject.stub :syslog? => true
169
+ end
170
+
171
+ it "should use a SyslogLogger" do
172
+ Rivendell::Import.should_receive(:logger=).with(kind_of(Syslog::Logger))
173
+ subject.setup_logger
174
+ end
175
+
176
+ end
177
+
178
+ end
179
+
180
+ describe "#config_loader" do
181
+
182
+ it "should use config_file" do
183
+ subject.config_loader.file.should == subject.config_file
184
+ end
185
+
186
+ it "should be auto_reload if listen mode is enabled" do
187
+ subject.stub :listen_mode? => true, :config_file => "dummy"
188
+ subject.config_loader.should be_auto_reload
189
+ end
190
+
191
+ end
192
+
149
193
  end
@@ -1,16 +1,38 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Rivendell::Import::Config do
4
+
5
+ let(:config) { Rivendell::Import::Config.new }
4
6
 
5
7
  describe "#to_prepare" do
6
8
 
7
9
  let(:user_block) { Proc.new {} }
8
10
 
9
- it "should define to_prepare proc with given block" do
11
+ it "should define Base.default_to_prepare with given block" do
10
12
  subject.to_prepare(&user_block)
11
- subject.to_prepare.should == user_block
13
+ Rivendell::Import::Base.default_to_prepare.should == user_block
12
14
  end
13
15
 
14
16
  end
15
17
 
18
+ describe "#rivendell" do
19
+
20
+ subject { config.rivendell }
21
+
22
+ def self.it_should_define_task_default_xport_option(attribute)
23
+ describe "#{attribute}=" do
24
+ attribute = attribute.to_sym
25
+ it "should define Task.default_xport_options[:#{attribute}]" do
26
+ subject.send "#{attribute}=", "dummy"
27
+ Rivendell::Import::Task.default_xport_options[attribute].should == "dummy"
28
+ end
29
+ end
30
+ end
31
+
32
+ it_should_define_task_default_xport_option :host
33
+ it_should_define_task_default_xport_option :login_name
34
+ it_should_define_task_default_xport_option :password
35
+
36
+ end
37
+
16
38
  end
@@ -55,9 +55,23 @@ describe Rivendell::Import::Notifier::Base do
55
55
  subject.key = nil
56
56
  subject.stub :parameters => { :dummy => true }
57
57
  subject.save!
58
- subject.key.should == subject.parameters.hash
58
+ subject.key.should == subject.parameters_hash
59
59
  end
60
60
 
61
61
  end
62
62
 
63
+ describe "#parameters_hash" do
64
+
65
+ let(:other) { Rivendell::Import::Notifier::Test.new }
66
+
67
+ it "should be identical when parameters are identical" do
68
+ subject.stub :parameters => { :first => 1, :second => 2 }
69
+ other.stub :parameters => { :second => 2, :first => 1 }
70
+
71
+ subject.parameters_hash.should == other.parameters_hash
72
+ end
73
+
74
+
75
+ end
76
+
63
77
  end
@@ -27,6 +27,26 @@ describe Rivendell::Import::Task do
27
27
  subject.xport.should be_instance_of(Rivendell::API::Xport)
28
28
  end
29
29
 
30
+ it "should use xport_options" do
31
+ subject.xport_options[:host] = "dummy"
32
+ subject.xport.host.should == "dummy"
33
+ end
34
+
35
+ end
36
+
37
+ describe "#xport_options" do
38
+
39
+ it "should use #default_xport_options" do
40
+ subject.stub :default_xport_options => { :host => "dummy" }
41
+ subject.xport_options.should == { :host => "dummy" }
42
+ end
43
+
44
+ it "should not modified #default_xport_options" do
45
+ subject.stub :default_xport_options => { :host => "dummy" }
46
+ subject.xport_options[:host] = "other"
47
+ subject.default_xport_options.should == { :host => "dummy" }
48
+ end
49
+
30
50
  end
31
51
 
32
52
  describe "#prepare" do
@@ -119,16 +139,29 @@ describe Rivendell::Import::Task do
119
139
  subject.destination.should == Rivendell::Import::Task.find(subject).destination
120
140
  end
121
141
 
142
+ def reloaded_task
143
+ subject.save
144
+ Rivendell::Import::Task.find(subject)
145
+ end
146
+
122
147
  it "should store tags separated with commas" do
123
148
  subject.tags << "tag1" << "tag2"
124
- subject.save
125
- Rivendell::Import::Task.find(subject).raw_tags.should == "tag1,tag2"
149
+ reloaded_task.raw_tags.should == "tag1,tag2"
126
150
  end
127
151
 
128
152
  it "should store cart" do
129
153
  subject.cart.number = 123
130
- subject.save
131
- Rivendell::Import::Task.find(subject).cart.number.should == 123
154
+ reloaded_task.cart.number.should == 123
155
+ end
156
+
157
+ it "should store cart" do
158
+ subject.cart.number = 123
159
+ reloaded_task.cart.number.should == 123
160
+ end
161
+
162
+ it "should store cart" do
163
+ subject.xport_options[:host] = "dummy"
164
+ reloaded_task.xport_options[:host].should == "dummy"
132
165
  end
133
166
 
134
167
  end
@@ -214,4 +247,37 @@ describe Rivendell::Import::Task do
214
247
 
215
248
  end
216
249
 
250
+ describe "#delete_file!" do
251
+
252
+ it "should set flag delete_file" do
253
+ subject.delete_file!
254
+ subject.delete_file.should be_true
255
+ end
256
+
257
+ context "defined" do
258
+
259
+ before do
260
+ subject.stub :cart => mock.as_null_object
261
+ end
262
+
263
+
264
+ it "should destroy! file when task is completed" do
265
+ subject.delete_file!
266
+ subject.file.should_receive(:destroy!)
267
+ subject.run
268
+ end
269
+
270
+ end
271
+
272
+ context "not defined" do
273
+
274
+ it "should destroy! file when task is completed" do
275
+ subject.file.should_not_receive(:destroy!)
276
+ subject.run
277
+ end
278
+
279
+ end
280
+
281
+ end
282
+
217
283
  end
@@ -4,19 +4,6 @@ describe Rivendell::Import::Tasks do
4
4
 
5
5
  let(:file) { Rivendell::Import::File.new "dummy.wav" }
6
6
 
7
- describe "#pending?" do
8
-
9
- it "should be true when a task is pending" do
10
- subject.create file
11
- subject.should be_pending
12
- end
13
-
14
- it "should be false when queue is empty?" do
15
- subject.should_not be_pending
16
- end
17
-
18
- end
19
-
20
7
  describe "#run" do
21
8
 
22
9
  it "should run each task" do
@@ -16,6 +16,7 @@ describe Rivendell::Import::Worker do
16
16
  end
17
17
 
18
18
  it "should run Import tasks in a separated Thread" do
19
+ pending "Transaction masks changes for Worker"
19
20
  import.tasks.create file
20
21
  subject.start
21
22
  sleep 0.5
@@ -3,7 +3,8 @@ require 'database_cleaner'
3
3
  RSpec.configure do |config|
4
4
 
5
5
  config.before(:suite) do
6
- DatabaseCleaner.strategy = :truncation
6
+ # DatabaseCleaner.strategy = :truncation
7
+ DatabaseCleaner.strategy = :transaction
7
8
  end
8
9
 
9
10
  config.before(:each) do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rivendell-import
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-10-04 00:00:00.000000000 Z
12
+ date: 2012-10-07 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: listen
@@ -50,7 +50,7 @@ dependencies:
50
50
  requirements:
51
51
  - - ~>
52
52
  - !ruby/object:Gem::Version
53
- version: 0.0.2
53
+ version: 0.0.3
54
54
  type: :runtime
55
55
  prerelease: false
56
56
  version_requirements: !ruby/object:Gem::Requirement
@@ -58,7 +58,7 @@ dependencies:
58
58
  requirements:
59
59
  - - ~>
60
60
  - !ruby/object:Gem::Version
61
- version: 0.0.2
61
+ version: 0.0.3
62
62
  - !ruby/object:Gem::Dependency
63
63
  name: trollop
64
64
  requirement: !ruby/object:Gem::Requirement
@@ -139,6 +139,54 @@ dependencies:
139
139
  - - ! '>='
140
140
  - !ruby/object:Gem::Version
141
141
  version: '0'
142
+ - !ruby/object:Gem::Dependency
143
+ name: SyslogLogger
144
+ requirement: !ruby/object:Gem::Requirement
145
+ none: false
146
+ requirements:
147
+ - - ~>
148
+ - !ruby/object:Gem::Version
149
+ version: '2.0'
150
+ type: :runtime
151
+ prerelease: false
152
+ version_requirements: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ~>
156
+ - !ruby/object:Gem::Version
157
+ version: '2.0'
158
+ - !ruby/object:Gem::Dependency
159
+ name: sinatra
160
+ requirement: !ruby/object:Gem::Requirement
161
+ none: false
162
+ requirements:
163
+ - - ! '>='
164
+ - !ruby/object:Gem::Version
165
+ version: '0'
166
+ type: :runtime
167
+ prerelease: false
168
+ version_requirements: !ruby/object:Gem::Requirement
169
+ none: false
170
+ requirements:
171
+ - - ! '>='
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ - !ruby/object:Gem::Dependency
175
+ name: will_paginate
176
+ requirement: !ruby/object:Gem::Requirement
177
+ none: false
178
+ requirements:
179
+ - - ~>
180
+ - !ruby/object:Gem::Version
181
+ version: 3.0.0
182
+ type: :runtime
183
+ prerelease: false
184
+ version_requirements: !ruby/object:Gem::Requirement
185
+ none: false
186
+ requirements:
187
+ - - ~>
188
+ - !ruby/object:Gem::Version
189
+ version: 3.0.0
142
190
  - !ruby/object:Gem::Dependency
143
191
  name: simplecov
144
192
  requirement: !ruby/object:Gem::Requirement
@@ -285,6 +333,8 @@ files:
285
333
  - db/migrate/20120920712200_create_tasks.rb
286
334
  - db/migrate/20120927194300_create_notifiers.rb
287
335
  - db/migrate/20120927203600_create_notifications.rb
336
+ - db/migrate/20121004194900_add_delete_file_to_task.rb
337
+ - db/migrate/2012100620361349548586_add_xport_options_to_task.rb
288
338
  - examples/.gitignore
289
339
  - examples/config.rb
290
340
  - features/manage_cart_attributes.feature
@@ -293,11 +343,13 @@ files:
293
343
  - features/support/env.rb
294
344
  - features/support/mock_xport.rb
295
345
  - lib/rivendell/import.rb
346
+ - lib/rivendell/import/application.rb
296
347
  - lib/rivendell/import/base.rb
297
348
  - lib/rivendell/import/cart.rb
298
349
  - lib/rivendell/import/carts_cache.rb
299
350
  - lib/rivendell/import/cli.rb
300
351
  - lib/rivendell/import/config.rb
352
+ - lib/rivendell/import/config_loader.rb
301
353
  - lib/rivendell/import/context.rb
302
354
  - lib/rivendell/import/cut.rb
303
355
  - lib/rivendell/import/file.rb
@@ -307,6 +359,8 @@ files:
307
359
  - lib/rivendell/import/notifier/mail-subject.erb
308
360
  - lib/rivendell/import/notifier/mail.rb
309
361
  - lib/rivendell/import/notifiers.rb
362
+ - lib/rivendell/import/static/rivendell-import.png
363
+ - lib/rivendell/import/static/screen.css
310
364
  - lib/rivendell/import/task.rb
311
365
  - lib/rivendell/import/tasking/cart.rb
312
366
  - lib/rivendell/import/tasking/destination.rb
@@ -315,6 +369,7 @@ files:
315
369
  - lib/rivendell/import/tasking/tags.rb
316
370
  - lib/rivendell/import/tasks.rb
317
371
  - lib/rivendell/import/version.rb
372
+ - lib/rivendell/import/views/index.erb
318
373
  - lib/rivendell/import/worker.rb
319
374
  - log/.gitkeep
320
375
  - rivendell-import.gemspec
@@ -356,7 +411,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
356
411
  version: '0'
357
412
  segments:
358
413
  - 0
359
- hash: -2415825681364958613
414
+ hash: 900421457418897316
360
415
  required_rubygems_version: !ruby/object:Gem::Requirement
361
416
  none: false
362
417
  requirements:
@@ -365,7 +420,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
365
420
  version: '0'
366
421
  segments:
367
422
  - 0
368
- hash: -2415825681364958613
423
+ hash: 900421457418897316
369
424
  requirements: []
370
425
  rubyforge_project:
371
426
  rubygems_version: 1.8.23