rivendell-import 0.0.1 → 0.0.2

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