8000 adjust uniqueness handling by iiwo · Pull Request #528 · ffaker/ffaker · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

adjust uniqueness handling #528

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

Merged
merged 1 commit into from
Apr 9, 2023
Merged
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
19 changes: 15 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,23 @@ name will be set with data from `ffaker/data/name/first_names`.

To get repeatable results in Minitest or Rspec, follow [these instructions](RANDOM.md#using-the-same-random-seed-as-your-tests).

## Unique results
## Unique values

You can get unique value from any methods in FFaker like this:
You can ensure unique values are generated using the `unique` method. `ffaker` will retry the generation
until an unique value if found.

```rb
FFaker::Name.unique.name
Example:
```ruby
FFaker::Name.unique.name # ensures an unique value is returned for FFaker::Name
```

If an unique value cannot be generated within a maximum limit of retries for a generator
a `FFaker::UniqueUtils::RetryLimitExceeded` error will be raised.

You can prevent exceeding the limit by clearing the record of used values (e.g. between tests):
```ruby
FFaker::Name.unique.clear # clears the used values for FFaker::Name
FFaker::UniqueUtils.clear # clears the used values for all generators
```

## TODO
Expand Down
2 changes: 1 addition & 1 deletion lib/ffaker/utils/module_utils.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def underscore(string)
end

def unique(max_retries = 10_000)
@unique_generator ||= FFaker::UniqueUtils.new(self, max_retries)
FFaker::UniqueUtils.add_instance(self, max_retries)
end

# http://en.wikipedia.org/wiki/Luhn_algorithm
Expand Down
49 changes: 28 additions & 21 deletions lib/ffaker/utils/unique_utils.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,42 +3,49 @@

module FFaker
class UniqueUtils
def initialize(generator, max_retries)
@generator = generator
@max_retries = max_retries
@previous_results = Hash.new { |hash, key| hash[key] = Set.new }
end
RetryLimitExceeded = Class.new(StandardError)

def method_missing(name, *arguments)
@generator.respond_to?(name) ? add_results_to_hash(name, *arguments) : super
end
class << self
def add_instance(generator, max_retries)
instances[generator] ||= FFaker::UniqueUtils.new(generator, max_retries)
end

def respond_to_missing?(method_name, include_private = false)
super
end
def instances
Thread.current[:ffaker_unique_utils] ||= {}
end

RetryLimitExceeded = Class.new(StandardError)
def clear
instances.values.each(&:clear)
instances.clear
end
end

def clear
@previous_results.clear
def initialize(generator, max_retries)
@generator = generator
@max_retries = max_retries
end

def self.clear
ObjectSpace.each_object(self, &:clear)
def clear
previous_results.clear
end

private

def add_results_to_hash(name, *arguments)
def method_missing(name, *arguments)
@max_retries.times do
result = @generator.send(name, *arguments)
result = @generator.public_send(name, *arguments)

next if @previous_results[[name, arguments]].include?(result)
next if previous_results[[name, arguments]].include?(result)

@previous_results[[name, arguments]] << result
previous_results[[name, arguments]] << result
return result
end
raise RetryLimitExceeded

raise RetryLimitExceeded, "Retry limit exceeded for #{name}"
end

def previous_results
@previous_results ||= Hash.new { |hash, key| hash[key] = Set.new }
end
end
end
33 changes: 33 additions & 0 deletions test/test_module_utils.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,37 @@ def test_provides_a_k_method_for_generating_constant_arrays
assert result.frozen?
result.each { |e| assert e.frozen? }
end

def test_unique
generator = Object.new
generator.extend FFaker::ModuleUtils
# returns [1 1 2 2 1 1 2 2 ..][call_index]
def generator.test
index = Thread.current[:test_unique] ||= 0
Thread.current[:test_unique] = (index > 2 ? 0 : index + 1)
(index / 2) + 1
end

assert_equal(1, generator.unique.test)
assert_equal(2, generator.unique.test)

Thread.new do
assert_equal(1, generator.unique.test)
assert_equal(2, generator.unique.test)

assert_raises FFaker::UniqueUtils::RetryLimitExceeded do
generator.unique.test
end

generator.unique.clear
generator.unique.test
end.join

assert_raises FFaker::UniqueUtils::RetryLimitExceeded do
generator.unique.test
end

FFaker::UniqueUtils.clear
generator.unique.test
end
end
30 changes: 25 additions & 5 deletions test/test_unique_utils.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,36 @@ def stubbed_generator.test
unique_object.test
end

FFaker::UniqueUtils.clear
unique_object.clear

assert_equal(1, unique_object.test)
end

assert_raises FFaker::UniqueUtils::RetryLimitExceeded do
unique_object.test
def test_clears_all_unique_values
stubbed_generator = Object.new
def stubbed_generator.test
1
end
other_stubbed_generator = Object.new
def other_stubbed_generator.test
1
end

unique_object.clear
unique_object = FFaker::UniqueUtils.add_instance(stubbed_generator, 3)
other_unique_object = FFaker::UniqueUtils.add_instance(other_stubbed_generator, 3)

assert_equal(1, unique_object.test)
[unique_object, other_unique_object].each do |tested_unique_object|
assert_equal(1, tested_unique_object.test)

assert_raises FFaker::UniqueUtils::RetryLimitExceeded do
tested_unique_object.test
end
end

FFaker::UniqueUtils.clear

[unique_object, other_unique_object].each do |tested_unique_object|
assert_equal(1, tested_unique_object.test)
end
end
end
0