faked_csv 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 56102a797c421e299e8ba038c686b6c513015b96
4
+ data.tar.gz: fba1e160834bd6f27d90896c258e9e30b2c588f8
5
+ SHA512:
6
+ metadata.gz: 53d314f1d7eeffb899dfe8c17629aa8245c98e19e22e4455f02b1508d28953f1bd7ee35c8989dbe85740765172223ac8d955e97c40202f4787f54a04a271d0cf
7
+ data.tar.gz: 4704171816ab7dbd1751b72099ea51b8dccf92c543de2c679ca0cc6fc80edf9c47e0797aed507e78c4473f10c3f539bdac01d00cac9c849be9cc3146c7a65f83
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in faked_csv.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Jianan Lu
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,199 @@
1
+ # Faking CSV data made easy
2
+
3
+ faked_csv, using the awesome [Faker](https://github.com/stympy/faker) gem, helps you to generate CSV file with fake random data in your specified way. You may find it particularly useful in testing something out.
4
+
5
+ ## Installation
6
+
7
+ ```
8
+ gem install faked_csv
9
+ ```
10
+
11
+ Test your installation by running the following command in your terminal/console:
12
+
13
+ ```
14
+ faked_csv --version
15
+ ```
16
+
17
+ You can also install the gem using bundler.
18
+
19
+ The gem is developed and tested under Ruby 2.1.
20
+
21
+ ## Usage
22
+
23
+ For each faked CSV file, you first create a `<name>.csv.json` file that contains the configuration of how you want to generate the faked data, as hinted by the name, in `json` format.
24
+
25
+ ## Example
26
+
27
+ A simple example `.csv.json` file looks like the following. Noted, JSON file doesn't support inline comment, so the below example is only for illustration. You can use the `example.csv.json` file in the root folder of the source code as a boilerplate. More details on the configuration file are in later section.
28
+
29
+ ```
30
+ {
31
+ "rows": 200, <- how many rows in the generated CSV
32
+ "fields": [ <- a list of fields/columns in the generated CSV
33
+ {
34
+ "name": "ID", <- field/column name
35
+ "type": "rand:char", <- type/format of the field
36
+ "length": 5 <- 5 characters random word
37
+ },
38
+ {
39
+ "name": "First Name",
40
+ "type": "fixed", <- values will only come from the below list
41
+ "values": ["Peter", "Tom", "Jane", "Tony", "Steve", "John"]
42
+ },
43
+ {
44
+ "name": "Last Name",
45
+ "type": "faker:name:last_name", <- via Faker::Name.last_name method
46
+ "inject": ["Lu", "Smith"] <- "must-have" values
47
+ },
48
+ {
49
+ "name": "City",
50
+ "type": "faker:address:city", <- via Faker::Address.city method
51
+ "rotate": "rows/5" <- rows/5 (=>200/5 =>40) unique values
52
+ (and only these values) will appear
53
+ },
54
+ {
55
+ "name": "State",
56
+ "type": "faker:address:state_abbr",
57
+ "rotate": 10, <- both rotate and inject? will make sure 10 unique
58
+ values (including "CA") appear
59
+ "inject": ["CA"]
60
+ },
61
+ {
62
+ "name": "Age",
63
+ "type": "rand:int",
64
+ "range": [10, 80] <- range (including boundaries) for random integers
65
+ },
66
+ {
67
+ "name": "Height",
68
+ "type": "rand:float",
69
+ "range": [150, 190], <- range for random floats
70
+ "inject": [200, 210], <- "must-have" values. can be any format
71
+ "rotate": 20, <- 20 unique values (including injects)
72
+ "precision": 2 <- precision of the float numbers
73
+ }
74
+ ]
75
+ }
76
+ ```
77
+
78
+ With the `<name>.csv.json` file, now you can generate a random CSV by invoking the following command:
79
+
80
+ ```
81
+ faked_csv -i example.csv.json -o example.csv
82
+ ```
83
+
84
+ The output CSV looks something like:
85
+
86
+ ```
87
+ ID,First Name,Last Name,City,State,Age,Height
88
+ iW00K,Jane,Bosco,Lake Dandreland,ME,61,170.57
89
+ BzxTl,Steve,Smith,Uniqueside,PA,35,152.93
90
+ vobj8,Tony,Auer,Tressiestad,MS,63,160.86
91
+ X78RS,Steve,Cole,Port Zellachester,OH,72,159.84
92
+ 0YpWG,John,Lu,East Vernaview,OH,18,200
93
+ 4JaoZ,Peter,Simonis,Wernerchester,HI,25,161.64
94
+ hP48C,Tom,Lu,Uniqueside,ME,45,161.64
95
+ WDQpb,John,Casper,East Felicityshire,OH,41,170.57
96
+ wyPwB,Jane,Johns,Maggiehaven,CA,16,180.42
97
+ VCtYR,Peter,Jast,Schadenberg,ME,41,161.64
98
+ 1VYOs,John,Daniel,Port Zellachester,HI,34,200
99
+ V7pg9,Tom,Mayert,Schadenberg,OH,71,152.93
100
+ SmE9w,Jane,Lu,Stephanchester,HI,25,176.95
101
+ ALyok,Tom,Smith,Ryanchester,PA,70,176.95
102
+ fgE5v,Peter,Bailey,Bednarstad,PA,67,170.24
103
+ eBIIM,Peter,Haley,East Vernaview,MS,65,161.64
104
+ 5sv4L,Peter,Prosacco,Uniqueside,PA,50,178.25
105
+ a48IS,Peter,Marvin,Kossview,OH,63,200
106
+ 9mLcZ,John,VonRueden,East Vernaview,PA,20,174.24
107
+ N8ysZ,Tony,Barrows,Wernerchester,HI,62,159.84
108
+ PxsfA,John,Lind,East Vernaview,OH,21,182.45
109
+ lKM33,Steve,Bosco,South Reese,OH,75,176.95
110
+ HR9H5,Jane,Torp,Tressiestad,PA,23,170.57
111
+ JTRCw,Steve,Hermann,Kunzefort,MS,43,152.93
112
+ Wndzb,Tony,McGlynn,Lake Dandreland,CA,52,160.86
113
+ ksmvF,Peter,Rutherford,Josiannetown,PA,12,184.08
114
+ cfsBG,Peter,Lebsack,Port Zellachester,OH,43,182.45
115
+ 7ISEJ,Steve,Altenwerth,Stephanchester,HI,13,159.84
116
+ Letb8,John,Frami,South Reese,HI,65,170.57
117
+ ......
118
+ ```
119
+
120
+ ## CLI Options
121
+
122
+ `-i` to specify the input json configuration file. If omitted, the program will try to find `./faked.csv.json` by default. You can also specify a remote URL of the configuration file.
123
+
124
+ ```
125
+ faked_csv -i http://goo.gl/xDtkJs -o remote.csv
126
+ ```
127
+
128
+ `-o` to specify the output CSV file path. If omitted, the program will print the data to stdout.
129
+
130
+ ## More details on configuration file
131
+
132
+ `rows` to specify how many rows you want to generate in the output CSV file.
133
+
134
+ `fields` to specify the list of all fields of the generated CSV file.
135
+
136
+ ```
137
+ {
138
+ "rows": 1000,
139
+ "fields": [
140
+ ...
141
+ ]
142
+ }
143
+ ```
144
+
145
+ For each field, two attributes are required:
146
+
147
+ `name` to specify the column display name.
148
+
149
+ `type` to sepcify the format of the data in this column.
150
+
151
+ Optional attributes help finer control the generated output.
152
+
153
+ `inject` to specify a list of values that must appear in this column. this is useful when you want to specifically inject some special values into your final CSV. The inject values can be in the same or different format as the type of this column.
154
+
155
+ `rotate` to specify the number of unique values that will be used in this column. This number will include the number of items you put into `inject` list.
156
+
157
+ ```
158
+ "type": "faker:address:state_abbr",
159
+ "inject": ["CA", "HI"],
160
+ "rotate": 10
161
+ ```
162
+
163
+ In the above example, totally 10 states will be considered in the generation process. 2 of them are given by you, i.e. CA and HI; 8 of them will be faked by calling Faker::Address.state_abbr() method. If you specify `rows` to be greater than 10, some values will be repeated, meaning only 10 unique values will appear and these 10 values are ensured to appear at least once. If you specify `rows` less than 10, only `rows` number of unique values will appear; and your `inject` values will have higher priority, meaning if `rows` is 2, then only CA and HI will appear in the final output.
164
+
165
+ ### Supported types
166
+
167
+ #### rand:int and rand:float
168
+
169
+ Random integers/floats. The following attributes are available:
170
+
171
+ `range` to specify the minimum and maximum value that will be generated.
172
+
173
+ ```
174
+ "range": [10, 100] <- the min=10 and max=100. 10 and 100 are included.
175
+ ```
176
+
177
+ If it's `rand:float`, you can specify `precision` attribute to indicate the digit(s) you want to keep for each of the data.
178
+
179
+ #### rand:char
180
+
181
+ Random characters. Only A-Z, a-z and 0-9 will be considered as characters.
182
+
183
+ `length` to specify how many characters in each of the generated data, e.g. 15 will give you something like `9mLcZHR9H5V7pg9`. This is particularly useful if you want to mimic something like item ID, access token etc.
184
+
185
+ #### faker:[class]:[method]
186
+
187
+ This indicates you want to use one of the methods from Faker gem. You can find the list of all methods from [Faker gem's documentation](http://rubydoc.info/github/stympy/faker). Each method is a class name and a method name. Example, `Faker::Internet.url` can be mapped to `faker:internet:url` as a field type in the configuration. As current version, we don't support parameters into the faker method. Future version will add such support.
188
+
189
+ #### fixed
190
+
191
+ This indicates you only want to use the provided values in the output. Use `values` as a list to provide the values you want to use. They will be randomly picked independently each time; so no guarantee that every value in the list will be used. If you use `fixed` type, `inject` and `rotate` will be ignored.
192
+
193
+ ## Contributing
194
+
195
+ 1. Fork it ( https://github.com/jiananlu/faked_csv/fork )
196
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
197
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
198
+ 4. Push to the branch (`git push origin my-new-feature`)
199
+ 5. Create a new Pull Request
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require 'faked_csv/cli'
3
+ FakedCSV::CLI.start(ARGV)
@@ -0,0 +1,44 @@
1
+ {
2
+ "rows": 200,
3
+ "fields": [
4
+ {
5
+ "name": "ID",
6
+ "type": "rand:char",
7
+ "length": 5
8
+ },
9
+ {
10
+ "name": "First Name",
11
+ "type": "fixed",
12
+ "values": ["Peter", "Tom", "Jane", "Tony", "Steve", "John"]
13
+ },
14
+ {
15
+ "name": "Last Name",
16
+ "type": "faker:name:last_name",
17
+ "inject": ["Lu", "Smith"]
18
+ },
19
+ {
20
+ "name": "City",
21
+ "type": "faker:address:city",
22
+ "rotate": "rows/5"
23
+ },
24
+ {
25
+ "name": "State",
26
+ "type": "faker:address:state_abbr",
27
+ "rotate": 10,
28
+ "inject": ["CA"]
29
+ },
30
+ {
31
+ "name": "Age",
32
+ "type": "rand:int",
33
+ "range": [10, 80]
34
+ },
35
+ {
36
+ "name": "Height",
37
+ "type": "rand:float",
38
+ "range": [150, 190],
39
+ "inject": [200, 210],
40
+ "rotate": 20,
41
+ "precision": 2
42
+ }
43
+ ]
44
+ }
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'faked_csv/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "faked_csv"
8
+ spec.version = FakedCSV::VERSION
9
+ spec.authors = ["Jianan Lu"]
10
+ spec.email = ["jianan.lu@salesforce.com"]
11
+ spec.summary = %q{Generate faked CSV data in the specified way.}
12
+ spec.description = %q{Makes generating CSV file with faked, random data easy to do.}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.6"
22
+ spec.add_development_dependency "rake", "~> 10.3"
23
+ spec.add_development_dependency "rspec", "~> 2.14"
24
+ spec.add_development_dependency "cucumber", "~> 1.3"
25
+ spec.add_development_dependency "aruba", "~> 0.5"
26
+ spec.add_development_dependency "byebug", "~> 3.1"
27
+
28
+ spec.add_dependency "faker", "~> 1.3"
29
+ spec.add_dependency "json", "~> 1.8"
30
+ spec.add_dependency "open_uri_redirections", "~> 0.1"
31
+ end
@@ -0,0 +1,28 @@
1
+ Feature: faked_csv CLI program
2
+
3
+ Scenario: Show help message
4
+ When I run `faked_csv -h`
5
+ Then the output should contain "Usage: faked_csv -i <input.csv.json> -o <output.csv>"
6
+
7
+ Scenario: Show version
8
+ When I run `faked_csv --version`
9
+ Then the output should contain "version:"
10
+
11
+ Scenario: Generate basic csv
12
+ When I run `faked_csv -i ../../spec/data/basic.csv.json -o output.csv`
13
+ Then the output should contain exactly ""
14
+ And the file "output.csv" should contain "ID,First Name,Last Name,City,State,Age,Height"
15
+
16
+ Scenario: Generate basic csv and print to stdout
17
+ When I run `faked_csv -i ../../spec/data/basic.csv.json`
18
+ Then the output should contain "ID,First Name,Last Name,City,State,Age,Height"
19
+
20
+ Scenario: Find no default files
21
+ When I run `faked_csv`
22
+ Then the output should contain "error openning the input source: No such file or directory"
23
+ And the output should contain "./faked.csv.json"
24
+
25
+ Scenario: Getting from HTTP
26
+ When I run `faked_csv -i http://goo.gl/xDtkJs -o remote.csv`
27
+ Then the output should contain exactly ""
28
+ And the file "remote.csv" should contain "ID,First Name,Last Name,City,State,Age,Height"
@@ -0,0 +1 @@
1
+ require 'aruba/cucumber'
@@ -0,0 +1,9 @@
1
+ require "faked_csv/version"
2
+ require "faked_csv/config"
3
+ require "faked_csv/fakerer"
4
+ require "faked_csv/generator"
5
+ require "faked_csv/printer"
6
+ require "faked_csv/cli"
7
+
8
+ module FakedCSV
9
+ end
@@ -0,0 +1,65 @@
1
+ require 'optparse'
2
+ require 'json'
3
+ require 'open-uri'
4
+ require 'open_uri_redirections'
5
+ require 'faked_csv'
6
+
7
+ module FakedCSV
8
+ class CLI
9
+ def self.start(args)
10
+ options = {}
11
+ OptionParser.new do |opts|
12
+ opts.banner = "Usage: faked_csv -i <input.csv.json> -o <output.csv>"
13
+ opts.on("-i", "--input JSON", "input path to json configuration file. default: ./faked.csv.json") do |input|
14
+ options[:input] = input
15
+ end
16
+ opts.on("-o", "--output CSV", "output path to csv file. if omit, print to STDOUT") do |output|
17
+ options[:output] = output
18
+ end
19
+ opts.on("-v", "--version", "print version message") do
20
+ puts "version: #{VERSION}"
21
+ return
22
+ end
23
+ end.parse!(args)
24
+
25
+ options[:input] = './faked.csv.json' unless options.has_key? :input
26
+
27
+ json = nil
28
+ begin
29
+ if options[:input] =~ /^http/
30
+ # remote resouce
31
+ open(options[:input], allow_redirections: :all) do |f|
32
+ arr = []
33
+ f.each_line {|line| arr << line}
34
+ json = arr.join("\n")
35
+ end
36
+ else
37
+ # normal file
38
+ json = File.read options[:input]
39
+ end
40
+ rescue Exception=>e
41
+ puts "error openning the input source: #{e.to_s}"
42
+ exit 1
43
+ end
44
+
45
+ generator = FakedCSV::Generator.new FakedCSV::Config.new JSON.parse json
46
+ generator.generate
47
+ printer = FakedCSV::Printer.new(generator.headers, generator.rows)
48
+ s = printer.print
49
+
50
+ unless options.has_key? :output
51
+ puts s
52
+ return
53
+ end
54
+
55
+ begin
56
+ File.open options[:output], 'w' do |f|
57
+ f.write s
58
+ end
59
+ rescue Exception=>e
60
+ puts "error writing to csv file: #{e.to_s}"
61
+ exit 2
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,111 @@
1
+ module FakedCSV
2
+ class Config
3
+ attr_reader :config, :fields, :row_count
4
+
5
+ def initialize(config)
6
+ @config = config
7
+ end
8
+
9
+ def fields
10
+ parse if @fields.nil? # parse first if not parsed yet
11
+ @fields
12
+ end
13
+
14
+ def headers
15
+ fields.map{|f| f[:name]}
16
+ end
17
+
18
+ # prepare the json config and generate the fields
19
+ def parse
20
+ if @config["rows"].nil? || @config["rows"].to_i < 0
21
+ @row_count = 100 # default value
22
+ else
23
+ @row_count = @config["rows"].to_i
24
+ end
25
+
26
+ @fields = []
27
+ if @config["fields"].nil? || @config["fields"].empty?
28
+ raise "need 'fields' in the config file and at least 1 field in it"
29
+ end
30
+
31
+ @config["fields"].each do |cfg|
32
+ field = {}
33
+
34
+ if cfg["name"].nil?
35
+ raise "field needs a name"
36
+ end
37
+ field[:name] = cfg["name"].to_s
38
+
39
+ if cfg["type"].nil? || cfg["type"].empty?
40
+ raise "field needs a type"
41
+ end
42
+ field[:type] = cfg["type"].to_s
43
+
44
+ unless cfg["inject"].nil? || cfg["inject"].empty? || !cfg["inject"].kind_of?(Array)
45
+ field[:inject] = cfg["inject"].uniq # get rid of duplicates
46
+ end
47
+
48
+ unless cfg["rotate"].nil?
49
+ field[:rotate] = _validate_rotate cfg["rotate"]
50
+ end
51
+
52
+ case field[:type]
53
+ when /rand:int/i
54
+ field[:type] = :rand_int
55
+ if cfg["range"].nil?
56
+ # no range specified? use the default range: [0, 100]
57
+ field[:min], field[:max] = 0, 100
58
+ else
59
+ field[:min], field[:max] = _min_max cfg["range"]
60
+ end
61
+ when /rand:float/i
62
+ field[:type] = :rand_float
63
+ if cfg["range"].nil?
64
+ # no range specified? use the default range: [0, 1]
65
+ field[:min], field[:max] = 0, 1
66
+ else
67
+ field[:min], field[:max] = _min_max cfg["range"]
68
+ end
69
+ field[:precision] = cfg["precision"].nil? ? 1 : cfg["precision"].to_i
70
+ when /rand:char/i
71
+ field[:type] = :rand_char
72
+ field[:length] = cfg["length"].nil? ? 10 : cfg["length"]
73
+ when /fixed/i
74
+ field[:type] = :fixed
75
+ raise "need values for fixed type" if cfg["values"].nil?
76
+ field[:values] = cfg["values"]
77
+ when /faker:\S+/i
78
+ field[:type] = cfg["type"]
79
+ else
80
+ raise "unsupported type: #{field[:type]}. supported types: #{_supported_types}"
81
+ end
82
+
83
+ fields << field
84
+ end
85
+ end
86
+
87
+ def _supported_types
88
+ ['rand:int', 'rand:float', 'rand:char', 'fixed', 'faker:<class>:<method>'].join ", "
89
+ end
90
+
91
+ def _min_max(range)
92
+ unless range.kind_of?(Array) && range.size == 2
93
+ raise "invalid range. should be like: [0, 100]"
94
+ end
95
+ if range[0] >= range[1]
96
+ raise "invalid range. 1st is >= 2nd"
97
+ end
98
+ return range[0], range[1]
99
+ end
100
+
101
+ def _validate_rotate(rotate)
102
+ if rotate.to_s.include? "rows/"
103
+ div = rotate.split("/")[1].to_i
104
+ r = @row_count / div
105
+ else
106
+ r = rotate.to_i
107
+ end
108
+ return r > @row_count ? @row_count : r
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,31 @@
1
+ require 'faker'
2
+
3
+ # skip validation of locales
4
+ I18n.enforce_available_locales = false
5
+
6
+ module FakedCSV
7
+ class Fakerer
8
+ attr_reader :type
9
+ def initialize(type)
10
+ @type = type
11
+ faker, class_name, @method = @type.split ':'
12
+ begin
13
+ @class = Kernel.const_get("Faker::#{_camelize class_name}")
14
+ rescue
15
+ raise "unsupported faker class: #{class_name}"
16
+ end
17
+ end
18
+
19
+ def fake
20
+ begin
21
+ @class.send @method
22
+ rescue
23
+ raise "unsupported faker method: #{@method}"
24
+ end
25
+ end
26
+
27
+ def _camelize(str)
28
+ str.split('_').map {|w| w.capitalize}.join
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,170 @@
1
+ module FakedCSV
2
+ class Generator
3
+ attr_reader :config, :rows
4
+
5
+ def initialize(config)
6
+ @config = config
7
+ end
8
+
9
+ def headers
10
+ @config.headers
11
+ end
12
+
13
+ def rows
14
+ return @rows unless @rows.nil?
15
+ @rows = []
16
+ (0...@config.row_count).each do |r|
17
+ row = []
18
+ @config.fields.each do |field|
19
+ row << field[:data][r]
20
+ end
21
+ @rows << row
22
+ end
23
+ @rows
24
+ end
25
+
26
+ def generate
27
+ prepare_values
28
+
29
+ @config.fields.each do |field|
30
+ field[:data] = []
31
+
32
+ # let's get some data!
33
+ if field[:rotate].nil? || field[:type] == :fixed
34
+ # not rotating? or fixed values? generate random value each time
35
+ @config.row_count.times do
36
+ field[:data] << _random_value(field)
37
+ end
38
+
39
+ # inject user values if given and not fixed type
40
+ unless field[:type] == :fixed || field[:inject].nil?
41
+ _random_inject(field[:data], field[:inject])
42
+ end
43
+ else
44
+ # rotating? pick from prepared values
45
+ _random_distribution(@config.row_count, field[:values].size) do |i, j|
46
+ field[:data][i] = field[:values][j]
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ def prepare_values
53
+ @config.fields.each do |field|
54
+ # if it's fixed values or no rotate
55
+ # we don't want to prepare values for this field
56
+ if field[:type] == :fixed || field[:rotate].nil?
57
+ next
58
+ end
59
+
60
+ # we don't have enough integers for the rotate
61
+ if field[:type] == :rand_int && field[:rotate] > field[:max] - field[:min] + 1
62
+ raise "rotate should not be greater than the size of the range"
63
+ end
64
+
65
+ values = {}
66
+ # let's first inject all user values if given
67
+ unless field[:inject].nil?
68
+ field[:inject].each do |inj|
69
+ values[inj] = true
70
+ # truncate more inject values if we go over the rows count
71
+ break if values.size == @config.row_count
72
+ end
73
+ end
74
+ # then generate as many data as we need
75
+ _loop do
76
+ # we want to get <rotate> unique values. stop when we got enough
77
+ break if values.size >= field[:rotate]
78
+ v = _random_value(field)
79
+ values[v] = true
80
+ end
81
+ field[:values] = values.keys
82
+ end
83
+ end
84
+
85
+ def _random_distribution(total, parts)
86
+ raise "parts has to be greater than 0" unless parts > 0
87
+ raise "parts should not be greater than total" if total < parts
88
+ cuts = {}
89
+ _loop do
90
+ break if cuts.size == parts - 1
91
+ cuts[rand(total - 1)] = true
92
+ end
93
+ arr = []
94
+ part_index = 0
95
+ (0...total).each do |i|
96
+ arr << part_index
97
+ part_index += 1 if cuts.has_key? i
98
+ end
99
+ arr.shuffle.each_with_index do |v, i|
100
+ yield(i, v)
101
+ end
102
+ end
103
+
104
+ # inject <injects> into <values>
105
+ def _random_inject(values, injects)
106
+ used_indexes = {}
107
+ count = injects.size > values.size ? values.size : injects.size
108
+ (0...count).each do |i|
109
+ inj = injects[i]
110
+ times_inject = rand(values.size / injects.size / 10)
111
+ times_inject = 1 if times_inject < 1
112
+ times_inject.times do
113
+ rand_index = rand(values.size)
114
+ _loop do
115
+ break unless used_indexes.has_key? rand_index
116
+ rand_index = rand(values.size)
117
+ end
118
+ used_indexes[rand_index] = true
119
+ values[rand_index] = inj
120
+ end
121
+ end
122
+ end
123
+
124
+ def _random_value(field)
125
+ case field[:type]
126
+ when :rand_int
127
+ return Generator.rand_int field[:min], field[:max]
128
+ when :rand_float
129
+ return Generator.rand_float field[:min], field[:max], field[:precision]
130
+ when :rand_char
131
+ return Generator.rand_char field[:length]
132
+ when :fixed
133
+ return field[:values].sample
134
+ else # faker
135
+ return Generator.fake field[:type]
136
+ end
137
+ end
138
+
139
+ def _loop
140
+ max_attempts = 1000_000_000_000
141
+ i = 0
142
+ (0...max_attempts).each do |j|
143
+ yield
144
+ i += 1
145
+ end
146
+ raise "max attempts reached" if i == max_attempts
147
+ end
148
+
149
+ ## individual random generators
150
+
151
+ def self.rand_char(length)
152
+ o = [('a'..'z'), ('A'..'Z'), (0..9)].map { |i| i.to_a }.flatten
153
+ string = (0...length).map { o[rand(o.length)] }.join
154
+ end
155
+
156
+ def self.rand_int(min, max)
157
+ raise "min > max" if min > max
158
+ min + rand(max - min + 1)
159
+ end
160
+
161
+ def self.rand_float(min, max, precision)
162
+ raise "min > max" if min > max
163
+ (rand * (max - min) + min).round(precision)
164
+ end
165
+
166
+ def self.fake(type)
167
+ Fakerer.new(type).fake
168
+ end
169
+ end
170
+ end
@@ -0,0 +1,16 @@
1
+ module FakedCSV
2
+ class Printer
3
+ def initialize(headers, rows)
4
+ @headers = headers
5
+ @rows = rows
6
+ end
7
+
8
+ def print
9
+ s = @headers.join(',') + "\n"
10
+ @rows.each do |row|
11
+ s += row.join(',') + "\n"
12
+ end
13
+ s
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,3 @@
1
+ module FakedCSV
2
+ VERSION = "0.1.1"
3
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ describe FakedCSV::Config do
4
+ it "parses the basic json config" do
5
+ json = JSON.parse File.read 'spec/data/basic.csv.json'
6
+ config = FakedCSV::Config.new json
7
+ config.config["rows"].should == 200
8
+ config.parse
9
+ config.row_count.should == 200
10
+ config.fields.should == [
11
+ {:name=>"ID", :type=>:rand_char, :length=>5},
12
+ {:name=>"First Name", :type=>:fixed, :values=>["Peter", "Tom", "Jane", "Tony", "Steve", "John"]},
13
+ {:name=>"Last Name", :type=>"faker:name:last_name", :inject=>["Lu", "Smith"]},
14
+ {:name=>"City", :type=>"faker:address:city", :rotate=>40},
15
+ {:name=>"State", :type=>"faker:address:state_abbr", :inject=>["CA"], :rotate=>10},
16
+ {:name=>"Age", :type=>:rand_int, :min=>10, :max=>80},
17
+ {:name=>"Height", :type=>:rand_float, :inject=>[200, 210], :rotate=>20, :min=>150, :max=>190, :precision=>2}
18
+ ]
19
+ end
20
+
21
+ it "provides fields headers" do
22
+ json = JSON.parse File.read 'spec/data/basic.csv.json'
23
+ config = FakedCSV::Config.new json
24
+ config.headers.should == ["ID", "First Name", "Last Name", "City", "State", "Age", "Height"]
25
+ end
26
+ end
@@ -0,0 +1,44 @@
1
+ {
2
+ "rows": 200,
3
+ "fields": [
4
+ {
5
+ "name": "ID",
6
+ "type": "rand:char",
7
+ "length": 5
8
+ },
9
+ {
10
+ "name": "First Name",
11
+ "type": "fixed",
12
+ "values": ["Peter", "Tom", "Jane", "Tony", "Steve", "John"]
13
+ },
14
+ {
15
+ "name": "Last Name",
16
+ "type": "faker:name:last_name",
17
+ "inject": ["Lu", "Smith"]
18
+ },
19
+ {
20
+ "name": "City",
21
+ "type": "faker:address:city",
22
+ "rotate": "rows/5"
23
+ },
24
+ {
25
+ "name": "State",
26
+ "type": "faker:address:state_abbr",
27
+ "rotate": 10,
28
+ "inject": ["CA"]
29
+ },
30
+ {
31
+ "name": "Age",
32
+ "type": "rand:int",
33
+ "range": [10, 80]
34
+ },
35
+ {
36
+ "name": "Height",
37
+ "type": "rand:float",
38
+ "range": [150, 190],
39
+ "inject": [200, 210],
40
+ "rotate": 20,
41
+ "precision": 2
42
+ }
43
+ ]
44
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "rows": 10,
3
+ "fields": [
4
+ {
5
+ "name": "inject without rotate",
6
+ "type": "faker:name:first_name",
7
+ "inject": ["aa", "bb"]
8
+ }
9
+ ]
10
+ }
@@ -0,0 +1,36 @@
1
+ {
2
+ "rows": 10,
3
+ "fields": [
4
+ {
5
+ "name": "faker no rotate",
6
+ "type": "faker:name:first_name"
7
+ },
8
+ {
9
+ "name": "faker with rotate",
10
+ "type": "faker:name:first_name",
11
+ "rotate": 10
12
+ },
13
+ {
14
+ "name": "faker with rotate and inject",
15
+ "type": "faker:name:first_name",
16
+ "rotate": 10,
17
+ "inject": ["aaa", "bbb"]
18
+ },
19
+
20
+ {
21
+ "name": "rand int no rotate",
22
+ "type": "rand:int"
23
+ },
24
+ {
25
+ "name": "rand int with rotate",
26
+ "type": "rand:int",
27
+ "rotate": 10
28
+ },
29
+ {
30
+ "name": "rand int with rotate and inject",
31
+ "type": "rand:int",
32
+ "rotate": 10,
33
+ "inject": [11, 12, 13]
34
+ }
35
+ ]
36
+ }
@@ -0,0 +1,28 @@
1
+ require 'spec_helper'
2
+
3
+ describe FakedCSV::Fakerer do
4
+ it "validates input type" do
5
+ FakedCSV::Fakerer.new("faker:name:last_name").fake.size.should > 1
6
+ end
7
+
8
+ it "invalidates input type: class" do
9
+ raised = false
10
+ begin
11
+ f = FakedCSV::Fakerer.new("faker:name1:last_name")
12
+ rescue
13
+ raised = true
14
+ end
15
+ raise "not raised invalid faker class" unless raised
16
+ end
17
+
18
+ it "invalidates input type: method" do
19
+ raised = false
20
+ f = FakedCSV::Fakerer.new("faker:name:last_name1")
21
+ begin
22
+ f.fake_single
23
+ rescue
24
+ raised = true
25
+ end
26
+ raise "not raised invalid faker method" unless raised
27
+ end
28
+ end
@@ -0,0 +1,140 @@
1
+ require 'spec_helper'
2
+
3
+ describe FakedCSV::Generator do
4
+ it "generates rand char" do
5
+ FakedCSV::Generator.rand_char(1).size.should == 1
6
+ FakedCSV::Generator.rand_char(10).size.should == 10
7
+ FakedCSV::Generator.rand_char(100).size.should == 100
8
+ end
9
+
10
+ it "generates rand int" do
11
+ 1000.times do
12
+ i = FakedCSV::Generator.rand_int(0, 10)
13
+ i.should <= 10
14
+ i.should >= 0
15
+ end
16
+ FakedCSV::Generator.rand_int(0, 0).should == 0
17
+ raised = false
18
+ begin
19
+ FakedCSV::Generator.rand_int(1, 0).should == 0
20
+ rescue
21
+ raised = true
22
+ end
23
+ raise "not raised the min > max exception" unless raised
24
+ 1000.times do
25
+ i = FakedCSV::Generator.rand_int(-10, 0)
26
+ i.should <= 0
27
+ i.should >= -10
28
+ end
29
+ end
30
+
31
+ it "generates rand float" do
32
+ 1000.times do
33
+ i = FakedCSV::Generator.rand_float(0, 10, 2)
34
+ i.should <= 10
35
+ i.should >= 0
36
+ end
37
+ FakedCSV::Generator.rand_float(0, 0, 0).should == 0
38
+ raised = false
39
+ begin
40
+ FakedCSV::Generator.rand_float(1, 0, 2).should == 0
41
+ rescue
42
+ raised = true
43
+ end
44
+ raise "not raised the min > max exception" unless raised
45
+ 1000.times do
46
+ i = FakedCSV::Generator.rand_float(-10, 0, 10)
47
+ i.should <= 0
48
+ i.should >= -10
49
+ end
50
+ end
51
+
52
+ it "generates faker data" do
53
+ FakedCSV::Generator.fake("faker:name:last_name").size.should > 1
54
+ end
55
+
56
+ it "prepare values for rotate fields" do
57
+ generator = FakedCSV::Generator.new FakedCSV::Config.new JSON.parse File.read 'spec/data/rotate.csv.json'
58
+ generator.prepare_values
59
+ f = generator.config.fields
60
+ f[0].should == {:name=>"faker no rotate", :type=>"faker:name:first_name"}
61
+ f[1][:rotate].should == 10
62
+ f[1][:values].size.should == 10
63
+ f[2][:rotate].should == 10
64
+ f[2][:values].size.should == 10
65
+ f[2][:inject].should == ["aaa", "bbb"]
66
+ f[3].should == {:name=>"rand int no rotate", :type=>:rand_int, :min=>0, :max=>100}
67
+ f[4][:rotate].should == 10
68
+ f[4][:values].size.should == 10
69
+ f[5][:rotate].should == 10
70
+ f[5][:values].size.should == 10
71
+ f[5][:inject].should == [11, 12, 13]
72
+ end
73
+
74
+ it "generates random data" do
75
+ generator = FakedCSV::Generator.new FakedCSV::Config.new JSON.parse File.read 'spec/data/rotate.csv.json'
76
+ generator.generate
77
+ f = generator.config.fields
78
+ f[0][:data].size.should == 10
79
+ f[1][:data].size.should == 10
80
+ f[1][:data].each do |d|
81
+ f[1][:values].include?(d).should == true
82
+ end
83
+ f[2][:data].each do |d|
84
+ (f[2][:values].include?(d) || f[2][:values].include?(d)).should == true
85
+ end
86
+ f[3][:data].size.should == 10
87
+ f[4][:data].size.should == 10
88
+ f[4][:data].each do |d|
89
+ f[4][:values].include?(d).should == true
90
+ end
91
+ f[5][:data].each do |d|
92
+ (f[5][:values].include?(d) || f[5][:values].include?(d)).should == true
93
+ end
94
+ end
95
+
96
+ it "generates random data from basic csv" do
97
+ generator = FakedCSV::Generator.new FakedCSV::Config.new JSON.parse File.read 'spec/data/basic.csv.json'
98
+ generator.generate
99
+ f = generator.config.fields
100
+ f.each{|ff| ff[:data].size.should == 200}
101
+ f[0][:data].each{|d|d.size.should == 5}
102
+ f[1][:data].each do |d|
103
+ f[1][:values].include?(d).should == true
104
+ end
105
+ f[1][:data].uniq.size.should == f[1][:values].size
106
+ f[2][:inject].each do |inj|
107
+ f[2][:data].include?(inj).should == true
108
+ end
109
+ f[3][:data].uniq.size.should == 40
110
+ f[4][:data].uniq.size.should == 10
111
+ f[4][:data].include?("CA").should == true
112
+ f[5][:data].each do |d|
113
+ d.kind_of?(Integer).should == true
114
+ (d >= 10 && d <= 80).should == true
115
+ end
116
+ f[6][:data].uniq.size.should == 20
117
+ f[6][:data].include?(200).should == true
118
+ f[6][:data].include?(210).should == true
119
+ f[6][:data].each do |d|
120
+ ((d >= 150 && d <= 190) || d == 200 || d == 210).should == true
121
+ end
122
+ end
123
+
124
+ it "provides data as rows" do
125
+ generator = FakedCSV::Generator.new FakedCSV::Config.new JSON.parse File.read 'spec/data/rotate.csv.json'
126
+ generator.generate
127
+ rows = generator.rows
128
+ rows.size.should == 10
129
+ rows.each do |row|
130
+ row.size.should == 6
131
+ end
132
+ end
133
+
134
+ it "handles inject without rotate" do
135
+ generator = FakedCSV::Generator.new FakedCSV::Config.new JSON.parse File.read 'spec/data/inject.csv.json'
136
+ generator.generate
137
+ generator.config.fields[0][:data].include?("aa").should == true
138
+ generator.config.fields[0][:data].include?("bb").should == true
139
+ end
140
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ describe FakedCSV::Printer do
4
+ it "returns csv as string" do
5
+ generator = FakedCSV::Generator.new FakedCSV::Config.new JSON.parse File.read 'spec/data/rotate.csv.json'
6
+ generator.generate
7
+ printer = FakedCSV::Printer.new(generator.headers, generator.rows)
8
+ s = printer.print
9
+ s.include?("faker no rotate,").should == true
10
+ s.include?(",rand int with rotate and inject").should == true
11
+ s.include?("13").should == true
12
+ s.include?("aaa").should == true
13
+ s.include?("bbb").should == true
14
+ end
15
+ end
@@ -0,0 +1,4 @@
1
+ require 'byebug'
2
+ require 'json'
3
+ require 'faked_csv'
4
+
metadata ADDED
@@ -0,0 +1,206 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: faked_csv
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Jianan Lu
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-05-31 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.6'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.3'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '2.14'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '2.14'
55
+ - !ruby/object:Gem::Dependency
56
+ name: cucumber
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.3'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.3'
69
+ - !ruby/object:Gem::Dependency
70
+ name: aruba
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0.5'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0.5'
83
+ - !ruby/object:Gem::Dependency
84
+ name: byebug
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '3.1'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '3.1'
97
+ - !ruby/object:Gem::Dependency
98
+ name: faker
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '1.3'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '1.3'
111
+ - !ruby/object:Gem::Dependency
112
+ name: json
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '1.8'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '1.8'
125
+ - !ruby/object:Gem::Dependency
126
+ name: open_uri_redirections
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '0.1'
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '0.1'
139
+ description: Makes generating CSV file with faked, random data easy to do.
140
+ email:
141
+ - jianan.lu@salesforce.com
142
+ executables:
143
+ - faked_csv
144
+ extensions: []
145
+ extra_rdoc_files: []
146
+ files:
147
+ - ".gitignore"
148
+ - Gemfile
149
+ - LICENSE.txt
150
+ - README.md
151
+ - Rakefile
152
+ - bin/faked_csv
153
+ - example.csv.json
154
+ - faked_csv.gemspec
155
+ - features/faked_csv.feature
156
+ - features/support/setup.rb
157
+ - lib/faked_csv.rb
158
+ - lib/faked_csv/cli.rb
159
+ - lib/faked_csv/config.rb
160
+ - lib/faked_csv/fakerer.rb
161
+ - lib/faked_csv/generator.rb
162
+ - lib/faked_csv/printer.rb
163
+ - lib/faked_csv/version.rb
164
+ - spec/config_spec.rb
165
+ - spec/data/basic.csv.json
166
+ - spec/data/inject.csv.json
167
+ - spec/data/rotate.csv.json
168
+ - spec/fakerer_spec.rb
169
+ - spec/generator_spec.rb
170
+ - spec/printer_spec.rb
171
+ - spec/spec_helper.rb
172
+ homepage: ''
173
+ licenses:
174
+ - MIT
175
+ metadata: {}
176
+ post_install_message:
177
+ rdoc_options: []
178
+ require_paths:
179
+ - lib
180
+ required_ruby_version: !ruby/object:Gem::Requirement
181
+ requirements:
182
+ - - ">="
183
+ - !ruby/object:Gem::Version
184
+ version: '0'
185
+ required_rubygems_version: !ruby/object:Gem::Requirement
186
+ requirements:
187
+ - - ">="
188
+ - !ruby/object:Gem::Version
189
+ version: '0'
190
+ requirements: []
191
+ rubyforge_project:
192
+ rubygems_version: 2.2.2
193
+ signing_key:
194
+ specification_version: 4
195
+ summary: Generate faked CSV data in the specified way.
196
+ test_files:
197
+ - features/faked_csv.feature
198
+ - features/support/setup.rb
199
+ - spec/config_spec.rb
200
+ - spec/data/basic.csv.json
201
+ - spec/data/inject.csv.json
202
+ - spec/data/rotate.csv.json
203
+ - spec/fakerer_spec.rb
204
+ - spec/generator_spec.rb
205
+ - spec/printer_spec.rb
206
+ - spec/spec_helper.rb