komando 0.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.
- data/.document +5 -0
- data/.gitignore +25 -0
- data/.rvmrc +1 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/README.md +147 -0
- data/Rakefile +111 -0
- data/VERSION +1 -0
- data/komando.gemspec +82 -0
- data/lib/komando.rb +18 -0
- data/lib/komando/command.rb +76 -0
- data/lib/komando/command/dsl.rb +95 -0
- data/lib/komando/persistence.rb +6 -0
- data/lib/komando/persistence/active_record.rb +65 -0
- data/lib/komando/version.rb +13 -0
- data/samples/rails3/.gitignore +4 -0
- data/samples/rails3/.rvmrc +1 -0
- data/samples/rails3/lib/tasks/.gitkeep +0 -0
- data/samples/rails3/public/stylesheets/.gitkeep +0 -0
- data/samples/rails3/vendor/plugins/.gitkeep +0 -0
- data/spec/active_record_integration_spec.rb +35 -0
- data/spec/command_runner_spec.rb +242 -0
- data/spec/dsl_spec.rb +44 -0
- data/spec/spec_helper.rb +15 -0
- metadata +159 -0
data/.document
ADDED
data/.gitignore
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
## MAC OS
|
2
|
+
.DS_Store
|
3
|
+
|
4
|
+
## TEXTMATE
|
5
|
+
*.tmproj
|
6
|
+
tmtags
|
7
|
+
|
8
|
+
## EMACS
|
9
|
+
*~
|
10
|
+
\#*
|
11
|
+
.\#*
|
12
|
+
|
13
|
+
## VIM
|
14
|
+
*.swp
|
15
|
+
|
16
|
+
## PROJECT::GENERAL
|
17
|
+
coverage
|
18
|
+
rdoc
|
19
|
+
pkg
|
20
|
+
doc
|
21
|
+
|
22
|
+
## PROJECT::SPECIFIC
|
23
|
+
*.rbc
|
24
|
+
.bundle
|
25
|
+
Gemfile.lock
|
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm 1.9.2@komando
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 François Beausoleil
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,147 @@
|
|
1
|
+
Komando
|
2
|
+
=======
|
3
|
+
|
4
|
+
Commands are used in many applications, especially GUIs. Komando is an implementation of the [Command pattern][1],
|
5
|
+
especially suited to Rails applications. Commands are provided the information they need, then told to run. Commands
|
6
|
+
can be marked best effort, or mandatory. Commands can refer to other commands, and ask that the sub-commands be
|
7
|
+
mandatory or best effort.
|
8
|
+
|
9
|
+
Most web applications have a lot of before and after hooks that occur when working with objects: sending a
|
10
|
+
welcome email on registration, incrementing or decrementing counter caches, trigger validation on remote web
|
11
|
+
services. When implemented using callbacks, all these occur without the developer knowing about them. A
|
12
|
+
simple change in one area of the code can have a huge impact somewhere else. Inspiration for this came from
|
13
|
+
[Unintented Consequences: The Pitfalls of ActiveRecord Callbacks][2] and
|
14
|
+
[Crazy, Heretical, and Awesome: The Way I Write Rails Apps][3].
|
15
|
+
|
16
|
+
|
17
|
+
Examples
|
18
|
+
--------
|
19
|
+
|
20
|
+
require "komando/command"
|
21
|
+
require "komando/active_record"
|
22
|
+
|
23
|
+
class AdUpdateCommand
|
24
|
+
include Komando::Command
|
25
|
+
|
26
|
+
def initialize(*args)
|
27
|
+
@initiated_at = Time.now.utc
|
28
|
+
|
29
|
+
# If you must override #initialize, NEVER forget to call super
|
30
|
+
super
|
31
|
+
|
32
|
+
# Forgetting to call super will result in NoMethodError and such
|
33
|
+
# being raised from your code.
|
34
|
+
end
|
35
|
+
|
36
|
+
# Lets exceptions through -- nothing is rescued -- callers will have
|
37
|
+
# to handle exceptions themselves. The command's success/failure state
|
38
|
+
# is determined by the fact that no exceptions are raised. The block's
|
39
|
+
# return value is ignored.
|
40
|
+
#
|
41
|
+
# All #mandatory_steps are run within a single database transaction.
|
42
|
+
mandatory_steps do
|
43
|
+
@ad.update_attributes!(@params)
|
44
|
+
@ad.campaign.update_attribute(:active_ad_units_count, @ad.campaign.ad_units.active.count)
|
45
|
+
end
|
46
|
+
|
47
|
+
# The #transaction block can be used to root your transaction differently.
|
48
|
+
# The default #transaction block simply yields - no transactions will be
|
49
|
+
# processed. The komando-active_record gem will root your transactions
|
50
|
+
# against ActiveRecord::Base#transaction.
|
51
|
+
#
|
52
|
+
# This method is important if you have more than one database connection,
|
53
|
+
# where each model might open transactions against different databases.
|
54
|
+
transaction do
|
55
|
+
AdUnit.transaction do
|
56
|
+
yield
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class PlacementEventLoggerCommand
|
62
|
+
include Komando::Command
|
63
|
+
|
64
|
+
mandatory_steps do
|
65
|
+
Event.create!(:event_type => "placement:created", :actor => @actor, :subject => @subject)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
class PlacementCreationCommand
|
70
|
+
include Komando::Command
|
71
|
+
|
72
|
+
mandatory_steps do
|
73
|
+
@placement = Placement.create!(@params)
|
74
|
+
|
75
|
+
@placement.campaign.increment!(:active_placements_count, 1) if @placement.active?
|
76
|
+
@placement.campaign.increment!(:scheduled_placements_count, 1) if @placement.scheduled?
|
77
|
+
end
|
78
|
+
|
79
|
+
# Call #best_effort_step multiple times to declare each individual step
|
80
|
+
# that will be attempted. If a block fails, logging will ensue, and
|
81
|
+
# other blocks will be attempted.
|
82
|
+
#
|
83
|
+
# #best_effort_step can document what it's supposed to do, enabling
|
84
|
+
# better logging. Either pass a String or Symbol, the latter of which will
|
85
|
+
# be #humanized.
|
86
|
+
best_effort_step(:event_generation) do
|
87
|
+
|
88
|
+
# Note the availability of a class-level method named #run, which simply does the obvious
|
89
|
+
# instantiation and call the instance-level #run.
|
90
|
+
PlacementEventLoggerCommand.run(:actor => @placement.created_by, :subject => @placement)
|
91
|
+
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Usage from Rails
|
96
|
+
class AdsController < ApplicationController
|
97
|
+
|
98
|
+
def update
|
99
|
+
@ad = Ad.find(params[:id])
|
100
|
+
|
101
|
+
# Commands accept any number of parameters, as a Hash. Parameters are translated
|
102
|
+
# to instance variables within the Command object itself.
|
103
|
+
command = AdUpdateCommand.new(:ad => @ad,
|
104
|
+
:params => params[:ad])
|
105
|
+
if command.run then
|
106
|
+
flash[:notice] = "Ad updated"
|
107
|
+
redirect_to @ad
|
108
|
+
else
|
109
|
+
flash.new[:error] = "Ad failed to save"
|
110
|
+
render :action => :edit
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
|
117
|
+
Notes on Patches/Pull Requests
|
118
|
+
-----------------------------
|
119
|
+
|
120
|
+
* Fork the project.
|
121
|
+
* Make your feature addition or bug fix.
|
122
|
+
* Add tests for it. This is important so I don't break it in a
|
123
|
+
future version unintentionally.
|
124
|
+
* Commit, do not mess with rakefile, version, or history.
|
125
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
126
|
+
* Send me a pull request. Bonus points for topic branches.
|
127
|
+
|
128
|
+
|
129
|
+
Compatibility
|
130
|
+
-------------
|
131
|
+
|
132
|
+
Komando is known to pass it's specifications on the following Ruby implementations (rvm version specifiers):
|
133
|
+
|
134
|
+
* jruby-1.5.6 [ x86_64-java ]
|
135
|
+
* ree-1.8.7-2011.01 [ x86_64 ]
|
136
|
+
* ruby-1.8.7-p330 [ x86_64 ]
|
137
|
+
* ruby-1.9.2-p136 [ x86_64 ]
|
138
|
+
|
139
|
+
|
140
|
+
Copyright
|
141
|
+
---------
|
142
|
+
|
143
|
+
Copyright (c) 2010 François Beausoleil. See LICENSE for details.
|
144
|
+
|
145
|
+
[1]: http://en.wikipedia.org/wiki/Command_pattern
|
146
|
+
[2]: http://blog.teksol.info/2010/09/28/unintented-consequences-the-pitfalls-of-activerecord-callbacks.html
|
147
|
+
[3]: http://jamesgolick.com/2010/3/14/crazy-heretical-and-awesome-the-way-i-write-rails-apps.html
|
data/Rakefile
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'rake'
|
4
|
+
|
5
|
+
begin
|
6
|
+
require 'jeweler'
|
7
|
+
Jeweler::Tasks.new do |gem|
|
8
|
+
gem.name = "komando"
|
9
|
+
gem.summary = %Q{Command-driven framework, especially suited for web applications}
|
10
|
+
gem.description = %Q{Most web applications have a lot of before/after hooks that occur when working with objects: sending a welcome email on registration, incrementing/decrementing counter caches, trigger validation on remote web services. When implemented using callbacks, all these occur without the developer knowing about them. A simple change in one area of the code can have a huge impact somewhere else. Inspiration for this came from http://blog.teksol.info/2010/09/28/unintented-consequences-the-pitfalls-of-activerecord-callbacks.html and http://jamesgolick.com/2010/3/14/crazy-heretical-and-awesome-the-way-i-write-rails-apps.html}
|
11
|
+
gem.email = "francois@teksol.info"
|
12
|
+
gem.homepage = "http://github.com/francois/komando"
|
13
|
+
gem.authors = ["François Beausoleil"]
|
14
|
+
|
15
|
+
# Don't bundle development code with the gem
|
16
|
+
gem.files -= FileList["samples/**/*"]
|
17
|
+
|
18
|
+
gem.add_development_dependency "bacon", ">= 0"
|
19
|
+
gem.add_development_dependency "yard", ">= 0"
|
20
|
+
gem.add_development_dependency "bluecloth", ">= 0"
|
21
|
+
# BlueCloth is "required" by yard, where it is used to format the docs, but is not really a required dependency.
|
22
|
+
# Use 1.9.2 to generate the docs.
|
23
|
+
|
24
|
+
gem.add_development_dependency "activerecord", "~> 2.3.8"
|
25
|
+
gem.add_development_dependency "jeweler", "~> 1.4.0"
|
26
|
+
|
27
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
28
|
+
end
|
29
|
+
Jeweler::GemcutterTasks.new
|
30
|
+
rescue LoadError
|
31
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
32
|
+
end
|
33
|
+
|
34
|
+
require 'rake/testtask'
|
35
|
+
Rake::TestTask.new(:spec) do |spec|
|
36
|
+
spec.libs << 'lib' << 'spec'
|
37
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
38
|
+
spec.verbose = true
|
39
|
+
end
|
40
|
+
|
41
|
+
begin
|
42
|
+
require 'rcov/rcovtask'
|
43
|
+
Rcov::RcovTask.new do |spec|
|
44
|
+
spec.libs << 'spec'
|
45
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
46
|
+
spec.verbose = true
|
47
|
+
end
|
48
|
+
rescue LoadError
|
49
|
+
task :rcov do
|
50
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
task :spec => :check_dependencies
|
55
|
+
|
56
|
+
task :default => :spec
|
57
|
+
|
58
|
+
begin
|
59
|
+
require 'yard'
|
60
|
+
YARD::Rake::YardocTask.new
|
61
|
+
rescue LoadError
|
62
|
+
task :yardoc do
|
63
|
+
abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
RUBIES = %w(
|
68
|
+
1.9.2@komando
|
69
|
+
1.8.7@komando
|
70
|
+
ree@komando
|
71
|
+
jruby@komando
|
72
|
+
)
|
73
|
+
|
74
|
+
def rvm(command)
|
75
|
+
sh "rvm #{RUBIES.join(",")} exec #{command}"
|
76
|
+
end
|
77
|
+
|
78
|
+
namespace :rubies do
|
79
|
+
namespace :bundle do
|
80
|
+
task :install do
|
81
|
+
rvm "gem install bundler"
|
82
|
+
rvm "bundle install"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
task :version do
|
87
|
+
rvm "ruby --version"
|
88
|
+
end
|
89
|
+
|
90
|
+
task :spec do
|
91
|
+
rvm "rake spec"
|
92
|
+
end
|
93
|
+
|
94
|
+
task :default do
|
95
|
+
sh "rvm use #{RUBIES.first}"
|
96
|
+
end
|
97
|
+
|
98
|
+
namespace :gemset do
|
99
|
+
task :create do
|
100
|
+
RUBIES.each do |ruby|
|
101
|
+
sh "rvm use #{ruby} && rvm --force gemset create komando"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
task :delete do
|
106
|
+
RUBIES.each do |ruby|
|
107
|
+
sh "rvm use #{ruby} && rvm --force gemset delete"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.0
|
data/komando.gemspec
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{komando}
|
8
|
+
s.version = "0.0.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["François Beausoleil"]
|
12
|
+
s.date = %q{2011-03-15}
|
13
|
+
s.description = %q{Most web applications have a lot of before/after hooks that occur when working with objects: sending a welcome email on registration, incrementing/decrementing counter caches, trigger validation on remote web services. When implemented using callbacks, all these occur without the developer knowing about them. A simple change in one area of the code can have a huge impact somewhere else. Inspiration for this came from http://blog.teksol.info/2010/09/28/unintented-consequences-the-pitfalls-of-activerecord-callbacks.html and http://jamesgolick.com/2010/3/14/crazy-heretical-and-awesome-the-way-i-write-rails-apps.html}
|
14
|
+
s.email = %q{francois@teksol.info}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.md"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
".gitignore",
|
22
|
+
".rvmrc",
|
23
|
+
"Gemfile",
|
24
|
+
"LICENSE",
|
25
|
+
"README.md",
|
26
|
+
"Rakefile",
|
27
|
+
"VERSION",
|
28
|
+
"komando.gemspec",
|
29
|
+
"lib/komando.rb",
|
30
|
+
"lib/komando/command.rb",
|
31
|
+
"lib/komando/command/dsl.rb",
|
32
|
+
"lib/komando/persistence.rb",
|
33
|
+
"lib/komando/persistence/active_record.rb",
|
34
|
+
"lib/komando/version.rb",
|
35
|
+
"samples/rails3/.gitignore",
|
36
|
+
"samples/rails3/.rvmrc",
|
37
|
+
"samples/rails3/lib/tasks/.gitkeep",
|
38
|
+
"samples/rails3/public/stylesheets/.gitkeep",
|
39
|
+
"samples/rails3/vendor/plugins/.gitkeep",
|
40
|
+
"spec/active_record_integration_spec.rb",
|
41
|
+
"spec/command_runner_spec.rb",
|
42
|
+
"spec/dsl_spec.rb",
|
43
|
+
"spec/spec_helper.rb"
|
44
|
+
]
|
45
|
+
s.homepage = %q{http://github.com/francois/komando}
|
46
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
47
|
+
s.require_paths = ["lib"]
|
48
|
+
s.rubygems_version = %q{1.3.7}
|
49
|
+
s.summary = %q{Command-driven framework, especially suited for web applications}
|
50
|
+
s.test_files = [
|
51
|
+
"spec/active_record_integration_spec.rb",
|
52
|
+
"spec/command_runner_spec.rb",
|
53
|
+
"spec/dsl_spec.rb",
|
54
|
+
"spec/spec_helper.rb"
|
55
|
+
]
|
56
|
+
|
57
|
+
if s.respond_to? :specification_version then
|
58
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
59
|
+
s.specification_version = 3
|
60
|
+
|
61
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
62
|
+
s.add_development_dependency(%q<bacon>, [">= 0"])
|
63
|
+
s.add_development_dependency(%q<yard>, [">= 0"])
|
64
|
+
s.add_development_dependency(%q<bluecloth>, [">= 0"])
|
65
|
+
s.add_development_dependency(%q<activerecord>, ["~> 2.3.8"])
|
66
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.4.0"])
|
67
|
+
else
|
68
|
+
s.add_dependency(%q<bacon>, [">= 0"])
|
69
|
+
s.add_dependency(%q<yard>, [">= 0"])
|
70
|
+
s.add_dependency(%q<bluecloth>, [">= 0"])
|
71
|
+
s.add_dependency(%q<activerecord>, ["~> 2.3.8"])
|
72
|
+
s.add_dependency(%q<jeweler>, ["~> 1.4.0"])
|
73
|
+
end
|
74
|
+
else
|
75
|
+
s.add_dependency(%q<bacon>, [">= 0"])
|
76
|
+
s.add_dependency(%q<yard>, [">= 0"])
|
77
|
+
s.add_dependency(%q<bluecloth>, [">= 0"])
|
78
|
+
s.add_dependency(%q<activerecord>, ["~> 2.3.8"])
|
79
|
+
s.add_dependency(%q<jeweler>, ["~> 1.4.0"])
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
data/lib/komando.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# The main Komando module. Holder for declarations.
|
2
|
+
module Komando
|
3
|
+
|
4
|
+
# Indicates the mandatory steps have not been declared on instances of this class.
|
5
|
+
class MissingMandatoryStepsError < StandardError; end
|
6
|
+
|
7
|
+
autoload :Command, "komando/command"
|
8
|
+
|
9
|
+
def self.make_command(base)
|
10
|
+
base.send :include, Komando::Command
|
11
|
+
base.send :extend, Komando::Command::Dsl
|
12
|
+
if defined?(ActiveRecord) then
|
13
|
+
require "komando/persistence/active_record"
|
14
|
+
base.send :include, Komando::Persistence::ActiveRecord
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module Komando
|
2
|
+
module Command
|
3
|
+
|
4
|
+
autoload :Dsl, "komando/command/dsl"
|
5
|
+
|
6
|
+
# Instantiates and runs this command.
|
7
|
+
#
|
8
|
+
# @param (see #initialize)
|
9
|
+
# @return [true] When the mandatory parts of the command are run to completion.
|
10
|
+
def self.run!(instance_variable_declarations)
|
11
|
+
new.run!(instance_variable_declarations)
|
12
|
+
end
|
13
|
+
|
14
|
+
# @param [Hash] instance_variable_declarations The list of instance variables to declare on this instance.
|
15
|
+
# The instance variables will be available in the blocks, courtesy of Ruby
|
16
|
+
# and it's dynamic nature.
|
17
|
+
def initialize(instance_variable_declarations={})
|
18
|
+
instance_variable_declarations.each do |name, value|
|
19
|
+
instance_variable_set("@#{name}", value)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Executes the mandatory and best effort steps, in order of definition, raising or
|
24
|
+
# logging and swallowing exceptions as appropriate.
|
25
|
+
#
|
26
|
+
# @return [true] Always returns +true+, since exceptions indicate failure.
|
27
|
+
# @raise [Exception] In case of a failure in the mandatory part of the command.
|
28
|
+
def run!
|
29
|
+
run_mandatory!
|
30
|
+
run_best_effort
|
31
|
+
true
|
32
|
+
end
|
33
|
+
|
34
|
+
# Returns a Logger-like object that will log receive #warn, #info and #debug calls.
|
35
|
+
# The logger can do whatever it wants with those calls.
|
36
|
+
#
|
37
|
+
# @return [#warn, #info, #debug] A Logger-like object that responds to logging commands.
|
38
|
+
#
|
39
|
+
# @example
|
40
|
+
#
|
41
|
+
# # config/initializers/komando.rb
|
42
|
+
# require "komando/command"
|
43
|
+
#
|
44
|
+
# module Komando::Command
|
45
|
+
# def logger
|
46
|
+
# Rails.logger
|
47
|
+
# end
|
48
|
+
# end
|
49
|
+
def logger
|
50
|
+
@logger ||= Logger.new(STDERR)
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
# Runs and raises
|
56
|
+
def run_mandatory!
|
57
|
+
steps = self.class.mandatory_steps
|
58
|
+
raise Komando::MissingMandatoryStepsError if steps.empty?
|
59
|
+
steps.each do |step|
|
60
|
+
instance_exec(&step)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Runs and logs
|
65
|
+
def run_best_effort
|
66
|
+
self.class.best_effort_steps.each do |name, block|
|
67
|
+
begin
|
68
|
+
instance_exec &block
|
69
|
+
rescue StandardError => e
|
70
|
+
logger.warn "[Komando] Ignoring failed #{name.inspect} step in #{self.class}: #{e.class.name} - #{e.message}"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module Komando
|
2
|
+
module Command
|
3
|
+
|
4
|
+
# The Komando DSL. Extend your command classes with this module to start using
|
5
|
+
# Komando in your application.
|
6
|
+
#
|
7
|
+
# It is recommended you do not implement #initialize in your commands. If you do,
|
8
|
+
# you *must* call super or your parameters will not be available as instance variables.
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
#
|
12
|
+
# require "komando/command/dsl"
|
13
|
+
#
|
14
|
+
# class CreateUserCommand
|
15
|
+
# extend Komando::Command::Dsl
|
16
|
+
#
|
17
|
+
# # If you must override #initialize, make sure you call super,
|
18
|
+
# # or your instance variables won't be assigned.
|
19
|
+
# def initialize(*args)
|
20
|
+
# super # MUST call, or all hell will break loose
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# mandatory_step "generate records" do
|
24
|
+
# # TODO
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# mandatory_step "generate audit log" do
|
28
|
+
# # TODO
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# end
|
32
|
+
module Dsl
|
33
|
+
|
34
|
+
# Returns the list of mandatory steps
|
35
|
+
def mandatory_steps
|
36
|
+
@mandatory_steps ||= []
|
37
|
+
end
|
38
|
+
|
39
|
+
# Declares a set of actions that must run to completion for this command to be deemed successful.
|
40
|
+
# The declared actions may be anything: method calls or direct actions. Parameters are passed from the
|
41
|
+
# environment as instance variables passed to the instance.
|
42
|
+
#
|
43
|
+
# @example
|
44
|
+
#
|
45
|
+
# class CreateUserCommand
|
46
|
+
# extend Komando::Command::Dsl
|
47
|
+
#
|
48
|
+
# mandatory_step do
|
49
|
+
# # Assuming an ActiveRecord-like User class exists
|
50
|
+
# User.create!(@attributes)
|
51
|
+
# end
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
# # Run the command with parameters gathered from the environment
|
55
|
+
# CreateUserCommand.new(:attributes => params[:user]).run!
|
56
|
+
def mandatory_step(name=nil, &block)
|
57
|
+
mandatory_steps << block
|
58
|
+
end
|
59
|
+
|
60
|
+
# Declares a new best effort step - one that will be executed, but will not stop processing.
|
61
|
+
# If the block raises an exception, {Komando::Command#run!} will log and swallow the exception.
|
62
|
+
# Best effort stop blocks have access to the same environment as {#mandatory_step} blocks -
|
63
|
+
# they execute within the same instance. You can pass values from one block to the next by
|
64
|
+
# using instance variables.
|
65
|
+
#
|
66
|
+
# @example
|
67
|
+
#
|
68
|
+
# class CreateUserCommand
|
69
|
+
# extend Komando::Command::Dsl
|
70
|
+
#
|
71
|
+
# mandatory_step do
|
72
|
+
# @user = User.create!(@attributes)
|
73
|
+
# end
|
74
|
+
#
|
75
|
+
# best_effort_step("generate audit log record") do
|
76
|
+
# AuditLog.append(@user, @created_by)
|
77
|
+
# end
|
78
|
+
# end
|
79
|
+
#
|
80
|
+
# # Run the command with parameters gathered from the environment
|
81
|
+
# CreateUserCommand.new(:attributes => params[:user], :created_by => current_user).run!
|
82
|
+
def best_effort_step(name=nil, &block)
|
83
|
+
@best_effort_steps ||= Array.new
|
84
|
+
@best_effort_steps << [name, block]
|
85
|
+
end
|
86
|
+
|
87
|
+
# Helper method to access the declared blocks.
|
88
|
+
#
|
89
|
+
# @return [Array] The list of best effort blocks that were collected. This array may be empty.
|
90
|
+
def best_effort_steps
|
91
|
+
@best_effort_steps ||= Array.new
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require "komando"
|
2
|
+
require "active_record"
|
3
|
+
|
4
|
+
module Komando
|
5
|
+
module Persistence
|
6
|
+
# Wraps command executions within a database transaction.
|
7
|
+
module ActiveRecord
|
8
|
+
|
9
|
+
# Insinuates this module within {Komando::Command}.
|
10
|
+
#
|
11
|
+
# @private
|
12
|
+
def self.included(base)
|
13
|
+
base.send :alias_method_chain, :run!, :transaction
|
14
|
+
end
|
15
|
+
|
16
|
+
# Wraps a command execution within a database transactions.
|
17
|
+
# This method delegates actual transaction semantics to {#wrap_transaction}.
|
18
|
+
# This method is renamed as {Komando::Command##run!} during inclusion.
|
19
|
+
def run_with_transaction!
|
20
|
+
wrap_transaction do
|
21
|
+
run_without_transaction!
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Does the actual work of wrapping in a transaction. The default is to wrap
|
26
|
+
# using {ActiveRecord::Base#transaction}.
|
27
|
+
#
|
28
|
+
# @example Wrapping using a specific connection
|
29
|
+
# # config/database.yml
|
30
|
+
# development:
|
31
|
+
# adapter: sqlite3
|
32
|
+
# database: db/development.sqlite3
|
33
|
+
#
|
34
|
+
# require "komando/persistence/active_record"
|
35
|
+
#
|
36
|
+
# class User < ActiveRecord::Base
|
37
|
+
# establish_connection :adapter => "sqlite3", :database => "/var/dbs/users.sqlite3"
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
# class CreateUserCommand
|
41
|
+
# include Komando::Command
|
42
|
+
# include Komando::Persistence::ActiveRecord
|
43
|
+
#
|
44
|
+
# def wrap_transaction
|
45
|
+
# User.transaction do
|
46
|
+
# yield
|
47
|
+
# end
|
48
|
+
# end
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# @yieldreturn The block's last value.
|
52
|
+
def wrap_transaction
|
53
|
+
::ActiveRecord::Base.transaction do
|
54
|
+
yield
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Uses the same Logger instance as ActiveRecord.
|
59
|
+
def logger
|
60
|
+
::ActiveRecord::Base.logger
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
rvm 1.9.2@komando-rails3
|
File without changes
|
File without changes
|
File without changes
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "komando/persistence/active_record"
|
3
|
+
|
4
|
+
describe "An active-record enabled command" do
|
5
|
+
|
6
|
+
before do
|
7
|
+
ActiveRecord::Base.establish_connection :adapter => adapter_name, :database => ":memory:"
|
8
|
+
end
|
9
|
+
|
10
|
+
def adapter_name
|
11
|
+
return "jdbcsqlite3" if defined?(JRUBY_VERSION)
|
12
|
+
return "sqlite3"
|
13
|
+
end
|
14
|
+
|
15
|
+
before do
|
16
|
+
@command = Class.new do
|
17
|
+
extend Komando::Command::Dsl
|
18
|
+
include Komando::Command
|
19
|
+
include Komando::Persistence::ActiveRecord
|
20
|
+
|
21
|
+
attr_reader :open_transactions
|
22
|
+
|
23
|
+
mandatory_step do
|
24
|
+
@open_transactions = ActiveRecord::Base.connection.open_transactions
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
should "wrap the mandatory_steps within an ActiveRecord transaction" do
|
30
|
+
command = @command.new
|
31
|
+
command.run!
|
32
|
+
command.open_transactions.should == 1
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,242 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "A command with a mandatory step" do
|
4
|
+
|
5
|
+
before do
|
6
|
+
@command = Class.new do
|
7
|
+
extend Komando::Command::Dsl
|
8
|
+
include Komando::Command
|
9
|
+
|
10
|
+
attr_reader :ran
|
11
|
+
|
12
|
+
mandatory_step do
|
13
|
+
@ran = true
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
should "run the mandatory steps" do
|
19
|
+
command = @command.new
|
20
|
+
command.run!
|
21
|
+
|
22
|
+
command.ran.should == true
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "A command with a failing mandatory step" do
|
28
|
+
|
29
|
+
before do
|
30
|
+
@command = Class.new do
|
31
|
+
extend Komando::Command::Dsl
|
32
|
+
include Komando::Command
|
33
|
+
|
34
|
+
attr_reader :ran, :log
|
35
|
+
|
36
|
+
mandatory_step do
|
37
|
+
raise "failure to run"
|
38
|
+
end
|
39
|
+
|
40
|
+
best_effort_step do
|
41
|
+
@log ||= []
|
42
|
+
@log << 1
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
should "let the exception bubble through" do
|
48
|
+
lambda do
|
49
|
+
@command.new.run!
|
50
|
+
end.should.raise(RuntimeError)
|
51
|
+
end
|
52
|
+
|
53
|
+
should "NOT run best effort blocks" do
|
54
|
+
command = @command.new
|
55
|
+
begin
|
56
|
+
command.run!
|
57
|
+
rescue StandardError => ignored
|
58
|
+
# NOP
|
59
|
+
end
|
60
|
+
|
61
|
+
command.log.should.be.nil?
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
describe "A command with no step declarations" do
|
67
|
+
|
68
|
+
before do
|
69
|
+
@command = Class.new do
|
70
|
+
extend Komando::Command::Dsl
|
71
|
+
include Komando::Command
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
should "raise an exception when running" do
|
76
|
+
lambda { @command.new.run! }.should.raise(Komando::MissingMandatoryStepsError)
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
describe "A command with a best effort step" do
|
82
|
+
|
83
|
+
before do
|
84
|
+
@command = Class.new do
|
85
|
+
extend Komando::Command::Dsl
|
86
|
+
include Komando::Command
|
87
|
+
|
88
|
+
attr_reader :ran
|
89
|
+
|
90
|
+
mandatory_step do
|
91
|
+
# NOP
|
92
|
+
end
|
93
|
+
|
94
|
+
best_effort_step(:always_succeeds) do
|
95
|
+
@ran = true
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
should "run the best effort step" do
|
101
|
+
command = @command.new
|
102
|
+
command.run!
|
103
|
+
command.ran.should == true
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
|
108
|
+
describe "A command with two mandatory steps" do
|
109
|
+
|
110
|
+
before do
|
111
|
+
@command = Class.new do
|
112
|
+
extend Komando::Command::Dsl
|
113
|
+
include Komando::Command
|
114
|
+
|
115
|
+
attr_accessor :raise_in_first, :raise_in_second
|
116
|
+
attr_reader :log
|
117
|
+
|
118
|
+
def initialize(*args)
|
119
|
+
@log = []
|
120
|
+
super
|
121
|
+
end
|
122
|
+
|
123
|
+
mandatory_step do
|
124
|
+
raise "asked to raise" if raise_in_first
|
125
|
+
log << 1
|
126
|
+
end
|
127
|
+
|
128
|
+
mandatory_step do
|
129
|
+
raise "asked to raise" if raise_in_second
|
130
|
+
log << 2
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
should "run both blocks in order" do
|
136
|
+
command = @command.new
|
137
|
+
command.run!
|
138
|
+
|
139
|
+
command.log.should == [1, 2]
|
140
|
+
end
|
141
|
+
|
142
|
+
should "NOT run the 2nd block when the 1st one raises" do
|
143
|
+
command = @command.new
|
144
|
+
command.raise_in_first = true
|
145
|
+
|
146
|
+
lambda do
|
147
|
+
command.run!
|
148
|
+
end.should.raise
|
149
|
+
|
150
|
+
command.log.should == []
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|
154
|
+
|
155
|
+
describe "A command with two best effort steps" do
|
156
|
+
|
157
|
+
before do
|
158
|
+
@command = Class.new do
|
159
|
+
extend Komando::Command::Dsl
|
160
|
+
include Komando::Command
|
161
|
+
|
162
|
+
attr_reader :log
|
163
|
+
|
164
|
+
mandatory_step do
|
165
|
+
# NOP
|
166
|
+
end
|
167
|
+
|
168
|
+
best_effort_step(:first) do
|
169
|
+
@log ||= []
|
170
|
+
@log << 1
|
171
|
+
end
|
172
|
+
|
173
|
+
best_effort_step(:second) do
|
174
|
+
@log ||= []
|
175
|
+
@log << 2
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
should "run both blocks, in order" do
|
181
|
+
command = @command.new
|
182
|
+
command.run!
|
183
|
+
command.log.should == [1, 2]
|
184
|
+
end
|
185
|
+
|
186
|
+
end
|
187
|
+
|
188
|
+
describe "A command with two best effort steps, where the 1st will fail" do
|
189
|
+
|
190
|
+
after do
|
191
|
+
$OK = false
|
192
|
+
end
|
193
|
+
|
194
|
+
before do
|
195
|
+
@command = Class.new do
|
196
|
+
extend Komando::Command::Dsl
|
197
|
+
include Komando::Command
|
198
|
+
|
199
|
+
attr_reader :log
|
200
|
+
|
201
|
+
mandatory_step do
|
202
|
+
# NOP
|
203
|
+
end
|
204
|
+
|
205
|
+
best_effort_step(:first) do
|
206
|
+
raise "failure to run"
|
207
|
+
end
|
208
|
+
|
209
|
+
best_effort_step(:second) do
|
210
|
+
@log ||= []
|
211
|
+
@log << 2
|
212
|
+
end
|
213
|
+
|
214
|
+
def logger
|
215
|
+
@logger ||= Class.new do
|
216
|
+
attr_reader :messages
|
217
|
+
|
218
|
+
def warn(message)
|
219
|
+
@messages ||= []
|
220
|
+
@messages << message
|
221
|
+
end
|
222
|
+
end.new
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
should "run both blocks, in order, and not return errors" do
|
228
|
+
command = @command.new
|
229
|
+
command.run!
|
230
|
+
command.log.should == [2]
|
231
|
+
end
|
232
|
+
|
233
|
+
should "log errors using #logger" do
|
234
|
+
command = @command.new
|
235
|
+
command.run!
|
236
|
+
messages = command.logger.messages
|
237
|
+
|
238
|
+
messages.length.should == 1
|
239
|
+
messages.first.should.match /ignoring failed.*first.*RuntimeError.*failure to run/im
|
240
|
+
end
|
241
|
+
|
242
|
+
end
|
data/spec/dsl_spec.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "A command with no step declarations" do
|
4
|
+
|
5
|
+
before do
|
6
|
+
@command = Class.new do
|
7
|
+
extend Komando::Command::Dsl
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
should "have no mandatory step blocks" do
|
12
|
+
@command.mandatory_steps.should == []
|
13
|
+
end
|
14
|
+
|
15
|
+
should "have no best effort blocks" do
|
16
|
+
@command.best_effort_steps.should == []
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "A command with one mandatory step block and no best effort blocks" do
|
21
|
+
|
22
|
+
before do
|
23
|
+
@command = Class.new do
|
24
|
+
extend Komando::Command::Dsl
|
25
|
+
|
26
|
+
mandatory_step do
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
should "have a mandatory step block" do
|
32
|
+
@command.mandatory_steps.should.not.be.nil?
|
33
|
+
end
|
34
|
+
|
35
|
+
should "allow declaring a second mandatory step" do
|
36
|
+
lambda do
|
37
|
+
@command.class_eval do
|
38
|
+
mandatory_step do
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end.should.not.raise
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bacon'
|
3
|
+
|
4
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
5
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
6
|
+
|
7
|
+
require 'komando'
|
8
|
+
require "komando/command"
|
9
|
+
require "komando/command/dsl"
|
10
|
+
|
11
|
+
print "\n\n#{RUBY_PLATFORM} -- #{RUBY_VERSION} -- #{RUBY_RELEASE_DATE}"
|
12
|
+
print "-- JRuby Detected" if Object.const_defined?("JRUBY_VERSION")
|
13
|
+
print "\n\n"
|
14
|
+
|
15
|
+
Bacon.summary_on_exit
|
metadata
ADDED
@@ -0,0 +1,159 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: komando
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
version: 0.0.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- "Fran\xC3\xA7ois Beausoleil"
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2011-03-15 00:00:00 -04:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: bacon
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 0
|
30
|
+
version: "0"
|
31
|
+
type: :development
|
32
|
+
version_requirements: *id001
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: yard
|
35
|
+
prerelease: false
|
36
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
37
|
+
none: false
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
segments:
|
42
|
+
- 0
|
43
|
+
version: "0"
|
44
|
+
type: :development
|
45
|
+
version_requirements: *id002
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: bluecloth
|
48
|
+
prerelease: false
|
49
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
segments:
|
55
|
+
- 0
|
56
|
+
version: "0"
|
57
|
+
type: :development
|
58
|
+
version_requirements: *id003
|
59
|
+
- !ruby/object:Gem::Dependency
|
60
|
+
name: activerecord
|
61
|
+
prerelease: false
|
62
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
63
|
+
none: false
|
64
|
+
requirements:
|
65
|
+
- - ~>
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
segments:
|
68
|
+
- 2
|
69
|
+
- 3
|
70
|
+
- 8
|
71
|
+
version: 2.3.8
|
72
|
+
type: :development
|
73
|
+
version_requirements: *id004
|
74
|
+
- !ruby/object:Gem::Dependency
|
75
|
+
name: jeweler
|
76
|
+
prerelease: false
|
77
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
78
|
+
none: false
|
79
|
+
requirements:
|
80
|
+
- - ~>
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
segments:
|
83
|
+
- 1
|
84
|
+
- 4
|
85
|
+
- 0
|
86
|
+
version: 1.4.0
|
87
|
+
type: :development
|
88
|
+
version_requirements: *id005
|
89
|
+
description: "Most web applications have a lot of before/after hooks that occur when working with objects: sending a welcome email on registration, incrementing/decrementing counter caches, trigger validation on remote web services. When implemented using callbacks, all these occur without the developer knowing about them. A simple change in one area of the code can have a huge impact somewhere else. Inspiration for this came from http://blog.teksol.info/2010/09/28/unintented-consequences-the-pitfalls-of-activerecord-callbacks.html and http://jamesgolick.com/2010/3/14/crazy-heretical-and-awesome-the-way-i-write-rails-apps.html"
|
90
|
+
email: francois@teksol.info
|
91
|
+
executables: []
|
92
|
+
|
93
|
+
extensions: []
|
94
|
+
|
95
|
+
extra_rdoc_files:
|
96
|
+
- LICENSE
|
97
|
+
- README.md
|
98
|
+
files:
|
99
|
+
- .document
|
100
|
+
- .gitignore
|
101
|
+
- .rvmrc
|
102
|
+
- Gemfile
|
103
|
+
- LICENSE
|
104
|
+
- README.md
|
105
|
+
- Rakefile
|
106
|
+
- VERSION
|
107
|
+
- komando.gemspec
|
108
|
+
- lib/komando.rb
|
109
|
+
- lib/komando/command.rb
|
110
|
+
- lib/komando/command/dsl.rb
|
111
|
+
- lib/komando/persistence.rb
|
112
|
+
- lib/komando/persistence/active_record.rb
|
113
|
+
- lib/komando/version.rb
|
114
|
+
- samples/rails3/.gitignore
|
115
|
+
- samples/rails3/.rvmrc
|
116
|
+
- samples/rails3/lib/tasks/.gitkeep
|
117
|
+
- samples/rails3/public/stylesheets/.gitkeep
|
118
|
+
- samples/rails3/vendor/plugins/.gitkeep
|
119
|
+
- spec/active_record_integration_spec.rb
|
120
|
+
- spec/command_runner_spec.rb
|
121
|
+
- spec/dsl_spec.rb
|
122
|
+
- spec/spec_helper.rb
|
123
|
+
has_rdoc: true
|
124
|
+
homepage: http://github.com/francois/komando
|
125
|
+
licenses: []
|
126
|
+
|
127
|
+
post_install_message:
|
128
|
+
rdoc_options:
|
129
|
+
- --charset=UTF-8
|
130
|
+
require_paths:
|
131
|
+
- lib
|
132
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
133
|
+
none: false
|
134
|
+
requirements:
|
135
|
+
- - ">="
|
136
|
+
- !ruby/object:Gem::Version
|
137
|
+
segments:
|
138
|
+
- 0
|
139
|
+
version: "0"
|
140
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
141
|
+
none: false
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
segments:
|
146
|
+
- 0
|
147
|
+
version: "0"
|
148
|
+
requirements: []
|
149
|
+
|
150
|
+
rubyforge_project:
|
151
|
+
rubygems_version: 1.3.7
|
152
|
+
signing_key:
|
153
|
+
specification_version: 3
|
154
|
+
summary: Command-driven framework, especially suited for web applications
|
155
|
+
test_files:
|
156
|
+
- spec/active_record_integration_spec.rb
|
157
|
+
- spec/command_runner_spec.rb
|
158
|
+
- spec/dsl_spec.rb
|
159
|
+
- spec/spec_helper.rb
|