Class: Tina4::FakeData
- Inherits:
-
Object
- Object
- Tina4::FakeData
- 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.
Determinism is PER-LANGUAGE, not cross-language (SEED-DETERMINISM-PERLANG):
FakeData.new(seed: 42) reproduces the identical sequence on every run
within Ruby, but the same seed on Python/PHP/Node's FakeData will NOT
produce the same values -- each language uses its own PRNG (Ruby's
Random, Python's Mersenne Twister, PHP's per-instance Mt19937, Node's
mulberry32). There is no shared cross-language PRNG, and hand-rolling one
would add cost for no real benefit -- use a seed to make ONE language's
run reproducible, never to compare output across languages.
NOT FOR SECRETS (SEED-SECRETS-DOC): this is a non-cryptographic PRNG meant
for realistic-looking fixtures and test data. Never use it to generate API
keys, passwords, tokens, or anything else that must be unguessable -- use
SecureRandom (or Tina4::Auth for password hashing) instead.
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
-
.seed(seed) ⇒ Object
Static factory — create a seeded FakeData instance.
Instance Method Summary collapse
- #address ⇒ Object
- #blob(size: 64) ⇒ Object
- #boolean ⇒ Object
- #choice(items) ⇒ Object
- #city ⇒ Object
- #color_hex ⇒ Object
- #company ⇒ Object
- #country ⇒ Object
-
#credit_card ⇒ Object
Generate a fake credit card number (test numbers only, e.g. 4111...).
- #currency ⇒ Object
- #date(start_year: 2020, end_year: 2026) ⇒ Object
- #datetime(start_year: 2020, end_year: 2026) ⇒ Object
- #email(from_name: nil) ⇒ Object
- #first_name ⇒ Object
-
#for_field(field_def, column_name = nil) ⇒ Object
Generate appropriate data based on field definition and column name.
-
#initialize(seed: nil) ⇒ FakeData
constructor
A new instance of FakeData.
- #integer(min: 0, max: 10_000) ⇒ Object
- #ip_address ⇒ Object
- #job_title ⇒ Object
- #json_data(keys: nil) ⇒ Object
- #last_name ⇒ Object
- #name ⇒ Object
- #numeric(min: 0.0, max: 1000.0, decimals: 2) ⇒ Object
- #paragraph(sentences: 3) ⇒ Object
- #password(length: 16) ⇒ Object
- #phone ⇒ Object
-
#run(count = 1, &block) ⇒ Object
Run a generator block
counttimes and return the results. - #sentence(words: 6) ⇒ Object
- #slug(words: 3) ⇒ Object
- #text(max_length: 200) ⇒ Object
- #timestamp(start_year: 2020, end_year: 2026) ⇒ Object
- #url ⇒ Object
- #uuid ⇒ Object
- #word ⇒ Object
- #zip_code ⇒ Object
Constructor Details
#initialize(seed: nil) ⇒ FakeData
Returns a new instance of FakeData.
111 112 113 |
# File 'lib/tina4/seeder.rb', line 111 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
118 119 120 |
# File 'lib/tina4/seeder.rb', line 118 def self.seed(seed) new(seed: seed) end |
Instance Method Details
#address ⇒ Object
231 232 233 |
# File 'lib/tina4/seeder.rb', line 231 def address "#{@rng.rand(1..9999)} #{STREETS[@rng.rand(STREETS.length)]} #{STREET_TYPES[@rng.rand(STREET_TYPES.length)]}" end |
#blob(size: 64) ⇒ Object
206 207 208 |
# File 'lib/tina4/seeder.rb', line 206 def blob(size: 64) SecureRandom.random_bytes(size) end |
#boolean ⇒ Object
187 188 189 |
# File 'lib/tina4/seeder.rb', line 187 def boolean @rng.rand(2) == 1 end |
#choice(items) ⇒ Object
219 220 221 |
# File 'lib/tina4/seeder.rb', line 219 def choice(items) items[@rng.rand(items.length)] end |
#city ⇒ Object
223 224 225 |
# File 'lib/tina4/seeder.rb', line 223 def city CITIES[@rng.rand(CITIES.length)] end |
#color_hex ⇒ Object
265 266 267 |
# File 'lib/tina4/seeder.rb', line 265 def color_hex "#%06x" % @rng.rand(0..0xFFFFFF) end |
#company ⇒ Object
239 240 241 242 243 244 |
# File 'lib/tina4/seeder.rb', line 239 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 |
#country ⇒ Object
227 228 229 |
# File 'lib/tina4/seeder.rb', line 227 def country COUNTRIES[@rng.rand(COUNTRIES.length)] end |
#credit_card ⇒ Object
Generate a fake credit card number (test numbers only, e.g. 4111...).
259 260 261 262 263 |
# File 'lib/tina4/seeder.rb', line 259 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 |
#currency ⇒ Object
250 251 252 |
# File 'lib/tina4/seeder.rb', line 250 def currency CURRENCIES[@rng.rand(CURRENCIES.length)] end |
#date(start_year: 2020, end_year: 2026) ⇒ Object
198 199 200 |
# File 'lib/tina4/seeder.rb', line 198 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
191 192 193 194 195 196 |
# File 'lib/tina4/seeder.rb', line 191 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
134 135 136 137 138 139 140 141 142 |
# File 'lib/tina4/seeder.rb', line 134 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_name ⇒ Object
122 123 124 |
# File 'lib/tina4/seeder.rb', line 122 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.
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 319 320 321 322 323 324 325 326 327 328 329 330 331 332 |
# File 'lib/tina4/seeder.rb', line 285 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 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
178 179 180 |
# File 'lib/tina4/seeder.rb', line 178 def integer(min: 0, max: 10_000) @rng.rand(min..max) end |
#ip_address ⇒ Object
254 255 256 |
# File 'lib/tina4/seeder.rb', line 254 def ip_address "#{@rng.rand(1..255)}.#{@rng.rand(0..255)}.#{@rng.rand(0..255)}.#{@rng.rand(1..254)}" end |
#job_title ⇒ Object
246 247 248 |
# File 'lib/tina4/seeder.rb', line 246 def job_title JOB_TITLES[@rng.rand(JOB_TITLES.length)] end |
#json_data(keys: nil) ⇒ Object
210 211 212 213 214 215 216 217 |
# File 'lib/tina4/seeder.rb', line 210 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_name ⇒ Object
126 127 128 |
# File 'lib/tina4/seeder.rb', line 126 def last_name LAST_NAMES[@rng.rand(LAST_NAMES.length)] end |
#name ⇒ Object
130 131 132 |
# File 'lib/tina4/seeder.rb', line 130 def name "#{first_name} #{last_name}" end |
#numeric(min: 0.0, max: 1000.0, decimals: 2) ⇒ Object
182 183 184 185 |
# File 'lib/tina4/seeder.rb', line 182 def numeric(min: 0.0, max: 1000.0, decimals: 2) val = min + @rng.rand * (max - min) val.round(decimals) end |
#paragraph(sentences: 3) ⇒ Object
157 158 159 |
# File 'lib/tina4/seeder.rb', line 157 def paragraph(sentences: 3) Array.new(sentences) { sentence(words: @rng.rand(5..12)) }.join(" ") end |
#password(length: 16) ⇒ Object
274 275 276 277 |
# File 'lib/tina4/seeder.rb', line 274 def password(length: 16) chars = [*"a".."z", *"A".."Z", *"0".."9"] Array.new(length) { chars[@rng.rand(chars.length)] }.join end |
#phone ⇒ Object
144 145 146 147 148 149 |
# File 'lib/tina4/seeder.rb', line 144 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.
280 281 282 |
# File 'lib/tina4/seeder.rb', line 280 def run(count = 1, &block) Array.new(count) { block.call } end |
#sentence(words: 6) ⇒ Object
151 152 153 154 155 |
# File 'lib/tina4/seeder.rb', line 151 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
170 171 172 |
# File 'lib/tina4/seeder.rb', line 170 def slug(words: 3) Array.new(words) { WORDS[@rng.rand(WORDS.length)] }.join("-") end |
#text(max_length: 200) ⇒ Object
161 162 163 164 |
# File 'lib/tina4/seeder.rb', line 161 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
202 203 204 |
# File 'lib/tina4/seeder.rb', line 202 def (start_year: 2020, end_year: 2026) datetime(start_year: start_year, end_year: end_year).strftime("%Y-%m-%d %H:%M:%S") end |
#url ⇒ Object
174 175 176 |
# File 'lib/tina4/seeder.rb', line 174 def url "https://#{DOMAINS[@rng.rand(DOMAINS.length)]}/#{slug}" end |
#uuid ⇒ Object
269 270 271 272 |
# File 'lib/tina4/seeder.rb', line 269 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 |
#word ⇒ Object
166 167 168 |
# File 'lib/tina4/seeder.rb', line 166 def word WORDS[@rng.rand(WORDS.length)] end |
#zip_code ⇒ Object
235 236 237 |
# File 'lib/tina4/seeder.rb', line 235 def zip_code @rng.rand(10_000..99_999).to_s end |