ffaker 2.20.0 → 2.22.0

Sign up to get free protection for your applications and to get access to all the features.
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