foreman-tasks 0.7.6 → 0.7.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -13
- data/app/assets/javascripts/trigger_form.js +41 -0
- data/app/controllers/foreman_tasks/recurring_logics_controller.rb +33 -0
- data/app/helpers/foreman_tasks/foreman_tasks_helper.rb +144 -0
- data/app/helpers/foreman_tasks/tasks_helper.rb +8 -0
- data/app/lib/actions/base.rb +4 -0
- data/app/lib/actions/entry_action.rb +5 -1
- data/app/lib/actions/foreman/host/import_facts.rb +4 -0
- data/app/lib/actions/middleware/inherit_task_groups.rb +39 -0
- data/app/lib/actions/middleware/recurring_logic.rb +42 -0
- data/app/models/foreman_tasks/recurring_logic.rb +136 -0
- data/app/models/foreman_tasks/task.rb +10 -0
- data/app/models/foreman_tasks/task_group.rb +16 -0
- data/app/models/foreman_tasks/task_group_member.rb +8 -0
- data/app/models/foreman_tasks/task_groups/recurring_logic_task_group.rb +16 -0
- data/app/models/foreman_tasks/triggering.rb +99 -0
- data/app/views/common/_trigger_form.html.erb +14 -0
- data/app/views/foreman_tasks/recurring_logics/_tab_related.html.erb +3 -0
- data/app/views/foreman_tasks/recurring_logics/index.html.erb +34 -0
- data/app/views/foreman_tasks/recurring_logics/show.html.erb +12 -0
- data/app/views/foreman_tasks/task_groups/_common.html.erb +12 -0
- data/app/views/foreman_tasks/task_groups/_detail.html.erb +7 -0
- data/app/views/foreman_tasks/task_groups/_tab_related.html.erb +17 -0
- data/app/views/foreman_tasks/task_groups/recurring_logic_task_groups/_recurring_logic_task_group.html.erb +39 -0
- data/config/routes.rb +6 -0
- data/db/migrate/20150814204140_add_task_type_value_index.rb +5 -0
- data/db/migrate/20150907124936_create_recurring_logic.rb +11 -0
- data/db/migrate/20150907131503_create_task_groups.rb +19 -0
- data/db/migrate/20151022123457_add_recurring_logic_state.rb +9 -0
- data/db/migrate/20151112152108_create_triggerings.rb +18 -0
- data/lib/foreman_tasks/engine.rb +12 -0
- data/lib/foreman_tasks/tasks/export_tasks.rake +2 -2
- data/lib/foreman_tasks/version.rb +1 -1
- data/test/unit/recurring_logic_test.rb +74 -0
- data/test/unit/task_groups_test.rb +76 -0
- metadata +61 -29
@@ -13,6 +13,10 @@ module ForemanTasks
|
|
13
13
|
has_many :sub_tasks, :class_name => 'ForemanTasks::Task', :foreign_key => :parent_task_id
|
14
14
|
has_many :locks
|
15
15
|
|
16
|
+
has_many :task_group_members
|
17
|
+
has_many :task_groups, :through => :task_group_members
|
18
|
+
has_many :recurring_logic_task_groups, :through => :task_group_members, :conditions => { :type => 'ForemanTasks::TaskGroups::RecurringLogicTaskGroup' }, :source => :task_group
|
19
|
+
|
16
20
|
# in fact, the task has only one owner but Rails don't let you to
|
17
21
|
# specify has_one relation though has_many relation
|
18
22
|
has_many :owners, :through => :locks, :source => :resource, :source_type => 'User',
|
@@ -30,6 +34,7 @@ module ForemanTasks
|
|
30
34
|
scoped_search :in => :owners, :on => :id, :complete_value => true, :rename => "owner.id", :ext_method => :search_by_owner
|
31
35
|
scoped_search :in => :owners, :on => :login, :complete_value => true, :rename => "owner.login", :ext_method => :search_by_owner
|
32
36
|
scoped_search :in => :owners, :on => :firstname, :complete_value => true, :rename => "owner.firstname", :ext_method => :search_by_owner
|
37
|
+
scoped_search :in => :task_groups, :on => :id, :complete_value => true, :rename => "task_group.id"
|
33
38
|
|
34
39
|
scope :active, -> { where('foreman_tasks_tasks.state != ?', :stopped) }
|
35
40
|
scope :running, -> { where("foreman_tasks_tasks.state NOT IN ('stopped', 'paused')") }
|
@@ -141,6 +146,11 @@ module ForemanTasks
|
|
141
146
|
'ForemanTasks::Task'
|
142
147
|
end
|
143
148
|
|
149
|
+
def add_missing_task_groups(groups)
|
150
|
+
groups = [groups] unless groups.is_a? Array
|
151
|
+
(groups - task_groups).each { |group| task_groups << group }
|
152
|
+
end
|
153
|
+
|
144
154
|
protected
|
145
155
|
|
146
156
|
def generate_id
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module ForemanTasks
|
2
|
+
class TaskGroup < ActiveRecord::Base
|
3
|
+
|
4
|
+
has_many :task_group_members
|
5
|
+
has_many :tasks, :through => :task_group_members
|
6
|
+
|
7
|
+
def resource_name
|
8
|
+
raise NotImplementedError
|
9
|
+
end
|
10
|
+
|
11
|
+
def resource
|
12
|
+
raise NotImplementedError
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module ForemanTasks
|
2
|
+
module TaskGroups
|
3
|
+
class RecurringLogicTaskGroup < ::ForemanTasks::TaskGroup
|
4
|
+
|
5
|
+
has_one :recurring_logic, :foreign_key => :task_group_id
|
6
|
+
|
7
|
+
alias_method :resource, :recurring_logic
|
8
|
+
|
9
|
+
def resource_name
|
10
|
+
N_('Recurring logic')
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module ForemanTasks
|
2
|
+
class Triggering < ActiveRecord::Base
|
3
|
+
|
4
|
+
attr_accessor :start_at_raw, :start_before_raw, :max_iteration, :input_type,
|
5
|
+
:cronline, :days, :days_of_week, :time, :end_time_limited,
|
6
|
+
:end_time
|
7
|
+
|
8
|
+
before_save do
|
9
|
+
if future?
|
10
|
+
parse_start_at!
|
11
|
+
parse_start_before!
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
after_find do |triggering|
|
16
|
+
triggering.mode = triggering.mode.to_sym
|
17
|
+
end
|
18
|
+
|
19
|
+
ALLOWED_MODES = [:immediate, :future, :recurring]
|
20
|
+
ALLOWED_INPUT_TYPES = [:cronline, :monthly, :weekly, :daily, :hourly]
|
21
|
+
|
22
|
+
TIME_FORMAT = "%Y-%m-%d %H:%M"
|
23
|
+
TIME_REGEXP = /\A\d{4}-\d{2}-\d{2} \d{2}:\d{2}\Z/
|
24
|
+
DAYS_REGEXP = /\A(\s*\d{1,2}\s*)(,\s*\d{1,2}\s*)*\Z/
|
25
|
+
|
26
|
+
has_one :recurring_logic, :foreign_key => :triggering_id
|
27
|
+
|
28
|
+
validates :mode, :inclusion => { :in => ALLOWED_MODES,
|
29
|
+
:message => _("%{value} is not allowed triggering mode") }
|
30
|
+
validates :input_type, :if => :recurring?,
|
31
|
+
:inclusion => { :in => ALLOWED_INPUT_TYPES,
|
32
|
+
:message => _("%{value} is not allowed input type") }
|
33
|
+
validates_format_of :start_at_raw, :with => TIME_REGEXP, :if => :future?,
|
34
|
+
:message => _("%{value} is wrong format")
|
35
|
+
validates_format_of :start_before_raw, :with => TIME_REGEXP, :if => :future?,
|
36
|
+
:message => _("%{value} is wrong format"), :allow_blank => true
|
37
|
+
validates_format_of :days, :with => DAYS_REGEXP,
|
38
|
+
:if => Proc.new { |t| t.recurring? && t.input_type == :monthly }
|
39
|
+
validate :correct_cronline, :if => Proc.new { |t| t.recurring? && t.input_type == :cronline }
|
40
|
+
|
41
|
+
def self.new_from_params(params = {})
|
42
|
+
self.new(params).tap do |targeting|
|
43
|
+
targeting.mode = params.fetch(:mode, :immediate).to_sym
|
44
|
+
targeting.input_type = params.fetch(:input_type, :cronline).to_sym
|
45
|
+
targeting.end_time_limited = params[:end_time_limited] == "true"
|
46
|
+
targeting.start_at_raw = Time.now.strftime(TIME_FORMAT)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def trigger(action, *args)
|
51
|
+
case mode
|
52
|
+
when :immediate
|
53
|
+
::ForemanTasks.async_task action, *args
|
54
|
+
when :future
|
55
|
+
::ForemanTasks.delay action,
|
56
|
+
delay_options,
|
57
|
+
*args
|
58
|
+
when :recurring
|
59
|
+
::ForemanTasks::RecurringLogic.new_from_triggering(self)
|
60
|
+
.start(action, *args)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def delay_options
|
65
|
+
{
|
66
|
+
:start_at => start_at,
|
67
|
+
:start_before => start_before
|
68
|
+
}
|
69
|
+
end
|
70
|
+
|
71
|
+
def future?
|
72
|
+
mode == :future
|
73
|
+
end
|
74
|
+
|
75
|
+
def immediate?
|
76
|
+
mode == :immediate
|
77
|
+
end
|
78
|
+
|
79
|
+
def recurring?
|
80
|
+
mode == :recurring
|
81
|
+
end
|
82
|
+
|
83
|
+
def parse_start_at!
|
84
|
+
self.start_at ||= Time.strptime(start_at_raw, TIME_FORMAT)
|
85
|
+
end
|
86
|
+
|
87
|
+
def parse_start_before!
|
88
|
+
self.start_before ||= Time.strptime(start_before_raw, TIME_FORMAT) unless start_before_raw.blank?
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
|
93
|
+
def correct_cronline
|
94
|
+
ForemanTasks::RecurringLogic.new_from_cronline(cronline).next_occurrence_time
|
95
|
+
rescue ArgumentError => _
|
96
|
+
self.errors.add(:cronline, _("#{cronline} is not valid format of cronline"))
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<div class="form-group trigger_mode">
|
2
|
+
<% javascript 'trigger_form' %>
|
3
|
+
<%= javascript_tag do %>
|
4
|
+
$(function() { trigger_form_selector_binds('<%= f.options[:html][:id] %>','<%= f.object_name %>') });
|
5
|
+
<% end %>
|
6
|
+
<%= fields_for :triggering, triggering do |trigger_fields| %>
|
7
|
+
<legend><%= _('Schedule Job') %></legend>
|
8
|
+
<%= radio_button_f trigger_fields, :mode, :class => 'trigger_mode_selector', :value => 'immediate', :text => _("Execute now") %>
|
9
|
+
<%= radio_button_f trigger_fields, :mode, :class => 'trigger_mode_selector', :value => 'future', :text => _("Schedule future execution") %>
|
10
|
+
<%= radio_button_f trigger_fields, :mode, :class => 'trigger_mode_selector', :value => 'recurring', :text => _("Set up recurring execution") %>
|
11
|
+
<%= future_mode_fieldset trigger_fields, triggering %>
|
12
|
+
<%= recurring_mode_fieldset trigger_fields, triggering %>
|
13
|
+
<% end %>
|
14
|
+
</div>
|
@@ -0,0 +1,34 @@
|
|
1
|
+
<% title _("Recurring logics") %>
|
2
|
+
<% title_actions help_path %>
|
3
|
+
|
4
|
+
<table class="table table-fixed table-bordered table-striped table-condensed">
|
5
|
+
<thead>
|
6
|
+
<th><%= N_("Cronline") %></th>
|
7
|
+
<th><%= N_("Task count") %></th>
|
8
|
+
<th><%= N_("Action") %></th>
|
9
|
+
<th><%= N_("Last occurrence") %></th>
|
10
|
+
<th><%= N_("Next occurrence") %></th>
|
11
|
+
<th><%= N_("Current iteration") %></th>
|
12
|
+
<th><%= N_("Iteration limit") %></th>
|
13
|
+
<th><%= N_("Repeat until") %></th>
|
14
|
+
<th><%= N_("State") %></th>
|
15
|
+
<th/>
|
16
|
+
</thead>
|
17
|
+
<% @recurring_logics.each do |recurring_logic| %>
|
18
|
+
<tr>
|
19
|
+
<td><%= link_to(recurring_logic.cron_line, foreman_tasks_recurring_logic_path(recurring_logic)) %></td>
|
20
|
+
<td><%= link_to(recurring_logic.tasks.count, foreman_tasks_tasks_url(:search => "task_group.id = #{recurring_logic.task_group.id}")) %></td>
|
21
|
+
<td><%= format_task_input(recurring_logic.tasks.first, true) %></td>
|
22
|
+
<td><%= recurring_logic.tasks.order(:started_at).where('started_at IS NOT NULL').last.try(:started_at) || N_("-") %></td>
|
23
|
+
<td><%= recurring_logic_next_occurrence recurring_logic %></td>
|
24
|
+
<td><%= recurring_logic.iteration %></td>
|
25
|
+
<td><%= format_recurring_logic_limit recurring_logic.max_iteration %></td>
|
26
|
+
<td><%= format_recurring_logic_limit recurring_logic.end_time.try(:in_time_zone) %></td>
|
27
|
+
<td><%= recurring_logic_state(recurring_logic) %></td>
|
28
|
+
<td><%= recurring_logic_action_buttons(recurring_logic) %></td>
|
29
|
+
</tr>
|
30
|
+
<% end %>
|
31
|
+
</table>
|
32
|
+
|
33
|
+
<%= page_entries_info @recurring_logics %>
|
34
|
+
<%= will_paginate @recurring_logics %>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<% title N_('Recurring logic') %>
|
2
|
+
|
3
|
+
<% title_actions(button_group(recurring_logic_action_buttons(@recurring_logic))) %>
|
4
|
+
|
5
|
+
<div class="tab-content col-md-6">
|
6
|
+
<legend><%= _('Details') %></legend>
|
7
|
+
<%= render @recurring_logic.task_group, :task_group => @recurring_logic.task_group %>
|
8
|
+
</div>
|
9
|
+
|
10
|
+
<div class="tab-content col-md-6">
|
11
|
+
<%= render 'tab_related', :source => @recurring_logic %>
|
12
|
+
</div>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<div>
|
2
|
+
<table class='table table-condensed'>
|
3
|
+
<tr>
|
4
|
+
<th><%= N_('ID') %></th>
|
5
|
+
<td><%= link_to(task_group.id, foreman_tasks_task_group_url(task_group)) %></td>
|
6
|
+
</tr>
|
7
|
+
<tr>
|
8
|
+
<th><%= N_('Task count') %></th>
|
9
|
+
<td><%= link_to(task_group.tasks.count, foreman_tasks_tasks_url(:search => "task_group.id = #{task_group.id}")) %></td>
|
10
|
+
</tr>
|
11
|
+
</table>
|
12
|
+
</div>
|
@@ -0,0 +1,7 @@
|
|
1
|
+
<legend><%= N_('Task group common') %></legend>
|
2
|
+
<%= render 'foreman_tasks/task_groups/common', :task_group => task_group %>
|
3
|
+
<legend><%= task_group.humanized_name %> specific</legend>
|
4
|
+
<div>
|
5
|
+
<%= begin; render task_group.to_partial_path.dup, :task_group => task_group; rescue ActionView::MissingTemplate => e; end %>
|
6
|
+
</div>
|
7
|
+
|
@@ -0,0 +1,17 @@
|
|
1
|
+
<div>
|
2
|
+
<legend><%= _("Associated resources") %></legend>
|
3
|
+
<ul>
|
4
|
+
<% source.task_groups.pluck(:type).uniq.each do |type| %>
|
5
|
+
<% next if type == source.task_group.type %>
|
6
|
+
<% groups = source.task_groups.where(:type => type) %>
|
7
|
+
<% begin %>
|
8
|
+
<%= render groups.first.to_partial_path.pluralize.dup, :source => source, :task_groups => groups %>
|
9
|
+
<% rescue ActionView::MissingTemplate => _ %>
|
10
|
+
<% groups.each do |group| %>
|
11
|
+
<%= render 'foreman_tasks/task_groups/common', :source => source, :task_group => group %>
|
12
|
+
<% end %>
|
13
|
+
<% end %>
|
14
|
+
<% end %>
|
15
|
+
</ul>
|
16
|
+
</div>
|
17
|
+
|
@@ -0,0 +1,39 @@
|
|
1
|
+
<% recurring_logic = task_group.recurring_logic -%>
|
2
|
+
<table class='table table-condensed'>
|
3
|
+
<tr>
|
4
|
+
<th>ID</th>
|
5
|
+
<td><%= link_to(recurring_logic.id, recurring_logic) %></td>
|
6
|
+
</tr>
|
7
|
+
<tr>
|
8
|
+
<th><%= N_("Cronline") %></th>
|
9
|
+
<td><%= recurring_logic.cron_line %></td>
|
10
|
+
</tr>
|
11
|
+
<tr>
|
12
|
+
<th><%= N_("Action") %></th>
|
13
|
+
<td><%= format_task_input(recurring_logic.tasks.first, true) %></td>
|
14
|
+
</tr>
|
15
|
+
<tr>
|
16
|
+
<th><%= N_("Last occurrence") %></th>
|
17
|
+
<td><%= recurring_logic.tasks.order(:started_at).where('started_at IS NOT NULL').last.try(:started_at) || N_('-') %></td>
|
18
|
+
</tr>
|
19
|
+
<tr>
|
20
|
+
<th><%= N_("Next occurrence") %></th>
|
21
|
+
<td><%= recurring_logic_next_occurrence recurring_logic %></td>
|
22
|
+
</tr>
|
23
|
+
<tr>
|
24
|
+
<th><%= N_("Current iteration") %></th>
|
25
|
+
<td><%= recurring_logic.iteration %></td>
|
26
|
+
</tr>
|
27
|
+
<tr>
|
28
|
+
<th><%= N_("Iteration limit") %></th>
|
29
|
+
<td><%= format_recurring_logic_limit recurring_logic.max_iteration %></td>
|
30
|
+
</tr>
|
31
|
+
<tr>
|
32
|
+
<th><%= N_("Repeat until") %></th>
|
33
|
+
<td><%= format_recurring_logic_limit recurring_logic.end_time.try(:in_time_zone) %></td>
|
34
|
+
</tr>
|
35
|
+
<tr>
|
36
|
+
<th><%= N_("State") %></th>
|
37
|
+
<td><%= recurring_logic_state(recurring_logic) %></td>
|
38
|
+
</tr>
|
39
|
+
</table>
|
data/config/routes.rb
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
Foreman::Application.routes.draw do
|
2
2
|
namespace :foreman_tasks do
|
3
|
+
resources :recurring_logics, :only => [:index, :show] do
|
4
|
+
member do
|
5
|
+
post :cancel
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
3
9
|
resources :tasks, :only => [:index, :show] do
|
4
10
|
collection do
|
5
11
|
get 'auto_complete_search'
|
@@ -0,0 +1,11 @@
|
|
1
|
+
class CreateRecurringLogic < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :foreman_tasks_recurring_logics do |t|
|
4
|
+
t.string :cron_line, :null => false
|
5
|
+
t.datetime :end_time
|
6
|
+
t.integer :max_iteration
|
7
|
+
t.integer :iteration, :default => 0
|
8
|
+
t.integer :task_group_id, :index => true, :null => false
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class CreateTaskGroups < ActiveRecord::Migration
|
2
|
+
def up
|
3
|
+
create_table :foreman_tasks_task_groups do |t|
|
4
|
+
t.string :type, index: true, null: false
|
5
|
+
end
|
6
|
+
|
7
|
+
create_table :foreman_tasks_task_group_members do |t|
|
8
|
+
t.string :task_id, null: false
|
9
|
+
t.integer :task_group_id, null: false
|
10
|
+
end
|
11
|
+
|
12
|
+
add_index :foreman_tasks_task_group_members, [:task_id, :task_group_id], unique: true, name: 'foreman_tasks_task_group_members_index'
|
13
|
+
end
|
14
|
+
|
15
|
+
def down
|
16
|
+
drop_table :foreman_tasks_task_groups
|
17
|
+
drop_table :foreman_tasks_task_group_members
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class CreateTriggerings < ActiveRecord::Migration
|
2
|
+
def up
|
3
|
+
create_table :foreman_tasks_triggerings do |t|
|
4
|
+
t.string :mode, null: false
|
5
|
+
t.datetime :start_at
|
6
|
+
t.datetime :start_before
|
7
|
+
end
|
8
|
+
|
9
|
+
change_table :foreman_tasks_recurring_logics do |t|
|
10
|
+
t.integer :triggering_id
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def down
|
15
|
+
drop_table :foreman_tasks_triggerings
|
16
|
+
remove_column :foreman_tasks_recurring_logics, :triggering_id
|
17
|
+
end
|
18
|
+
end
|
data/lib/foreman_tasks/engine.rb
CHANGED
@@ -15,11 +15,23 @@ module ForemanTasks
|
|
15
15
|
:caption => N_('Tasks'),
|
16
16
|
:parent => :monitor_menu
|
17
17
|
|
18
|
+
menu :top_menu, :recurring_logics, :after => :tasks,
|
19
|
+
:url_hash => { :controller => 'foreman_tasks/recurring_logics', :action => :index },
|
20
|
+
:caption => N_('Recurring logics'),
|
21
|
+
:parent => :monitor_menu
|
22
|
+
|
18
23
|
security_block :foreman_tasks do |map|
|
19
24
|
permission :view_foreman_tasks, {:'foreman_tasks/tasks' => [:auto_complete_search, :sub_tasks, :index, :show],
|
20
25
|
:'foreman_tasks/api/tasks' => [:bulk_search, :show, :index, :summary] }, :resource_type => ForemanTasks::Task.name
|
21
26
|
permission :edit_foreman_tasks, {:'foreman_tasks/tasks' => [:resume, :unlock, :force_unlock, :cancel_step, :cancel],
|
22
27
|
:'foreman_tasks/api/tasks' => [:bulk_resume]}, :resource_type => ForemanTasks::Task.name
|
28
|
+
|
29
|
+
permission :create_recurring_logics, { }, :resource_type => ForemanTasks::RecurringLogic
|
30
|
+
|
31
|
+
permission :view_recurring_logics, { :'foreman_tasks/recurring_logics' => [:index, :show] }, :resource_type => ForemanTasks::RecurringLogic
|
32
|
+
|
33
|
+
permission :edit_recurring_logics, { :'foreman_tasks/recurring_logics' => [:cancel] }, :resource_type => ForemanTasks::RecurringLogic
|
34
|
+
|
23
35
|
end
|
24
36
|
|
25
37
|
logger :dynflow, :enabled => true
|
@@ -27,7 +27,7 @@ namespace :foreman_tasks do
|
|
27
27
|
|
28
28
|
def render_task(task)
|
29
29
|
@plan = task.execution_plan
|
30
|
-
erb('show'
|
30
|
+
erb('show')
|
31
31
|
end
|
32
32
|
|
33
33
|
def world
|
@@ -38,7 +38,7 @@ namespace :foreman_tasks do
|
|
38
38
|
File.join(Gem::Specification.find_by_name("dynflow").gem_dir, 'web', 'views', "#{filename}.erb")
|
39
39
|
end
|
40
40
|
|
41
|
-
def erb(file, options)
|
41
|
+
def erb(file, options = {})
|
42
42
|
unless @cache[file]
|
43
43
|
@cache[file] = Tilt.new(template(file))
|
44
44
|
end
|