Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add scope to unique #2471

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,16 @@ Prefix your method call with `unique`. For example:
Faker::Name.unique.name # This will return a unique name every time it is called
```

You can also provide a scope for the unique generator.

```ruby
Faker::Name.unique(scope: :user).name # This will return a unique name for the :user scope
Faker::Name.unique(scope: :customer).name # This will return a unique name for the :customer scope. This value may have already been returned in the :user scope.

# The unique scope can be any ruby Object so you can make a scope for a particular class attribute like this:
Faker::Name.unique(scope: [:customer, :name]).name
```

If too many unique values are requested from a generator that has a limited
number of potential values, a `Faker::UniqueGenerator::RetryLimitExceeded`
exception may be raised. It is possible to clear the record of unique values
Expand Down
8 changes: 6 additions & 2 deletions lib/faker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -217,9 +217,11 @@ def resolve(value)
# Return unique values from the generator every time.
#
# @param max_retries [Integer] The max number of retries that should be done before giving up.
# @param scope [Object] The scope of the unique values
# @return [self]
def unique(max_retries = 10_000)
@unique ||= UniqueGenerator.new(self, max_retries)
def unique(max_retries = 10_000, scope: DefaultUniqueScope)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why the scope is a class?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

scope can be just be a string or symbol. If desired a Faker::DEFAULT_UNIQUE_SCOPE contant could be set instead.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yea I prefer Symbol
to save memory

@unique ||= {}
@unique[scope] ||= UniqueGenerator.new(self, max_retries)
end

def sample(list, num = nil)
Expand Down Expand Up @@ -258,6 +260,8 @@ def disable_enforce_available_locales

private

class DefaultUniqueScope; end

def warn_for_deprecated_arguments
keywords = []
yield(keywords)
Expand Down
18 changes: 18 additions & 0 deletions test/test_faker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,28 @@ def test_rand_for_range
end

def test_unique
Faker::Base.unique.clear

unique_numbers = Array.new(8) do
Faker::Base.unique.numerify('#')
end

assert_equal(unique_numbers.uniq, unique_numbers)
end

def test_unique_scope
Faker::Base.unique.clear
Faker::Base.unique(scope: :other).clear

unique_numbers_default_scope = Array.new(8) do
Faker::Base.unique.numerify('#')
end

unique_numbers_other_scope = Array.new(8) do
Faker::Base.unique(scope: :other).numerify('#')
end

# Assert that the intersection of the "default" scope and the "other" scope is not empty
assert !(unique_numbers_default_scope & unique_numbers_other_scope).empty?
end
end