ratch 0.4.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. data/COPYING +17 -669
  2. data/HISTORY +6 -0
  3. data/MANIFEST +36 -0
  4. data/METADATA +14 -0
  5. data/NEWS +7 -0
  6. data/README +67 -17
  7. data/bin/ratch +5 -78
  8. data/demo/tryme-task.ratch +12 -0
  9. data/demo/tryme1.ratch +6 -0
  10. data/lib/ratch/core_ext.rb +6 -0
  11. data/lib/ratch/core_ext/facets.rb +1 -0
  12. data/lib/ratch/core_ext/filetest.rb +52 -0
  13. data/lib/ratch/core_ext/object.rb +8 -0
  14. data/lib/ratch/core_ext/pathname.rb +38 -0
  15. data/lib/ratch/core_ext/string.rb +44 -0
  16. data/lib/ratch/{dsl/console.rb → core_ext/to_console.rb} +2 -76
  17. data/lib/ratch/core_ext/to_list.rb +29 -0
  18. data/lib/ratch/dsl.rb +494 -49
  19. data/lib/ratch/index.rb +4 -0
  20. data/lib/ratch/io.rb +116 -0
  21. data/lib/ratch/pathglob.rb +73 -0
  22. data/lib/ratch/plugin.rb +55 -0
  23. data/lib/ratch/runmode.rb +69 -0
  24. data/lib/ratch/script.rb +52 -0
  25. data/lib/ratch/service.rb +33 -0
  26. data/lib/ratch/task.rb +249 -0
  27. data/lib/ratch/task2.rb +298 -0
  28. data/test/README +1 -0
  29. data/test/test_helper.rb +4 -0
  30. data/test/test_task.rb +46 -0
  31. metadata +90 -150
  32. data/CHANGES +0 -22
  33. data/TODO +0 -2
  34. data/bin/lt +0 -56
  35. data/bin/ludo +0 -14
  36. data/bin/manifest +0 -451
  37. data/bin/ratch-find +0 -21
  38. data/demo/WILMA +0 -1
  39. data/demo/XR +0 -9
  40. data/demo/lib/foo/foo.rb +0 -7
  41. data/demo/p.rb +0 -9
  42. data/demo/r.rb +0 -6
  43. data/demo/t.rb +0 -3
  44. data/demo/task/config.yaml +0 -4
  45. data/demo/task/one +0 -6
  46. data/demo/task/simplebuild +0 -15
  47. data/demo/task/stats +0 -4
  48. data/demo/task/task +0 -6
  49. data/demo/task/tryme +0 -10
  50. data/lib/ratch/dsl/argv.rb +0 -112
  51. data/lib/ratch/dsl/batch.rb +0 -232
  52. data/lib/ratch/dsl/build.rb +0 -174
  53. data/lib/ratch/dsl/email.rb +0 -108
  54. data/lib/ratch/dsl/file.rb +0 -205
  55. data/lib/ratch/dsl/meta.rb +0 -125
  56. data/lib/ratch/dsl/options.rb +0 -98
  57. data/lib/ratch/dsl/setup.rb +0 -124
  58. data/lib/ratch/dsl/sign.rb +0 -243
  59. data/lib/ratch/dsl/stage.rb +0 -147
  60. data/lib/ratch/dsl/task.rb +0 -139
  61. data/lib/ratch/dsl/upload.rb +0 -436
  62. data/lib/ratch/dsl/zip.rb +0 -59
  63. data/lib/ratch/extra/email.rb +0 -5
  64. data/lib/ratch/extra/stage.rb +0 -5
  65. data/lib/ratch/extra/zip.rb +0 -5
  66. data/lib/ratch/manager.rb +0 -53
  67. data/lib/ratch/manifest.rb +0 -540
  68. data/lib/ratch/metadata/information.rb +0 -258
  69. data/lib/ratch/metadata/package.rb +0 -108
  70. data/lib/ratch/metadata/project.rb +0 -523
  71. data/lib/ratch/metadata/release.rb +0 -108
  72. data/lib/ratch/support/errors.rb +0 -4
  73. data/lib/ratch/support/filename.rb +0 -18
  74. data/lib/ratch/support/filetest.rb +0 -29
  75. data/lib/ratch/toolset/ruby/announce +0 -224
  76. data/lib/ratch/toolset/ruby/compile +0 -49
  77. data/lib/ratch/toolset/ruby/install +0 -77
  78. data/lib/ratch/toolset/ruby/notes +0 -185
  79. data/lib/ratch/toolset/ruby/pack/gem +0 -93
  80. data/lib/ratch/toolset/ruby/pack/tgz +0 -46
  81. data/lib/ratch/toolset/ruby/pack/zip +0 -46
  82. data/lib/ratch/toolset/ruby/publish +0 -57
  83. data/lib/ratch/toolset/ruby/release +0 -8
  84. data/lib/ratch/toolset/ruby/setup +0 -1616
  85. data/lib/ratch/toolset/ruby/stamp +0 -33
  86. data/lib/ratch/toolset/ruby/stats +0 -138
  87. data/lib/ratch/toolset/ruby/test/crosstest +0 -305
  88. data/lib/ratch/toolset/ruby/test/extest +0 -129
  89. data/lib/ratch/toolset/ruby/test/isotest +0 -293
  90. data/lib/ratch/toolset/ruby/test/load +0 -39
  91. data/lib/ratch/toolset/ruby/test/loadtest +0 -28
  92. data/lib/ratch/toolset/ruby/test/syntax +0 -29
  93. data/lib/ratch/toolset/ruby/test/test +0 -26
  94. data/lib/ratch/toolset/sandbox/query +0 -11
  95. data/man/ratch.man +0 -73
  96. data/meta/MANIFEST +0 -130
  97. data/meta/config.yaml +0 -9
  98. data/meta/icli.yaml +0 -16
  99. data/meta/project.yaml +0 -20
  100. data/meta/ratch.roll +0 -2
  101. data/meta/xProjectInfo +0 -41
  102. data/task/clobber/package +0 -10
  103. data/task/man +0 -14
  104. data/task/publish +0 -57
  105. data/task/release +0 -9
  106. data/task/setup +0 -1616
  107. data/task/stats +0 -138
@@ -0,0 +1,4 @@
1
+ module Ratch
2
+ VERSION = "1.0.0"
3
+ end
4
+
@@ -0,0 +1,116 @@
1
+ require 'facets/consoleutils'
2
+
3
+ module Ratch
4
+
5
+ # = Ratch IO
6
+ #
7
+ # The IO class is used to cleanly separate out the
8
+ # basic input/output "dialog" between user and script.
9
+ #
10
+ class IO
11
+
12
+ #
13
+ attr :runmode
14
+
15
+ #
16
+ def initialize(runmode)
17
+ @runmode = runmode
18
+ end
19
+
20
+ def force? ; runmode.force? ; end
21
+ def quiet? ; runmode.quiet? ; end
22
+ def trace? ; runmode.trace? ; end
23
+ def debug? ; runmode.debug? ; end
24
+ def dryrun? ; runmode.dryrun? ; end
25
+ def noharm? ; runmode.noharm? ; end
26
+
27
+ # Internal status report.
28
+ #
29
+ # Only output if dryrun or trace mode.
30
+ #
31
+ def status(message)
32
+ if runmode.dryrun? or runmode.trace?
33
+ puts message
34
+ end
35
+ end
36
+
37
+ # Convenient method to get simple console reply.
38
+ #
39
+ def ask(question, answers=nil)
40
+ print "#{question}"
41
+ print " [#{answers}] " if answers
42
+ until inp = $stdin.gets ; sleep 1 ; end
43
+ inp.strip
44
+ end
45
+
46
+ # Ask for a password. (FIXME: only for unix so far)
47
+ #
48
+ def password(prompt=nil)
49
+ msg ||= "Enter Password: "
50
+ inp = ''
51
+ print "#{prompt} "
52
+ begin
53
+ #system "stty -echo"
54
+ #inp = gets.chomp
55
+ until inp = $stdin.gets
56
+ sleep 1
57
+ end
58
+ ensure
59
+ #system "stty echo"
60
+ end
61
+ return inp.chomp
62
+ end
63
+
64
+ def print(str)
65
+ super(str) unless quiet?
66
+ end
67
+
68
+ def puts(str)
69
+ super(str) unless quiet?
70
+ end
71
+
72
+ #
73
+ #
74
+ def printline(left, right='', options={})
75
+ return if runmode.quiet?
76
+
77
+ separator = options[:seperator] || options[:sep] || ' '
78
+ padding = options[:padding] || options[:pad] || 0
79
+
80
+ left, right = left.to_s, right.to_s
81
+
82
+ left_size = left.size
83
+ right_size = right.size
84
+
85
+ left = colorize(left)
86
+ right = colorize(right)
87
+
88
+ l = padding
89
+ r = -(right_size + padding + 1)
90
+
91
+ line = separator * screen_width
92
+ line[l, left_size] = left if left_size != 0
93
+ line[r, right_size] = right if right_size != 0
94
+
95
+ puts line
96
+ end
97
+
98
+ #
99
+ def colorize(text)
100
+ return text unless text.color
101
+ if PLATFORM =~ /win/
102
+ text.to_s
103
+ else
104
+ ANSICode.send(text.color){ text.to_s }
105
+ end
106
+ end
107
+
108
+ #
109
+ def screen_width
110
+ ConsoleUtils.screen_width
111
+ end
112
+
113
+ end
114
+
115
+ end
116
+
@@ -0,0 +1,73 @@
1
+ # NOT USED. Expiremental.
2
+
3
+ require 'pathname'
4
+
5
+ =begin
6
+ class Pathname
7
+
8
+ def glob(*options)
9
+ opts = 0
10
+ options.each do |option|
11
+ case option
12
+ when :nocase
13
+ opts += File::FNM_CASEFOLD
14
+ else
15
+ opts += option
16
+ end
17
+ end
18
+
19
+ self.class.glob(to_s, opts).collect{ |f| Pathname.new(f) }
20
+ end
21
+
22
+ end
23
+ =end
24
+
25
+ # How does this differ from facets/filelist?
26
+
27
+ class PathGlob #:nodoc:
28
+
29
+ #
30
+ attr :patterns
31
+ attr :options
32
+
33
+ def <<(pattern)
34
+ patterns << pattern
35
+ end
36
+
37
+ #
38
+ def match?(path)
39
+ patterns.find{ |pattern| File.fnmatch?(pattern, path) }
40
+ end
41
+
42
+ #
43
+ def match(*options)
44
+ opts = 0
45
+ options.each do
46
+ case options
47
+ when :nocase
48
+ opts += File::FNM_CASEFOLD
49
+ end
50
+ end
51
+ patterns.collect{ |pattern| File.glob(pattern, opts) }.flatten
52
+ end
53
+
54
+ def file?(*options)
55
+ match(options).all?{ |f| File.file?(f) }
56
+ end
57
+
58
+ def directory?(*options)
59
+ match(options).all?{ |f| File.directory?(f) }
60
+ end
61
+
62
+ private
63
+
64
+ def initialize(*patterns)
65
+ @patterns = patterns
66
+ end
67
+
68
+ def self.[](*patterns)
69
+ new(*patterns)
70
+ end
71
+
72
+ end
73
+
@@ -0,0 +1,55 @@
1
+ module Ratch
2
+
3
+ # = PLugin
4
+ #
5
+ # A Plugin is essentially a delegated Service class..
6
+ #
7
+ # The plugin acts a base class for ecapsulating batch routines.
8
+ # This helps to keep the main batch context free of the clutter
9
+ # of private supporting methods.
10
+ #
11
+ # Plugins are tightly coupled to the batch context,
12
+ # which allows them to call on the context easily.
13
+ # However this means plugins cannot be used independent
14
+ # of a batch context, and changes in the batch context
15
+ # can cause effects in plujgin behvior that can be harder
16
+ # to track down and fix if a bug arises.
17
+ #
18
+ # Unless the tight coupling of a plugin is required, use the
19
+ # loose coupling of a Service class instead.
20
+
21
+ class Plugin
22
+
23
+ # The batch context.
24
+ attr :context
25
+
26
+ alias_method :project, :context
27
+
28
+ private
29
+
30
+ #
31
+ def initialize(context, options=nil)
32
+ @context = context
33
+
34
+ raise TypeError, "context must be a subclass of Ratch::DSL" unless context.is_a?(Ratch::DSL)
35
+
36
+ initialize_defaults
37
+
38
+ options ||= {}
39
+ options.each do |k, v|
40
+ send("#{k}=", v) if respond_to?("#{k}=")
41
+ end
42
+ end
43
+
44
+ def initialize_defaults
45
+ end
46
+
47
+ # TODO: This should be optional? How?
48
+ def method_missing(s, *a, &b)
49
+ @context.send(s, *a, &b)
50
+ end
51
+
52
+ end
53
+
54
+ end
55
+
@@ -0,0 +1,69 @@
1
+ module Ratch
2
+
3
+ # = Runmode
4
+ #
5
+ # The Runmode class encapsulates common options for command line scripts.
6
+ # The built-in modes are:
7
+ #
8
+ # force
9
+ # trace
10
+ # debug
11
+ # dryrun -or- noharm
12
+ # silent -or- quiet
13
+ # verbose
14
+ #
15
+ class Runmode
16
+
17
+ def self.load_argv!
18
+ options = {
19
+ :force => %w{--force}.any?{ |x| ARGV.delete(x) },
20
+ :trace => %w{--trace}.any?{ |x| ARGV.delete(x) },
21
+ :debug => %w{--debug}.any?{ |x| ARGV.delete(x) },
22
+ :dryrun => %w{--noharm --dryrun --dry-run}.any?{ |x| ARGV.delete(x) },
23
+ :silent => %w{--silent --quiet}.any?{ |x| ARGV.delete(x) },
24
+ :verbose => %w{--verbose}.any?{ |x| ARGV.delete(x) }
25
+ }
26
+ new(options)
27
+ end
28
+
29
+ def initialize(options={})
30
+ options.rekey(&:to_sym)
31
+
32
+ @force = options[:force]
33
+ @trace = options[:trace]
34
+ @debug = options[:debug]
35
+ @noharm = options[:noharm] || options[:dryrun]
36
+ @silent = options[:silent] || options[:quiet]
37
+ @verbose = options[:verbose]
38
+ end
39
+
40
+ attr_accessor :force
41
+
42
+ attr_accessor :trace
43
+
44
+ attr_accessor :verbose
45
+
46
+ attr_accessor :silent
47
+
48
+ attr_accessor :debug
49
+
50
+ attr_accessor :noharm
51
+
52
+ alias_method :dryrun, :noharm
53
+ alias_method :dryrun=,:noharm
54
+
55
+ alias_method :quiet, :silent
56
+ alias_method :quiet=, :silent=
57
+
58
+ def force? ; @force ; end
59
+ def trace? ; @trace ; end
60
+ def debug? ; @debug ; end
61
+ def noharm? ; @noharm ; end
62
+ def dryrun? ; @noharm ; end
63
+ def quiet? ; @silent ; end
64
+ def verbose? ; @verbose ; end
65
+
66
+ end
67
+
68
+ end
69
+
@@ -0,0 +1,52 @@
1
+ require 'ratch/dsl'
2
+ #require 'annotatable'
3
+
4
+ module Ratch
5
+
6
+ # = Ratch Script
7
+ #
8
+ # The Ratch Script class is used to run stand-alone ratch scripts.
9
+ # Yep, this is actaully a class named exactly for what it is.
10
+ # How rare.
11
+ #
12
+ class Script < DSL
13
+ #include Annotatable
14
+
15
+ #
16
+ #annotation :cmd
17
+
18
+ #
19
+ #annotation :opt
20
+
21
+ #
22
+ #def commands
23
+ # c = {}
24
+ # self.class.annotations.each do |n, a|
25
+ # if a.key?(:cmd)
26
+ # c[n] = a[:cmd]
27
+ # end
28
+ # end
29
+ # c
30
+ #end
31
+
32
+ #
33
+ #def initialize
34
+ # @noharm = %w{-n --noharm --dryrun --dry-run}.any?{ |a| ARGV.delete(a) }
35
+ # @verbose = %w{--verbose}.any?{|a| ARGV.delete(a) }
36
+ # @quiet = %w{--quiet}.any?{|a| ARGV.delete(a) }
37
+ # @force = %w{--force}.any?{|a| ARGV.delete(a) }
38
+ # @debug = %w{--debug}.any?{|a| ARGV.delete(a) }
39
+ # @trace = %w{--trace}.any?{|a| ARGV.delete(a) }
40
+ #
41
+ # super
42
+ #end
43
+
44
+ # @noharm ||= %w{noharm n dryrun dry-run}.any?{|a| commandline.options[a] }
45
+ # @verbose ||= %w{verbose}.any?{|a| commandline.options[a] }
46
+ # @force ||= %w{force}.any?{|a| commandline.options[a] }
47
+ # @debug ||= %w{debug}.any?{|a| commandline.options[a] }
48
+ # @trace ||= %w{trace}.any?{|a| commandline.options[a] }
49
+
50
+ end
51
+
52
+ end
@@ -0,0 +1,33 @@
1
+ require 'ratch/dsl'
2
+
3
+ module Ratch
4
+
5
+ # = Service
6
+ #
7
+ # In particular this means creating module for RunModes and FileUtils
8
+ # which uses it, as these are the primary couplings between the batch
9
+ # context and the services that are shared by all.
10
+
11
+ class Service < DSL
12
+
13
+ private
14
+
15
+ #
16
+ def initialize(options=nil)
17
+ options ||= {}
18
+
19
+ initialize_defaults
20
+
21
+ options.each do |k, v|
22
+ send("#{k}=", v) if respond_to?("#{k}=")
23
+ end
24
+ end
25
+
26
+ #
27
+ def initialize_defaults
28
+ end
29
+
30
+ end
31
+
32
+ end
33
+
@@ -0,0 +1,249 @@
1
+
2
+ module Taskable
3
+
4
+ def self.included(base)
5
+ base.extend(Dsl)
6
+ end
7
+
8
+ # Run a task. Better name?
9
+ def run(target)
10
+ #t = self.class.tasks(target)
11
+ #t.run(self)
12
+ send("#{target}:target")
13
+ end
14
+
15
+ module Dsl
16
+ # Without an argument, returns list of tasks defined for this class.
17
+ #
18
+ # If a task's target name is given, will return the first
19
+ # task mathing the name found in the class' inheritance chain.
20
+ # This is important ot ensure task are inherited in the same manner
21
+ # that methods are.
22
+ def tasks(target=nil)
23
+ if target
24
+ target = target.to_sym
25
+ anc = ancestors.select{|a| a < Taskable}
26
+ t = nil; anc.find{|a| t = a.tasks[target]}
27
+ return t
28
+ else
29
+ @tasks ||= {}
30
+ end
31
+ end
32
+
33
+ # Set a description to be used by then next defined task in this class.
34
+ def desc(description)
35
+ @desc = description
36
+ end
37
+
38
+ # Define a task.
39
+ def task(target_and_requisite, &function)
40
+ target, requisite, function = *Task.parse_arguments(target_and_requisite, &function)
41
+ task = tasks[target.to_sym] ||= (
42
+ tdesc = @desc
43
+ @desc = nil
44
+ Task.new(self, target, tdesc) #, reqs, actions)
45
+ )
46
+ task.update(requisite, &function)
47
+ define_method("#{target}:target"){ task.run(self) } # or use #run?
48
+ define_method("#{target}:task", &function) # TODO: in 1.9 use instance_exec instead.
49
+ end
50
+ end
51
+
52
+ # = Task Class
53
+ #
54
+ class Task
55
+ attr :base
56
+ attr :target
57
+ attr :requisite
58
+ attr :function
59
+ attr :description
60
+
61
+ def initialize(base, target, description=nil, requisite=nil, &function)
62
+ @base = base
63
+ @target = target.to_sym
64
+ @description = description
65
+ @requisite = requisite || []
66
+ @function = function
67
+ end
68
+
69
+ #
70
+ def update(requisite, &function)
71
+ @requisite.concat(requisite).uniq!
72
+ @function = function if function
73
+ end
74
+
75
+ #
76
+ def prerequisite
77
+ base.ancestors.select{|a| a < Taskable}.collect{ |a|
78
+ a.tasks[target].requisite
79
+ }.flatten.uniq
80
+ end
81
+
82
+ # invoke target
83
+ def run(object)
84
+ rd = rule_dag
85
+ rd.each do |t|
86
+ object.send("#{t}:task")
87
+ end
88
+ end
89
+
90
+ #
91
+ #def call(object)
92
+ # object.instance_eval(&function)
93
+ #end
94
+
95
+ # Collect task dependencies for running.
96
+ def rule_dag(cache=[])
97
+ prerequisite.each do |r|
98
+ next if cache.include?(r)
99
+ t = base.tasks[r]
100
+ t.rule_dag(cache)
101
+ #cache << dep
102
+ end
103
+ cache << target.to_s
104
+ cache
105
+ end
106
+
107
+ #
108
+ def self.parse_arguments(name_and_reqs, &action)
109
+ if Hash===name_and_reqs
110
+ target = name_and_reqs.keys.first.to_s
111
+ reqs = [name_and_reqs.values.first].flatten
112
+ else
113
+ target = name_and_reqs.to_s
114
+ reqs = []
115
+ end
116
+ return target, reqs, action
117
+ end
118
+ end
119
+
120
+ # = File Task Class
121
+ #
122
+ class FileTask < Task
123
+
124
+ def needed?
125
+ if prerequisite.empty?
126
+ dated = true
127
+ elsif File.exist?(target)
128
+ mtime = File.mtime(target)
129
+ dated = prerequisite.find do |file|
130
+ !File.exist?(file) || File.mtime(file) > mtime
131
+ end
132
+ else
133
+ dated = true
134
+ end
135
+ return dated
136
+ end
137
+
138
+ #
139
+ def call(object)
140
+ object.instance_eval(&function) if needed?
141
+ end
142
+ end
143
+
144
+ end
145
+
146
+
147
+ =begin
148
+ # turn yaml file into tasks
149
+ def parse(file)
150
+ script = YAML.load(File.new(file.to_s))
151
+
152
+ imports = script.delete('import') || []
153
+ #plugins = script.delete('plugin') || []
154
+ srvs = script.delete('services') || {}
155
+ tgts = script.delete('targets') || {}
156
+
157
+ imports.each do |import|
158
+ path = Reap::Domain::LIB_DIRECTORY + 'systems' + (import + '.reap').to_s
159
+ parse(path)
160
+ end
161
+
162
+ srvs.each do |label, options|
163
+ type = options.delete('type')
164
+ @services[label] = domain.send("#{type}_service") # FIXME
165
+ end
166
+
167
+ tgts.each do |target, options|
168
+ @targets[target] = Task.new(self, target, options)
169
+ end
170
+ end
171
+ =end
172
+
173
+
174
+
175
+
176
+
177
+
178
+ =begin
179
+ # Collect task dependencies for running.
180
+ def self.rule_dag(target, cache=[])
181
+ t = tasks[target.to_sym]
182
+ d = t.prerequisite
183
+ d.each do |r|
184
+ next if cache.include?(r)
185
+ rule_dag(r, cache)
186
+ #cache << dep
187
+ end
188
+
189
+ # file requirements
190
+ #q = self.class.ann(name, :reqs) || []
191
+ #q.each do |req|
192
+ # path = Pathname.new(req)
193
+ # next if r.include?(path)
194
+ # mat = annotations.select{ |n, a| File.fnmatch?(a[:file].first, req) if a[:file] }.compact
195
+ # mat.each do |n, a|
196
+ # rule_dag(n, r)
197
+ # end
198
+ # r << path
199
+ #end
200
+
201
+ cache << target.to_s
202
+ return cache
203
+ end
204
+
205
+ # invoke target
206
+ def run(target)
207
+ target = target.to_sym
208
+ rd = self.class.rule_dag(target)
209
+ rd.each do |t|
210
+ send("#{t}:task")
211
+ end
212
+ end
213
+ =end
214
+
215
+ =begin
216
+ if target == name.to_s
217
+ tasks[target].call
218
+ else
219
+ case action
220
+ when Pathname
221
+ raise unless action.exist?
222
+ else
223
+ run_rec(action)
224
+ end
225
+ end
226
+ end
227
+ end
228
+
229
+ def run_rec(action)
230
+ # creates a file?
231
+ dated = true
232
+ if creates = self.class.ann(action, :file)
233
+ if self.class.ann(name, :reqs).empty?
234
+ dated = true
235
+ elsif File.exist?(creates)
236
+ mtime = File.mtime(creates)
237
+ dated = self.class.ann(name, :reqs).find do |file|
238
+ !File.exist?(file) || File.mtime(file) > mtime
239
+ end
240
+ else
241
+ dated = true
242
+ end
243
+ end
244
+ return unless dated
245
+ send(action)
246
+ end
247
+ =end
248
+
249
+