diff options
Diffstat (limited to 'railties/doc/guides/source')
9 files changed, 179 insertions, 151 deletions
diff --git a/railties/doc/guides/source/creating_plugins/acts_as_yaffle.txt b/railties/doc/guides/source/creating_plugins/acts_as_yaffle.txt index 12d40deb18..06878543e4 100644 --- a/railties/doc/guides/source/creating_plugins/acts_as_yaffle.txt +++ b/railties/doc/guides/source/creating_plugins/acts_as_yaffle.txt @@ -4,10 +4,10 @@ A common pattern in plugins is to add a method called `acts_as_something` to mod To keep things clean, create a new test file called 'acts_as_yaffle_test.rb' in your plugin's test directory and require your test helper. +*vendor/plugins/yaffle/test/acts_as_yaffle_test.rb* + [source, ruby] ------------------------------------------------------ -# File: vendor/plugins/yaffle/test/acts_as_yaffle_test.rb - require File.dirname(__FILE__) + '/test_helper.rb' class Hickwall < ActiveRecord::Base @@ -18,16 +18,18 @@ class ActsAsYaffleTest < Test::Unit::TestCase end ------------------------------------------------------ +*vendor/plugins/lib/acts_as_yaffle.rb* + [source, ruby] ------------------------------------------------------ -# File: vendor/plugins/lib/acts_as_yaffle.rb - module Yaffle end ------------------------------------------------------ One of the most common plugin patterns for `acts_as_yaffle` plugins is to structure your file like so: +*vendor/plugins/lib/acts_as_yaffle.rb* + [source, ruby] ------------------------------------------------------ module Yaffle @@ -65,10 +67,10 @@ end Now that test should pass. Since your plugin is going to work with field names, you need to allow people to define the field names, in case there is a naming conflict. You can write a few simple tests for this: +*vendor/plugins/yaffle/test/acts_as_yaffle_test.rb* + [source, ruby] ------------------------------------------------------ -# File: vendor/plugins/yaffle/test/acts_as_yaffle_test.rb - require File.dirname(__FILE__) + '/test_helper.rb' class ActsAsYaffleTest < Test::Unit::TestCase @@ -92,10 +94,10 @@ end To make these tests pass, you could modify your `acts_as_yaffle` file like so: +*vendor/plugins/yaffle/lib/acts_as_yaffle.rb* + [source, ruby] ------------------------------------------------------ -# File: vendor/plugins/yaffle/lib/acts_as_yaffle.rb - module Yaffle def self.included(base) base.send :extend, ClassMethods @@ -117,10 +119,10 @@ end Now you can add tests for the instance methods, and the instance method itself: +*vendor/plugins/yaffle/test/acts_as_yaffle_test.rb* + [source, ruby] ------------------------------------------------------ -# File: vendor/plugins/yaffle/test/acts_as_yaffle_test.rb - require File.dirname(__FILE__) + '/test_helper.rb' class ActsAsYaffleTest < Test::Unit::TestCase @@ -163,10 +165,10 @@ class ActsAsYaffleTest < Test::Unit::TestCase end ------------------------------------------------------ +*vendor/plugins/yaffle/lib/acts_as_yaffle.rb* + [source, ruby] ------------------------------------------------------ -# File: vendor/plugins/yaffle/lib/acts_as_yaffle.rb - module Yaffle def self.included(base) base.send :extend, ClassMethods @@ -190,4 +192,5 @@ module Yaffle end ------------------------------------------------------ -Note the use of `write_attribute` to write to the field in model. +.Editor's note: +NOTE: The use of `write_attribute` to write to the field in model is just one example of how a plugin can interact with the model, and will not always be the right method to use. For example, you could also use `send("#{self.class.yaffle_text_field}=", string.to_squawk)`. diff --git a/railties/doc/guides/source/creating_plugins/custom_generator.txt b/railties/doc/guides/source/creating_plugins/custom_generator.txt index 6d9613ea01..a8cf1b48ce 100644 --- a/railties/doc/guides/source/creating_plugins/custom_generator.txt +++ b/railties/doc/guides/source/creating_plugins/custom_generator.txt @@ -8,19 +8,20 @@ You may have noticed above that you can used one of the built-in rails migration Working with the internals of generators is beyond the scope of this tutorial, but here is a basic example: +*vendor/plugins/yaffle/init.rb* + [source, ruby] ----------------------------------------------------------- -# File: vendor/plugins/yaffle/init.rb require "commands" Rails::Generator::Commands::Create.send :include, Yaffle::Generator::Commands::Create Rails::Generator::Commands::Destroy.send :include, Yaffle::Generator::Commands::Destroy Rails::Generator::Commands::List.send :include, Yaffle::Generator::Commands::List ----------------------------------------------------------- +*vendor/plugins/yaffle/lib/commands.rb* + [source, ruby] ----------------------------------------------------------- -# File: vendor/plugins/yaffle/lib/commands.rb - require 'rails_generator' require 'rails_generator/commands' @@ -49,16 +50,15 @@ module Yaffle #:nodoc: end ----------------------------------------------------------- +*vendor/plugins/yaffle/generators/yaffle/templates/definition.txt* ----------------------------------------------------------- -# File: vendor/plugins/yaffle/generators/yaffle/templates/definition.txt - Yaffle is a bird ----------------------------------------------------------- +*vendor/plugins/yaffle/generators/yaffle/yaffle_generator.rb* + [source, ruby] ----------------------------------------------------------- -# File: vendor/plugins/yaffle/generators/yaffle/yaffle_generator.rb - class YaffleGenerator < Rails::Generator::NamedBase def manifest m.yaffle_definition diff --git a/railties/doc/guides/source/creating_plugins/custom_route.txt b/railties/doc/guides/source/creating_plugins/custom_route.txt index 7e399247ee..1fce902a4e 100644 --- a/railties/doc/guides/source/creating_plugins/custom_route.txt +++ b/railties/doc/guides/source/creating_plugins/custom_route.txt @@ -2,10 +2,10 @@ Testing routes in plugins can be complex, especially if the controllers are also in the plugin itself. Jamis Buck showed a great example of this in http://weblog.jamisbuck.org/2006/10/26/monkey-patching-rails-extending-routes-2. +*vendor/plugins/yaffle/test/routing_test.rb* + [source, ruby] -------------------------------------------------------- -# File: vendor/plugins/yaffle/test/routing_test.rb - require "#{File.dirname(__FILE__)}/test_helper" class RoutingTest < Test::Unit::TestCase @@ -33,18 +33,18 @@ class RoutingTest < Test::Unit::TestCase end -------------------------------------------------------- +*vendor/plugins/yaffle/init.rb* + [source, ruby] -------------------------------------------------------- -# File: vendor/plugins/yaffle/init.rb - require "routing" ActionController::Routing::RouteSet::Mapper.send :include, Yaffle::Routing::MapperExtensions -------------------------------------------------------- +*vendor/plugins/yaffle/lib/routing.rb* + [source, ruby] -------------------------------------------------------- -# File: vendor/plugins/yaffle/lib/routing.rb - module Yaffle #:nodoc: module Routing #:nodoc: module MapperExtensions @@ -56,10 +56,10 @@ module Yaffle #:nodoc: end -------------------------------------------------------- +*config/routes.rb* + [source, ruby] -------------------------------------------------------- -# File: config/routes.rb - ActionController::Routing::Routes.draw do |map| ... map.yaffles diff --git a/railties/doc/guides/source/creating_plugins/index.txt b/railties/doc/guides/source/creating_plugins/index.txt index f2ed6ed8bb..d3042f8d56 100644 --- a/railties/doc/guides/source/creating_plugins/index.txt +++ b/railties/doc/guides/source/creating_plugins/index.txt @@ -1,68 +1,32 @@ The Basics of Creating Rails Plugins ==================================== -Pretend for a moment that you are an avid bird watcher. Your favorite bird is the Yaffle, and you want to create a plugin that allows other developers to share in the Yaffle goodness. - -In this tutorial you will learn how to create a plugin that includes: - - * Core Extensions - extending String with a `to_squawk` method: -+ -[source, ruby] -------------------------------------------- -# Anywhere -"hello!".to_squawk # => "squawk! hello!" -------------------------------------------- - -* An `acts_as_yaffle` method for ActiveRecord models that adds a `squawk` method: -+ -[source, ruby] -------------------------------------------- -class Hickwall < ActiveRecord::Base - acts_as_yaffle :yaffle_text_field => :last_sang_at -end - -Hickwall.new.squawk("Hello World") -------------------------------------------- - -* A view helper that will print out squawking info: -+ -[source, ruby] -------------------------------------------- -squawk_info_for(@hickwall) -------------------------------------------- - -* A generator that creates a migration to add squawk columns to a model: -+ -------------------------------------------- -script/generate yaffle hickwall -------------------------------------------- - -* A custom generator command: -+ -[source, ruby] -------------------------------------------- -class YaffleGenerator < Rails::Generator::NamedBase - def manifest - m.yaffle_definition - end -end -------------------------------------------- - -* A custom route method: -+ -[source, ruby] -------------------------------------------- -ActionController::Routing::Routes.draw do |map| - map.yaffles -end -------------------------------------------- - -In addition you'll learn how to: - - * test your plugins. - * work with 'init.rb', how to store model, views, controllers, helpers and even other plugins in your plugins. - * create documentation for your plugin. - * write custom Rake tasks in your plugin. +A Rails plugin is either an extension or a modification of the core framework. Plugins provide: + + * a way for developers to share bleeding-edge ideas without hurting the stable code base + * a segmented architecture so that units of code can be fixed or updated on their own release schedule + * an outlet for the core developers so that they don’t have to include every cool new feature under the sun + +After reading this guide you should be familiar with: + + * Creating a plugin from scratch + * Writing and running tests for the plugin + * Storing models, views, controllers, helpers and even other plugins in your plugins + * Writing generators + * Writing custom Rake tasks in your plugin + * Generating RDoc documentation for your plugin + * Avoiding common pitfalls with 'init.rb' + +This guide describes how to build a test-driven plugin that will: + + * Extend core ruby classes like Hash and String + * Add methods to ActiveRecord::Base in the tradition of the 'acts_as' plugins + * Add a view helper that can be used in erb templates + * Add a new generator that will generate a migration + * Add a custom generator command + * A custom route method that can be used in routes.rb + +For the purpose of this guide pretend for a moment that you are an avid bird watcher. Your favorite bird is the Yaffle, and you want to create a plugin that allows other developers to share in the Yaffle goodness. First, you need to get setup for development. include::preparation.txt[] diff --git a/railties/doc/guides/source/creating_plugins/migration_generator.txt b/railties/doc/guides/source/creating_plugins/migration_generator.txt index 598a0c8437..1a477a69ab 100644 --- a/railties/doc/guides/source/creating_plugins/migration_generator.txt +++ b/railties/doc/guides/source/creating_plugins/migration_generator.txt @@ -6,11 +6,15 @@ We'll be relying on the built-in rails generate template for this tutorial. Goi Type: - script/generate +------------------------------------------------------------------ +script/generate +------------------------------------------------------------------ You should see the line: - Plugins (vendor/plugins): yaffle +------------------------------------------------------------------ +Plugins (vendor/plugins): yaffle +------------------------------------------------------------------ When you run `script/generate yaffle` you should see the contents of your USAGE file. For this plugin, the USAGE file looks like this: @@ -27,10 +31,10 @@ Example: Now you can add code to your generator: +*vendor/plugins/yaffle/generators/yaffle/yaffle_generator.rb* + [source, ruby] ------------------------------------------------------------------ -# File: vendor/plugins/yaffle/generators/yaffle/yaffle_generator.rb - class YaffleGenerator < Rails::Generator::NamedBase def manifest record do |m| @@ -67,14 +71,16 @@ This does a few things: When you run the generator like - script/generate yaffle bird +------------------------------------------------------------------ +script/generate yaffle bird +------------------------------------------------------------------ You will see a new file: +*db/migrate/20080529225649_add_yaffle_fields_to_birds.rb* + [source, ruby] ------------------------------------------------------------------ -# File: db/migrate/20080529225649_add_yaffle_fields_to_birds.rb - class AddYaffleFieldsToBirds < ActiveRecord::Migration def self.up add_column :birds, :last_squawk, :string diff --git a/railties/doc/guides/source/creating_plugins/odds_and_ends.txt b/railties/doc/guides/source/creating_plugins/odds_and_ends.txt index eb127f73ca..88cd4fe9ed 100644 --- a/railties/doc/guides/source/creating_plugins/odds_and_ends.txt +++ b/railties/doc/guides/source/creating_plugins/odds_and_ends.txt @@ -4,27 +4,30 @@ The plugin initializer script 'init.rb' is invoked via `eval` (not `require`) so it has slightly different behavior. -If you reopen any classes in init.rb itself your changes will potentially be made to the wrong module. There are 2 ways around this: +If you reopen any classes in init.rb itself your changes will potentially be made to the wrong module. As a rule, it's better not to open any classes in `init.rb`, and it makes the plugin more difficult to turn into a gem. -The first way is to explicitly define the top-level module space for all modules and classes, like `::Hash`: +A better alternative is to reopen the class in a different file, and require that file from `init.rb`. + +If you must reopen a class in `init.rb`, there are various techniques. One way is to use `module_eval` or `class_eval`: + +*vendor/plugins/yaffle/init.rb* [source, ruby] --------------------------------------------------- -# File: vendor/plugins/yaffle/init.rb - -class ::Hash +Hash.class_eval do def is_a_special_hash? true end end --------------------------------------------------- -OR you can use `module_eval` or `class_eval`: +Another way is to explicitly define the top-level module space for all modules and classes, like `::Hash`: ---------------------------------------------------- -# File: vendor/plugins/yaffle/init.rb +*vendor/plugins/yaffle/init.rb* -Hash.class_eval do +[source, ruby] +--------------------------------------------------- +class ::Hash def is_a_special_hash? true end @@ -78,10 +81,10 @@ When you created the plugin with the built-in rails generator, it generated a ra Many plugin authors put all of their rake tasks into a common namespace that is the same as the plugin, like so: +*vendor/plugins/yaffle/tasks/yaffle.rake* + [source, ruby] --------------------------------------------------------- -# File: vendor/plugins/yaffle/tasks/yaffle.rake - namespace :yaffle do desc "Prints out the word 'Yaffle'" task :squawk => :environment do diff --git a/railties/doc/guides/source/creating_plugins/preparation.txt b/railties/doc/guides/source/creating_plugins/preparation.txt index 77e3a3561f..dc9ef6bc29 100644 --- a/railties/doc/guides/source/creating_plugins/preparation.txt +++ b/railties/doc/guides/source/creating_plugins/preparation.txt @@ -2,11 +2,12 @@ === Create the basic app === -In this tutorial we will create a basic rails application with 1 resource: bird. Start out by building the basic rails app: +The examples in this guide require that you have a working rails application. To create a simple rails app execute: ------------------------------------------------ -rails plugin_demo -cd plugin_demo +gem install rails +rails yaffle_guide +cd yaffle_guide script/generate scaffold bird name:string rake db:migrate script/server @@ -14,25 +15,28 @@ script/server Then navigate to http://localhost:3000/birds. Make sure you have a functioning rails app before continuing. +.Editor's note: NOTE: The aforementioned instructions will work for sqlite3. For more detailed instructions on how to create a rails app for other databases see the API docs. -=== Create the plugin === +=== Generate the plugin skeleton === -The built-in Rails plugin generator stubs out a new plugin. Pass the plugin name, either 'CamelCased' or 'under_scored', as an argument. Pass `\--with-generator` to add an example generator also. +Rails ships with a plugin generator which creates a basic plugin skeleton. Pass the plugin name, either 'CamelCased' or 'under_scored', as an argument. Pass `\--with-generator` to add an example generator also. This creates a plugin in 'vendor/plugins' including an 'init.rb' and 'README' as well as standard 'lib', 'task', and 'test' directories. Examples: ---------------------------------------------- -./script/generate plugin BrowserFilters -./script/generate plugin BrowserFilters --with-generator +./script/generate plugin yaffle +./script/generate plugin yaffle --with-generator ---------------------------------------------- -Later in the plugin we will create a generator, so go ahead and add the `\--with-generator` option now: +To get more detailed help on the plugin generator, type `./script/generate plugin`. + +Later on this guide will describe how to work with generators, so go ahead and generate your plugin with the `\--with-generator` option now: ---------------------------------------------- -script/generate plugin yaffle --with-generator +./script/generate plugin yaffle --with-generator ---------------------------------------------- You should see the following output: @@ -57,19 +61,10 @@ create vendor/plugins/yaffle/generators/yaffle/yaffle_generator.rb create vendor/plugins/yaffle/generators/yaffle/USAGE ---------------------------------------------- -For this plugin you won't need the file 'vendor/plugins/yaffle/lib/yaffle.rb' so you can delete that. - ----------------------------------------------- -rm vendor/plugins/yaffle/lib/yaffle.rb ----------------------------------------------- - -.Editor's note: -NOTE: Many plugin authors prefer to keep this file, and add all of the require statements in it. That way, they only line in init.rb would be `require "yaffle"`. If you are developing a plugin that has a lot of files in the lib directory, you may want to create a subdirectory like lib/yaffle and store your files in there. That way your init.rb file stays clean - === Setup the plugin for testing === -Testing plugins that use the entire Rails stack can be complex, and the generator doesn't offer any help. In this tutorial you will learn how to test your plugin against multiple different adapters using ActiveRecord. This tutorial will not cover how to use fixtures in plugin tests. +In this guide you will learn how to test your plugin against multiple different adapters using Active Record. This guide will not cover how to use fixtures in plugin tests. To setup your plugin to allow for easy testing you'll need to add 3 files: @@ -77,8 +72,6 @@ To setup your plugin to allow for easy testing you'll need to add 3 files: * A 'schema.rb' file with your table definitions. * A test helper that sets up the database before your tests. -For this plugin you'll need 2 tables/models, Hickwalls and Wickwalls, so add the following files: - *vendor/plugins/yaffle/test/database.yml:* ---------------------------------------------- @@ -105,7 +98,9 @@ mysql: :database: yaffle_plugin_test ---------------------------------------------- -*vendor/plugins/yaffle/test/test_helper.rb:* +For this guide you'll need 2 tables/models, Hickwalls and Wickwalls, so add the following: + +*vendor/plugins/yaffle/test/schema.rb:* [source, ruby] ---------------------------------------------- @@ -121,9 +116,12 @@ ActiveRecord::Schema.define(:version => 0) do t.datetime :last_tweeted_at end end +---------------------------------------------- -# File: vendor/plugins/yaffle/test/test_helper.rb +*vendor/plugins/yaffle/test/test_helper.rb:* +[source, ruby] +---------------------------------------------- ENV['RAILS_ENV'] = 'test' ENV['RAILS_ROOT'] ||= File.dirname(__FILE__) + '/../../../..' @@ -135,7 +133,6 @@ ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/debug.log") db_adapter = ENV['DB'] -# no db passed, try one of these fine config-free DBs before bombing. db_adapter ||= begin require 'rubygems' @@ -160,10 +157,66 @@ load(File.dirname(__FILE__) + "/schema.rb") require File.dirname(__FILE__) + '/../init.rb' class Hickwall < ActiveRecord::Base - acts_as_yaffle end class Wickwall < ActiveRecord::Base - acts_as_yaffle :yaffle_text_field => :last_tweet, :yaffle_date_field => :last_tweeted_at end ---------------------------------------------- + +=== Run the plugin tests === + +Once you have these files in place, you can write your first test to ensure that your plugin-testing setup is correct. By default rails generates a file in 'vendor/plugins/yaffle/test/yaffle_test.rb' with a sample test. Replace the contents of that file with: + +*vendor/plugins/yaffle/test/yaffle_test.rb:* + +[source, ruby] +---------------------------------------------- +require File.dirname(__FILE__) + '/test_helper.rb' + +class YaffleTest < Test::Unit::TestCase + + def test_active_record_classes_from_test_helper + assert_kind_of Hickwall, Hickwall.new + assert_kind_of Wickwall, Wickwall.new + end + +end +---------------------------------------------- + +To run this, go to the plugin directory and run `rake`: + +---------------------------------------------- +cd vendor/plugins/yaffle +rake +---------------------------------------------- + +You should see output like: + +---------------------------------------------- +/opt/local/bin/ruby -Ilib:lib "/opt/local/lib/ruby/gems/1.8/gems/rake-0.8.3/lib/rake/rake_test_loader.rb" "test/yaffle_test.rb" +-- create_table(:hickwalls, {:force=>true}) + -> 0.0220s +-- create_table(:wickwalls, {:force=>true}) + -> 0.0077s +-- initialize_schema_migrations_table() + -> 0.0007s +-- assume_migrated_upto_version(0) + -> 0.0007s +Loaded suite /opt/local/lib/ruby/gems/1.8/gems/rake-0.8.3/lib/rake/rake_test_loader +Started +. +Finished in 0.002236 seconds. + +1 test, 1 assertion, 0 failures, 0 errors +---------------------------------------------- + +By default the setup above runs your tests with sqlite or sqlite3. To run tests with one of the other connection strings specified in database.yml, pass the DB environment variable to rake: + +---------------------------------------------- +rake DB=sqlite +rake DB=sqlite3 +rake DB=mysql +rake DB=postgresql +---------------------------------------------- + +Now you are ready to test-drive your plugin! diff --git a/railties/doc/guides/source/creating_plugins/string_to_squawk.txt b/railties/doc/guides/source/creating_plugins/string_to_squawk.txt index 50516cef69..63f1131442 100644 --- a/railties/doc/guides/source/creating_plugins/string_to_squawk.txt +++ b/railties/doc/guides/source/creating_plugins/string_to_squawk.txt @@ -10,10 +10,10 @@ Most plugins store their code classes in the plugin's lib directory. When you a First, you need to write the tests. Testing plugins is very similar to testing rails apps. The generated test file should look something like this: +*vendor/plugins/yaffle/test/core_ext_test.rb* + [source, ruby] -------------------------------------------------------- -# File: vendor/plugins/yaffle/test/core_ext_test.rb - require 'test/unit' class CoreExtTest < Test::Unit::TestCase @@ -26,11 +26,10 @@ end Start off by removing the default test, and adding a require statement for your test helper. +*vendor/plugins/yaffle/test/core_ext_test.rb* + [source, ruby] -------------------------------------------------------- -# File: vendor/plugins/yaffle/test/core_ext_test.rb - -require 'test/unit' require File.dirname(__FILE__) + '/test_helper.rb' class CoreExtTest < Test::Unit::TestCase @@ -53,10 +52,10 @@ No tests were specified Great - now you are ready to start development. The first thing we'll do is to add a method to String called `to_squawk` which will prefix the string with the word ``squawk!''. The test will look something like this: +*vendor/plugins/yaffle/init.rb* + [source, ruby] -------------------------------------------------------- -# File: vendor/plugins/yaffle/init.rb - class CoreExtTest < Test::Unit::TestCase def test_string_should_respond_to_squawk assert_equal true, "".respond_to?(:to_squawk) @@ -72,17 +71,17 @@ class CoreExtTest < Test::Unit::TestCase end -------------------------------------------------------- +*vendor/plugins/yaffle/init.rb* + [source, ruby] -------------------------------------------------------- -# File: vendor/plugins/yaffle/init.rb - require "core_ext" -------------------------------------------------------- +*vendor/plugins/yaffle/lib/core_ext.rb* + [source, ruby] -------------------------------------------------------- -# File: vendor/plugins/yaffle/lib/core_ext.rb - String.class_eval do def to_squawk "squawk! #{self}".strip diff --git a/railties/doc/guides/source/creating_plugins/view_helper.txt b/railties/doc/guides/source/creating_plugins/view_helper.txt index b03a190e1a..4eaec93824 100644 --- a/railties/doc/guides/source/creating_plugins/view_helper.txt +++ b/railties/doc/guides/source/creating_plugins/view_helper.txt @@ -8,10 +8,10 @@ Creating a view helper is a 3-step process: First, create the test to define the functionality you want: +*vendor/plugins/yaffle/test/view_helpers_test.rb* + [source, ruby] --------------------------------------------------------------- -# File: vendor/plugins/yaffle/test/view_helpers_test.rb - require File.dirname(__FILE__) + '/test_helper.rb' include YaffleViewHelper @@ -28,20 +28,20 @@ end Then add the following statements to init.rb: +*vendor/plugins/yaffle/init.rb* + [source, ruby] --------------------------------------------------------------- -# File: vendor/plugins/yaffle/init.rb - require "view_helpers" ActionView::Base.send :include, YaffleViewHelper --------------------------------------------------------------- Then add the view helpers file and +*vendor/plugins/yaffle/lib/view_helpers.rb* + [source, ruby] --------------------------------------------------------------- -# File: vendor/plugins/yaffle/lib/view_helpers.rb - module YaffleViewHelper def squawk_info_for(yaffle) returning "" do |result| |