Class: Tina4::FakeData

Inherits:
Object
  • Object
show all
Defined in:
lib/tina4/seeder.rb

Overview

Zero-dependency fake data generator with deterministic seeding. Uses Ruby’s built-in Random for reproducible data generation.

Examples:

fake = Tina4::FakeData.new(seed: 42)
fake.name        # => "Sarah Johnson"
fake.email       # => "sarah.johnson123@example.com"
fake.integer(1, 100)

Constant Summary collapse

FIRST_NAMES =
%w[
  James Mary Robert Patricia John Jennifer Michael Linda David Elizabeth
  William Barbara Richard Susan Joseph Jessica Thomas Sarah Charles Karen
  Christopher Lisa Daniel Nancy Matthew Betty Anthony Margaret Mark Sandra
  Donald Ashley Steven Dorothy Paul Kimberly Andrew Emily Joshua Donna
  Kenneth Michelle Kevin Carol Brian Amanda George Melissa Timothy Deborah
  Ronald Stephanie Edward Rebecca Jason Sharon Jeffrey Laura Ryan Cynthia
  Jacob Kathleen Gary Amy Nicholas Angela Eric Shirley Jonathan Anna
  Stephen Brenda Larry Pamela Justin Emma Scott Nicole Brandon Helen
  Benjamin Samantha Samuel Katherine Raymond Christine Gregory Debra
  Frank Rachel Alexander Carolyn Patrick Janet Jack Catherine Andre Aisha
  Wei Yuki Carlos Fatima Raj Priya Mohammed Sophia Liam Olivia Noah Ava
  Ethan Mia Lucas Isabella Mason Charlotte Logan Amelia Aiden Harper
].freeze
LAST_NAMES =
%w[
  Smith Johnson Williams Brown Jones Garcia Miller Davis Rodriguez Martinez
  Hernandez Lopez Gonzalez Wilson Anderson Thomas Taylor Moore Jackson Martin
  Lee Perez Thompson White Harris Sanchez Clark Ramirez Lewis Robinson Walker
  Young Allen King Wright Scott Torres Nguyen Hill Flores Green Adams Nelson
  Baker Hall Rivera Campbell Mitchell Carter Roberts Gomez Phillips Evans
  Turner Diaz Parker Cruz Edwards Collins Reyes Stewart Morris Morales
  Murphy Cook Rogers Gutierrez Ortiz Morgan Cooper Peterson Bailey Reed
  Kelly Howard Ramos Kim Cox Ward Richardson Watson Brooks Chavez Wood
  James Bennett Gray Mendoza Ruiz Hughes Price Alvarez Castillo Sanders
  Patel Müller Nakamura Singh Chen Silva Ali Okafor
].freeze
WORDS =
%w[
  the be to of and a in that have it for not on with he as you do at
  this but his by from they we say her she or an will my one all would
  there their what so up out if about who get which go me when make can
  like time no just him know take people into year your good some could
  them see other than then now look only come its over think also back
  after use two how our work first well way even new want because any
  these give day most us great small large every found still between name
  should home big end along each much both help line turn move thing right
  same old better point long real system data report order product service
  customer account payment record total status market world company project
  team value process business group result information development management
  quality performance technology support research design program network
].freeze
CITIES =
[
  "New York", "London", "Tokyo", "Paris", "Berlin", "Sydney", "Toronto",
  "Mumbai", "São Paulo", "Cairo", "Lagos", "Dubai", "Singapore",
  "Hong Kong", "Seoul", "Mexico City", "Bangkok", "Istanbul", "Moscow",
  "Rome", "Barcelona", "Amsterdam", "Nairobi", "Cape Town", "Johannesburg",
  "Buenos Aires", "Lima", "Santiago", "Jakarta", "Manila", "Kuala Lumpur",
  "Auckland", "Vancouver", "Chicago", "San Francisco", "Los Angeles",
  "Miami", "Boston", "Seattle", "Denver"
].freeze
COUNTRIES =
[
  "United States", "United Kingdom", "Canada", "Australia", "Germany",
  "France", "Japan", "Brazil", "India", "South Africa", "Nigeria",
  "Egypt", "Kenya", "Mexico", "Argentina", "Chile", "Colombia", "Spain",
  "Italy", "Netherlands", "Sweden", "Norway", "Denmark", "Finland",
  "Switzerland", "Belgium", "Austria", "New Zealand", "Singapore",
  "South Korea", "Thailand", "Indonesia", "Philippines", "Vietnam",
  "Malaysia", "United Arab Emirates", "Saudi Arabia", "Turkey", "Poland"
].freeze
DOMAINS =
%w[
  example.com test.org sample.net demo.io mail.com
  inbox.org webmail.net company.com corp.io biz.net
].freeze
STREETS =
%w[Main Oak Pine Maple Cedar Elm Park Lake Hill River Church Market King Queen High].freeze
STREET_TYPES =
%w[Street Avenue Road Drive Lane Boulevard Way Place].freeze
COMPANY_WORDS =
%w[Tech Global Apex Nova Core Prime Next Blue Bright Smart Swift Peak Fusion Pulse Vertex].freeze
COMPANY_SUFFIXES =
%w[Inc Corp Ltd LLC Group Solutions Systems Labs].freeze
JOB_TITLES =
[
  "Software Engineer", "Product Manager", "Designer", "Data Analyst",
  "DevOps Engineer", "CEO", "CTO", "Sales Manager", "Marketing Lead",
  "Accountant", "Operations Manager", "QA Engineer", "UX Researcher",
  "Support Specialist", "HR Manager", "Technical Writer"
].freeze
CURRENCIES =
%w[USD EUR GBP JPY CAD AUD CHF ZAR INR CNY].freeze
CREDIT_CARD_PREFIXES =
%w[4111 4242 5500 5105].freeze

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(seed: nil) ⇒ FakeData

Returns a new instance of FakeData.



97
98
99
# File 'lib/tina4/seeder.rb', line 97

def initialize(seed: nil)
  @rng = seed ? Random.new(seed) : Random.new
end

Class Method Details

.seed(seed) ⇒ Object

Static factory — create a seeded FakeData instance.

fake = FakeData.seed(42)
fake.name  # deterministic


104
105
106
# File 'lib/tina4/seeder.rb', line 104

def self.seed(seed)
  new(seed: seed)
end

Instance Method Details

#addressObject



217
218
219
# File 'lib/tina4/seeder.rb', line 217

def address
  "#{@rng.rand(1..9999)} #{STREETS[@rng.rand(STREETS.length)]} #{STREET_TYPES[@rng.rand(STREET_TYPES.length)]}"
end

#blob(size: 64) ⇒ Object



192
193
194
# File 'lib/tina4/seeder.rb', line 192

def blob(size: 64)
  SecureRandom.random_bytes(size)
end

#booleanObject



173
174
175
# File 'lib/tina4/seeder.rb', line 173

def boolean
  @rng.rand(2)
end

#choice(items) ⇒ Object



205
206
207
# File 'lib/tina4/seeder.rb', line 205

def choice(items)
  items[@rng.rand(items.length)]
end

#cityObject



209
210
211
# File 'lib/tina4/seeder.rb', line 209

def city
  CITIES[@rng.rand(CITIES.length)]
end

#color_hexObject



251
252
253
# File 'lib/tina4/seeder.rb', line 251

def color_hex
  "#%06x" % @rng.rand(0..0xFFFFFF)
end

#companyObject



225
226
227
228
229
230
# File 'lib/tina4/seeder.rb', line 225

def company
  w1 = COMPANY_WORDS[@rng.rand(COMPANY_WORDS.length)]
  w2 = COMPANY_WORDS[@rng.rand(COMPANY_WORDS.length)]
  suffix = COMPANY_SUFFIXES[@rng.rand(COMPANY_SUFFIXES.length)]
  "#{w1}#{w2} #{suffix}"
end

#countryObject



213
214
215
# File 'lib/tina4/seeder.rb', line 213

def country
  COUNTRIES[@rng.rand(COUNTRIES.length)]
end

#credit_cardObject

Generate a fake credit card number (test numbers only, e.g. 4111…).



245
246
247
248
249
# File 'lib/tina4/seeder.rb', line 245

def credit_card
  prefix = CREDIT_CARD_PREFIXES[@rng.rand(CREDIT_CARD_PREFIXES.length)]
  rest = Array.new(12) { @rng.rand(0..9) }.join
  prefix + rest
end

#currencyObject



236
237
238
# File 'lib/tina4/seeder.rb', line 236

def currency
  CURRENCIES[@rng.rand(CURRENCIES.length)]
end

#date(start_year: 2020, end_year: 2026) ⇒ Object



184
185
186
# File 'lib/tina4/seeder.rb', line 184

def date(start_year: 2020, end_year: 2026)
  datetime(start_year: start_year, end_year: end_year).strftime("%Y-%m-%d")
end

#datetime(start_year: 2020, end_year: 2026) ⇒ Object



177
178
179
180
181
182
# File 'lib/tina4/seeder.rb', line 177

def datetime(start_year: 2020, end_year: 2026)
  start_time = Time.new(start_year, 1, 1)
  end_time = Time.new(end_year, 12, 31, 23, 59, 59)
  delta = (end_time - start_time).to_i
  Time.at(start_time.to_i + @rng.rand(0..delta))
end

#email(from_name: nil) ⇒ Object



120
121
122
123
124
125
126
127
128
# File 'lib/tina4/seeder.rb', line 120

def email(from_name: nil)
  if from_name
    local = from_name.downcase.split.join(".")
  else
    local = "#{first_name.downcase}.#{last_name.downcase}"
  end
  local += @rng.rand(1..999).to_s
  "#{local}@#{DOMAINS[@rng.rand(DOMAINS.length)]}"
end

#first_nameObject



108
109
110
# File 'lib/tina4/seeder.rb', line 108

def first_name
  FIRST_NAMES[@rng.rand(FIRST_NAMES.length)]
end

#for_field(field_def, column_name = nil) ⇒ Object

Generate appropriate data based on field definition and column name.



271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
# File 'lib/tina4/seeder.rb', line 271

def for_field(field_def, column_name = nil)
  col = (column_name || "").to_s.downcase
  type = field_def[:type]

  # Skip auto-increment primary keys
  return nil if field_def[:primary_key] && field_def[:auto_increment]

  case type
  when :integer
    return integer(min: 18, max: 85) if col.include?("age")
    return integer(min: 1950, max: 2026) if col.include?("year")
    return integer(min: 1, max: 100) if col =~ /quantity|qty|count/
    return boolean if col =~ /active|enabled|visible|^is_/
    return integer(min: 1, max: 10) if col =~ /rating|score/
    integer(min: 1, max: 10_000)

  when :float, :decimal
    decimals = field_def[:scale] || 2
    return numeric(min: 0.01, max: 9999.99, decimals: decimals) if col =~ /price|cost|amount|total|fee/
    return numeric(min: 0.0, max: 100.0, decimals: decimals) if col =~ /rate|percent|ratio/
    return numeric(min: -90.0, max: 90.0, decimals: 6) if col.include?("lat")
    return numeric(min: -180.0, max: 180.0, decimals: 6) if col =~ /lon|lng/
    numeric(min: 0.0, max: 10_000.0, decimals: decimals)

  when :date
    date

  when :datetime, :timestamp
    timestamp

  when :boolean
    boolean

  when :blob
    blob

  when :json
    json_data

  when :string, :text
    max_len = field_def[:length] || 255
    val = generate_string_for(col, max_len)
    val.length > max_len ? val[0...max_len] : val

  else
    word
  end
end

#integer(min: 0, max: 10_000) ⇒ Object



164
165
166
# File 'lib/tina4/seeder.rb', line 164

def integer(min: 0, max: 10_000)
  @rng.rand(min..max)
end

#ip_addressObject



240
241
242
# File 'lib/tina4/seeder.rb', line 240

def ip_address
  "#{@rng.rand(1..255)}.#{@rng.rand(0..255)}.#{@rng.rand(0..255)}.#{@rng.rand(1..254)}"
end

#job_titleObject



232
233
234
# File 'lib/tina4/seeder.rb', line 232

def job_title
  JOB_TITLES[@rng.rand(JOB_TITLES.length)]
end

#json_data(keys: nil) ⇒ Object



196
197
198
199
200
201
202
203
# File 'lib/tina4/seeder.rb', line 196

def json_data(keys: nil)
  if keys
    keys.each_with_object({}) { |k, h| h[k] = word }
  else
    n = @rng.rand(2..5)
    n.times.each_with_object({}) { |_, h| h[word] = word }
  end
end

#last_nameObject



112
113
114
# File 'lib/tina4/seeder.rb', line 112

def last_name
  LAST_NAMES[@rng.rand(LAST_NAMES.length)]
end

#nameObject



116
117
118
# File 'lib/tina4/seeder.rb', line 116

def name
  "#{first_name} #{last_name}"
end

#numeric(min: 0.0, max: 1000.0, decimals: 2) ⇒ Object



168
169
170
171
# File 'lib/tina4/seeder.rb', line 168

def numeric(min: 0.0, max: 1000.0, decimals: 2)
  val = min + @rng.rand * (max - min)
  val.round(decimals)
end

#paragraph(sentences: 3) ⇒ Object



143
144
145
# File 'lib/tina4/seeder.rb', line 143

def paragraph(sentences: 3)
  Array.new(sentences) { sentence(words: @rng.rand(5..12)) }.join(" ")
end

#password(length: 16) ⇒ Object



260
261
262
263
# File 'lib/tina4/seeder.rb', line 260

def password(length: 16)
  chars = [*"a".."z", *"A".."Z", *"0".."9"]
  Array.new(length) { chars[@rng.rand(chars.length)] }.join
end

#phoneObject



130
131
132
133
134
135
# File 'lib/tina4/seeder.rb', line 130

def phone
  area = @rng.rand(200..999)
  mid = @rng.rand(100..999)
  tail = @rng.rand(1000..9999)
  "+1 (#{area}) #{mid}-#{tail}"
end

#run(count = 1, &block) ⇒ Object

Run a generator block ‘count` times and return the results.



266
267
268
# File 'lib/tina4/seeder.rb', line 266

def run(count = 1, &block)
  Array.new(count) { block.call }
end

#sentence(words: 6) ⇒ Object



137
138
139
140
141
# File 'lib/tina4/seeder.rb', line 137

def sentence(words: 6)
  w = Array.new(words) { WORDS[@rng.rand(WORDS.length)] }
  w[0] = w[0].capitalize
  "#{w.join(' ')}."
end

#slug(words: 3) ⇒ Object



156
157
158
# File 'lib/tina4/seeder.rb', line 156

def slug(words: 3)
  Array.new(words) { WORDS[@rng.rand(WORDS.length)] }.join("-")
end

#text(max_length: 200) ⇒ Object



147
148
149
150
# File 'lib/tina4/seeder.rb', line 147

def text(max_length: 200)
  t = paragraph(sentences: 2)
  t.length > max_length ? t[0...max_length] : t
end

#timestamp(start_year: 2020, end_year: 2026) ⇒ Object



188
189
190
# File 'lib/tina4/seeder.rb', line 188

def timestamp(start_year: 2020, end_year: 2026)
  datetime(start_year: start_year, end_year: end_year).strftime("%Y-%m-%d %H:%M:%S")
end

#urlObject



160
161
162
# File 'lib/tina4/seeder.rb', line 160

def url
  "https://#{DOMAINS[@rng.rand(DOMAINS.length)]}/#{slug}"
end

#uuidObject



255
256
257
258
# File 'lib/tina4/seeder.rb', line 255

def uuid
  h = Array.new(32) { "0123456789abcdef"[@rng.rand(16)] }.join
  "#{h[0..7]}-#{h[8..11]}-#{h[12..15]}-#{h[16..19]}-#{h[20..31]}"
end

#wordObject



152
153
154
# File 'lib/tina4/seeder.rb', line 152

def word
  WORDS[@rng.rand(WORDS.length)]
end

#zip_codeObject



221
222
223
# File 'lib/tina4/seeder.rb', line 221

def zip_code
  @rng.rand(10_000..99_999).to_s
end