forgery 0.2.2

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 (60) hide show
  1. data/README.markdown +99 -0
  2. data/VERSION.yml +4 -0
  3. data/generators/forgery/USAGE +6 -0
  4. data/generators/forgery/forgery_generator.rb +12 -0
  5. data/lib/dictionaries.rb +24 -0
  6. data/lib/dictionaries/cities +478 -0
  7. data/lib/dictionaries/colors +19 -0
  8. data/lib/dictionaries/company_names +400 -0
  9. data/lib/dictionaries/countries +249 -0
  10. data/lib/dictionaries/female_first_names +100 -0
  11. data/lib/dictionaries/frequencies +8 -0
  12. data/lib/dictionaries/genders +2 -0
  13. data/lib/dictionaries/languages +97 -0
  14. data/lib/dictionaries/last_names +250 -0
  15. data/lib/dictionaries/lorem_ipsum +151 -0
  16. data/lib/dictionaries/male_first_names +100 -0
  17. data/lib/dictionaries/name_suffixes +5 -0
  18. data/lib/dictionaries/name_titles +6 -0
  19. data/lib/dictionaries/province_abbrevs +13 -0
  20. data/lib/dictionaries/provinces +13 -0
  21. data/lib/dictionaries/races +93 -0
  22. data/lib/dictionaries/shirt_sizes +7 -0
  23. data/lib/dictionaries/state_abbrevs +50 -0
  24. data/lib/dictionaries/states +50 -0
  25. data/lib/dictionaries/street_suffixes +21 -0
  26. data/lib/dictionaries/streets +500 -0
  27. data/lib/dictionaries/top_level_domains +9 -0
  28. data/lib/extensions/array.rb +11 -0
  29. data/lib/extensions/hash.rb +12 -0
  30. data/lib/extensions/range.rb +9 -0
  31. data/lib/extensions/string.rb +48 -0
  32. data/lib/file_reader.rb +53 -0
  33. data/lib/forgeries/address_forgery.rb +136 -0
  34. data/lib/forgeries/basic_forgery.rb +71 -0
  35. data/lib/forgeries/internet_forgery.rb +17 -0
  36. data/lib/forgeries/lorem_ipsum_forgery.rb +112 -0
  37. data/lib/forgeries/monetary_forgery.rb +13 -0
  38. data/lib/forgeries/name_forgery.rb +34 -0
  39. data/lib/forgeries/personal_forgery.rb +22 -0
  40. data/lib/forgery.rb +47 -0
  41. data/lib/formats.rb +24 -0
  42. data/lib/formats/phone +1 -0
  43. data/lib/formats/street_number +5 -0
  44. data/lib/formats/zip +2 -0
  45. data/spec/dictionaries_spec.rb +35 -0
  46. data/spec/extensions/array_spec.rb +25 -0
  47. data/spec/extensions/range_spec.rb +26 -0
  48. data/spec/extensions/string_spec.rb +29 -0
  49. data/spec/file_reader_spec.rb +11 -0
  50. data/spec/forgeries/address_forgery_spec.rb +79 -0
  51. data/spec/forgeries/basic_forgery_spec.rb +175 -0
  52. data/spec/forgeries/internet_forgery_spec.rb +30 -0
  53. data/spec/forgeries/lorem_ipsum_forgery_spec.rb +128 -0
  54. data/spec/forgeries/monetary_forgery_spec.rb +4 -0
  55. data/spec/forgeries/name_forgery_spec.rb +4 -0
  56. data/spec/forgeries/personal_forgery_spec.rb +15 -0
  57. data/spec/forgery_spec.rb +42 -0
  58. data/spec/formats_spec.rb +35 -0
  59. data/spec/spec_helper.rb +32 -0
  60. metadata +114 -0
@@ -0,0 +1,9 @@
1
+ biz
2
+ com
3
+ info
4
+ name
5
+ net
6
+ org
7
+ gov
8
+ edu
9
+ mil
@@ -0,0 +1,11 @@
1
+ class Array
2
+ def random
3
+ self[Kernel.rand(size)]
4
+ end
5
+
6
+ def random_subset(len=2)
7
+ rs = []
8
+ len.times { rs << random }
9
+ rs
10
+ end
11
+ end
@@ -0,0 +1,12 @@
1
+ class Hash
2
+ # Performs the opposite of merge, with the keys and values from the first hash taking precedence over the second.
3
+ def reverse_merge(other_hash)
4
+ other_hash.merge(self)
5
+ end
6
+
7
+ # Performs the opposite of merge, with the keys and values from the first hash taking precedence over the second.
8
+ # Modifies the receiver in place.
9
+ def reverse_merge!(other_hash)
10
+ replace(reverse_merge(other_hash))
11
+ end
12
+ end
@@ -0,0 +1,9 @@
1
+ class Range
2
+ def random
3
+ Integer(first) && Integer(last)
4
+ raise ArgumentError if first > last
5
+ Kernel.rand(last - first + 1) + first
6
+ rescue ArgumentError
7
+ self.to_a.random
8
+ end
9
+ end
@@ -0,0 +1,48 @@
1
+ class String
2
+ def to_numbers(replace='#')
3
+ self.gsub(/#{replace}/){ Kernel.rand(10) }
4
+ end
5
+
6
+ if !defined?(RAILS_ROOT)
7
+ def camelize(first_letter = :upper)
8
+ case first_letter
9
+ when :upper
10
+ to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
11
+ when :lower
12
+ first.downcase + camelize(self)[1..-1]
13
+ end
14
+ end
15
+
16
+ def underscore
17
+ to_s.gsub(/::/, '/').
18
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
19
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
20
+ tr("-", "_").
21
+ downcase
22
+ end
23
+
24
+ if Module.method(:const_get).arity == 1
25
+ def constantize
26
+ names = self.split('::')
27
+ names.shift if names.empty? || names.first.empty?
28
+
29
+ constant = Object
30
+ names.each do |name|
31
+ constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
32
+ end
33
+ constant
34
+ end
35
+ else
36
+ def constantize
37
+ names = self.split('::')
38
+ names.shift if names.empty? || names.first.empty?
39
+
40
+ constant = Object
41
+ names.each do |name|
42
+ constant = constant.const_get(name, false) || constant.const_missing(name)
43
+ end
44
+ constant
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,53 @@
1
+ class FileReader
2
+
3
+ def self.read_dictionary(dictionary)
4
+ read_file(path_to_dictionary(dictionary))
5
+ end
6
+
7
+ def self.read_format(format)
8
+ read_file(path_to_format(format))
9
+ end
10
+
11
+ protected
12
+
13
+ def self.read_file(file)
14
+ lines = []
15
+ IO.foreach(file) do |line|
16
+ lines << line.chomp unless line.chomp == ''
17
+ end
18
+ lines
19
+ end
20
+
21
+ def self.path_to_format(format)
22
+ if external_path_to_format(format) && File.exists?(external_path_to_format(format))
23
+ external_path_to_format(format)
24
+ else
25
+ internal_path_to_format(format)
26
+ end
27
+ end
28
+
29
+ def self.external_path_to_format(format)
30
+ RAILS_ROOT + '/lib/forgery/formats/' + format.to_s if defined?(RAILS_ROOT)
31
+ end
32
+
33
+ def self.internal_path_to_format(format)
34
+ File.dirname(__FILE__) + '/formats/' + format.to_s
35
+ end
36
+
37
+ def self.path_to_dictionary(dictionary)
38
+ if external_path_to_dictionary(dictionary) && File.exists?(external_path_to_dictionary(dictionary))
39
+ external_path_to_dictionary(dictionary)
40
+ else
41
+ internal_path_to_dictionary(dictionary)
42
+ end
43
+ end
44
+
45
+ def self.external_path_to_dictionary(dictionary)
46
+ RAILS_ROOT + '/lib/forgery/dictionaries/' + dictionary.to_s if defined?(RAILS_ROOT)
47
+ end
48
+
49
+ def self.internal_path_to_dictionary(dictionary)
50
+ File.dirname(__FILE__) + '/dictionaries/' + dictionary.to_s
51
+ end
52
+
53
+ end
@@ -0,0 +1,136 @@
1
+ # Generates random address information.
2
+ class AddressForgery < Forgery
3
+ # Gets a random street name out of the 'streets' dictionary.
4
+ #
5
+ # AddressForgery.street_name
6
+ # # => "Atwood"
7
+ #
8
+ # AddressForgery.street_name
9
+ # # => "Fordem"
10
+ def self.street_name
11
+ dictionaries[:streets].random
12
+ end
13
+
14
+ # Gets one of the formats from 'street_number_formats' and converts it to
15
+ # numbers.
16
+ #
17
+ # AddressForgery.street_number
18
+ # # => 1
19
+ #
20
+ # AddressForgery.street_number
21
+ # # => 1234
22
+ def self.street_number
23
+ formats[:street_number].random.to_numbers
24
+ end
25
+
26
+ # Gets a random street suffix out of the 'street_suffixes' dictionary.
27
+ #
28
+ # AddressForgery.street_suffix
29
+ # # => "Street"
30
+ #
31
+ # AddressForgery.street_suffix
32
+ # # => "Parkway"
33
+ def self.street_suffix
34
+ dictionaries[:street_suffixes].random
35
+ end
36
+
37
+ # Gets a full street address, including street number, street name, and
38
+ # street suffix.
39
+ #
40
+ # AddressForgery.street_address
41
+ # # => "92 West Circle"
42
+ #
43
+ # AddressForgery.street_address
44
+ # # => "93265 Harper Lane"
45
+ def self.street_address
46
+ "#{street_number} #{street_name} #{street_suffix}"
47
+ end
48
+
49
+ # Gets a random city out of the 'cities' dictionary.
50
+ #
51
+ # AddressForgery.city
52
+ # # => "Anaheim"
53
+ #
54
+ # AddressForgery.city
55
+ # # => "Sacramento"
56
+ def self.city
57
+ dictionaries[:cities].random
58
+ end
59
+
60
+ # Gets a random state out of the 'states' dictionary.
61
+ #
62
+ # AddressForgery.state
63
+ # # => "Mississippi"
64
+ #
65
+ # AddressForgery.state
66
+ # # => "Minnesota"
67
+ def self.state
68
+ dictionaries[:states].random
69
+ end
70
+
71
+ # Gets a random state abbreviation out of the 'state_abbrev' dictionary.
72
+ #
73
+ # AddressForgery.state_abbrev
74
+ # # => "GA"
75
+ #
76
+ # AddressForgery.state_abbrev
77
+ # # => "TX"
78
+ def self.state_abbrev
79
+ dictionaries[:state_abbrevs].random
80
+ end
81
+
82
+ # Gets a random Canadian province or territory out of the 'provinces' dictionary.
83
+ #
84
+ # AddressForgery.province
85
+ # # => "Ontario"
86
+ #
87
+ # AddressForgery.province
88
+ # # => "Northwest Territories"
89
+ def self.province
90
+ dictionaries[:provinces].random
91
+ end
92
+
93
+ # Gets a random Canadian province or territory abbreviation out of the 'province_abbrev' dictionary.
94
+ #
95
+ # AddressForgery.province_abbrev
96
+ # # => "ON"
97
+ #
98
+ # AddressForgery.province_abbrev
99
+ # # => "NT"
100
+ def self.province_abbrev
101
+ dictionaries[:province_abbrevs].random
102
+ end
103
+
104
+ # Gets one of the formats from 'zip_formats' and converts it to numbers.
105
+ #
106
+ # AddressForgery.zip
107
+ # # => "52474"
108
+ #
109
+ # AddressForgery.zip
110
+ # # => "66702-4349"
111
+ def self.zip
112
+ formats[:zip].random.to_numbers
113
+ end
114
+
115
+ # Gets one of the formats from 'phone_formats' and converts it to numbers.
116
+ #
117
+ # AddressForgery.phone
118
+ # # => "1-(416)185-8799"
119
+ #
120
+ # AddressForgery.phone
121
+ # # => "1-(589)248-0418"
122
+ def self.phone
123
+ formats[:phone].random.to_numbers
124
+ end
125
+
126
+ # Gets a random country out of the 'countries' dictionary.
127
+ #
128
+ # AddressForgery.country
129
+ # # => "Uruguay"
130
+ #
131
+ # AddressForgery.country
132
+ # # => "Romania"
133
+ def self.country
134
+ dictionaries[:countries].random
135
+ end
136
+ end
@@ -0,0 +1,71 @@
1
+ require 'digest/sha1'
2
+
3
+ class BasicForgery < Forgery
4
+ HEX_DIGITS = %w{0 1 2 3 4 5 6 7 8 9 a b c d e f}
5
+ UPPER_ALPHA = ('A'..'Z').to_a
6
+ LOWER_ALPHA = ('a'..'z').to_a
7
+ NUMERIC = ('0'..'9').to_a
8
+ SPECIAL_CHARACTERS = %w{! ' @ # $ % ^ & * ( ) _ + - = [ ] { } ; : " , . / ?}
9
+ BOOLEAN = [true, false]
10
+
11
+ def self.password(options={})
12
+ options = {:at_least => 6,
13
+ :at_most => 12,
14
+ :allow_lower => true,
15
+ :allow_upper => true,
16
+ :allow_numeric => true,
17
+ :allow_special => false}.merge!(options)
18
+ self.text(options)
19
+ end
20
+
21
+ def self.encrypt(password="password", salt=Time.now.to_s)
22
+ Digest::SHA1.hexdigest("--#{salt}--#{password}--")
23
+ end
24
+
25
+ def self.boolean
26
+ BOOLEAN.random
27
+ end
28
+
29
+ def self.color
30
+ dictionaries[:colors].random
31
+ end
32
+
33
+ def self.hex_color
34
+ hex_digits = (1..6).collect { HEX_DIGITS.random}
35
+ "##{hex_digits.join}"
36
+ end
37
+
38
+ def self.short_hex_color
39
+ hex_color[0,4]
40
+ end
41
+
42
+ def self.number(options={})
43
+ options = {:at_least => 1,
44
+ :at_most => 10}.merge(options)
45
+
46
+ (options[:at_least]..options[:at_most]).random
47
+ end
48
+
49
+ def self.text(options={})
50
+ options = {:at_least => 10,
51
+ :at_most => 15,
52
+ :allow_lower => true,
53
+ :allow_upper => true,
54
+ :allow_numeric => true,
55
+ :allow_special => false}.merge!(options)
56
+
57
+ allowed_characters = []
58
+ allowed_characters += LOWER_ALPHA if options[:allow_lower]
59
+ allowed_characters += UPPER_ALPHA if options[:allow_upper]
60
+ allowed_characters += NUMERIC if options[:allow_numeric]
61
+ allowed_characters += SPECIAL_CHARACTERS if options[:allow_special]
62
+
63
+ length = (options[:at_least]..options[:at_most]).random
64
+
65
+ allowed_characters.random_subset(length).join
66
+ end
67
+
68
+ def self.frequency
69
+ dictionaries[:frequencies].random
70
+ end
71
+ end
@@ -0,0 +1,17 @@
1
+ class InternetForgery < Forgery
2
+ def self.user_name
3
+ (dictionaries[:male_first_names].random[0,1] + dictionaries[:last_names].random).downcase
4
+ end
5
+
6
+ def self.top_level_domain
7
+ dictionaries[:top_level_domains].random
8
+ end
9
+
10
+ def self.domain_name
11
+ dictionaries[:company_names].random.downcase + '.' + self.top_level_domain
12
+ end
13
+
14
+ def self.email_address
15
+ self.user_name + '@' + self.domain_name
16
+ end
17
+ end
@@ -0,0 +1,112 @@
1
+ class LoremIpsumForgery < Forgery
2
+
3
+ def self.text(what=:sentence, quantity=2, options={})
4
+ case what
5
+ when :character
6
+ self.character(options)
7
+ when :characters
8
+ self.characters(quantity, options)
9
+ when :word
10
+ self.word(options)
11
+ when :words
12
+ self.words(quantity, options)
13
+ when :sentence
14
+ self.sentence(options)
15
+ when :sentences
16
+ self.sentences(quantity, options)
17
+ when :paragraph
18
+ self.paragraph(options)
19
+ when :paragraphs
20
+ self.paragraphs(quantity, options)
21
+ end
22
+ end
23
+
24
+
25
+ def self.character(options={})
26
+ self.characters(1, options)
27
+ end
28
+
29
+ def self.characters(quantity=10, options={})
30
+ options.merge!(:random_limit => lorem_ipsum_characters.length-quantity) if quantity.is_a?(Fixnum)
31
+
32
+ lorem_ipsum_characters[range_from_quantity(quantity, options)]
33
+ end
34
+
35
+ def self.word(options={})
36
+ self.words(1, options)
37
+ end
38
+
39
+ def self.words(quantity=10, options={})
40
+ options.merge!(:random_limit => lorem_ipsum_words.length-quantity) if quantity.is_a?(Fixnum)
41
+
42
+ lorem_ipsum_words[range_from_quantity(quantity, options)].join(" ")
43
+ end
44
+
45
+ def self.sentence(options={})
46
+ self.sentences(1, options)
47
+ end
48
+
49
+ def self.sentences(quantity=2, options={})
50
+ options.merge!(:random_limit => (dictionaries[:lorem_ipsum].length-quantity)) if quantity.is_a?(Fixnum)
51
+
52
+ dictionaries[:lorem_ipsum][range_from_quantity(quantity, options)].join(" ")
53
+ end
54
+
55
+ def self.paragraph(options={})
56
+ self.paragraphs(1, options)
57
+ end
58
+
59
+ def self.paragraphs(quantity=2, options={})
60
+ options.reverse_merge!(:separator => "\n\n",
61
+ :wrap => {
62
+ :start => "",
63
+ :end => "" },
64
+ :html => false,
65
+ :sentences => 3)
66
+ options.merge!(:random_limit => (dictionaries[:lorem_ipsum].length/options[:sentences])-quantity) if quantity.is_a?(Fixnum)
67
+
68
+ if options[:html]
69
+ options[:wrap] = { :start => "<p>",
70
+ :end => "</p>" }
71
+ end
72
+
73
+ range = range_from_quantity(quantity, options)
74
+ start = range.first * options[:sentences]
75
+
76
+ paragraphs = []
77
+
78
+ range.to_a.length.times do |i|
79
+ paragraphs << (
80
+ options[:wrap][:start] +
81
+ dictionaries[:lorem_ipsum][start..(start+options[:sentences]-1)].join(" ") +
82
+ options[:wrap][:end]
83
+ )
84
+ start += options[:sentences]
85
+ end
86
+
87
+ paragraphs.join(options[:separator])
88
+ end
89
+
90
+
91
+ protected
92
+
93
+ def self.range_from_quantity(quantity, options={})
94
+ return quantity if quantity.is_a?(Range)
95
+
96
+ if options[:random]
97
+ start = (0..options[:random_limit]).random
98
+ start..(start+quantity-1)
99
+ else
100
+ 0..(quantity-1)
101
+ end
102
+ end
103
+
104
+ def self.lorem_ipsum_words
105
+ @@lorem_ipsum_words ||= dictionaries[:lorem_ipsum].join(" ").downcase.gsub(/\.|,|;/, '').split(" ")
106
+ end
107
+
108
+ def self.lorem_ipsum_characters
109
+ @@lorem_ipsum_characters ||= dictionaries[:lorem_ipsum].join("").downcase.gsub(/[^a-z\s]/,'')
110
+ end
111
+
112
+ end