scottmotte-merb_auth_slice_multisite 0.1.1 → 0.2.6
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/LICENSE +2 -2
- data/README.textile +115 -0
- data/VERSION.yml +2 -2
- data/bin/autospec +31 -0
- data/bin/autotest +31 -0
- data/bin/common.rb +68 -0
- data/bin/edit_json.rb +31 -0
- data/bin/erubis +31 -0
- data/bin/merb +31 -0
- data/bin/multigem +31 -0
- data/bin/multiruby +31 -0
- data/bin/multiruby_setup +31 -0
- data/bin/parse_tree_abc +31 -0
- data/bin/parse_tree_audit +31 -0
- data/bin/parse_tree_deps +31 -0
- data/bin/parse_tree_show +31 -0
- data/bin/r2r_show +31 -0
- data/bin/rackup +31 -0
- data/bin/rake +31 -0
- data/bin/rake2thor +31 -0
- data/bin/slice +31 -0
- data/bin/spec +31 -0
- data/bin/thor +31 -0
- data/bin/unit_diff +31 -0
- data/bin/zentest +31 -0
- data/lib/merb_auth_slice_multisite.rb +41 -10
- data/lib/merb_auth_slice_multisite/mixins/user_belongs_to_site.rb +63 -0
- data/lib/merb_auth_slice_multisite/mixins/user_belongs_to_site/dm_user_belongs_to_site.rb +28 -0
- data/spec/mixins/user_belongs_to_site_spec.rb +56 -0
- data/spec/models/site_spec.rb +83 -0
- data/spec/spec_helper.rb +46 -1
- metadata +56 -8
- data/README.rdoc +0 -7
- data/spec/requests/main_spec.rb +0 -30
- data/test/merb_auth_slice_multisite_test.rb +0 -7
- data/test/test_helper.rb +0 -10
data/LICENSE
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Copyright (c) 2009
|
1
|
+
Copyright (c) 2009 Scott Motte
|
2
2
|
|
3
3
|
Permission is hereby granted, free of charge, to any person obtaining
|
4
4
|
a copy of this software and associated documentation files (the
|
@@ -17,4 +17,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
17
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
18
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
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.
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.textile
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
h1. MerbAuthSliceMultisite
|
2
|
+
|
3
|
+
This slice setups multisite capabilities in your merb application with an authentication check.
|
4
|
+
|
5
|
+
It works for subdomains (i.e. coolcars.yourapp.com) or for domains routed to your application via your server (i.e. coolcars.com).
|
6
|
+
|
7
|
+
It has a check to make a user to the site he's trying to login to. If the user does not match than authentication fails.
|
8
|
+
|
9
|
+
h2. Instructions for installation:
|
10
|
+
|
11
|
+
1. Add github as a gem source & install
|
12
|
+
<pre><code>
|
13
|
+
gem sources -a http://gems.github.com
|
14
|
+
sudo gem install scottmotte-merb_auth_slice_multisite
|
15
|
+
</code></pre>
|
16
|
+
|
17
|
+
2. Setup your application to use the gem.* Add the following to the end of dependencies.rb.
|
18
|
+
<pre><code>dependency "scottmotte-merb_auth_slice_multisite", :require_as => 'merb_auth_slice_multisite'</code></pre>
|
19
|
+
|
20
|
+
3. Add in mixin. In your user model or in merb/merb-auth/setup.rb add the mixin
|
21
|
+
include Merb::Authentication::Mixins::UserBelongsToSite and then migrate your database.
|
22
|
+
<pre><code>
|
23
|
+
# in model
|
24
|
+
class User
|
25
|
+
include Merb::Authentication::Mixins::UserBelongsToSite
|
26
|
+
end
|
27
|
+
|
28
|
+
# or as I prefer in merb/merb-auth/setup.rb
|
29
|
+
Merb::Authentication.user_class.class_eval{
|
30
|
+
include Merb::Authentication::Mixins::SaltedUser
|
31
|
+
include Merb::Authentication::Mixins::ActivatedUser
|
32
|
+
include Merb::Authentication::Mixins::UserBelongsToSite # <-- this one
|
33
|
+
}
|
34
|
+
</code></pre>
|
35
|
+
|
36
|
+
_Don't forget to migrate your database schema with rake db:autoupgrade or rake db:automigrate_
|
37
|
+
|
38
|
+
|
39
|
+
4. Setup @current_site value. I haven't worked out how to make it automatically accessible yet so for now I just paste the following into my app's application.rb file.
|
40
|
+
<pre><code>
|
41
|
+
before :get_site
|
42
|
+
def get_site
|
43
|
+
# uses @current_site to create pages under appropriate site like @current_site.pages.new
|
44
|
+
# if there is a subdomain then fetch from subdomain, otherwise fetch from domain
|
45
|
+
if request.first_subdomain != nil
|
46
|
+
@current_site = Site.first(:subdomain => request.first_subdomain)
|
47
|
+
else
|
48
|
+
@current_site = Site.first(:domain => request.domain)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
</pre></code>
|
52
|
+
|
53
|
+
|
54
|
+
Check out the video. <a href="http://vimeo.com/2844037">How to install merb_auth_slice_multisite in your application.</a>
|
55
|
+
|
56
|
+
|
57
|
+
h2. Additional details:
|
58
|
+
|
59
|
+
Schema/Migrations. The mixin adds some fields to your user model. Where needed include these in your migrations if you are using migrations.
|
60
|
+
<pre><code># Relationships/Associations
|
61
|
+
belongs_to :site
|
62
|
+
property :site_id, Integer
|
63
|
+
validates_is_unique :login, :scope => :site_id
|
64
|
+
</code></pre>
|
65
|
+
|
66
|
+
Site model. You're probably wondering where the heck is the site model. It's in the slice. You can override it by running one of the rake tasks or you can create your own site.rb model and add additional fields. For example, if you have pages under a site, you might do something like:
|
67
|
+
<pre><code># site.rb
|
68
|
+
class Site
|
69
|
+
has n, :pages, :order => [:position.asc]
|
70
|
+
end
|
71
|
+
</code></pre>
|
72
|
+
|
73
|
+
@current_site. You can use @current_site in your controllers like so:
|
74
|
+
<pre><code>
|
75
|
+
class Pages < Application
|
76
|
+
# provides :xml, :yaml, :js
|
77
|
+
|
78
|
+
def index
|
79
|
+
@pages = @current_site.pages.all
|
80
|
+
display @pages
|
81
|
+
end
|
82
|
+
|
83
|
+
def show(id)
|
84
|
+
@page = @current_site.pages.get(id)
|
85
|
+
raise NotFound unless @page
|
86
|
+
display @page
|
87
|
+
end
|
88
|
+
|
89
|
+
end # Pages
|
90
|
+
</pre></code>
|
91
|
+
|
92
|
+
|
93
|
+
h2. Assumptions
|
94
|
+
|
95
|
+
* works for subdomains (i.e. coolcars.yourapp.com) or for domains routed to your application via your server (i.e. coolcars.com). see lines 40-55 in lib/merb_auth_slice_multisite.rb and lib/merb_auth_slice_multisite/mixins/user_belongs_to_site/dm_user_belongs_to_site.rb for details
|
96
|
+
* merb only
|
97
|
+
* merb-auth-core dependency
|
98
|
+
* merb-auth-more dependency
|
99
|
+
* *only supports datamapper so far* (help me extend it! fork the project and request me to pull)
|
100
|
+
* A site has n (has_many) users. A user belongs_to a site.
|
101
|
+
* You can have multiple users with the same username and password as long as they each belong_to a different site. For example, there can be an admin user with the credentials { :login => 'admin', :email => 'admin@example.org', :site_id => 1} and an admin user with the credentials { :login => 'admin', :email => 'admin@example.org', :site_id => 66}. As long as the site_id is different then it is ok. This allows more freedom when your users want to setup multiple sites.
|
102
|
+
|
103
|
+
|
104
|
+
h2. How does it work?
|
105
|
+
|
106
|
+
When logging in the "user" object found by merb-auth-core will be asked whether the user's site_id matches the id of the current_site. It user merb-auth's after_authentication method to do this.
|
107
|
+
|
108
|
+
The current_site is queried by using a model lookup off the request variable.
|
109
|
+
|
110
|
+
|
111
|
+
h2. Rake tasks
|
112
|
+
|
113
|
+
To see all available tasks for MerbAuthSliceMultisite run:
|
114
|
+
|
115
|
+
rake -T slices:merb_auth_slice_multisite
|
data/VERSION.yml
CHANGED
data/bin/autospec
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# This was added by Merb's bundler
|
4
|
+
|
5
|
+
require "rubygems"
|
6
|
+
require File.join(File.dirname(__FILE__), "common")
|
7
|
+
|
8
|
+
gems_dir = File.join(File.dirname(__FILE__), '..', 'gems')
|
9
|
+
|
10
|
+
if File.directory?(gems_dir)
|
11
|
+
$BUNDLE = true
|
12
|
+
Gem.clear_paths
|
13
|
+
Gem.path.replace([File.expand_path(gems_dir)])
|
14
|
+
ENV["PATH"] = "#{File.dirname(__FILE__)}:#{ENV["PATH"]}"
|
15
|
+
|
16
|
+
gem_file = File.join(gems_dir, "specifications", "rspec-*.gemspec")
|
17
|
+
|
18
|
+
if local_gem = Dir[gem_file].last
|
19
|
+
version = File.basename(local_gem)[/-([\.\d]+)\.gemspec$/, 1]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
version ||= ">= 0"
|
24
|
+
|
25
|
+
if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
|
26
|
+
version = $1
|
27
|
+
ARGV.shift
|
28
|
+
end
|
29
|
+
|
30
|
+
gem 'rspec', version
|
31
|
+
load 'autospec'
|
data/bin/autotest
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# This was added by Merb's bundler
|
4
|
+
|
5
|
+
require "rubygems"
|
6
|
+
require File.join(File.dirname(__FILE__), "common")
|
7
|
+
|
8
|
+
gems_dir = File.join(File.dirname(__FILE__), '..', 'gems')
|
9
|
+
|
10
|
+
if File.directory?(gems_dir)
|
11
|
+
$BUNDLE = true
|
12
|
+
Gem.clear_paths
|
13
|
+
Gem.path.replace([File.expand_path(gems_dir)])
|
14
|
+
ENV["PATH"] = "#{File.dirname(__FILE__)}:#{ENV["PATH"]}"
|
15
|
+
|
16
|
+
gem_file = File.join(gems_dir, "specifications", "ZenTest-*.gemspec")
|
17
|
+
|
18
|
+
if local_gem = Dir[gem_file].last
|
19
|
+
version = File.basename(local_gem)[/-([\.\d]+)\.gemspec$/, 1]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
version ||= ">= 0"
|
24
|
+
|
25
|
+
if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
|
26
|
+
version = $1
|
27
|
+
ARGV.shift
|
28
|
+
end
|
29
|
+
|
30
|
+
gem 'ZenTest', version
|
31
|
+
load 'autotest'
|
data/bin/common.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
# This was added via Merb's bundler
|
2
|
+
|
3
|
+
require "rubygems"
|
4
|
+
require "rubygems/source_index"
|
5
|
+
|
6
|
+
module Gem
|
7
|
+
BUNDLED_SPECS = File.join(Dir.pwd, "gems", "specifications")
|
8
|
+
MAIN_INDEX = Gem::SourceIndex.from_gems_in(BUNDLED_SPECS)
|
9
|
+
FALLBACK_INDEX = Gem::SourceIndex.from_installed_gems
|
10
|
+
|
11
|
+
def self.source_index
|
12
|
+
MultiSourceIndex.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.searcher
|
16
|
+
MultiPathSearcher.new
|
17
|
+
end
|
18
|
+
|
19
|
+
class ArbitrarySearcher < GemPathSearcher
|
20
|
+
def initialize(source_index)
|
21
|
+
@source_index = source_index
|
22
|
+
super()
|
23
|
+
end
|
24
|
+
|
25
|
+
def init_gemspecs
|
26
|
+
@source_index.map { |_, spec| spec }.sort { |a,b|
|
27
|
+
(a.name <=> b.name).nonzero? || (b.version <=> a.version)
|
28
|
+
}
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class MultiPathSearcher
|
33
|
+
def initialize
|
34
|
+
@main_searcher = ArbitrarySearcher.new(MAIN_INDEX)
|
35
|
+
@fallback_searcher = ArbitrarySearcher.new(FALLBACK_INDEX)
|
36
|
+
end
|
37
|
+
|
38
|
+
def find(path)
|
39
|
+
try = @main_searcher.find(path)
|
40
|
+
return try if try
|
41
|
+
@fallback_searcher.find(path)
|
42
|
+
end
|
43
|
+
|
44
|
+
def find_all(path)
|
45
|
+
try = @main_searcher.find_all(path)
|
46
|
+
return try unless try.empty?
|
47
|
+
@fallback_searcher.find_all(path)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class MultiSourceIndex
|
52
|
+
# Used by merb.thor to confirm; not needed when MSI is in use
|
53
|
+
def load_gems_in(*args)
|
54
|
+
end
|
55
|
+
|
56
|
+
def search(*args)
|
57
|
+
try = MAIN_INDEX.search(*args)
|
58
|
+
return try unless try.empty?
|
59
|
+
FALLBACK_INDEX.search(*args)
|
60
|
+
end
|
61
|
+
|
62
|
+
def find_name(*args)
|
63
|
+
try = MAIN_INDEX.find_name(*args)
|
64
|
+
return try unless try.empty?
|
65
|
+
FALLBACK_INDEX.find_name(*args)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
data/bin/edit_json.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# This was added by Merb's bundler
|
4
|
+
|
5
|
+
require "rubygems"
|
6
|
+
require File.join(File.dirname(__FILE__), "common")
|
7
|
+
|
8
|
+
gems_dir = File.join(File.dirname(__FILE__), '..', 'gems')
|
9
|
+
|
10
|
+
if File.directory?(gems_dir)
|
11
|
+
$BUNDLE = true
|
12
|
+
Gem.clear_paths
|
13
|
+
Gem.path.replace([File.expand_path(gems_dir)])
|
14
|
+
ENV["PATH"] = "#{File.dirname(__FILE__)}:#{ENV["PATH"]}"
|
15
|
+
|
16
|
+
gem_file = File.join(gems_dir, "specifications", "json-*.gemspec")
|
17
|
+
|
18
|
+
if local_gem = Dir[gem_file].last
|
19
|
+
version = File.basename(local_gem)[/-([\.\d]+)\.gemspec$/, 1]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
version ||= ">= 0"
|
24
|
+
|
25
|
+
if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
|
26
|
+
version = $1
|
27
|
+
ARGV.shift
|
28
|
+
end
|
29
|
+
|
30
|
+
gem 'json', version
|
31
|
+
load 'edit_json.rb'
|
data/bin/erubis
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# This was added by Merb's bundler
|
4
|
+
|
5
|
+
require "rubygems"
|
6
|
+
require File.join(File.dirname(__FILE__), "common")
|
7
|
+
|
8
|
+
gems_dir = File.join(File.dirname(__FILE__), '..', 'gems')
|
9
|
+
|
10
|
+
if File.directory?(gems_dir)
|
11
|
+
$BUNDLE = true
|
12
|
+
Gem.clear_paths
|
13
|
+
Gem.path.replace([File.expand_path(gems_dir)])
|
14
|
+
ENV["PATH"] = "#{File.dirname(__FILE__)}:#{ENV["PATH"]}"
|
15
|
+
|
16
|
+
gem_file = File.join(gems_dir, "specifications", "erubis-*.gemspec")
|
17
|
+
|
18
|
+
if local_gem = Dir[gem_file].last
|
19
|
+
version = File.basename(local_gem)[/-([\.\d]+)\.gemspec$/, 1]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
version ||= ">= 0"
|
24
|
+
|
25
|
+
if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
|
26
|
+
version = $1
|
27
|
+
ARGV.shift
|
28
|
+
end
|
29
|
+
|
30
|
+
gem 'erubis', version
|
31
|
+
load 'erubis'
|
data/bin/merb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# This was added by Merb's bundler
|
4
|
+
|
5
|
+
require "rubygems"
|
6
|
+
require File.join(File.dirname(__FILE__), "common")
|
7
|
+
|
8
|
+
gems_dir = File.join(File.dirname(__FILE__), '..', 'gems')
|
9
|
+
|
10
|
+
if File.directory?(gems_dir)
|
11
|
+
$BUNDLE = true
|
12
|
+
Gem.clear_paths
|
13
|
+
Gem.path.replace([File.expand_path(gems_dir)])
|
14
|
+
ENV["PATH"] = "#{File.dirname(__FILE__)}:#{ENV["PATH"]}"
|
15
|
+
|
16
|
+
gem_file = File.join(gems_dir, "specifications", "merb-core-*.gemspec")
|
17
|
+
|
18
|
+
if local_gem = Dir[gem_file].last
|
19
|
+
version = File.basename(local_gem)[/-([\.\d]+)\.gemspec$/, 1]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
version ||= ">= 0"
|
24
|
+
|
25
|
+
if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
|
26
|
+
version = $1
|
27
|
+
ARGV.shift
|
28
|
+
end
|
29
|
+
|
30
|
+
gem 'merb-core', version
|
31
|
+
load 'merb'
|
data/bin/multigem
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# This was added by Merb's bundler
|
4
|
+
|
5
|
+
require "rubygems"
|
6
|
+
require File.join(File.dirname(__FILE__), "common")
|
7
|
+
|
8
|
+
gems_dir = File.join(File.dirname(__FILE__), '..', 'gems')
|
9
|
+
|
10
|
+
if File.directory?(gems_dir)
|
11
|
+
$BUNDLE = true
|
12
|
+
Gem.clear_paths
|
13
|
+
Gem.path.replace([File.expand_path(gems_dir)])
|
14
|
+
ENV["PATH"] = "#{File.dirname(__FILE__)}:#{ENV["PATH"]}"
|
15
|
+
|
16
|
+
gem_file = File.join(gems_dir, "specifications", "ZenTest-*.gemspec")
|
17
|
+
|
18
|
+
if local_gem = Dir[gem_file].last
|
19
|
+
version = File.basename(local_gem)[/-([\.\d]+)\.gemspec$/, 1]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
version ||= ">= 0"
|
24
|
+
|
25
|
+
if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
|
26
|
+
version = $1
|
27
|
+
ARGV.shift
|
28
|
+
end
|
29
|
+
|
30
|
+
gem 'ZenTest', version
|
31
|
+
load 'multigem'
|
data/bin/multiruby
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# This was added by Merb's bundler
|
4
|
+
|
5
|
+
require "rubygems"
|
6
|
+
require File.join(File.dirname(__FILE__), "common")
|
7
|
+
|
8
|
+
gems_dir = File.join(File.dirname(__FILE__), '..', 'gems')
|
9
|
+
|
10
|
+
if File.directory?(gems_dir)
|
11
|
+
$BUNDLE = true
|
12
|
+
Gem.clear_paths
|
13
|
+
Gem.path.replace([File.expand_path(gems_dir)])
|
14
|
+
ENV["PATH"] = "#{File.dirname(__FILE__)}:#{ENV["PATH"]}"
|
15
|
+
|
16
|
+
gem_file = File.join(gems_dir, "specifications", "ZenTest-*.gemspec")
|
17
|
+
|
18
|
+
if local_gem = Dir[gem_file].last
|
19
|
+
version = File.basename(local_gem)[/-([\.\d]+)\.gemspec$/, 1]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
version ||= ">= 0"
|
24
|
+
|
25
|
+
if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
|
26
|
+
version = $1
|
27
|
+
ARGV.shift
|
28
|
+
end
|
29
|
+
|
30
|
+
gem 'ZenTest', version
|
31
|
+
load 'multiruby'
|
data/bin/multiruby_setup
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# This was added by Merb's bundler
|
4
|
+
|
5
|
+
require "rubygems"
|
6
|
+
require File.join(File.dirname(__FILE__), "common")
|
7
|
+
|
8
|
+
gems_dir = File.join(File.dirname(__FILE__), '..', 'gems')
|
9
|
+
|
10
|
+
if File.directory?(gems_dir)
|
11
|
+
$BUNDLE = true
|
12
|
+
Gem.clear_paths
|
13
|
+
Gem.path.replace([File.expand_path(gems_dir)])
|
14
|
+
ENV["PATH"] = "#{File.dirname(__FILE__)}:#{ENV["PATH"]}"
|
15
|
+
|
16
|
+
gem_file = File.join(gems_dir, "specifications", "ZenTest-*.gemspec")
|
17
|
+
|
18
|
+
if local_gem = Dir[gem_file].last
|
19
|
+
version = File.basename(local_gem)[/-([\.\d]+)\.gemspec$/, 1]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
version ||= ">= 0"
|
24
|
+
|
25
|
+
if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
|
26
|
+
version = $1
|
27
|
+
ARGV.shift
|
28
|
+
end
|
29
|
+
|
30
|
+
gem 'ZenTest', version
|
31
|
+
load 'multiruby_setup'
|