ffaker 2.20.0 → 2.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/Changelog.md +21 -0
  3. data/README.md +15 -4
  4. data/REFERENCE.md +1423 -1396
  5. data/Rakefile +9 -40
  6. data/bin/console +9 -0
  7. data/ffaker.gemspec +9 -19
  8. data/lib/ffaker/address_br.rb +14 -2
  9. data/lib/ffaker/address_it.rb +0 -1
  10. data/lib/ffaker/address_ua.rb +1 -1
  11. data/lib/ffaker/animal_br.rb +12 -0
  12. data/lib/ffaker/book.rb +5 -5
  13. data/lib/ffaker/code.rb +1 -1
  14. data/lib/ffaker/company.rb +4 -7
  15. data/lib/ffaker/company_fr.rb +1 -1
  16. data/lib/ffaker/data/address_br/complement +3 -0
  17. data/lib/ffaker/data/address_br/neighborhood_prefixes +3 -0
  18. data/lib/ffaker/data/address_de/state +1 -0
  19. data/lib/ffaker/data/animal_br/common_names +423 -0
  20. data/lib/ffaker/data/jo_jo/first_names +356 -0
  21. data/lib/ffaker/data/jo_jo/full_names +225 -0
  22. data/lib/ffaker/data/jo_jo/last_names +153 -0
  23. data/lib/ffaker/data/jo_jo/stands +149 -0
  24. data/lib/ffaker/date.rb +31 -0
  25. data/lib/ffaker/geolocation.rb +2 -2
  26. data/lib/ffaker/html_ipsum.rb +7 -1
  27. data/lib/ffaker/identification_br.rb +1 -1
  28. data/lib/ffaker/identification_ec.rb +39 -0
  29. data/lib/ffaker/identification_es_cl.rb +2 -2
  30. data/lib/ffaker/identification_es_co.rb +1 -1
  31. data/lib/ffaker/identification_kr.rb +3 -3
  32. data/lib/ffaker/identification_pl.rb +4 -4
  33. data/lib/ffaker/identification_tw.rb +4 -3
  34. data/lib/ffaker/jo_jo.rb +24 -0
  35. data/lib/ffaker/name_pl.rb +1 -1
  36. data/lib/ffaker/number.rb +2 -2
  37. data/lib/ffaker/ssn_se.rb +2 -2
  38. data/lib/ffaker/string.rb +7 -7
  39. data/lib/ffaker/time.rb +4 -4
  40. data/lib/ffaker/utils/module_utils.rb +4 -5
  41. data/lib/ffaker/utils/unique_utils.rb +30 -21
  42. data/lib/ffaker/vehicle.rb +63 -1
  43. data/lib/{version.rb → ffaker/version.rb} +1 -1
  44. data/lib/ffaker.rb +8 -4
  45. data/scripts/reference.rb +15 -13
  46. data/test/helper.rb +2 -4
  47. data/test/test_address_br.rb +15 -2
  48. data/test/test_address_da.rb +3 -3
  49. data/test/test_address_fi.rb +2 -2
  50. data/test/test_address_se.rb +2 -2
  51. data/test/test_animal_br.rb +17 -0
  52. data/test/test_cheesy_lingo.rb +1 -1
  53. data/test/test_color.rb +3 -3
  54. data/test/test_date.rb +37 -0
  55. data/test/test_filesystem.rb +3 -3
  56. data/test/test_freedom_ipsum.rb +1 -1
  57. data/test/test_gender_it.rb +1 -1
  58. data/test/test_gender_ja.rb +1 -1
  59. data/test/test_gender_jp.rb +1 -1
  60. data/test/test_gender_pl.rb +1 -1
  61. data/test/test_healthcare_ru.rb +1 -1
  62. data/test/test_hipster_ipsum.rb +1 -1
  63. data/test/test_html_ipsum.rb +6 -0
  64. data/test/test_identification_ec.rb +33 -0
  65. data/test/test_identification_es_mx.rb +1 -1
  66. data/test/test_internet_se.rb +2 -1
  67. data/test/test_jo_jo.rb +29 -0
  68. data/test/test_lorem_br.rb +1 -1
  69. data/test/test_lorem_cn.rb +3 -3
  70. data/test/test_lorem_fr.rb +3 -3
  71. data/test/test_lorem_ie.rb +2 -2
  72. data/test/test_lorem_kr.rb +3 -3
  73. data/test/test_lorem_pl.rb +2 -2
  74. data/test/test_lorem_ru.rb +3 -3
  75. data/test/test_lorem_ua.rb +3 -3
  76. data/test/test_module_utils.rb +33 -0
  77. data/test/test_name_da.rb +1 -1
  78. data/test/test_name_ph.rb +1 -1
  79. data/test/test_name_ru.rb +2 -2
  80. data/test/test_name_ua.rb +2 -2
  81. data/test/test_phone_number_se.rb +1 -1
  82. data/test/test_phone_number_sg.rb +8 -8
  83. data/test/test_unique_utils.rb +25 -5
  84. data/test/test_vehicle.rb +28 -1
  85. metadata +24 -197
@@ -0,0 +1,149 @@
1
+ Anubis
2
+ Atum
3
+ Bastet
4
+ Cream
5
+ Death Thirteen
6
+ Geb
7
+ High Priestess
8
+ Horus
9
+ Judgement
10
+ Khnum
11
+ Osiris
12
+ Sethan
13
+ Tenore Sax
14
+ Tohth
15
+ Dark Blue Moon
16
+ Emperor
17
+ Ebony Devil
18
+ Empress
19
+ Hanged Man
20
+ Hermit Purple
21
+ Hierophant Green
22
+ Justice
23
+ Lovers
24
+ Magician's Red
25
+ Silver Chariot
26
+ Star Platinum
27
+ Strength
28
+ Sun
29
+ The Fool
30
+ The World
31
+ Tower of Gray
32
+ Wheel of Fortune
33
+ Yellow Temperance
34
+ Achtung Baby
35
+ Aqua Necklace
36
+ Atom Heart Father
37
+ Bad Company
38
+ Boy II Man
39
+ Cheap Trick
40
+ Cinderella
41
+ Crazy Diamond
42
+ Enigma
43
+ Earth Wind and Fire
44
+ Harvest
45
+ Heaven's Door
46
+ Highway Star
47
+ Killer Queen
48
+ Sheer Heart Attack
49
+ Bites the Dust
50
+ Love Deluxe
51
+ Pearl Jam
52
+ Ratt
53
+ Red Hot Chili Pepper
54
+ Super Fly
55
+ Surface
56
+ The Lock
57
+ The Hand
58
+ Stray Cat
59
+ Black Sabbath
60
+ Baby Face
61
+ Gold Experience
62
+ Sticky Fingers
63
+ Metallica
64
+ Purple Haze
65
+ Moody Blues
66
+ Sex Pistols
67
+ The Grateful Dead
68
+ Aerosmith
69
+ Beach Boy
70
+ Man in the Mirror
71
+ Baby Face (GoGo)
72
+ Chariot Requiem
73
+ Clash
74
+ Echoes
75
+ Gold Experience Requiem
76
+ Green Day
77
+ King Crimson
78
+ Kraft Work
79
+ Little Feet
80
+ Mr.President
81
+ Notorious B.I.G
82
+ Oasis
83
+ Rolling Stones
84
+ Soft Machine
85
+ Spice Girl
86
+ Talking Head
87
+ White Album
88
+ Whitesnake
89
+ C-Moon
90
+ Jail House Lock
91
+ Stone Free
92
+ Goo Goo Dolls
93
+ Under World
94
+ Sky High
95
+ Star Platinum
96
+ Bohemian Rhapsody
97
+ Burning Down the House
98
+ Diver Down
99
+ Dragon's Dream
100
+ Foo Fighters
101
+ Green
102
+ Highway to Hell
103
+ Kiss
104
+ Jumpin' Jack Flash
105
+ Limp Bizkit
106
+ Made in Heaven
107
+ Manhattan Transfer
108
+ Marilyn Manson
109
+ Planet Waves
110
+ Survivor
111
+ Weather Report
112
+ Yo-Yo Ma
113
+ 20th Century Boy
114
+ Ball Breaker
115
+ Boku no Rhythm wo Kiitekure
116
+ Catch the Rainbow
117
+ Chocolate Disco
118
+ Civil War
119
+ Cream Starter
120
+ Dirty Deeds Done Dirt Cheap
121
+ D4C Love Train
122
+ Hey Ya!
123
+ In a Silent Way
124
+ Mandom
125
+ Oh! Lonesome Me
126
+ Scary Monsters
127
+ Sugar Mountain
128
+ Tatoo You!
129
+ Ticket to Ride
130
+ Tomb of the Boom
131
+ Tubular Bells
132
+ Tusk
133
+ Wired
134
+ Born This Way
135
+ California King Bed
136
+ Fun Fun Fun
137
+ Nut King Call
138
+ Paisley Park
139
+ Paper Moon King
140
+ Soft & Wet
141
+ Manic Depression
142
+ Voodoo Child
143
+ All Along Watchtower
144
+ Dolly Dagger
145
+ Nightbird Flying
146
+ Purple Haze Distortion
147
+ Rainy Day Dream Away
148
+ Remote Romance
149
+ House of Holy
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'date'
4
+
5
+ module FFaker
6
+ module Date
7
+ extend FFaker::ModuleUtils
8
+ extend self
9
+
10
+ # Generates a random date between 2 dates
11
+ def between(from, to)
12
+ FFaker::Time.between(from, to).to_date
13
+ end
14
+
15
+ # Generates a random date up to `days` days in the past
16
+ def backward(days = 365)
17
+ from = ::Date.today - days
18
+ to = ::Date.today - 1
19
+
20
+ between(from, to)
21
+ end
22
+
23
+ # Generates a random date up to `days` days in the future
24
+ def forward(days = 365)
25
+ from = ::Date.today + 1
26
+ to = ::Date.today + days
27
+
28
+ between(from, to)
29
+ end
30
+ end
31
+ end
@@ -51,8 +51,8 @@ module FFaker
51
51
  latitude_range = [upper_left[0], lower_right[0]].sort
52
52
  longitude_range = [upper_left[1], lower_right[1]].sort
53
53
 
54
- latitude = latitude_range[0] + rand * (latitude_range[1] - latitude_range[0])
55
- longitude = longitude_range[0] + rand * (longitude_range[1] - longitude_range[0])
54
+ latitude = latitude_range[0] + (rand * (latitude_range[1] - latitude_range[0]))
55
+ longitude = longitude_range[0] + (rand * (longitude_range[1] - longitude_range[0]))
56
56
 
57
57
  [latitude, longitude]
58
58
  end
@@ -134,9 +134,15 @@ module FFaker
134
134
  a = k([
135
135
  content_tag_for(:strong, words(2).capitalize!),
136
136
  content_tag_for(:em, paragraph),
137
+ content_tag_for(:mark, paragraph),
138
+ content_tag_for(:del, words(2)),
139
+ content_tag_for(:ins, words(2)),
140
+ content_tag_for(:sub, words(2)),
141
+ content_tag_for(:sup, words(2)),
137
142
  content_tag_for(:code, words(2)),
143
+ content_tag_for(:small, words(2)),
138
144
  (a 2).to_s
139
- ] + FFaker::Lorem.paragraphs(count))
145
+ ] + FFaker::Lorem.paragraphs(count - 1))
140
146
  fetch_sample(a, count: count).join(sep)
141
147
  end
142
148
 
@@ -71,7 +71,7 @@ module FFaker
71
71
  if document_digits.size + 1 == verification_multipliers.size && document_digits.uniq.size > 1
72
72
  0.upto(1) do |i|
73
73
  sum = verification_multipliers.drop(1 - i).each_with_index.inject(0) do |val, (value, j)|
74
- val + document_digits[j] * value
74
+ val + (document_digits[j] * value)
75
75
  end
76
76
  validation_digits[i] = (sum %= 11) < 2 ? 0 : 11 - sum
77
77
  document_digits.push(validation_digits[i])
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FFaker
4
+ module IdentificationEC
5
+ extend ModuleUtils
6
+ extend self
7
+
8
+ # https://medium.com/@bryansuarez/c%C3%B3mo-validar-c%C3%A9dula-y-ruc-en-ecuador-b62c5666186f
9
+ # Cedula de Identificacion
10
+ def ci
11
+ first_digits = ci_digits
12
+ "#{first_digits}#{last_digit(first_digits)}"
13
+ end
14
+
15
+ private
16
+
17
+ def ci_digits
18
+ "#{FFaker.numerify('##')}#{rand(0..5)}#{FFaker.numerify('######')}"
19
+ end
20
+
21
+ def last_digit(digits)
22
+ mod = digits_sum(digits) % 10
23
+ 10 - mod if mod > 0
24
+ end
25
+
26
+ def digits_sum(digits)
27
+ sum = 0
28
+ digits.chars.each_with_index do |char, index|
29
+ if index.even?
30
+ multiple = char.to_i * 2
31
+ sum += multiple > 9 ? multiple - 9 : multiple
32
+ else
33
+ sum += char.to_i
34
+ end
35
+ end
36
+ sum
37
+ end
38
+ end
39
+ end
@@ -20,10 +20,10 @@ module FFaker
20
20
  private
21
21
 
22
22
  def dv(rut)
23
- total = rut.to_s.rjust(8, '0').split(//).zip(%w[3 2 7 6 5 4 3 2]).sum do |a, b|
23
+ total = rut.to_s.rjust(8, '0').split('').zip(%w[3 2 7 6 5 4 3 2]).sum do |a, b|
24
24
  a.to_i * b.to_i
25
25
  end
26
- (11 - total % 11).to_s.gsub(/10/, 'k').gsub(/11/, '0')
26
+ (11 - (total % 11)).to_s.gsub('10', 'k').gsub('11', '0')
27
27
  end
28
28
  end
29
29
  end
@@ -32,7 +32,7 @@ module FFaker
32
32
  end
33
33
 
34
34
  def expedition_date
35
- today = Date.today
35
+ today = ::Date.today
36
36
  today - rand(today.year)
37
37
  end
38
38
  end
@@ -8,11 +8,11 @@ module FFaker
8
8
  # Resident Registration Number
9
9
  # http://ko.wikipedia.org/wiki/%EC%A3%BC%EB%AF%BC%EB%93%B1%EB%A1%9D%EB%B2%88%ED%98%B8
10
10
  def rrn
11
- birth = fetch_sample(Date.new(1970, 1, 1)..Date.new(1999, 12, 31)).strftime('%y%d%m')
11
+ birth = fetch_sample(::Date.new(1970, 1, 1)..::Date.new(1999, 12, 31)).strftime('%y%d%m')
12
12
  sex = fetch_sample([1, 2])
13
13
  loc = FFaker.numerify("#{fetch_sample(Array('00'..'95'))}###")
14
- a, b, c, d, e, f, g, h, i, j, k, l = "#{birth}#{sex}#{loc}".split(//).map(&:to_i)
15
- checksum = (11 - (2 * a + 3 * b + 4 * c + 5 * d + 6 * e + 7 * f + 8 * g + 9 * h + 2 * i + 3 * j + 4 * k + 5 * l) % 11) % 10
14
+ a, b, c, d, e, f, g, h, i, j, k, l = "#{birth}#{sex}#{loc}".split('').map(&:to_i)
15
+ checksum = (11 - (((2 * a) + (3 * b) + (4 * c) + (5 * d) + (6 * e) + (7 * f) + (8 * g) + (9 * h) + (2 * i) + (3 * j) + (4 * k) + (5 * l)) % 11)) % 10
16
16
  "#{birth}-#{sex}#{loc}#{checksum}"
17
17
  end
18
18
  end
@@ -37,8 +37,8 @@ module FFaker
37
37
  private
38
38
 
39
39
  def generate_valid_pesel_date
40
- from = Date.new(1800, 1, 1)
41
- to = [Date.today, Date.new(2299, 12, 31)].min
40
+ from = ::Date.new(1800, 1, 1)
41
+ to = [::Date.today, ::Date.new(2299, 12, 31)].min
42
42
  fetch_sample(from..to)
43
43
  end
44
44
 
@@ -65,13 +65,13 @@ module FFaker
65
65
  def pesel_checksum(date, serial_number)
66
66
  pesel_digits = "#{date}#{serial_number}".split('').map(&:to_i)
67
67
  a, b, c, d, e, f, g, h, i, j = pesel_digits
68
- (a * 9 + b * 7 + c * 3 + d + e * 9 + f * 7 + g * 3 + h + i * 9 + j * 7) % 10
68
+ ((a * 9) + (b * 7) + (c * 3) + d + (e * 9) + (f * 7) + (g * 3) + h + (i * 9) + (j * 7)) % 10
69
69
  end
70
70
 
71
71
  def identity_card_checksum(letter_part, number_part)
72
72
  a, b, c = letter_part.codepoints.map { |codepoints| codepoints - 55 }
73
73
  d, e, f, g, h = number_part.split('').map(&:to_i)
74
- (a * 7 + b * 3 + c + 7 * d + 3 * e + f + 7 * g + 3 * h) % 10
74
+ ((a * 7) + (b * 3) + c + (7 * d) + (3 * e) + f + (7 * g) + (3 * h)) % 10
75
75
  end
76
76
  end
77
77
  end
@@ -5,7 +5,8 @@ module FFaker
5
5
  extend ModuleUtils
6
6
  extend self
7
7
 
8
- AREA_CODE = { a: 10, b: 11, c: 12, d: 13, e: 14, f: 15, g: 16, h: 17, i: 34, j: 18, k: 19, l: 20, m: 21, n: 22, o: 35, p: 23, q: 24, r: 25, s: 26, t: 27, u: 28, v: 29, w: 30, x: 31, y: 32, z: 33 }.freeze
8
+ AREA_CODE = { a: 10, b: 11, c: 12, d: 13, e: 14, f: 15, g: 16, h: 17, i: 34, j: 18, k: 19, l: 20, m: 21, n: 22,
9
+ o: 35, p: 23, q: 24, r: 25, s: 26, t: 27, u: 28, v: 29, w: 32, x: 30, y: 31, z: 33 }.freeze
9
10
 
10
11
  # Identity Card Number
11
12
  # https://en.wikipedia.org/wiki/National_identification_card_(Taiwan)#Identity_card_number
@@ -14,9 +15,9 @@ module FFaker
14
15
  code = AREA_CODE[letter.to_sym]
15
16
  gender = fetch_sample([1, 2])
16
17
  serial = FFaker.numerify('#######')
17
- a, b, c, d, e, f, g, h, i, j = "#{code}#{gender}#{serial}".split(//).map(&:to_i)
18
+ a, b, c, d, e, f, g, h, i, j = "#{code}#{gender}#{serial}".chars.map(&:to_i)
18
19
 
19
- checksum = (9 * a + b + 2 * c + 3 * d + 4 * e + 5 * f + 6 * g + 7 * h + 8 * i + 9 * j) % 10
20
+ checksum = ((9 * a) + b + (2 * c) + (3 * d) + (4 * e) + (5 * f) + (6 * g) + (7 * h) + (8 * i) + (9 * j)) % 10
20
21
  "#{letter.upcase}#{gender}#{serial}#{checksum}"
21
22
  end
22
23
  end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FFaker
4
+ module JoJo
5
+ extend ModuleUtils
6
+ extend self
7
+
8
+ def full_name
9
+ fetch_sample(FULL_NAMES)
10
+ end
11
+
12
+ def first_name
13
+ fetch_sample(FIRST_NAMES)
14
+ end
15
+
16
+ def last_name
17
+ fetch_sample(LAST_NAMES)
18
+ end
19
+
20
+ def stand
21
+ fetch_sample(STANDS)
22
+ end
23
+ end
24
+ end
@@ -11,7 +11,7 @@ module FFaker
11
11
  GENDERS = %i[random female male].freeze
12
12
  MALE_PREFIXES = %w[Pan].freeze
13
13
  FEMALE_PREFIXES = %w[Pani].freeze
14
- ACADEMIC_DEGREE_PREFIXES = %w[lic. inż. mgr mgr\ inż. dr dr\ hab. prof.].freeze
14
+ ACADEMIC_DEGREE_PREFIXES = ['lic.', 'inż.', 'mgr', 'mgr inż.', 'dr', 'dr hab.', 'prof.'].freeze
15
15
  PREFIXES = (MALE_PREFIXES + FEMALE_PREFIXES + ACADEMIC_DEGREE_PREFIXES).freeze
16
16
 
17
17
  # Generates a random full name for a given gender (random by default).
data/lib/ffaker/number.rb CHANGED
@@ -18,9 +18,9 @@ module FFaker
18
18
  private
19
19
 
20
20
  def generate_pattern(digits)
21
- raise ArgumentError.new('Digits cannot be less than 1') if digits < 1
21
+ raise ArgumentError, 'Digits cannot be less than 1' if digits < 1
22
22
 
23
- "#{fetch_sample(1..9)}#{"#" * (digits - 1)}"
23
+ "#{fetch_sample(1..9)}#{'#' * (digits - 1)}"
24
24
  end
25
25
  end
26
26
  end
data/lib/ffaker/ssn_se.rb CHANGED
@@ -54,7 +54,7 @@ module FFaker
54
54
  end
55
55
 
56
56
  def random_birth_time_between(from = ::Time.local(1940, 1, 1), to = ::Time.now)
57
- ::Time.at(from + rand * (to.to_f - from.to_f))
57
+ ::Time.at(from + (rand * (to.to_f - from.to_f)))
58
58
  end
59
59
 
60
60
  def random_region_for(gender)
@@ -70,7 +70,7 @@ module FFaker
70
70
  end
71
71
 
72
72
  def random_region_odd
73
- rand(0..498) * 2 + 1
73
+ (rand(0..498) * 2) + 1
74
74
  end
75
75
 
76
76
  def three_character_string(number)
data/lib/ffaker/string.rb CHANGED
@@ -21,7 +21,7 @@ module FFaker
21
21
  @last_token = nil
22
22
 
23
23
  # Drop surrounding /'s and split into characters
24
- tokens = exp.inspect[1...-1].split(//)
24
+ tokens = exp.inspect[1...-1].split('')
25
25
  result << process_token(tokens) until tokens.empty?
26
26
 
27
27
  result
@@ -53,15 +53,15 @@ module FFaker
53
53
  when '?'
54
54
  # TODO: Let ? generate nothing
55
55
  '' # We already printed its target
56
- when '+' then
56
+ when '+'
57
57
  tokens.unshift(token) if rand(0..1) == 1 # Leave the `+` on to run again
58
58
  process_token(@last_token) # Run the last one at least once
59
- when '*' then
59
+ when '*'
60
60
  tokens.unshift(token) if rand(0..1) == 1 # Leave the `*` on to run again
61
61
  return '' if rand(0..1) == 1 # Or maybe do nothing
62
62
 
63
63
  process_token(@last_token) # Else run the last one again
64
- when '{' then
64
+ when '{'
65
65
  number = +''
66
66
  while (ch = tokens.shift) != '}'
67
67
  number << ch
@@ -76,14 +76,14 @@ module FFaker
76
76
 
77
77
  def generate_token(token, tokens)
78
78
  case token
79
- when /\w/ then
79
+ when /\w/
80
80
  @last_token = [token]
81
81
  token
82
- when BACKSLASH then
82
+ when BACKSLASH
83
83
  token = tokens.shift
84
84
  @last_token = ['\\', token]
85
85
  special(token)
86
- when '[' then
86
+ when '['
87
87
  set = []
88
88
  while (ch = tokens.shift) != ']'
89
89
  set << ch
data/lib/ffaker/time.rb CHANGED
@@ -22,17 +22,17 @@ module FFaker
22
22
 
23
23
  def datetime(params = {})
24
24
  years_back = params[:year_range] || 5
25
- latest_year = params [:year_latest] || 0
25
+ latest_year = params[:year_latest] || 0
26
26
  year = (rand * years_back).ceil + (::DateTime.now.year - latest_year - years_back)
27
27
  month = rand(1..12)
28
- day = rand(1..Date.new(year, month, -1).day)
28
+ day = rand(1..::Date.new(year, month, -1).day)
29
29
  hours = params[:hours] || rand(0..23)
30
30
  minutes = params[:minutes] || rand(0..59)
31
31
  series = [date = ::DateTime.new(year, month, day, hours, minutes)]
32
32
  return date unless params[:series]
33
33
 
34
34
  params[:series].each do |some_time_after|
35
- series << series.last + (rand * some_time_after).ceil
35
+ series << (series.last + (rand * some_time_after).ceil)
36
36
  end
37
37
  series
38
38
  end
@@ -44,7 +44,7 @@ module FFaker
44
44
  def between(from, to)
45
45
  from_value = convert_to_time(from)
46
46
  to_value = convert_to_time(to)
47
- ::Time.at(from_value + rand * (to_value.to_f - from_value.to_f))
47
+ ::Time.at(from_value + (rand * (to_value.to_f - from_value.to_f)))
48
48
  end
49
49
 
50
50
  private
@@ -25,7 +25,7 @@ module FFaker
25
25
  end
26
26
 
27
27
  def underscore(string)
28
- string.gsub(/::/, '/')
28
+ string.gsub('::', '/')
29
29
  .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
30
30
  .gsub(/([a-z\d])([A-Z])/, '\1_\2')
31
31
  .tr('-', '_')
@@ -33,14 +33,14 @@ module FFaker
33
33
  end
34
34
 
35
35
  def unique(max_retries = 10_000)
36
- @unique_generator ||= FFaker::UniqueUtils.new(self, max_retries)
36
+ FFaker::UniqueUtils.add_instance(self, max_retries)
37
37
  end
38
38
 
39
39
  # http://en.wikipedia.org/wiki/Luhn_algorithm
40
40
  def luhn_check(number)
41
41
  multiplications = []
42
42
 
43
- number.split(//).each_with_index do |digit, i|
43
+ number.split('').each_with_index do |digit, i|
44
44
  multiplications << i.even? ? digit.to_i * 2 : digit.to_i
45
45
  end
46
46
 
@@ -51,9 +51,8 @@ module FFaker
51
51
  end
52
52
  end
53
53
 
54
- control_digit = (sum % 10).zero? ? 0 : (sum / 10 + 1) * 10 - sum
54
+ control_digit = (sum % 10).zero? ? 0 : (((sum / 10) + 1) * 10) - sum
55
55
  control_digit.to_s
56
56
  end
57
-
58
57
  end
59
58
  end
@@ -1,43 +1,52 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'set'
4
+
3
5
  module FFaker
4
6
  class UniqueUtils
5
- def initialize(generator, max_retries)
6
- @generator = generator
7
- @max_retries = max_retries
8
- @previous_results = Hash.new { |hash, key| hash[key] = Set.new }
9
- end
7
+ RetryLimitExceeded = Class.new(StandardError)
10
8
 
11
- def method_missing(name, *arguments)
12
- @generator.respond_to?(name) ? add_results_to_hash(name, *arguments) : super
13
- end
9
+ class << self
10
+ def add_instance(generator, max_retries)
11
+ instances[generator] ||= FFaker::UniqueUtils.new(generator, max_retries)
12
+ end
14
13
 
15
- def respond_to_missing?(method_name, include_private = false)
16
- super
17
- end
14
+ def instances
15
+ Thread.current[:ffaker_unique_utils] ||= {}
16
+ end
18
17
 
19
- RetryLimitExceeded = Class.new(StandardError)
18
+ def clear
19
+ instances.values.each(&:clear)
20
+ instances.clear
21
+ end
22
+ end
20
23
 
21
- def clear
22
- @previous_results.clear
24
+ def initialize(generator, max_retries)
25
+ @generator = generator
26
+ @max_retries = max_retries
23
27
  end
24
28
 
25
- def self.clear
26
- ObjectSpace.each_object(self, &:clear)
29
+ def clear
30
+ previous_results.clear
27
31
  end
28
32
 
29
33
  private
30
34
 
31
- def add_results_to_hash(name, *arguments)
35
+ def method_missing(name, *arguments)
32
36
  @max_retries.times do
33
- result = @generator.send(name, *arguments)
37
+ result = @generator.public_send(name, *arguments)
34
38
 
35
- next if @previous_results[[name, arguments]].include?(result)
39
+ next if previous_results[[name, arguments]].include?(result)
36
40
 
37
- @previous_results[[name, arguments]] << result
41
+ previous_results[[name, arguments]] << result
38
42
  return result
39
43
  end
40
- raise RetryLimitExceeded
44
+
45
+ raise RetryLimitExceeded, "Retry limit exceeded for #{name}"
46
+ end
47
+
48
+ def previous_results
49
+ @previous_results ||= Hash.new { |hash, key| hash[key] = Set.new }
41
50
  end
42
51
  end
43
52
  end
@@ -2,6 +2,68 @@
2
2
 
3
3
  module FFaker
4
4
  module Vehicle
5
+ module VIN
6
+ extend ModuleUtils
7
+ extend self
8
+
9
+ # https://en.wikibooks.org/wiki/Vehicle_Identification_Numbers_(VIN_codes)/World_Manufacturer_Identifier_(WMI)
10
+ VALID_WMI_REGIONS = [*'A'..'C', *'J'..'N', 'P', *'R'..'Z', *'1'..'9'].freeze
11
+
12
+ VALID_YEAR_CHARS = %w[
13
+ 5 6 7 8 9 A B C D E F G H J K L M N P R S T V W X Y 1 2 3 4 5 6 7 8 9
14
+ ].freeze # 2005-2039
15
+
16
+ # https://en.wikibooks.org/wiki/Vehicle_Identification_Numbers_(VIN_codes)/Check_digit
17
+ TRANSLITERATION_VALUES = {
18
+ 'A' => 1, 'B' => 2, 'C' => 3, 'D' => 4, 'E' => 5, 'F' => 6, 'G' => 7, 'H' => 8,
19
+ 'J' => 1, 'K' => 2, 'L' => 3, 'M' => 4, 'N' => 5, 'P' => 7, 'R' => 9,
20
+ 'S' => 2, 'T' => 3, 'U' => 4, 'V' => 5, 'W' => 6, 'X' => 7, 'Y' => 8, 'Z' => 9
21
+ }.freeze
22
+ POSITION_WEIGHTS = [8, 7, 6, 5, 4, 3, 2, 10, 0, 9, 8, 7, 6, 5, 4, 3, 2].freeze
23
+
24
+ VALID_ALPHA = TRANSLITERATION_VALUES.keys
25
+ VALID_ALPHANUMERIC = [*VALID_ALPHA, *'1'..'9'].freeze
26
+
27
+ # Generate a VIN that is compliant with specifications of US Title 49 Section 565.15
28
+ # https://www.govinfo.gov/content/pkg/CFR-2019-title49-vol6/xml/CFR-2019-title49-vol6-part565.xml#seqnum565.15
29
+ #
30
+ # Position Meaning
31
+ # 1-3 Manufacturer ID aka WMI (alpha and digits)
32
+ # 4-8 Vehicle Description ("For passenger cars ..[position 7].. shall be alphabetic")
33
+ # 9 Check Digit (0-9 or "X")
34
+ # 10 Year (see VIN_VALID_YEARS)
35
+ # 11 Plant of manufacture (alpha and digits)
36
+ # 12-17 Serial number (digits only)
37
+ #
38
+ # I, O and Q are NOT allowed. VIN_VALID_ALPHA has valid alpha characters.
39
+ def vin
40
+ generated_vin = [
41
+ # Manufacturer ID / WMI
42
+ fetch_sample(VALID_WMI_REGIONS),
43
+ fetch_sample(VALID_ALPHANUMERIC, count: 2),
44
+ # Vehicle Description
45
+ fetch_sample(VALID_ALPHANUMERIC, count: 3),
46
+ fetch_sample(VALID_ALPHA),
47
+ fetch_sample(VALID_ALPHANUMERIC),
48
+ '0', # check digit placeholder
49
+ fetch_sample(VALID_YEAR_CHARS), # Year of Manufacture
50
+ fetch_sample(VALID_ALPHANUMERIC), # Plant ID
51
+ FFaker.numerify('######') # Serial Number
52
+ ].join
53
+
54
+ # Calculate the Check Digit
55
+ weighted_sum = generated_vin.chars.each_with_index.sum do |char, idx|
56
+ (TRANSLITERATION_VALUES[char] || char).to_i * POSITION_WEIGHTS[idx]
57
+ end
58
+
59
+ check_digit = weighted_sum % 11
60
+ check_digit = 'X' if check_digit == 10
61
+ generated_vin[8] = check_digit.to_s
62
+
63
+ generated_vin
64
+ end
65
+ end
66
+
5
67
  extend ModuleUtils
6
68
  extend self
7
69
 
@@ -37,7 +99,7 @@ module FFaker
37
99
  end
38
100
 
39
101
  def vin
40
- FFaker.bothify('1#???#####?######').upcase
102
+ VIN.vin
41
103
  end
42
104
 
43
105
  def year