Skip to content

Commit

Permalink
Add warn for positional arguments when using Faker 2.0 (#1698)
Browse files Browse the repository at this point in the history
## Summary

An `ArgumentError` occurred when upgrading to Faker 2.0.
I didn't know what argument to change until I arrived at the changelog.
https://github.com/faker-ruby/faker/blob/v2.1.2/CHANGELOG.md#important-note

This PR will warn users what keyword arguments should be changed when
positional arguments is used.

This reduces users pain and makes upgrades easier.

The following is an example.

```console
% cat example.rb
require 'faker'

Faker::Address.zip_code('NY')
```

### Before

```console
% bundle exec ruby example.rb
Traceback (most recent call last):
        1: from example.rb:3:in `<main>'
/Users/koic/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/faker-2.1.2/lib/faker/default/address.rb:32:in
`zip_code': wrong number of arguments (given 1, expected 0) (ArgumentError)
```

### After

```console
% bundle exec ruby example.rb
example.rb:3: Passing `state_abbreviation` with the 1st argument of
`Address.zip_code` is deprecated. Use keyword argument like
`Address.zip_code(state_abbreviation: ...)` instead.
```

## Other Information

The `uplevel` keyword argument of `warn` method is a feature from
Ruby 2.5. Faker supports Ruby 2.3 orh higehr, so it is emulated with
`warn_with_uplevel` method.
  • Loading branch information
koic authored and vbrazo committed Aug 26, 2019
1 parent bec236c commit 77b3c1e
Show file tree
Hide file tree
Showing 48 changed files with 1,359 additions and 143 deletions.
20 changes: 20 additions & 0 deletions lib/faker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ class Base
Letters = ULetters + Array('a'..'z')

class << self
NOT_GIVEN = Object.new

## by default numerify results do not start with a zero
def numerify(number_string, leading_zero: false)
return number_string.gsub(/#/) { rand(10).to_s } if leading_zero
Expand Down Expand Up @@ -245,6 +247,24 @@ def disable_enforce_available_locales
ensure
I18n.enforce_available_locales = old_enforce_available_locales
end

private

# Workaround for emulating `warn '...', uplevel: 1` in Ruby 2.4 or lower.
def warn_with_uplevel(message, uplevel: 1)
at = parse_caller(caller[uplevel]).join(':')
warn "#{at}: #{message}"
end

def parse_caller(at)
# rubocop:disable Style/GuardClause
if /^(.+?):(\d+)(?::in `.*')?/ =~ at
file = Regexp.last_match(1)
line = Regexp.last_match(2).to_i
[file, line]
end
# rubocop:enable Style/GuardClause
end
end
end
end
Expand Down
14 changes: 12 additions & 2 deletions lib/faker/books/dune.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@ def planet
fetch('dune.planets')
end

def quote(character: nil)
def quote(legacy_character = NOT_GIVEN, character: nil)
if legacy_character != NOT_GIVEN
warn_with_uplevel 'Passing `character` with the 1st argument of `Dune.quote` is deprecated. Use keyword argument like `Dune.quote(character: ...)` instead.', uplevel: 1
character = legacy_character
end

quoted_characters = translate('faker.dune.quotes').keys

if character.nil?
Expand All @@ -36,7 +41,12 @@ def quote(character: nil)
fetch('dune.quotes.' + character)
end

def saying(source: nil)
def saying(legacy_source = NOT_GIVEN, source: nil)
if legacy_source != NOT_GIVEN
warn_with_uplevel 'Passing `source` with the 1st argument of `Dune.saying` is deprecated. Use keyword argument like `Dune.saying(source: ...)` instead.', uplevel: 1
source = legacy_source
end

sourced_sayings = translate('faker.dune.sayings').keys

if source.nil?
Expand Down
61 changes: 54 additions & 7 deletions lib/faker/books/lovecraft.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@ def location
fetch('lovecraft.location')
end

def fhtagn(number: 1)
def fhtagn(legacy_number = NOT_GIVEN, number: 1)
if legacy_number != NOT_GIVEN
warn_with_uplevel 'Passing `number` with the 1st argument of `Lovecraft.fhtagn` is deprecated. Use keyword argument like `Lovecraft.fhtagn(number: ...)` instead.', uplevel: 1
number = legacy_number
end

Array.new(number) { fetch('lovecraft.fhtagn') }.join('. ')
end

Expand All @@ -20,7 +25,16 @@ def tome
fetch('lovecraft.tome')
end

def sentence(word_count: 4, random_words_to_add: 6)
def sentence(legacy_word_count = NOT_GIVEN, legacy_random_words_to_add = NOT_GIVEN, word_count: 4, random_words_to_add: 6)
if legacy_word_count != NOT_GIVEN
warn_with_uplevel 'Passing `word_count` with the 1st argument of `Lovecraft.sentence` is deprecated. Use keyword argument like `Lovecraft.sentence(word_count: ...)` instead.', uplevel: 1
word_count = legacy_word_count
end
if legacy_random_words_to_add != NOT_GIVEN
warn_with_uplevel 'Passing `random_words_to_add` with the 2nd argument of `Lovecraft.sentence` is deprecated. Use keyword argument like `Lovecraft.sentence(random_words_to_add: ...)` instead.', uplevel: 1
random_words_to_add = legacy_random_words_to_add
end

words(number: word_count + rand(random_words_to_add.to_i).to_i, spaces_allowed: true).join(' ').capitalize + '.'
end

Expand All @@ -29,7 +43,16 @@ def word
random_word =~ /\s/ ? word : random_word
end

def words(number: 3, spaces_allowed: false)
def words(legacy_number = NOT_GIVEN, legacy_spaces_allowed = NOT_GIVEN, number: 3, spaces_allowed: false)
if legacy_number != NOT_GIVEN
warn_with_uplevel 'Passing `number` with the 1st argument of `Lovecraft.words` is deprecated. Use keyword argument like `Lovecraft.words(number: ...)` instead.', uplevel: 1
number = legacy_number
end
if legacy_spaces_allowed != NOT_GIVEN
warn_with_uplevel 'Passing `spaces_allowed` with the 2nd argument of `Lovecraft.words` is deprecated. Use keyword argument like `Lovecraft.words(spaces_allowed: ...)` instead.', uplevel: 1
spaces_allowed = legacy_spaces_allowed
end

resolved_num = resolve(number)
word_list = translate('faker.lovecraft.words')
word_list *= ((resolved_num / word_list.length) + 1)
Expand All @@ -40,27 +63,51 @@ def words(number: 3, spaces_allowed: false)
words.each_with_index { |w, i| words[i] = word if w =~ /\s/ }
end

def sentences(number: 3)
def sentences(legacy_number = NOT_GIVEN, number: 3)
if legacy_number != NOT_GIVEN
warn_with_uplevel 'Passing `number` with the 1st argument of `Lovecraft.sentences` is deprecated. Use keyword argument like `Lovecraft.sentences(number: ...)` instead.', uplevel: 1
number = legacy_number
end

[].tap do |sentences|
1.upto(resolve(number)) do
sentences << sentence(word_count: 3)
end
end
end

def paragraph(sentence_count: 3, random_sentences_to_add: 3)
def paragraph(legacy_sentence_count = NOT_GIVEN, legacy_random_sentences_to_add = NOT_GIVEN, sentence_count: 3, random_sentences_to_add: 3)
if legacy_sentence_count != NOT_GIVEN
warn_with_uplevel 'Passing `sentence_count` with the 1st argument of `Lovecraft.paragraph` is deprecated. Use keyword argument like `Lovecraft.paragraph(sentence_count: ...)` instead.', uplevel: 1
sentence_count = legacy_sentence_count
end
if legacy_random_sentences_to_add != NOT_GIVEN
warn_with_uplevel 'Passing `random_sentences_to_add` with the 2nd argument of `Lovecraft.paragraph` is deprecated. Use keyword argument like `Lovecraft.paragraph(random_sentences_to_add: ...)` instead.', uplevel: 1
random_sentences_to_add = legacy_random_sentences_to_add
end

sentences(number: resolve(sentence_count) + rand(random_sentences_to_add.to_i).to_i).join(' ')
end

def paragraphs(number: 3)
def paragraphs(legacy_number = NOT_GIVEN, number: 3)
if legacy_number != NOT_GIVEN
warn_with_uplevel 'Passing `number` with the 1st argument of `Lovecraft.paragraphs` is deprecated. Use keyword argument like `Lovecraft.paragraphs(number: ...)` instead.', uplevel: 1
number = legacy_number
end

[].tap do |paragraphs|
1.upto(resolve(number)) do
paragraphs << paragraph(sentence_count: 3)
end
end
end

def paragraph_by_chars(characters: 256)
def paragraph_by_chars(legacy_characters = NOT_GIVEN, characters: 256)
if legacy_characters != NOT_GIVEN
warn_with_uplevel 'Passing `characters` with the 1st argument of `Lovecraft.paragraph_by_chars` is deprecated. Use keyword argument like `Lovecraft.paragraph_by_chars(characters: ...)` instead.', uplevel: 1
characters = legacy_characters
end

paragraph = paragraph(sentence_count: 3)

paragraph += ' ' + paragraph(sentence_count: 3) while paragraph.length < characters
Expand Down
35 changes: 30 additions & 5 deletions lib/faker/default/address.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,25 @@ class Address < Base
flexible :address

class << self
def city(options: {})
def city(legacy_options = NOT_GIVEN, options: {})
if legacy_options != NOT_GIVEN
warn_with_uplevel 'Passing `options` with the 1st argument of `Address.city` is deprecated. Use keyword argument like `Address.city(options: ...)` instead.', uplevel: 1
options = legacy_options
end

parse(options[:with_state] ? 'address.city_with_state' : 'address.city')
end

def street_name
parse('address.street_name')
end

def street_address(include_secondary: false)
def street_address(legacy_include_secondary = NOT_GIVEN, include_secondary: false)
if legacy_include_secondary != NOT_GIVEN
warn_with_uplevel 'Passing `include_secondary` with the 1st argument of `Address.street_address` is deprecated. Use keyword argument like `Address.street_address(include_secondary: ...)` instead.', uplevel: 1
include_secondary = legacy_include_secondary
end

numerify(parse('address.street_address') + (include_secondary ? ' ' + secondary_address : ''))
end

Expand All @@ -29,7 +39,12 @@ def community
parse('address.community')
end

def zip_code(state_abbreviation: '')
def zip_code(legacy_state_abbreviation = NOT_GIVEN, state_abbreviation: '')
if legacy_state_abbreviation != NOT_GIVEN
warn_with_uplevel 'Passing `state_abbreviation` with the 1st argument of `Address.zip_code` is deprecated. Use keyword argument like `Address.zip_code(state_abbreviation: ...)` instead.', uplevel: 1
state_abbreviation = legacy_state_abbreviation
end

if state_abbreviation.empty?
letterified_string = letterify(fetch('address.postcode'))
return numerify(letterified_string, leading_zero: true)
Expand Down Expand Up @@ -71,11 +86,21 @@ def country
fetch('address.country')
end

def country_by_code(code: 'US')
def country_by_code(legacy_code = NOT_GIVEN, code: 'US')
if legacy_code != NOT_GIVEN
warn_with_uplevel 'Passing `code` with the 1st argument of `Address.country_by_code` is deprecated. Use keyword argument like `Address.country_by_code(code: ...)` instead.', uplevel: 1
code = legacy_code
end

fetch('address.country_by_code.' + code)
end

def country_name_to_code(name: 'united_states')
def country_name_to_code(legacy_name = NOT_GIVEN, name: 'united_states')
if legacy_name != NOT_GIVEN
warn_with_uplevel 'Passing `name` with the 1st argument of `Address.country_name_to_code` is deprecated. Use keyword argument like `Address.country_name_to_code(name: ...)` instead.', uplevel: 1
name = legacy_name
end

fetch('address.country_by_name.' + name)
end

Expand Down
14 changes: 12 additions & 2 deletions lib/faker/default/alphanumeric.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,24 @@ class << self
ALPHABET = ('a'..'z').to_a
ALPHANUMS = ALPHABET + (0..9).to_a

def alpha(number: 32)
def alpha(legacy_number = NOT_GIVEN, number: 32)
if legacy_number != NOT_GIVEN
warn_with_uplevel 'Passing `number` with the 1st argument of `Alphanumeric.alpha` is deprecated. Use keyword argument like `Alphanumeric.alpha(number: ...)` instead.', uplevel: 1
number = legacy_number
end

char_count = resolve(number)
return '' if char_count.to_i < 1

Array.new(char_count) { sample(ALPHABET) }.join
end

def alphanumeric(number: 32)
def alphanumeric(legacy_number = NOT_GIVEN, number: 32)
if legacy_number != NOT_GIVEN
warn_with_uplevel 'Passing `number` with the 1st argument of `Alphanumeric.alphanumeric` is deprecated. Use keyword argument like `Alphanumeric.alphanumeric(number: ...)` instead.', uplevel: 1
number = legacy_number
end

char_count = resolve(number)
return '' if char_count.to_i < 1

Expand Down
17 changes: 16 additions & 1 deletion lib/faker/default/app.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,22 @@ def author
parse('app.author')
end

def semantic_version(major: 0..9, minor: 0..9, patch: 1..9)
# rubocop:disable Metrics/ParameterLists
def semantic_version(legacy_major = NOT_GIVEN, legacy_minor = NOT_GIVEN, legacy_patch = NOT_GIVEN, major: 0..9, minor: 0..9, patch: 1..9)
# rubocop:enable Metrics/ParameterLists
if legacy_major != NOT_GIVEN
warn_with_uplevel 'Passing `major` with the 1st argument of `App.semantic_version` is deprecated. Use keyword argument like `App.semantic_version(major: ...)` instead.', uplevel: 1
major = legacy_major
end
if legacy_minor != NOT_GIVEN
warn_with_uplevel 'Passing `minor` with the 2nd argument of `App.semantic_version` is deprecated. Use keyword argument like `App.semantic_version(minor: ...)` instead.', uplevel: 1
minor = legacy_minor
end
if legacy_patch != NOT_GIVEN
warn_with_uplevel 'Passing `patch` with the 3rd argument of `App.semantic_version` is deprecated. Use keyword argument like `App.semantic_version(patch: ...)` instead.', uplevel: 1
patch = legacy_patch
end

[major, minor, patch].map { |chunk| sample(Array(chunk)) }.join('.')
end
end
Expand Down
25 changes: 24 additions & 1 deletion lib/faker/default/avatar.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,30 @@ class Avatar < Base
class << self
SUPPORTED_FORMATS = %w[png jpg bmp].freeze

def image(slug: nil, size: '300x300', format: 'png', set: 'set1', bgset: nil)
# rubocop:disable Metrics/ParameterLists
def image(legacy_slug = NOT_GIVEN, legacy_size = NOT_GIVEN, legacy_format = NOT_GIVEN, legacy_set = NOT_GIVEN, legacy_bgset = NOT_GIVEN, slug: nil, size: '300x300', format: 'png', set: 'set1', bgset: nil)
# rubocop:enable Metrics/ParameterLists
if legacy_slug != NOT_GIVEN
warn_with_uplevel 'Passing `slug` with the 1st argument of `Avatar.image` is deprecated. Use keyword argument like `Avatar.image(slug: ...)` instead.', uplevel: 1
slug = legacy_slug
end
if legacy_size != NOT_GIVEN
warn_with_uplevel 'Passing `size` with the 2nd argument of `Avatar.image` is deprecated. Use keyword argument like `Avatar.image(size: ...)` instead.', uplevel: 1
size = legacy_size
end
if legacy_format != NOT_GIVEN
warn_with_uplevel 'Passing `format` with the 3rd argument of `Avatar.image` is deprecated. Use keyword argument like `Avatar.image(format: ...)` instead.', uplevel: 1
format = legacy_format
end
if legacy_set != NOT_GIVEN
warn_with_uplevel 'Passing `set` with the 4th argument of `Avatar.image` is deprecated. Use keyword argument like `Avatar.image(set: ...)` instead.', uplevel: 1
set = legacy_set
end
if legacy_bgset != NOT_GIVEN
warn_with_uplevel 'Passing `bgset` with the 5th argument of `Avatar.image` is deprecated. Use keyword argument like `Avatar.image(bgset: ...)` instead.', uplevel: 1
bgset = legacy_bgset
end

raise ArgumentError, 'Size should be specified in format 300x300' unless size =~ /^[0-9]+x[0-9]+$/
raise ArgumentError, "Supported formats are #{SUPPORTED_FORMATS.join(', ')}" unless SUPPORTED_FORMATS.include?(format)

Expand Down
14 changes: 12 additions & 2 deletions lib/faker/default/bank.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,28 @@ class Bank < Base
flexible :bank

class << self
def account_number(digits: 10)
def account_number(legacy_digits = NOT_GIVEN, digits: 10)
if legacy_digits != NOT_GIVEN
warn_with_uplevel 'Passing `digits` with the 1st argument of `Bank.account_number` is deprecated. Use keyword argument like `Bank.account_number(digits: ...)` instead.', uplevel: 1
digits = legacy_digits
end

output = ''

output += rand.to_s[2..-1] while output.length < digits

output[0...digits]
end

def iban(country_code: 'GB')
def iban(legacy_country_code = NOT_GIVEN, country_code: 'GB')
# Each country has it's own format for bank accounts
# Many of them use letters in certain parts of the account
# Using regex patterns we can create virtually any type of bank account
if legacy_country_code != NOT_GIVEN
warn_with_uplevel 'Passing `country_code` with the 1st argument of `Bank.iban` is deprecated. Use keyword argument like `Bank.iban(country_code: ...)` instead.', uplevel: 1
country_code = legacy_country_code
end

begin
pattern = fetch("bank.iban_details.#{country_code.downcase}.bban_pattern")
rescue I18n::MissingTranslationData
Expand Down
7 changes: 6 additions & 1 deletion lib/faker/default/boolean.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@
module Faker
class Boolean < Base
class << self
def boolean(true_ratio: 0.5)
def boolean(legacy_true_ratio = NOT_GIVEN, true_ratio: 0.5)
if legacy_true_ratio != NOT_GIVEN
warn_with_uplevel 'Passing `true_ratio` with the 1st argument of `Boolean.boolean` is deprecated. Use keyword argument like `Boolean.boolean(true_ratio: ...)` instead.', uplevel: 1
true_ratio = legacy_true_ratio
end

(rand < true_ratio)
end
end
Expand Down
22 changes: 20 additions & 2 deletions lib/faker/default/chile_rut.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,16 @@ class << self
@last_rut = nil

# Fixed param added for testing a specific RUT and check digit combination.
def rut(min_rut: 1, fixed: false)
def rut(legacy_min_rut = NOT_GIVEN, legacy_fixed = NOT_GIVEN, min_rut: 1, fixed: false)
if legacy_min_rut != NOT_GIVEN
warn_with_uplevel 'Passing `min_rut` with the 1st argument of `ChileRut.rut` is deprecated. Use keyword argument like `ChileRut.rut(min_rut: ...)` instead.', uplevel: 1
min_rut = legacy_min_rut
end
if legacy_fixed != NOT_GIVEN
warn_with_uplevel 'Passing `fixed` with the 2nd argument of `ChileRut.rut` is deprecated. Use keyword argument like `ChileRut.rut(fixed: ...)` instead.', uplevel: 1
fixed = legacy_fixed
end

@last_rut = fixed ? min_rut : rand_in_range(min_rut, 99_999_999)
end

Expand Down Expand Up @@ -34,7 +43,16 @@ def check_digit
dv
end

def full_rut(min_rut: 0, fixed: false)
def full_rut(legacy_min_rut = NOT_GIVEN, legacy_fixed = NOT_GIVEN, min_rut: 0, fixed: false)
if legacy_min_rut != NOT_GIVEN
warn_with_uplevel 'Passing `min_rut` with the 1st argument of `ChileRut.full_rut` is deprecated. Use keyword argument like `ChileRut.full_rut(min_rut: ...)` instead.', uplevel: 1
min_rut = legacy_min_rut
end
if legacy_fixed != NOT_GIVEN
warn_with_uplevel 'Passing `fixed` with the 2nd argument of `ChileRut.full_rut` is deprecated. Use keyword argument like `ChileRut.full_rut(fixed: ...)` instead.', uplevel: 1
fixed = legacy_fixed
end

"#{rut(min_rut: min_rut, fixed: fixed)}-#{dv}"
end

Expand Down
Loading

0 comments on commit 77b3c1e

Please sign in to comment.