amp-front 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +24 -0
- data/Ampfile +3 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +36 -0
- data/LICENSE +20 -0
- data/README.md +50 -0
- data/Rakefile +64 -0
- data/VERSION +1 -0
- data/design_docs/commands.md +91 -0
- data/design_docs/dependencies.md +35 -0
- data/design_docs/plugins.md +47 -0
- data/features/amp.feature +8 -0
- data/features/amp_help.feature +36 -0
- data/features/amp_plugin_list.feature +10 -0
- data/features/step_definitions/amp-front_steps.rb +23 -0
- data/features/support/env.rb +4 -0
- data/lib/amp-front.rb +30 -0
- data/lib/amp-front/dispatch/commands/base.rb +158 -0
- data/lib/amp-front/dispatch/commands/builtin/help.rb +23 -0
- data/lib/amp-front/dispatch/commands/builtin/plugin.rb +24 -0
- data/lib/amp-front/dispatch/commands/validations.rb +171 -0
- data/lib/amp-front/dispatch/runner.rb +86 -0
- data/lib/amp-front/help/entries/__default__.erb +31 -0
- data/lib/amp-front/help/entries/ampfiles.md +42 -0
- data/lib/amp-front/help/entries/commands.erb +6 -0
- data/lib/amp-front/help/entries/new-commands.md +81 -0
- data/lib/amp-front/help/help.rb +312 -0
- data/lib/amp-front/plugins/base.rb +87 -0
- data/lib/amp-front/support/module_extensions.rb +92 -0
- data/lib/amp-front/third_party/maruku.rb +136 -0
- data/lib/amp-front/third_party/maruku/attributes.rb +227 -0
- data/lib/amp-front/third_party/maruku/defaults.rb +71 -0
- data/lib/amp-front/third_party/maruku/errors_management.rb +92 -0
- data/lib/amp-front/third_party/maruku/helpers.rb +260 -0
- data/lib/amp-front/third_party/maruku/input/charsource.rb +326 -0
- data/lib/amp-front/third_party/maruku/input/extensions.rb +69 -0
- data/lib/amp-front/third_party/maruku/input/html_helper.rb +189 -0
- data/lib/amp-front/third_party/maruku/input/linesource.rb +111 -0
- data/lib/amp-front/third_party/maruku/input/parse_block.rb +615 -0
- data/lib/amp-front/third_party/maruku/input/parse_doc.rb +234 -0
- data/lib/amp-front/third_party/maruku/input/parse_span_better.rb +746 -0
- data/lib/amp-front/third_party/maruku/input/rubypants.rb +225 -0
- data/lib/amp-front/third_party/maruku/input/type_detection.rb +147 -0
- data/lib/amp-front/third_party/maruku/input_textile2/t2_parser.rb +163 -0
- data/lib/amp-front/third_party/maruku/maruku.rb +33 -0
- data/lib/amp-front/third_party/maruku/output/to_ansi.rb +223 -0
- data/lib/amp-front/third_party/maruku/output/to_html.rb +991 -0
- data/lib/amp-front/third_party/maruku/output/to_markdown.rb +164 -0
- data/lib/amp-front/third_party/maruku/output/to_s.rb +56 -0
- data/lib/amp-front/third_party/maruku/string_utils.rb +191 -0
- data/lib/amp-front/third_party/maruku/structures.rb +167 -0
- data/lib/amp-front/third_party/maruku/structures_inspect.rb +87 -0
- data/lib/amp-front/third_party/maruku/structures_iterators.rb +61 -0
- data/lib/amp-front/third_party/maruku/textile2.rb +1 -0
- data/lib/amp-front/third_party/maruku/toc.rb +199 -0
- data/lib/amp-front/third_party/maruku/usage/example1.rb +33 -0
- data/lib/amp-front/third_party/maruku/version.rb +40 -0
- data/lib/amp-front/third_party/trollop.rb +766 -0
- data/spec/amp-front_spec.rb +25 -0
- data/spec/command_specs/base_spec.rb +123 -0
- data/spec/command_specs/command_spec.rb +97 -0
- data/spec/command_specs/help_spec.rb +33 -0
- data/spec/command_specs/spec_helper.rb +37 -0
- data/spec/command_specs/validations_spec.rb +267 -0
- data/spec/dispatch_specs/runner_spec.rb +116 -0
- data/spec/dispatch_specs/spec_helper.rb +15 -0
- data/spec/help_specs/help_entry_spec.rb +78 -0
- data/spec/help_specs/help_registry_spec.rb +77 -0
- data/spec/help_specs/spec_helper.rb +15 -0
- data/spec/plugin_specs/base_spec.rb +36 -0
- data/spec/plugin_specs/spec_helper.rb +15 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +33 -0
- data/spec/support_specs/module_extensions_spec.rb +104 -0
- data/spec/support_specs/spec_helper.rb +15 -0
- data/test/third_party_tests/test_trollop.rb +1181 -0
- metadata +192 -0
@@ -0,0 +1,10 @@
|
|
1
|
+
Feature: Plugin List Command
|
2
|
+
In order to check my plugins
|
3
|
+
A user can use the "plugin list" command
|
4
|
+
to inspect the Amp runtime environment
|
5
|
+
|
6
|
+
Scenario: Running plugin list with no parameters
|
7
|
+
Given the argument plugin
|
8
|
+
And the argument list
|
9
|
+
When I run dispatch
|
10
|
+
Then I should see "Amp::Plugins::Base"
|
@@ -0,0 +1,23 @@
|
|
1
|
+
Given /the argument (.*)/ do |arg|
|
2
|
+
@arguments ||= []
|
3
|
+
@arguments << arg
|
4
|
+
end
|
5
|
+
|
6
|
+
When /I run dispatch/ do
|
7
|
+
dispatch = Amp::Dispatch::Runner.new(@arguments || [])
|
8
|
+
@result = swizzling_stdout { dispatch.run! }
|
9
|
+
end
|
10
|
+
|
11
|
+
Then /I should see "(.*)"/ do |arg|
|
12
|
+
@result.should =~ /#{arg}/
|
13
|
+
end
|
14
|
+
|
15
|
+
def swizzling_stdout
|
16
|
+
new_stdout = StringIO.new
|
17
|
+
$stdout, old_stdout = new_stdout, $stdout
|
18
|
+
yield
|
19
|
+
new_stdout.string
|
20
|
+
ensure
|
21
|
+
$stdout = old_stdout
|
22
|
+
new_stdout.string
|
23
|
+
end
|
data/lib/amp-front.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
module Amp
|
2
|
+
VERSION = '0.0.1'
|
3
|
+
VERSION_TITLE = 'Koyaanisqatsi'
|
4
|
+
|
5
|
+
$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__)))
|
6
|
+
|
7
|
+
module Dispatch
|
8
|
+
autoload :Runner, 'amp-front/dispatch/runner.rb'
|
9
|
+
end
|
10
|
+
|
11
|
+
module Help
|
12
|
+
autoload :HelpUI, 'amp-front/help/help.rb'
|
13
|
+
autoload :HelpEntry, 'amp-front/help/help.rb'
|
14
|
+
autoload :HelpRegistry, 'amp-front/help/help.rb'
|
15
|
+
autoload :CommandHelpEntry, 'amp-front/help/help.rb'
|
16
|
+
autoload :FileHelpEntry, 'amp-front/help/help.rb'
|
17
|
+
end
|
18
|
+
|
19
|
+
module Plugins
|
20
|
+
autoload :Base, 'amp-front/plugins/base.rb'
|
21
|
+
autoload :Registry, 'amp-front/plugins/registry.rb'
|
22
|
+
end
|
23
|
+
|
24
|
+
autoload :Command, 'amp-front/dispatch/commands/base.rb'
|
25
|
+
autoload :ModuleExtensions, 'amp-front/support/module_extensions.rb'
|
26
|
+
end
|
27
|
+
|
28
|
+
autoload :ERB, 'erb'
|
29
|
+
autoload :Maruku, 'amp-front/third_party/maruku.rb'
|
30
|
+
autoload :Trollop, 'amp-front/third_party/trollop.rb'
|
@@ -0,0 +1,158 @@
|
|
1
|
+
##################################################################
|
2
|
+
# Licensing Information #
|
3
|
+
# #
|
4
|
+
# The following code is licensed, as standalone code, under #
|
5
|
+
# the Ruby License, unless otherwise directed within the code. #
|
6
|
+
# #
|
7
|
+
# For information on the license of this code when distributed #
|
8
|
+
# with and used in conjunction with the other modules in the #
|
9
|
+
# Amp project, please see the root-level LICENSE file. #
|
10
|
+
# #
|
11
|
+
# © Michael J. Edgar and Ari Brown, 2009-2010 #
|
12
|
+
# #
|
13
|
+
##################################################################
|
14
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'validations.rb'))
|
15
|
+
|
16
|
+
module Amp
|
17
|
+
module Command
|
18
|
+
# Creates a new command class and sets its name appropriately. Yields
|
19
|
+
# it, so it can be customized by the caller, adding options, an on_call
|
20
|
+
# block, and so on.
|
21
|
+
def self.create(name)
|
22
|
+
@current_base_module ||= Amp::Command
|
23
|
+
name = name.capitalize
|
24
|
+
new_class = Class.new(Base)
|
25
|
+
new_class.name = name
|
26
|
+
yield new_class
|
27
|
+
@current_base_module.const_set(name, new_class)
|
28
|
+
Amp::Help::CommandHelpEntry.new(name.downcase, new_class)
|
29
|
+
new_class
|
30
|
+
end
|
31
|
+
|
32
|
+
# Runs the provided block with a base module of the given name. So
|
33
|
+
# instead of creating Amp::Command::NewCommand, this allows you to
|
34
|
+
# namespace created code as Amp::Command::MyModule::NewCommand, isolating
|
35
|
+
# it from other plugins.
|
36
|
+
def self.namespace(namespace)
|
37
|
+
old_namespace = @current_base_module ||= Amp::Command
|
38
|
+
namespace = namespace.capitalize
|
39
|
+
|
40
|
+
unless old_namespace.const_defined?(namespace)
|
41
|
+
new_namespace = Module.new
|
42
|
+
old_namespace.const_set(namespace, new_namespace)
|
43
|
+
end
|
44
|
+
@current_base_module = const_get(namespace)
|
45
|
+
yield
|
46
|
+
ensure
|
47
|
+
@current_base_module = old_namespace
|
48
|
+
end
|
49
|
+
|
50
|
+
# See if the specified module list matches a defined constant
|
51
|
+
def self.lookup_const(modules)
|
52
|
+
current = Amp::Command
|
53
|
+
modules.each do |module_name|
|
54
|
+
if module_name =~ /^[A-Za-z0-9_]+$/ && current.const_defined?(module_name)
|
55
|
+
current = current.const_get(module_name)
|
56
|
+
else
|
57
|
+
return current
|
58
|
+
end
|
59
|
+
end
|
60
|
+
return current
|
61
|
+
end
|
62
|
+
|
63
|
+
# return the argument if 'new' would succeed
|
64
|
+
def self.creatable(command)
|
65
|
+
command.is_a?(Class) ? command : nil
|
66
|
+
end
|
67
|
+
|
68
|
+
# Looks up the command with the given name.
|
69
|
+
def self.for_name(name)
|
70
|
+
modules = name.split.map {|name| name.capitalize}
|
71
|
+
creatable(lookup_const(modules))
|
72
|
+
end
|
73
|
+
|
74
|
+
# The base class frmo which all comamnds inherit.
|
75
|
+
class Base
|
76
|
+
include Validations
|
77
|
+
|
78
|
+
class << self
|
79
|
+
attr_accessor :name, :options, :desc
|
80
|
+
end
|
81
|
+
self.name = 'Base'
|
82
|
+
|
83
|
+
# These are the runtime options selected when the command is called.
|
84
|
+
attr_accessor :options, :arguments
|
85
|
+
|
86
|
+
# This tracks all subclasses (and subclasses of subclasses, etc). Plus, this
|
87
|
+
# method is inherited, so Wool::Plugins::Git.all_subclasses will have all
|
88
|
+
# subclasses of Wool::Plugins::Git!
|
89
|
+
def self.all_commands
|
90
|
+
@all_commands ||= [self]
|
91
|
+
end
|
92
|
+
|
93
|
+
# When a Plugin subclass is subclassed, store the subclass and inform the
|
94
|
+
# next superclass up the inheritance hierarchy.
|
95
|
+
def self.inherited(klass)
|
96
|
+
klass.name ||= 'anonymous ' + self.name
|
97
|
+
self.all_commands << klass
|
98
|
+
next_klass = self.superclass
|
99
|
+
while next_klass != Amp::Command::Base.superclass
|
100
|
+
next_klass.send(:inherited, klass)
|
101
|
+
next_klass = next_klass.superclass
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# Specifies the block to run, or returns the block.
|
106
|
+
def self.on_call(&block)
|
107
|
+
@on_call = block if block_given?
|
108
|
+
@on_call
|
109
|
+
end
|
110
|
+
|
111
|
+
def self.desc(*args)
|
112
|
+
self.desc = args[0] if args.size == 1 && args[0].is_a?(String)
|
113
|
+
@desc ||= ""
|
114
|
+
end
|
115
|
+
|
116
|
+
def self.options
|
117
|
+
@options ||= []
|
118
|
+
end
|
119
|
+
|
120
|
+
def self.opt(*args)
|
121
|
+
self.options << args
|
122
|
+
end
|
123
|
+
|
124
|
+
# Runs the command with the provided options and arguments.
|
125
|
+
def call(options, arguments)
|
126
|
+
self.options = options
|
127
|
+
self.arguments = arguments
|
128
|
+
instance_eval(&self.class.on_call)
|
129
|
+
end
|
130
|
+
|
131
|
+
# Collects the options specific to this command and returns them.
|
132
|
+
def collect_options(arguments)
|
133
|
+
args = arguments.dup
|
134
|
+
base_options = self.class.options # Trollop::options uses instance_eval
|
135
|
+
@parser, hash = Trollop::options(args) do
|
136
|
+
base_options.each do |option|
|
137
|
+
opt *option
|
138
|
+
end
|
139
|
+
end
|
140
|
+
[hash, args]
|
141
|
+
end
|
142
|
+
|
143
|
+
def education
|
144
|
+
if @parser
|
145
|
+
output = StringIO.new
|
146
|
+
@parser.educate(output)
|
147
|
+
output.string
|
148
|
+
else
|
149
|
+
''
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
Dir[File.expand_path(File.dirname(__FILE__)) + '/builtin/**/*.rb'].each do |file|
|
157
|
+
require file
|
158
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
##################################################################
|
2
|
+
# Licensing Information #
|
3
|
+
# #
|
4
|
+
# The following code is licensed, as standalone code, under #
|
5
|
+
# the Ruby License, unless otherwise directed within the code. #
|
6
|
+
# #
|
7
|
+
# For information on the license of this code when distributed #
|
8
|
+
# with and used in conjunction with the other modules in the #
|
9
|
+
# Amp project, please see the root-level LICENSE file. #
|
10
|
+
# #
|
11
|
+
# © Michael J. Edgar and Ari Brown, 2009-2010 #
|
12
|
+
# #
|
13
|
+
##################################################################
|
14
|
+
Amp::Command.create('help') do |c|
|
15
|
+
c.desc "Prints the help for the program."
|
16
|
+
|
17
|
+
c.on_call do
|
18
|
+
output = ""
|
19
|
+
|
20
|
+
cmd_name = arguments.empty? ? "__default__" : arguments.first
|
21
|
+
Amp::Help::HelpUI.print_entry(cmd_name, options)
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
##################################################################
|
2
|
+
# Licensing Information #
|
3
|
+
# #
|
4
|
+
# The following code is licensed, as standalone code, under #
|
5
|
+
# the Ruby License, unless otherwise directed within the code. #
|
6
|
+
# #
|
7
|
+
# For information on the license of this code when distributed #
|
8
|
+
# with and used in conjunction with the other modules in the #
|
9
|
+
# Amp project, please see the root-level LICENSE file. #
|
10
|
+
# #
|
11
|
+
# © Michael J. Edgar and Ari Brown, 2009-2010 #
|
12
|
+
# #
|
13
|
+
##################################################################
|
14
|
+
Amp::Command.namespace 'plugin' do
|
15
|
+
Amp::Command.create('list') do |c|
|
16
|
+
c.desc "Lists all the plugins known to Amp."
|
17
|
+
|
18
|
+
c.on_call do
|
19
|
+
Amp::Plugins::Base.all_plugins.each do |plugin|
|
20
|
+
puts plugin.name
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,171 @@
|
|
1
|
+
##################################################################
|
2
|
+
# Licensing Information #
|
3
|
+
# #
|
4
|
+
# The following code is licensed, as standalone code, under #
|
5
|
+
# the Ruby License, unless otherwise directed within the code. #
|
6
|
+
# #
|
7
|
+
# For information on the license of this code when distributed #
|
8
|
+
# with and used in conjunction with the other modules in the #
|
9
|
+
# Amp project, please see the root-level LICENSE file. #
|
10
|
+
# #
|
11
|
+
# © Michael J. Edgar and Ari Brown, 2009-2010 #
|
12
|
+
# #
|
13
|
+
##################################################################
|
14
|
+
|
15
|
+
module Amp
|
16
|
+
module Command
|
17
|
+
module Validations
|
18
|
+
def self.included(base)
|
19
|
+
base.extend(ClassMethods)
|
20
|
+
end
|
21
|
+
module ClassMethods
|
22
|
+
def before_blocks
|
23
|
+
@before_blocks ||= []
|
24
|
+
end
|
25
|
+
|
26
|
+
def after_blocks
|
27
|
+
@after_blocks ||= []
|
28
|
+
end
|
29
|
+
|
30
|
+
# Specifies one or more "before" callbacks that are run before
|
31
|
+
# the command executes. If any of them return non-truthy, then
|
32
|
+
# execution halts.
|
33
|
+
#
|
34
|
+
# @param block [Proc] a block to run
|
35
|
+
def before(&block)
|
36
|
+
before_blocks << block
|
37
|
+
end
|
38
|
+
|
39
|
+
# Specifies one or more "after" callbacks that are run before
|
40
|
+
# the command executes. If any of them return non-truthy, then
|
41
|
+
# execution halts.
|
42
|
+
#
|
43
|
+
# @param block [Proc] a block to run
|
44
|
+
def after(&block)
|
45
|
+
after_blocks << block
|
46
|
+
end
|
47
|
+
|
48
|
+
# Validates that the given block runs and returns true.
|
49
|
+
# TODO(adgar): Document how :if, :unless work
|
50
|
+
def validates_block(options={}, &block)
|
51
|
+
before do |opts, args|
|
52
|
+
if options[:if] || options[:unless]
|
53
|
+
checker = options[:if] || options[:unless]
|
54
|
+
result = if checker.is_a?(Proc)
|
55
|
+
checker.call(opts, args)
|
56
|
+
else
|
57
|
+
new.send(checker, opts, args)
|
58
|
+
end
|
59
|
+
result = !result if options[:unless]
|
60
|
+
next true unless result
|
61
|
+
end
|
62
|
+
block.call(opts, args)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Validates each the given options by running their values through the
|
67
|
+
# block.
|
68
|
+
def validates_each(*syms, &block)
|
69
|
+
options = syms.last.is_a?(Hash) ? syms.pop : {}
|
70
|
+
validates_block(options) do |opts, args|
|
71
|
+
syms.all? {|sym| block.call(opts, sym, opts[sym])}
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Validates that the given options were provided by the user.
|
76
|
+
def validates_presence_of(*syms)
|
77
|
+
validates_each(*syms) {|opts, attr, value| opts["#{attr}_given".to_sym]}
|
78
|
+
end
|
79
|
+
|
80
|
+
# Validates that the value of the given symbols is in the provided object.
|
81
|
+
# Takes an options hash that must contain the :in key:
|
82
|
+
#
|
83
|
+
# validates_inclusion_of :age, :favorite_number, :in => 13..18
|
84
|
+
#
|
85
|
+
# Also supports :if/:unless like the rails counterparts.
|
86
|
+
def validates_inclusion_of(*syms)
|
87
|
+
unless syms.last.is_a?(Hash)
|
88
|
+
raise ArgumentError.new('validates_inclusion_of takes an options hash')
|
89
|
+
end
|
90
|
+
options = syms.last
|
91
|
+
validates_each(*syms) {|opts, attr, value| options[:in].include?(value)}
|
92
|
+
end
|
93
|
+
|
94
|
+
VALID_LENGTH_KEYS = [:in, :within, :maximum, :minimum, :is]
|
95
|
+
def extract_length_key(options)
|
96
|
+
key_used = VALID_LENGTH_KEYS.find {|key| options.include?(key)}
|
97
|
+
if key_used.nil?
|
98
|
+
raise ArgumentError.new("One of #{VALID_LENGTH_KEYS.inspect} must be " +
|
99
|
+
"provided to validates_length_of")
|
100
|
+
end
|
101
|
+
key_used
|
102
|
+
end
|
103
|
+
|
104
|
+
# Validates the length of the string provided as an argument matches the
|
105
|
+
# provided constraints on length.
|
106
|
+
#
|
107
|
+
# @param [Symbol] attr the attribute to validate
|
108
|
+
# @param [Hash] options A set of options that specifies the constraint.
|
109
|
+
# Must have one of the following: :in, :within, :maximum, :minimum, :is.
|
110
|
+
def validates_length_of(attr, options={})
|
111
|
+
key_used = extract_length_key options
|
112
|
+
validates_block(options) do |opts, args|
|
113
|
+
value = opts[attr].size
|
114
|
+
numeric_comparison(value, key_used, options[key_used])
|
115
|
+
end
|
116
|
+
end
|
117
|
+
alias_method :validates_size_of, :validates_length_of
|
118
|
+
|
119
|
+
# Validates the number of arguments provided with the :in, :within, :maximum,
|
120
|
+
# :minimum, and :is relationships. To validate that a user provided at least
|
121
|
+
# 2 arguments in a copy command:
|
122
|
+
#
|
123
|
+
# command "copy" do
|
124
|
+
# validates_argument_count :minimum => 2
|
125
|
+
# end
|
126
|
+
#
|
127
|
+
def validates_argument_count(options={})
|
128
|
+
key_used = extract_length_key options
|
129
|
+
validates_block(options) do |opts, args|
|
130
|
+
value = args.size
|
131
|
+
numeric_comparison(value, key_used, options[key_used])
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# Compares a value to a given constraint and returns whether it matches it
|
136
|
+
# or not.
|
137
|
+
#
|
138
|
+
# @param [Integer] value a numeric value
|
139
|
+
# @param [Symbol] key_used the key used for comparison. Either :within,
|
140
|
+
# :in, :maximum, :minimum, and :is.
|
141
|
+
# @param [Integer, #include?] constraint a value to compare against
|
142
|
+
# @return [Boolean]
|
143
|
+
def numeric_comparison(value, key_used, constraint)
|
144
|
+
if key_used == :in || key_used == :within
|
145
|
+
constraint.include?(value)
|
146
|
+
elsif key_used == :is
|
147
|
+
constraint == value
|
148
|
+
elsif key_used == :minimum
|
149
|
+
constraint <= value
|
150
|
+
elsif key_used == :maximum
|
151
|
+
constraint >= value
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
# Is this a valid set of options and arguments for this command?
|
157
|
+
def valid?(opts, args)
|
158
|
+
self.class.before_blocks.all? {|block| block.call(opts, args)}
|
159
|
+
end
|
160
|
+
alias_method :run_before, :valid?
|
161
|
+
|
162
|
+
def invalid?(opts, args)
|
163
|
+
!valid?(opts, args)
|
164
|
+
end
|
165
|
+
|
166
|
+
def run_after(opts, args)
|
167
|
+
self.class.after_blocks.all? {|block| block.call(opts, args)}
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|