tiny_navigation 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Coroutine
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.rdoc ADDED
@@ -0,0 +1,107 @@
1
+ = Tiny Navigation
2
+
3
+ == Installation
4
+
5
+ === As a Plugin
6
+
7
+ * For Rails 2.x
8
+
9
+ script/generate plugin install git@github.com:coroutine/tiny_navigation.git
10
+
11
+ === As a gem
12
+
13
+ gem install tiny_navigation
14
+
15
+
16
+ Then, simply call:
17
+
18
+ script/generate tiny_navigation
19
+
20
+ This will place the tiny_navigation.rb config file into your config directory.
21
+
22
+ == Usage
23
+
24
+ TinyNavigation provides an easy-to-use DSL for defining navigation structures;
25
+ these structures are defined in config/tiny_navigation.rb.
26
+
27
+ === Here are a few things TinyNavigation WILL do:
28
+
29
+ * It provides the ability to define and map menu items to resources using the
30
+ convention set forth in the Rails 3 router. For example, to map a menu item
31
+ _Foo_ to the <tt>show</tt> action of the <tt>foos_controller</tt> simply do:
32
+
33
+ navigation :top_tabs do
34
+ item "Foo", :to => "foos#show"
35
+ end
36
+
37
+ If one were to omit the specified action from the <tt>:to</tt> option, the
38
+ navigation item's action would default to <tt>index</tt>.
39
+
40
+ The URL generated from this mapping can be accessed via the <tt>url</tt>
41
+ method of the navigation item.
42
+
43
+ * It provides a <tt>selected</tt> method for getting the selected menu items of
44
+ a navigational structure. For example, for this definition:
45
+
46
+ navigation :main do
47
+ item "Foo", :to => "foos#index"
48
+ item "Bar", :to => "bars#index" do
49
+ item "Baz", :to => "bazzs#index"
50
+ end
51
+ end
52
+
53
+ If the menu item _Foo_ is selected, an array containing that menu item is
54
+ returned. However, if the menu item _Baz_ is selected, an array containing
55
+ the _Bar_ and the _Baz_ menu items. This is useful for generating bread-crumbs
56
+ or simply highlighting both the main nav item and its selected sub-nav item.
57
+
58
+ * It allows for the declaration of custom attributes on nav items. For instance,
59
+ given the configuration in the previous example, we want to right-align the _Bar_
60
+ navigation item. To do this we could simply add another option to the item:
61
+
62
+ navigation :main do
63
+ item "Foo", :to => "foos#index", :align => :left
64
+ item "Bar", :to => "bars#index", :align => :right do
65
+ item "Baz", :to => "bazzs#index"
66
+ end
67
+ end
68
+
69
+ Now, when we render the navigation items we can call <tt>align</tt> on
70
+ the item to get its value:
71
+
72
+ navigation(:main).each do |item|
73
+ if item.align == :right
74
+ ...
75
+ end
76
+ end
77
+
78
+ * It delegates controller method calls made from within the config file to the
79
+ current controller. For instance, let's say you're using Ryan Bates' fantastic
80
+ CanCan gem for authorization--which adds a some methods to the controller, namely
81
+ the <tt>can?</tt> method--and you want to show or hide navigation items based upon
82
+ a user's ability. You can do that! Check it:
83
+
84
+ navigation :main do
85
+ item("Foo", :to => "foos#index") if can? :read, Foo
86
+ item "Bar", :to => "bars#index" do
87
+ item "Baz", :to => "bazzs#index"
88
+ end
89
+ end
90
+
91
+ *IMPORTANT* if a custom attribute is defined on an item, as mentioned earlier,
92
+ it will take precedence over a controller attribute of the same name, thus
93
+ hiding access to the controller attribute.
94
+
95
+ === Here are a couple things that TinyNavigation WILL NOT do:
96
+
97
+ * TinyNavigation makes no attempt at rendering the navigation. That's up
98
+ to you. You may want to render your nav items into <tt>div</tt> tags, while
99
+ I may want to use an unordered list. That's fine, go for it.
100
+
101
+ * TinyNavigation does provide authorization logic for limiting access to
102
+ navigation items; that's a separate concern. It's easy enough to use
103
+ an authorization gem that does that job quite well, and by allowing for calls
104
+ to the current controller from within config/tiny_navigation.rb, you can
105
+ do that.
106
+
107
+ Copyright (c) 2010 Coroutine, released under the MIT license
data/Rakefile ADDED
@@ -0,0 +1,38 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+
5
+ desc 'Default: run unit tests.'
6
+ task :default => :test
7
+
8
+ desc 'Test the tiny_navigation plugin.'
9
+ Rake::TestTask.new(:test) do |t|
10
+ t.libs << 'lib'
11
+ t.libs << 'test'
12
+ t.pattern = 'test/**/*_test.rb'
13
+ t.verbose = true
14
+ end
15
+
16
+ desc 'Generate documentation for the tiny_navigation plugin.'
17
+ Rake::RDocTask.new(:rdoc) do |rdoc|
18
+ rdoc.rdoc_dir = 'rdoc'
19
+ rdoc.title = 'TinyNavigation'
20
+ rdoc.options << '--line-numbers' << '--inline-source'
21
+ rdoc.rdoc_files.include('README')
22
+ rdoc.rdoc_files.include('lib/**/*.rb')
23
+ end
24
+
25
+ begin
26
+ require 'jeweler'
27
+ Jeweler::Tasks.new do |gemspec|
28
+ gemspec.name = "tiny_navigation"
29
+ gemspec.summary = "TinyNavigation provides an easy-to-use DSL for defining navigation structures."
30
+ gemspec.description = "TinyNavigation makes it easy to define site navigation using a small DSL."
31
+ gemspec.email = "gems@coroutine.com"
32
+ gemspec.homepage = "http://github.com/coroutine/tiny_navigation"
33
+ gemspec.authors = ["Coroutine", "Tim Lowrimore"]
34
+ end
35
+ Jeweler::GemcutterTasks.new
36
+ rescue LoadError
37
+ puts "Jeweler not available. Install it with: gem install jeweler"
38
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,5 @@
1
+ Call:
2
+
3
+ rails generate tiny_navigation
4
+
5
+ to generate the config/tiny_navigation.rb file.
@@ -0,0 +1,11 @@
1
+ # Within this file you'll define your navigation data.
2
+
3
+ # navigation :main do
4
+ # # Menu Item
5
+ # item "Foos", :to => "foos#index"
6
+ #
7
+ # # Menu Item with a sub-menu item
8
+ # item "Bars", :to => "bars#index" do
9
+ # item "Bazzes", :to => "bazzes#index"
10
+ # end
11
+ # end
@@ -0,0 +1,7 @@
1
+ class TinyNavigationGenerator < Rails::Generator::Base
2
+ def manifest
3
+ record do |m|
4
+ m.file "tiny_navigation.rb", "config/tiny_navigation.rb"
5
+ end
6
+ end
7
+ end
data/init.rb ADDED
@@ -0,0 +1,2 @@
1
+ require "tiny_navigation"
2
+ ActionController::Base.class_eval { include Coroutine::TinyNavigation::ControllerMethods }
@@ -0,0 +1,128 @@
1
+ require 'tiny_navigation/item'
2
+ require 'tiny_navigation/navigation'
3
+
4
+ module Coroutine
5
+
6
+ # TinyNavigation provides an easy-to-use DSL for defining navigation structures;
7
+ # these structures are defined in config/tiny_navigation.rb.
8
+ #
9
+ # == Here are a few things TinyNavigation WILL do:
10
+ #
11
+ # * It provides the ability to define and map menu items to resources using the
12
+ # convention set forth in the Rails 3 router. For example, to map a menu item
13
+ # _Foo_ to the <tt>show</tt> action of the <tt>foos_controller</tt> simply do:
14
+ #
15
+ # navigation :top_tabs do
16
+ # item "Foo", :to => "foos#show"
17
+ # end
18
+ #
19
+ # If one were to omit the specified action from the <tt>:to</tt> option, the
20
+ # navigation item's action would default to <tt>index</tt>.
21
+ #
22
+ # The URL generated from this mapping can be accessed via the <tt>url</tt>
23
+ # method of the navigation item.
24
+ #
25
+ # * It provides a <tt>selected</tt> method for getting the selected menu items of
26
+ # a navigational structure. For example, for this definition:
27
+ #
28
+ # navigation :main do
29
+ # item "Foo", :to => "foos#index"
30
+ # item "Bar", :to => "bars#index" do
31
+ # item "Baz", :to => "bazzs#index"
32
+ # end
33
+ # end
34
+ #
35
+ # If the menu item _Foo_ is selected, an array containing that menu item is
36
+ # returned. However, if the menu item _Baz_ is selected, an array containing
37
+ # the _Bar_ and the _Baz_ menu items. This is useful for generating bread-crumbs
38
+ # or simply highlighting both the main nav item and its selected sub-nav item.
39
+ #
40
+ # * It allows for the declaration of custom attributes on nav items. For instance,
41
+ # given the configuration in the previous example, we want to right-align the _Bar_
42
+ # navigation item. To do this we could simply add another option to the item:
43
+ #
44
+ # navigation :main do
45
+ # item "Foo", :to => "foos#index", :align => :left
46
+ # item "Bar", :to => "bars#index", :align => :right do
47
+ # item "Baz", :to => "bazzs#index"
48
+ # end
49
+ # end
50
+ #
51
+ # Now, when we render the navigation items we can call <tt>right_align</tt> on
52
+ # the item to get its value:
53
+ #
54
+ # navigation(:main).each do |item|
55
+ # if item.align == :right
56
+ # ...
57
+ # end
58
+ # end
59
+ #
60
+ # * It delegates controller method calls made from within the config file to the
61
+ # current controller. For instance, let's say you're using Ryan Bates' fantastic
62
+ # CanCan gem for authorization--which adds a some methods to the controller, namely
63
+ # the <tt>can?</tt> method--and you want to show or hide navigation items based upon
64
+ # a user's ability. You can do that! Check it:
65
+ #
66
+ # navigation :main do
67
+ # item("Foo", :to => "foos#index") if can? :read, Foo
68
+ # item "Bar", :to => "bars#index" do
69
+ # item "Baz", :to => "bazzs#index"
70
+ # end
71
+ # end
72
+ #
73
+ # *IMPORTANT* if a custom attribute is defined on an item, as mentioned earlier,
74
+ # it will take precedence over a controller attribute of the same name, thus
75
+ # hiding access to the controller attribute.
76
+ #
77
+ # == Here are a couple things that TinyNavigation WILL NOT do:
78
+ #
79
+ # * TinyNavigation makes no attempt at rendering the navigation. That's up
80
+ # to you. You may want to render your nav items into <tt>div</tt> tags, while
81
+ # I may want to use an unordered list. That's fine, go for it.
82
+ #
83
+ # * TinyNavigation does provide authorization logic for limiting access to
84
+ # navigation items; that's a separate concern. It's easy enough to use
85
+ # an authorization gem that does that job quite well, and by allowing for calls
86
+ # to the current controller from within config/tiny_navigation.rb you can
87
+ # do that.
88
+ module TinyNavigation
89
+ class Config #:nodoc:
90
+
91
+ attr_reader :nav
92
+
93
+ def initialize(current_controller, conf=File.join(Rails.root, "config", "tiny_navigation.rb"))
94
+ @current_controller = current_controller
95
+ @nav = {}
96
+
97
+ # Make sure we only load the config file the first time the navigation is loaded
98
+ # and never again.
99
+ Config.class_eval { class << self; attr_reader :file end; @file ||= File.read(conf) }
100
+ self.instance_eval(Config.file);
101
+ end
102
+
103
+ private
104
+
105
+ def navigation(name, &block)
106
+ raise "Navigation names must be unique. You specified '#{name}' twice." if @nav.has_key?(name)
107
+ @nav[name] = Navigation.new(name, @current_controller, &block)
108
+ end
109
+ end
110
+
111
+ # This module adds the navigation method to ActionController::Base and makes
112
+ # the method available as a helper.
113
+ module ControllerMethods
114
+ def self.included(base) #:nodoc:
115
+ base.send :helper_method, :navigation
116
+ end
117
+
118
+ private
119
+
120
+ # Returns a Coroutine::TinyNavigation::Navigation object for the supplied
121
+ # navigation name.
122
+ def navigation(which_navigation)
123
+ config = Coroutine::TinyNavigation::Config.new self
124
+ config.nav[which_navigation]
125
+ end
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,66 @@
1
+ require 'tiny_navigation/navigation'
2
+
3
+ module Coroutine
4
+ module TinyNavigation
5
+ class Item < Navigation
6
+
7
+ # Creates a new instance of a navigation item.
8
+ #
9
+ # <tt>name</tt> is the name of the navigation item. The name should
10
+ # be used for the label text when rendering the navigation item.
11
+ #
12
+ # <tt>current_controller</tt> the currently loaded controller
13
+ #
14
+ # <tt>options</tt> provide configuration options and custom properties
15
+ # to the nav item. Currently, the only configuration option is
16
+ # <tt>:to</tt> which is used to generate the URL of the navigation item.
17
+ # All other options provided to via the <tt>options</tt> hash will be
18
+ # treated as custom properties on the navigation item. These custom
19
+ # properties can be accessed as methods on the navigation item.
20
+ #
21
+ # The block given to the navigation item is used to define sub-navigation
22
+ # items of this item.
23
+ def initialize(name, current_controller, options={}, &block)
24
+ super name, current_controller, &block
25
+ set_controller_and_action options.delete(:to)
26
+ @extra_options = options
27
+ end
28
+
29
+ # Indicates whether the navigation item is currently selected. This
30
+ # takes into account any sub-nav items such that a parent item is selected
31
+ # if it has a selected child.
32
+ def selected?
33
+ @controller_name == @current_controller.controller_name || @items.any?(&:selected?)
34
+ end
35
+
36
+ # Returns the URL to which the navigation item points. This should be
37
+ # used in a scenario where the navigation item represents a link and the
38
+ # URL is the href of that link.
39
+ def url
40
+ @current_controller.url_for :controller => @controller_name, :action => @action_name
41
+ end
42
+
43
+ def method_missing(method_name, *args) #:nodoc:
44
+
45
+ # The extra_options hash takes precendence when looking for the
46
+ # called method. Otherwise, we'll let the super-class forward
47
+ # the method call to the current controller.
48
+ if @extra_options.has_key? method_name
49
+ @extra_options[method_name]
50
+ else
51
+ super method_name, *args
52
+ end
53
+ end
54
+
55
+ private
56
+
57
+ def set_controller_and_action(to)
58
+ if to
59
+ controller_and_action = to.split "#"
60
+ @controller_name = controller_and_action.shift
61
+ @action_name = controller_and_action.shift || "index"
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,47 @@
1
+ module Coroutine
2
+ module TinyNavigation
3
+ class Navigation
4
+
5
+ attr_reader :name, :items
6
+
7
+ # Creates a new navigation.
8
+ #
9
+ # <tt>name</tt> is the unique identifer of the navigation.
10
+ #
11
+ # <tt>current_controller</tt> the currently loaded controller
12
+ #
13
+ # The block given to the navigation item is used to define navigation
14
+ # items of this navigation object.
15
+ def initialize(name, current_controller, &block)
16
+ @items = []
17
+ @name = name
18
+ @current_controller = current_controller
19
+
20
+ self.instance_eval(&block) if block_given?
21
+ end
22
+
23
+ # Returns an array of selected navigation items. The array represents a
24
+ # bread-crumb list in that the head of the list represents a top-level
25
+ # navigation item, and the tail of the list represents selected sub-navigation
26
+ # items.
27
+ def selected(item=self)
28
+ items = []
29
+ item.items.each do |item|
30
+ items << item << selected(item) if item.selected?
31
+ end
32
+ items.flatten
33
+ end
34
+
35
+ def method_missing(method_name, *args) #:nodoc:
36
+ @current_controller.send method_name, *args
37
+ end
38
+
39
+ private
40
+
41
+ def item(name, options={}, &block)
42
+ @items << Item.new(name, @current_controller, options, &block)
43
+ end
44
+
45
+ end
46
+ end
47
+ end
Binary file
@@ -0,0 +1,54 @@
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{tiny_navigation}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Coroutine", "Tim Lowrimore"]
12
+ s.date = %q{2010-05-13}
13
+ s.description = %q{TinyNavigation makes it easy to define site navigation using a small DSL.}
14
+ s.email = %q{gems@coroutine.com}
15
+ s.extra_rdoc_files = [
16
+ "README.rdoc"
17
+ ]
18
+ s.files = [
19
+ "MIT-LICENSE",
20
+ "README.rdoc",
21
+ "Rakefile",
22
+ "VERSION",
23
+ "generators/tiny_navigation/USAGE",
24
+ "generators/tiny_navigation/tiny_navigation_generator.rb",
25
+ "generators/tiny_navigation/templates/tiny_navigation.rb",
26
+ "init.rb",
27
+ "lib/tiny_navigation.rb",
28
+ "lib/tiny_navigation/item.rb",
29
+ "lib/tiny_navigation/navigation.rb",
30
+ "test/navigation_test.rb",
31
+ "test/test_helper.rb",
32
+ "uninstall.rb"
33
+ ]
34
+ s.homepage = %q{http://github.com/coroutine/tiny_navigation}
35
+ s.rdoc_options = ["--charset=UTF-8"]
36
+ s.require_paths = ["lib"]
37
+ s.rubygems_version = %q{1.3.6}
38
+ s.summary = %q{TinyNavigation provides an easy-to-use DSL for defining navigation structures.}
39
+ s.test_files = [
40
+ "test/navigation_test.rb",
41
+ "test/test_helper.rb"
42
+ ]
43
+
44
+ if s.respond_to? :specification_version then
45
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
46
+ s.specification_version = 3
47
+
48
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
49
+ else
50
+ end
51
+ else
52
+ end
53
+ end
54
+
@@ -0,0 +1,49 @@
1
+ require 'test_helper'
2
+ require 'tiny_navigation/navigation'
3
+ require 'tiny_navigation/item'
4
+
5
+ class NavigationTest < ActiveSupport::TestCase
6
+
7
+ def setup
8
+ @controller_class = Struct.new(:controller_name)
9
+ @current_controller = @controller_class.new("tests")
10
+ end
11
+
12
+ test "can create a navigation with an item" do
13
+ nav = navigation do
14
+ item "Foos", :to => "foos#index"
15
+ end
16
+
17
+ assert_equal nav.items.length, 1
18
+ end
19
+
20
+ test "has correct selected item in one level of nesting" do
21
+ nav = navigation do
22
+ item "Tests", :to => "tests"
23
+ end
24
+
25
+ assert_equal nav.selected.length, 1
26
+ assert_equal nav.selected.first.name, "Tests"
27
+ end
28
+
29
+ test "has correct selected items in multiple levels of nesting" do
30
+ nav = navigation do
31
+ item "Foos", :to => "foos#index" do
32
+ item "Tests", :to => "tests#index"
33
+ end
34
+ end
35
+
36
+ assert_equal nav.selected.length, 2
37
+ assert_equal nav.selected.map(&:name), ["Foos", "Tests"]
38
+ end
39
+
40
+ # ------------------------------------------------------------------------------
41
+ # Helpers
42
+ # ------------------------------------------------------------------------------
43
+
44
+ private
45
+
46
+ def navigation(name = :main, current_controller = @current_controller, &block)
47
+ Coroutine::TinyNavigation::Navigation.new(name, current_controller, &block)
48
+ end
49
+ end
@@ -0,0 +1,3 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'active_support'
data/uninstall.rb ADDED
@@ -0,0 +1 @@
1
+ # Uninstall hook code here
metadata ADDED
@@ -0,0 +1,79 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tiny_navigation
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
10
+ platform: ruby
11
+ authors:
12
+ - Coroutine
13
+ - Tim Lowrimore
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-05-13 00:00:00 -05:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: TinyNavigation makes it easy to define site navigation using a small DSL.
23
+ email: gems@coroutine.com
24
+ executables: []
25
+
26
+ extensions: []
27
+
28
+ extra_rdoc_files:
29
+ - README.rdoc
30
+ files:
31
+ - MIT-LICENSE
32
+ - README.rdoc
33
+ - Rakefile
34
+ - VERSION
35
+ - generators/tiny_navigation/USAGE
36
+ - generators/tiny_navigation/templates/tiny_navigation.rb
37
+ - generators/tiny_navigation/tiny_navigation_generator.rb
38
+ - init.rb
39
+ - lib/tiny_navigation.rb
40
+ - lib/tiny_navigation/item.rb
41
+ - lib/tiny_navigation/navigation.rb
42
+ - pkg/simple_navigation-0.1.0.gem
43
+ - simple_navigation.gemspec
44
+ - test/navigation_test.rb
45
+ - test/test_helper.rb
46
+ - uninstall.rb
47
+ has_rdoc: true
48
+ homepage: http://github.com/coroutine/tiny_navigation
49
+ licenses: []
50
+
51
+ post_install_message:
52
+ rdoc_options:
53
+ - --charset=UTF-8
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ segments:
61
+ - 0
62
+ version: "0"
63
+ required_rubygems_version: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ segments:
68
+ - 0
69
+ version: "0"
70
+ requirements: []
71
+
72
+ rubyforge_project:
73
+ rubygems_version: 1.3.6
74
+ signing_key:
75
+ specification_version: 3
76
+ summary: TinyNavigation provides an easy-to-use DSL for defining navigation structures.
77
+ test_files:
78
+ - test/navigation_test.rb
79
+ - test/test_helper.rb