diff --git a/Changelog.md b/Changelog.md index 6f1d6ee0..07af279c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -7,6 +7,7 @@ suggestions, ideas and improvements to FriendlyId. * SimpleI18n: Handle regional locales ([#965](https://github.com/norman/friendly_id/pull/965)) * Fix: "unknown column" exception ([#943](https://github.com/norman/friendly_id/pull/943)) +* Add: `allow_nil: true` to the Finder ([#995])(https://github.com/norman/friendly_id/pull/995) ## 5.4.2 (2021-01-07) diff --git a/README.md b/README.md index 25707731..3f0577e9 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,27 @@ existing users, do this from the console, runner, or add a Rake task: User.find_each(&:save) ``` +## Options + +### `:allow_nil` + +You can pass `allow_nil: true` to the `friendly.find()` method if you're want to +avoid raising `ActiveRecord::RecordNotFound` and accept a `nil`. + +#### Example + +```ruby +MyModel.friendly.find("bad-slug") # where bad-slug is not a valid slug +MyModel.friendly.find(123) # where 123 is not a valid primary key ID +MyModel.friendly.find(nil) # when you have a variable/param that's possibly nil +#=> raise ActiveRecord::RecordNotFound + +MyModel.friendly.find("bad-slug", allow_nil: true) +MyModel.friendly.find(123, allow_nil: true) +MyModel.friendly.find(nil, allow_nil: true) +#=> nil +``` + ## Bugs Please report them on the [Github issue diff --git a/lib/friendly_id/finder_methods.rb b/lib/friendly_id/finder_methods.rb index 2a058973..46cd362d 100644 --- a/lib/friendly_id/finder_methods.rb +++ b/lib/friendly_id/finder_methods.rb @@ -7,19 +7,31 @@ module FinderMethods # id matching '123' and then fall back to looking for a record with the # numeric id '123'. # + # @param [Boolean] allow_nil (default: false) + # Use allow_nil: true if you'd like the finder to return nil instead of + # raising ActivRecord::RecordNotFound + # + # ### Example + # + # MyModel.friendly.find("bad-slug") + # #=> raise ActiveRecord::RecordNotFound + # + # MyModel.friendly.find("bad-slug", allow_nil: true) + # #=> nil + # # Since FriendlyId 5.0, if the id is a nonnumeric string like '123-foo' it # will *only* search by friendly id and not fall back to the regular find # method. # # If you want to search only by the friendly id, use {#find_by_friendly_id}. # @raise ActiveRecord::RecordNotFound - def find(*args) + def find(*args, allow_nil: false) id = args.first - return super if args.count != 1 || id.unfriendly_id? + return super(*args) if args.count != 1 || id.unfriendly_id? first_by_friendly_id(id).tap { |result| return result unless result.nil? } - return super if potential_primary_key?(id) + return super(*args) if potential_primary_key?(id) - raise_not_found_exception(id) + raise_not_found_exception(id) unless allow_nil end # Returns true if a record with the given id exists. diff --git a/test/finders_test.rb b/test/finders_test.rb index 9271f6ef..d40c1626 100644 --- a/test/finders_test.rb +++ b/test/finders_test.rb @@ -25,4 +25,16 @@ def model_class assert model_class.existing.find(record.friendly_id) end end + + test "allows nil with allow_nil: true" do + with_instance_of(model_class) do |record| + assert_nil model_class.find("foo", allow_nil: true) + end + end + + test "allows nil on relations with allow_nil: true" do + with_instance_of(model_class) do |record| + assert_nil model_class.existing.find("foo", allow_nil: true) + end + end end