diff options
author | Rolf Timmermans <r.timmermans@voormedia.com> | 2011-03-27 17:18:32 +0200 |
---|---|---|
committer | Rolf Timmermans <r.timmermans@voormedia.com> | 2011-03-27 17:18:32 +0200 |
commit | 512057d386075f207d8927a5e0ce3943174d5c78 (patch) | |
tree | c0b9122469af1e4af74142cc2cad6d560226753c /railties | |
parent | d89a7967b5af5c87bbfc268af72287b82541d384 (diff) | |
parent | a9d27c04abf24dc85be061ff9772d71897af02b1 (diff) | |
download | rails-512057d386075f207d8927a5e0ce3943174d5c78.tar.gz rails-512057d386075f207d8927a5e0ce3943174d5c78.tar.bz2 rails-512057d386075f207d8927a5e0ce3943174d5c78.zip |
Merge remote-tracking branch 'upstream/master' into desc_tracker
Diffstat (limited to 'railties')
14 files changed, 103 insertions, 51 deletions
diff --git a/railties/guides/source/3_0_release_notes.textile b/railties/guides/source/3_0_release_notes.textile index 001f458fd9..f75b245ed8 100644 --- a/railties/guides/source/3_0_release_notes.textile +++ b/railties/guides/source/3_0_release_notes.textile @@ -59,12 +59,12 @@ The +config.gem+ method is gone and has been replaced by using +bundler+ and a + h4. Upgrade Process -To help with the upgrade process, a plugin named "Rails Upgrade":http://github.com/rails/rails_upgrade has been created to automate part of it. +To help with the upgrade process, a plugin named "Rails Upgrade":http://github.com/jm/rails_upgrade has been created to automate part of it. Simply install the plugin, then run +rake rails:upgrade:check+ to check your app for pieces that need to be updated (with links to information on how to update them). It also offers a task to generate a +Gemfile+ based on your current +config.gem+ calls and a task to generate a new routes file from your current one. To get the plugin, simply run the following: <shell> -$ ruby script/plugin install git://github.com/rails/rails_upgrade.git +$ ruby script/plugin install git://github.com/jm/rails_upgrade.git </shell> You can see an example of how that works at "Rails Upgrade is now an Official Plugin":http://omgbloglol.com/post/364624593/rails-upgrade-is-now-an-official-plugin diff --git a/railties/guides/source/active_record_querying.textile b/railties/guides/source/active_record_querying.textile index ed3968e226..2c5d9e67e3 100644 --- a/railties/guides/source/active_record_querying.textile +++ b/railties/guides/source/active_record_querying.textile @@ -76,7 +76,7 @@ Primary operation of <tt>Model.find(options)</tt> can be summarized as: h4. Retrieving a Single Object -Active Record lets you retrieve a single object using three different ways. +Active Record lets you retrieve a single object using five different ways. h5. Using a Primary Key @@ -130,6 +130,40 @@ SELECT * FROM clients ORDER BY clients.id DESC LIMIT 1 <tt>Model.last</tt> returns +nil+ if no matching record is found. No exception will be raised. +h5. +first!+ + +<tt>Model.first!</tt> finds the first record. For example: + +<ruby> +client = Client.first! +=> #<Client id: 1, first_name: "Lifo"> +</ruby> + +SQL equivalent of the above is: + +<sql> +SELECT * FROM clients LIMIT 1 +</sql> + +<tt>Model.first!</tt> raises +RecordNotFound+ if no matching record is found. + +h5. +last!+ + +<tt>Model.last!</tt> finds the last record. For example: + +<ruby> +client = Client.last! +=> #<Client id: 221, first_name: "Russel"> +</ruby> + +SQL equivalent of the above is: + +<sql> +SELECT * FROM clients ORDER BY clients.id DESC LIMIT 1 +</sql> + +<tt>Model.last!</tt> raises +RecordNotFound+ if no matching record is found. + h4. Retrieving Multiple Objects h5. Using Multiple Primary Keys @@ -747,6 +781,22 @@ h4. Specifying Conditions on Eager Loaded Associations Even though Active Record lets you specify conditions on the eager loaded associations just like +joins+, the recommended way is to use "joins":#joining-tables instead. +However if you must do this, you may use +where+ as you would normally. + +<ruby> +Post.includes(:comments).where("comments.visible", true) +</ruby> + +This would generate a query which contains a +LEFT OUTER JOIN+ whereas the +joins+ method would generate one using the +INNER JOIN+ function instead. + +<ruby> + SELECT "posts"."id" AS t0_r0, ... "comments"."updated_at" AS t1_r5 FROM "posts" LEFT OUTER JOIN "comments" ON "comments"."post_id" = "posts"."id" WHERE (comments.visible) +</ruby> + +If there was no +where+ condition, this would generate the normal set of two queries. + +If, in the case of this +includes+ query, there were no comments for any posts, all the posts would still be loaded. By using +joins+ (an INNER JOIN), the join conditions *must* match, otherwise no records will be returned. + h3. Scopes Scoping allows you to specify commonly-used ARel queries which can be referenced as method calls on the association objects or models. With these scopes, you can use every method previously covered such as +where+, +joins+ and +includes+. All scope methods will return an +ActiveRecord::Relation+ object which will allow for further methods (such as other scopes) to be called on it. diff --git a/railties/guides/source/active_record_validations_callbacks.textile b/railties/guides/source/active_record_validations_callbacks.textile index 58a184a48e..e5349d546c 100644 --- a/railties/guides/source/active_record_validations_callbacks.textile +++ b/railties/guides/source/active_record_validations_callbacks.textile @@ -461,11 +461,11 @@ The block receives the model, the attribute's name and the attribute's value. Yo h3. Common Validation Options -There are some common options that all the validation helpers can use. Here they are, except for the +:if+ and +:unless+ options, which are discussed later in "Conditional Validation":#conditional-validation. +These are common validation options: h4. +:allow_nil+ -The +:allow_nil+ option skips the validation when the value being validated is +nil+. Using +:allow_nil+ with +validates_presence_of+ allows for +nil+, but any other +blank?+ value will still be rejected. +The +:allow_nil+ option skips the validation when the value being validated is +nil+. <ruby> class Coffee < ActiveRecord::Base @@ -474,6 +474,8 @@ class Coffee < ActiveRecord::Base end </ruby> +TIP: +:allow_nil+ is ignored by the presence validator. + h4. +:allow_blank+ The +:allow_blank+ option is similar to the +:allow_nil+ option. This option will let validation pass if the attribute's value is +blank?+, like +nil+ or an empty string for example. @@ -487,6 +489,8 @@ Topic.create("title" => "").valid? # => true Topic.create("title" => nil).valid? # => true </ruby> +TIP: +:allow_blank+ is ignored by the presence validator. + h4. +:message+ As you've already seen, the +:message+ option lets you specify the message that will be added to the +errors+ collection when validation fails. When this option is not used, Active Record will use the respective default error message for each validation helper. @@ -742,7 +746,7 @@ Rails maintains an official plugin that provides helpers to display the error me h4. Installing as a plugin <shell> -$ rails plugin install git://github.com/rails/dynamic_form.git +$ rails plugin install git://github.com/joelmoss/dynamic_form.git </shell> h4 Installing as a Gem diff --git a/railties/guides/source/caching_with_rails.textile b/railties/guides/source/caching_with_rails.textile index 1b5ec40d16..297ba2d661 100644 --- a/railties/guides/source/caching_with_rails.textile +++ b/railties/guides/source/caching_with_rails.textile @@ -65,7 +65,7 @@ end If you want a more complicated expiration scheme, you can use cache sweepers to expire cached objects when things change. This is covered in the section on Sweepers. -Note: Page caching ignores all parameters. For example +/products?page=1+ will be written out to the filesystem as +products.html+ with no reference to the +page+ parameter. Thus, if someone requests +/products?page=2+ later, they will get the cached first page. Be careful when page caching GET parameters in the URL! +NOTE: Page caching ignores all parameters. For example +/products?page=1+ will be written out to the filesystem as +products.html+ with no reference to the +page+ parameter. Thus, if someone requests +/products?page=2+ later, they will get the cached first page. Be careful when page caching GET parameters in the URL! INFO: Page caching runs in an after filter. Thus, invalid requests won't generate spurious cache entries as long as you halt them. Typically, a redirection in some before filter that checks request preconditions does the job. diff --git a/railties/guides/source/contributing_to_ruby_on_rails.textile b/railties/guides/source/contributing_to_ruby_on_rails.textile index 4ba01a2a8f..1fcc4fd7e3 100644 --- a/railties/guides/source/contributing_to_ruby_on_rails.textile +++ b/railties/guides/source/contributing_to_ruby_on_rails.textile @@ -364,19 +364,6 @@ Please make sure the patch does not introduce whitespace errors: $ git apply --whitespace=error-all mynew_patch.diff </shell> -You can check your patches by applying your patch to an different dedicated branch: - -<shell> -$ git checkout -b testing_branch -$ git apply --check my_new_patch.diff -</shell> - -You can make sure your patches don't add any whitespace by applying it yourself using the --whitespace=error-all option. Make sure you are on your dedicated test branche and: - -<shell> -$ git apply --whitespace=error-all mynew_patch.diff -</shell> - h4. Create a Lighthouse Ticket @@ -384,7 +371,7 @@ Now create a ticket with your patch. Go to the "new ticket":http://rails.lightho h4. Get Some Feedback -Now you need to get other people to look at your patch, just as you've looked at other people's patches. You can use the rubyonrails-core mailing list or the #rails-contrib channel on IRC freenode for this. You might also try just talking to Rails developers that you know. +Now you need to get other people to look at your patch, just as you've looked at other people's patches. You can use the "rubyonrails-core mailing list":http://groups.google.com/group/rubyonrails-core/ or the #rails-contrib channel on IRC freenode for this. You might also try just talking to Rails developers that you know. h4. Iterate as Necessary diff --git a/railties/guides/source/debugging_rails_applications.textile b/railties/guides/source/debugging_rails_applications.textile index 67e17056e5..045b8823ca 100644 --- a/railties/guides/source/debugging_rails_applications.textile +++ b/railties/guides/source/debugging_rails_applications.textile @@ -686,7 +686,7 @@ There are some Rails plugins to help you to find errors and debug your applicati * "Query Trace":https://github.com/ntalbott/query_trace/tree/master: Adds query origin tracing to your logs. * "Query Stats":https://github.com/dan-manges/query_stats/tree/master: A Rails plugin to track database queries. * "Query Reviewer":http://code.google.com/p/query-reviewer/: This rails plugin not only runs "EXPLAIN" before each of your select queries in development, but provides a small DIV in the rendered output of each page with the summary of warnings for each query that it analyzed. -* "Exception Notifier":https://github.com/rails/exception_notification/tree/master: Provides a mailer object and a default set of templates for sending email notifications when errors occur in a Rails application. +* "Exception Notifier":https://github.com/smartinez87/exception_notification/tree/master: Provides a mailer object and a default set of templates for sending email notifications when errors occur in a Rails application. * "Exception Logger":https://github.com/defunkt/exception_logger/tree/master: Logs your Rails exceptions in the database and provides a funky web interface to manage them. h3. References diff --git a/railties/guides/source/form_helpers.textile b/railties/guides/source/form_helpers.textile index 0cfc005f80..1f21c27ae6 100644 --- a/railties/guides/source/form_helpers.textile +++ b/railties/guides/source/form_helpers.textile @@ -475,7 +475,7 @@ To leverage time zone support in Rails, you have to ask your users what time zon There is also +time_zone_options_for_select+ helper for a more manual (therefore more customizable) way of doing this. Read the API documentation to learn about the possible arguments for these two methods. -Rails _used_ to have a +country_select+ helper for choosing countries, but this has been extracted to the "country_select plugin":https://github.com/rails/country_select/tree/master. When using this, be aware that the exclusion or inclusion of certain names from the list can be somewhat controversial (and was the reason this functionality was extracted from Rails). +Rails _used_ to have a +country_select+ helper for choosing countries, but this has been extracted to the "country_select plugin":https://github.com/chrislerum/country_select. When using this, be aware that the exclusion or inclusion of certain names from the list can be somewhat controversial (and was the reason this functionality was extracted from Rails). h3. Using Date and Time Form Helpers diff --git a/railties/guides/source/getting_started.textile b/railties/guides/source/getting_started.textile index e94bdc97b0..0661549644 100644 --- a/railties/guides/source/getting_started.textile +++ b/railties/guides/source/getting_started.textile @@ -149,7 +149,7 @@ Usually run this as the root user: # gem install rails </shell> -TIP. If you're working on Windows, you should be aware that the vast majority of Rails development is done in Unix environments. While Ruby and Rails themselves install easily using for example "Ruby Installer":http://rubyinstaller.org/, the supporting ecosystem often assumes you are able to build C-based rubygems and work in a command window. If at all possible, we suggest that you install a Linux virtual machine and use that for Rails development, instead of using Windows. +TIP. If you're working on Windows, you can quickly install Ruby and Rails with "Rails Installer":http://railsinstaller.org. h4. Creating the Blog Application diff --git a/railties/guides/source/plugins.textile b/railties/guides/source/plugins.textile index daca50ee9e..2d9821e627 100644 --- a/railties/guides/source/plugins.textile +++ b/railties/guides/source/plugins.textile @@ -45,9 +45,9 @@ as a gem. This tutorial will begin to bridge that gap by demonstrating how to c "Enginex gem":http://www.github.com/josevalim/enginex. <shell> - gem install enginex - enginex --help - enginex yaffle +$ gem install enginex +$ enginex --help +$ enginex yaffle </shell> This command will create a new directory named "yaffle" within the current directory. @@ -401,7 +401,9 @@ h3. Publishing your Gem Gem plugins in progress can be easily be shared from any Git repository. To share the Yaffle gem with others, simply commit the code to a Git repository (like Github) and add a line to the Gemfile of the any application: -gem 'yaffle', :git => 'git://github.com/yaffle_watcher/yaffle.git' +<ruby> +gem 'yaffle', :git => 'git://github.com/yaffle_watcher/yaffle.git' +</ruby> After running +bundle install+, your gem functionality will be available to the application. @@ -450,8 +452,6 @@ Once your comments are good to go, navigate to your plugin directory and run: $ rake rdoc </shell> -!!!!!!!!!!!!!! Make sure these still make sense. Add any references that you see fit. !!!!!!!!!!!!! - h4. References * "Developing a RubyGem using Bundler":https://github.com/radar/guides/blob/master/gem-development.md @@ -462,6 +462,7 @@ h4. References h3. Changelog +* March 10, 2011: Minor formatting tweaks. * February 13, 2011: Get guide in synch with Rails 3.0.3. Remove information not compatible with Rails 3. Send reader elsewhere for information that is covered elsewhere. * April 4, 2010: Fixed document to validate XHTML 1.0 Strict. "Jaime Iniesta":http://jaimeiniesta.com diff --git a/railties/guides/source/testing.textile b/railties/guides/source/testing.textile index a75cedbdc1..4ebdb3edf6 100644 --- a/railties/guides/source/testing.textile +++ b/railties/guides/source/testing.textile @@ -500,6 +500,8 @@ If you're familiar with the HTTP protocol, you'll know that +get+ is a type of r All of request types are methods that you can use, however, you'll probably end up using the first two more often than the others. +NOTE: Functional tests do not verify whether the specified request type should be accepted by the action. Request types in this context exist to make your tests more descriptive. + h4. The Four Hashes of the Apocalypse After a request has been made by using one of the 5 methods (+get+, +post+, etc.) and processed, you will have 4 Hash objects ready for use: diff --git a/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb b/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb index 5b4687ce64..3cf8410d1e 100644 --- a/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb +++ b/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb @@ -62,7 +62,7 @@ task :default => :test end def generate_test_dummy(force = false) - opts = (options || {}).slice(:skip_active_record, :skip_javascript, :database, :javascript) + opts = (options || {}).slice(:skip_active_record, :skip_javascript, :database, :javascript, :quiet, :pretend, :force, :skip) opts[:force] = force invoke Rails::Generators::AppGenerator, @@ -145,6 +145,7 @@ task :default => :test def initialize(*args) raise Error, "Options should be given after the plugin name. For details run: rails plugin --help" if args[0].blank? + @dummy_path = nil super end diff --git a/railties/lib/rails/railtie/configuration.rb b/railties/lib/rails/railtie/configuration.rb index e4368866a1..2c7b5bc048 100644 --- a/railties/lib/rails/railtie/configuration.rb +++ b/railties/lib/rails/railtie/configuration.rb @@ -31,26 +31,34 @@ module Rails app_generators(&block) end + # First configurable block to run. Called before any initializers are run. def before_configuration(&block) ActiveSupport.on_load(:before_configuration, :yield => true, &block) end + # Third configurable block to run. Does not run if config.cache_classes + # set to false. def before_eager_load(&block) ActiveSupport.on_load(:before_eager_load, :yield => true, &block) end + # Second configurable block to run. Called before frameworks initialize. def before_initialize(&block) ActiveSupport.on_load(:before_initialize, :yield => true, &block) end + # Last configurable block to run. Called after frameworks initialize. def after_initialize(&block) ActiveSupport.on_load(:after_initialize, :yield => true, &block) end + # Array of callbacks defined by #to_prepare. def to_prepare_blocks @@to_prepare_blocks ||= [] end + # Defines generic callbacks to run before #after_initialize. Useful for + # Rails::Railtie subclasses. def to_prepare(&blk) to_prepare_blocks << blk if blk end diff --git a/railties/test/generators/plugin_new_generator_test.rb b/railties/test/generators/plugin_new_generator_test.rb index 2a8b337144..3c11c8dbaf 100644 --- a/railties/test/generators/plugin_new_generator_test.rb +++ b/railties/test/generators/plugin_new_generator_test.rb @@ -55,7 +55,7 @@ class PluginNewGeneratorTest < Rails::Generators::TestCase def test_ensure_that_plugin_options_are_not_passed_to_app_generator FileUtils.cd(Rails.root) - assert_no_match /It works from file!.*It works_from_file/, run_generator([destination_root, "-m", "lib/template.rb"]) + assert_no_match(/It works from file!.*It works_from_file/, run_generator([destination_root, "-m", "lib/template.rb"])) end def test_ensure_that_test_dummy_can_be_generated_from_a_template @@ -85,7 +85,7 @@ class PluginNewGeneratorTest < Rails::Generators::TestCase def test_ensure_that_skip_active_record_option_is_passed_to_app_generator run_generator [destination_root, "--skip_active_record"] assert_no_file "test/dummy/config/database.yml" - assert_no_match /ActiveRecord/, File.read(File.join(destination_root, "test/test_helper.rb")) + assert_no_match(/ActiveRecord/, File.read(File.join(destination_root, "test/test_helper.rb"))) end def test_ensure_that_database_option_is_passed_to_app_generator @@ -134,21 +134,21 @@ class PluginNewGeneratorTest < Rails::Generators::TestCase def test_template_from_dir_pwd FileUtils.cd(Rails.root) - assert_match /It works from file!/, run_generator([destination_root, "-m", "lib/template.rb"]) + assert_match(/It works from file!/, run_generator([destination_root, "-m", "lib/template.rb"])) end def test_ensure_that_tests_works run_generator FileUtils.cd destination_root `bundle install` - assert_match /1 tests, 1 assertions, 0 failures, 0 errors/, `bundle exec rake test` + assert_match(/1 tests, 1 assertions, 0 failures, 0 errors/, `bundle exec rake test`) end def test_ensure_that_tests_works_in_full_mode run_generator [destination_root, "--full", "--skip_active_record"] FileUtils.cd destination_root `bundle install` - assert_match /2 tests, 2 assertions, 0 failures, 0 errors/, `bundle exec rake test` + assert_match(/2 tests, 2 assertions, 0 failures, 0 errors/, `bundle exec rake test`) end def test_creating_engine_in_full_mode @@ -159,7 +159,7 @@ class PluginNewGeneratorTest < Rails::Generators::TestCase end def test_being_quiet_while_creating_dummy_application - assert_no_match /create\s+config\/application.rb/, run_generator + assert_no_match(/create\s+config\/application.rb/, run_generator) end def test_create_mountable_application_with_mountable_option diff --git a/railties/test/generators/shared_generator_tests.rb b/railties/test/generators/shared_generator_tests.rb index 9e6169721b..c9c5d2fad2 100644 --- a/railties/test/generators/shared_generator_tests.rb +++ b/railties/test/generators/shared_generator_tests.rb @@ -26,13 +26,12 @@ module SharedGeneratorTests def test_plugin_new_generate_pretend run_generator ["testapp", "--pretend"] - - default_files.each{ |path| assert_no_file path } + default_files.each{ |path| assert_no_file File.join("testapp",path) } end def test_invalid_database_option_raises_an_error content = capture(:stderr){ run_generator([destination_root, "-d", "unknown"]) } - assert_match /Invalid value for \-\-database option/, content + assert_match(/Invalid value for \-\-database option/, content) end def test_test_unit_is_skipped_if_required @@ -42,21 +41,21 @@ module SharedGeneratorTests def test_options_before_application_name_raises_an_error content = capture(:stderr){ run_generator(["--pretend", destination_root]) } - assert_match /Options should be given after the \w+ name. For details run: rails( plugin)? --help\n/, content + assert_match(/Options should be given after the \w+ name. For details run: rails( plugin)? --help\n/, content) end def test_name_collision_raises_an_error reserved_words = %w[application destroy plugin runner test] reserved_words.each do |reserved| content = capture(:stderr){ run_generator [File.join(destination_root, reserved)] } - assert_match /Invalid \w+ name #{reserved}. Please give a name which does not match one of the reserved rails words.\n/, content + assert_match(/Invalid \w+ name #{reserved}. Please give a name which does not match one of the reserved rails words.\n/, content) end end def test_name_raises_an_error_if_name_already_used_constant %w{ String Hash Class Module Set Symbol }.each do |ruby_class| content = capture(:stderr){ run_generator [File.join(destination_root, ruby_class)] } - assert_match /Invalid \w+ name #{ruby_class}, constant #{ruby_class} is already in use. Please choose another \w+ name.\n/, content + assert_match(/Invalid \w+ name #{ruby_class}, constant #{ruby_class} is already in use. Please choose another \w+ name.\n/, content) end end @@ -72,8 +71,8 @@ module SharedGeneratorTests def test_template_raises_an_error_with_invalid_path content = capture(:stderr){ run_generator([destination_root, "-m", "non/existant/path"]) } - assert_match /The template \[.*\] could not be loaded/, content - assert_match /non\/existant\/path/, content + assert_match(/The template \[.*\] could not be loaded/, content) + assert_match(/non\/existant\/path/, content) end def test_template_is_executed_when_supplied @@ -82,7 +81,7 @@ module SharedGeneratorTests template.instance_eval "def read; self; end" # Make the string respond to read generator([destination_root], :template => path).expects(:open).with(path, 'Accept' => 'application/x-thor-template').returns(template) - assert_match /It works!/, silence(:stdout){ generator.invoke_all } + assert_match(/It works!/, silence(:stdout){ generator.invoke_all }) end def test_dev_option @@ -100,8 +99,8 @@ module SharedGeneratorTests def test_template_raises_an_error_with_invalid_path content = capture(:stderr){ run_generator([destination_root, "-m", "non/existant/path"]) } - assert_match /The template \[.*\] could not be loaded/, content - assert_match /non\/existant\/path/, content + assert_match(/The template \[.*\] could not be loaded/, content) + assert_match(/non\/existant\/path/, content) end def test_template_is_executed_when_supplied @@ -110,7 +109,7 @@ module SharedGeneratorTests template.instance_eval "def read; self; end" # Make the string respond to read generator([destination_root], :template => path).expects(:open).with(path, 'Accept' => 'application/x-thor-template').returns(template) - assert_match /It works!/, silence(:stdout){ generator.invoke_all } + assert_match(/It works!/, silence(:stdout){ generator.invoke_all }) end def test_template_is_executed_when_supplied_an_https_path @@ -119,7 +118,7 @@ module SharedGeneratorTests template.instance_eval "def read; self; end" # Make the string respond to read generator([destination_root], :template => path).expects(:open).with(path, 'Accept' => 'application/x-thor-template').returns(template) - assert_match /It works!/, silence(:stdout){ generator.invoke_all } + assert_match(/It works!/, silence(:stdout){ generator.invoke_all }) end def test_dev_option @@ -191,6 +190,6 @@ module SharedCustomGeneratorTests generator([destination_root], :builder => path).expects(:open).with(path, 'Accept' => 'application/x-thor-template').returns(template) capture(:stdout) { generator.invoke_all } - default_files.each{ |path| assert_no_file path } + default_files.each{ |path| assert_no_file(path) } end end |