dummy_data 0.9
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +4 -0
- data/LICENSE +20 -0
- data/README.md +70 -0
- data/lib/dummy_data.rb +4 -0
- data/lib/generators/common.rb +82 -0
- data/lib/generators/data/data_generator.rb +145 -0
- data/lib/generators/data/templates/dummy_data.rake +13 -0
- metadata +101 -0
data/CHANGELOG.md
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 Gonçalo Silva
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
# Dummy data
|
2
|
+
|
3
|
+
"Dummy data" uses dummy[http://github.com/goncalossilva/dummy] to generate clever data for your Rails 3 application; it also allows you to import it to the database.
|
4
|
+
|
5
|
+
## Description
|
6
|
+
|
7
|
+
Check dummy's[http://github.com/goncalossilva/dummy] description for a better notion of what exactly is dummy data. This gem uses dummy to generate this data for your models and stores it in YAML files. It also provides a rake task for you to import it to the database.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
$ gem install dummy_data
|
12
|
+
|
13
|
+
## Usage
|
14
|
+
|
15
|
+
Add the following to the Gemfile of your Rails 3 application:
|
16
|
+
gem "dummy_data"
|
17
|
+
|
18
|
+
Now you have access to the generator:
|
19
|
+
rails generate dummy:data
|
20
|
+
|
21
|
+
You can change the base amount of records and the growth ratio (what these mean exactly is explained latter on):
|
22
|
+
rails generate dummy:data --base-amount 5 --growth-ratio 1.5
|
23
|
+
|
24
|
+
Also, you can manually define the amount of records to generate for each model (or just accept the defaults):
|
25
|
+
rails generate dummy:data --manual-amounts
|
26
|
+
|
27
|
+
And you can manually set the output folder for the dummy data (which defaults to test/dummy):
|
28
|
+
rails generate dummy:data --output-folder test/awesome_dummy
|
29
|
+
|
30
|
+
The files will be placed under _output-folder_/data.
|
31
|
+
|
32
|
+
Feel free to mix all of these options.
|
33
|
+
|
34
|
+
The fixtures are stored in _test/dummy/data_ (by default) while a rake file is placed in _lib/tasks/dummy\_data.rake_. It allows you to import the generated data into the database:
|
35
|
+
RAILS_ENV="dummy" rake dummy:data:import
|
36
|
+
|
37
|
+
Don't forget to change RAILS_ENV to whatever is appropriate for you (and is configured under databases.yml). Your database doesn't need to be empty.
|
38
|
+
|
39
|
+
## More information
|
40
|
+
|
41
|
+
### Smart data
|
42
|
+
|
43
|
+
"Dummy data" tells dummy to try to understand your database columns and generate data accordingly, instead of dumping "Lorem ipsum" all over the place.
|
44
|
+
|
45
|
+
For instance, if you have a field called _company\_name_, it will generate a company name. If you have a field called _awesome\_postal\_code_, it will generate a valid ZIP Code. If you have a field called _longitude_, it will generate a valid longitude, and so on. You get the picture.
|
46
|
+
|
47
|
+
Dummy cares about associations. It will create random associations between the automatically generated records, so you don't have to worry about that.
|
48
|
+
|
49
|
+
### Smart amounts of data
|
50
|
+
|
51
|
+
"Dummy data" is aware that the amount of records that each model has in real world applications is different. For this reason, it will analyze your model associations to try to make a somewhat accurate estimation of the expected amount of records.
|
52
|
+
|
53
|
+
To illustrate this, consider an application with models for _Child_, _Parent_ and _GrandParent_. If the base amount is 10, the growth ratio is 2.0, and the models look like the following:
|
54
|
+
|
55
|
+
class GrandParent < ActiveRecord::Base
|
56
|
+
has_many :parents
|
57
|
+
end
|
58
|
+
|
59
|
+
class Parent < ActiveRecord::Base
|
60
|
+
belongs_to :grand_parent
|
61
|
+
has_many :children
|
62
|
+
end
|
63
|
+
|
64
|
+
class Child < ActiveRecord::Base
|
65
|
+
belongs_to :parent
|
66
|
+
end`
|
67
|
+
|
68
|
+
The generator will create dummy data for 10 _GrandParents_, 20 _Parents_ and 40 _Children_.
|
69
|
+
|
70
|
+
Copyright (c) 2010 Gonçalo Silva
|
data/lib/dummy_data.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
module Dummy
|
2
|
+
module Generators
|
3
|
+
# Methods used by other dummy-related gems are grouped here
|
4
|
+
# Other generators will just "include Dummy::Generators::Common"
|
5
|
+
module Common
|
6
|
+
def gather_associations
|
7
|
+
@models.each_key do |model|
|
8
|
+
model_symbol = model.to_s.underscore.pluralize.to_sym
|
9
|
+
associations = model.reflect_on_all_associations(:belongs_to)
|
10
|
+
|
11
|
+
associations.each do |assoc|
|
12
|
+
assoc_name = assoc.name.to_s.camelcase
|
13
|
+
assoc_options = assoc.options
|
14
|
+
|
15
|
+
if assoc_options.empty?
|
16
|
+
@models[model][:associations].push({
|
17
|
+
:model => assoc_name.constantize,
|
18
|
+
:foreign_key => "#{assoc_name.underscore}_id"
|
19
|
+
})
|
20
|
+
elsif assoc_options.has_key?(:class_name) and assoc_options.has_key?(:foreign_key)
|
21
|
+
@models[model][:associations].push({
|
22
|
+
:model => assoc_options[:class_name].constantize, # TODO: handle class_name
|
23
|
+
:foreign_key => assoc_options[:foreign_key]
|
24
|
+
})
|
25
|
+
else
|
26
|
+
next
|
27
|
+
end
|
28
|
+
|
29
|
+
assoc_model = @models[model][:associations].last[:model]
|
30
|
+
assoc_reflections = assoc_model.reflect_on_all_associations(:has_one)
|
31
|
+
@models[model][:associations].pop if assoc_reflections.map(&:name).include?(model_symbol)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def generate_record_data(name, info, column, fixtures=true)
|
37
|
+
column_name = String.new(column.name) # this shouldn't be needed, ruby bug?
|
38
|
+
if(column_name =~ /_at$/ and column.type == :datetime) or column_name == "id"
|
39
|
+
return
|
40
|
+
end
|
41
|
+
|
42
|
+
associated_model = associated_class_name(info, column_name)
|
43
|
+
|
44
|
+
if associated_model
|
45
|
+
if fixtures
|
46
|
+
val = generate_association_data(associated_model)
|
47
|
+
column_name.gsub!(/_id$/, "")
|
48
|
+
else
|
49
|
+
val = Fixtures.identify(generate_association_data(associated_model))
|
50
|
+
end
|
51
|
+
else
|
52
|
+
val = generate_regular_data(column)
|
53
|
+
end
|
54
|
+
|
55
|
+
{column_name => val}
|
56
|
+
end
|
57
|
+
|
58
|
+
def associated_class_name(info, name)
|
59
|
+
info[:associations].each do |assoc|
|
60
|
+
return assoc[:model] if assoc[:foreign_key] == name
|
61
|
+
end
|
62
|
+
false
|
63
|
+
end
|
64
|
+
|
65
|
+
def generate_association_data(associated_model)
|
66
|
+
random_record_num = rand(@models[associated_model][:record_amount])
|
67
|
+
"#{associated_model.to_s.underscore}_#{random_record_num}"
|
68
|
+
end
|
69
|
+
|
70
|
+
def generate_regular_data(column)
|
71
|
+
val = Dummy.magic_data(column.name, column.type)
|
72
|
+
|
73
|
+
if val
|
74
|
+
val
|
75
|
+
else
|
76
|
+
say_status :failed, "data generation for '#{column.name}' with type '#{column.type.to_s.downcase}'", :red
|
77
|
+
""
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
require "yaml"
|
2
|
+
require "active_support"
|
3
|
+
require "active_support/core_ext/string/inflections"
|
4
|
+
require "active_record"
|
5
|
+
require "active_record/fixtures"
|
6
|
+
require "rails/generators"
|
7
|
+
|
8
|
+
module Dummy
|
9
|
+
module Generators
|
10
|
+
class DataGenerator < Rails::Generators::Base
|
11
|
+
include Dummy::Generators::Common
|
12
|
+
|
13
|
+
def self.source_root
|
14
|
+
@source_root ||= File.expand_path("../templates", __FILE__)
|
15
|
+
end
|
16
|
+
|
17
|
+
class_option :base_amount, :type => :numeric, :default => 10,
|
18
|
+
:desc => "The base amount of records to generate for each model."
|
19
|
+
class_option :growth_ratio, :type => :numeric, :default => 2.0,
|
20
|
+
:desc => "The growth ratio of each model, according to its associations."
|
21
|
+
class_option :manual_amounts, :type => :boolean, :default => false,
|
22
|
+
:desc => "Manually set the amount of records for each model."
|
23
|
+
class_option :output_folder, :type => :string, :default => "test/dummy",
|
24
|
+
:desc => "Dummy output folder, data/ will be used when storing the resulting YAML files."
|
25
|
+
|
26
|
+
def install_dummy_data
|
27
|
+
initialize_application
|
28
|
+
generate_dummy_data
|
29
|
+
copy_rake_file
|
30
|
+
create_dummyfile
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def initialize_application
|
36
|
+
require File.expand_path("#{Rails.root}/config/environment.rb")
|
37
|
+
say_status :successful, "initialize Rails application"
|
38
|
+
end
|
39
|
+
|
40
|
+
def generate_dummy_data
|
41
|
+
get_table_names
|
42
|
+
gather_associations
|
43
|
+
predict_record_amounts
|
44
|
+
generate_and_write_data
|
45
|
+
end
|
46
|
+
|
47
|
+
def get_table_names
|
48
|
+
@models = Hash.new
|
49
|
+
Dir["app/models/*.rb"].each do |full_path|
|
50
|
+
model = File.basename(full_path).chomp(File.extname(full_path)).camelcase.constantize
|
51
|
+
@models.merge!({model => {
|
52
|
+
:record_amount => 0, :associations => []
|
53
|
+
}}) if model.respond_to?(:columns)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def predict_record_amounts
|
58
|
+
models = @models.dup
|
59
|
+
models.each do |model, info|
|
60
|
+
predict_record_amount(model, info, models, [])
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def predict_record_amount(model, info, models, stacked_models)
|
65
|
+
info[:associations].each do |assoc|
|
66
|
+
next if stacked_models.include?(assoc[:model])
|
67
|
+
|
68
|
+
if model != assoc[:model]
|
69
|
+
stacked_models << assoc[:model]
|
70
|
+
predict_record_amount(assoc[:model], @models[assoc[:model]], models, stacked_models)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
amount = options.base_amount
|
75
|
+
if not info[:associations].empty?
|
76
|
+
amount = info[:associations].map do |assoc|
|
77
|
+
@models[assoc[:model]][:record_amount]
|
78
|
+
end.max * options.growth_ratio # **info[:associations].size
|
79
|
+
end
|
80
|
+
|
81
|
+
if options.manual_amounts
|
82
|
+
user_defined = ask("Number of records for #{model} (default: #{amount}): ")
|
83
|
+
amount = user_defined unless user_defined.empty?
|
84
|
+
end
|
85
|
+
|
86
|
+
@models[model][:record_amount] = amount.to_i
|
87
|
+
stacked_models.delete(model)
|
88
|
+
models.delete(model)
|
89
|
+
end
|
90
|
+
|
91
|
+
def generate_and_write_data
|
92
|
+
empty_directory "#{options.output_folder}/data"
|
93
|
+
data = Hash.new
|
94
|
+
|
95
|
+
@models.each do |model, info|
|
96
|
+
name = model.to_s.underscore
|
97
|
+
|
98
|
+
(0..info[:record_amount]-1).each do |num|
|
99
|
+
key_value = Hash.new
|
100
|
+
fixture_data = Hash.new
|
101
|
+
|
102
|
+
model.columns.each do |column|
|
103
|
+
key_value = generate_record_data(name, info, column)
|
104
|
+
fixture_data.merge!(key_value) unless key_value.nil?
|
105
|
+
end
|
106
|
+
|
107
|
+
data[model.table_name] = Hash.new if data[model.table_name].nil?
|
108
|
+
data[model.table_name].merge!({ "#{name}_#{num}" => fixture_data })
|
109
|
+
end
|
110
|
+
|
111
|
+
say_status :successful, "generate #{info[:record_amount]} records for '#{name}'"
|
112
|
+
end
|
113
|
+
|
114
|
+
data.each do |name, fixtures|
|
115
|
+
content = "# '#{name}' data generated automatically by dummy at #{Time.now.strftime("%H:%M %m/%d/%Y")} (#{fixtures.size} records).\n"
|
116
|
+
|
117
|
+
content << YAML.dump(fixtures)
|
118
|
+
|
119
|
+
create_file "#{options.output_folder}/data/#{name}.yml", content
|
120
|
+
end
|
121
|
+
say_status :successful, "store fixtures"
|
122
|
+
end
|
123
|
+
|
124
|
+
def copy_rake_file
|
125
|
+
template "dummy_data.rake", "lib/tasks/dummy_data.rake"
|
126
|
+
end
|
127
|
+
|
128
|
+
def create_dummyfile
|
129
|
+
data = Hash.new
|
130
|
+
dummyfile_path = "#{options.output_folder}/Dummyfile"
|
131
|
+
|
132
|
+
@models.each do |model, info|
|
133
|
+
data[model.to_s.underscore.pluralize] = {:records => info[:record_amount]}
|
134
|
+
end
|
135
|
+
|
136
|
+
content = "# This file was automatically generated by Dummy. Do NOT change it.\n"
|
137
|
+
content << YAML.dump(data)
|
138
|
+
|
139
|
+
remove_file dummyfile_path, :verbose => false if File.exists?(dummyfile_path)
|
140
|
+
create_file dummyfile_path, content
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require "active_record/fixtures"
|
2
|
+
|
3
|
+
namespace :dummy do
|
4
|
+
namespace :data do
|
5
|
+
desc "Load the generated dummy data into the current environment's database."
|
6
|
+
task :import => :environment do
|
7
|
+
Fixtures.reset_cache
|
8
|
+
fixtures_folder = File.join(Rails.root, "<%= options.output_folder %>/data")
|
9
|
+
fixtures = Dir[File.join(fixtures_folder, '*.yml')].map {|f| File.basename(f, '.yml') }
|
10
|
+
Fixtures.create_fixtures(fixtures_folder, fixtures)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
metadata
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: dummy_data
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 9
|
8
|
+
version: "0.9"
|
9
|
+
platform: ruby
|
10
|
+
authors:
|
11
|
+
- "Gon\xC3\xA7alo Silva"
|
12
|
+
autorequire:
|
13
|
+
bindir: bin
|
14
|
+
cert_chain: []
|
15
|
+
|
16
|
+
date: 2010-08-11 00:00:00 +01:00
|
17
|
+
default_executable:
|
18
|
+
dependencies:
|
19
|
+
- !ruby/object:Gem::Dependency
|
20
|
+
name: dummy
|
21
|
+
prerelease: false
|
22
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
23
|
+
none: false
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 0
|
29
|
+
- 9
|
30
|
+
version: "0.9"
|
31
|
+
type: :runtime
|
32
|
+
version_requirements: *id001
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: rails
|
35
|
+
prerelease: false
|
36
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
37
|
+
none: false
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
segments:
|
42
|
+
- 3
|
43
|
+
- 0
|
44
|
+
- 0
|
45
|
+
- beta
|
46
|
+
version: 3.0.0.beta
|
47
|
+
type: :runtime
|
48
|
+
version_requirements: *id002
|
49
|
+
description: Uses dummy to generate consistent fake data for your models (including associations) and provides a rake task to import it into the database
|
50
|
+
email:
|
51
|
+
- goncalossilva@gmail.com
|
52
|
+
executables: []
|
53
|
+
|
54
|
+
extensions: []
|
55
|
+
|
56
|
+
extra_rdoc_files: []
|
57
|
+
|
58
|
+
files:
|
59
|
+
- lib/generators/data/data_generator.rb
|
60
|
+
- lib/generators/common.rb
|
61
|
+
- lib/dummy_data.rb
|
62
|
+
- lib/generators/data/templates/dummy_data.rake
|
63
|
+
- LICENSE
|
64
|
+
- README.md
|
65
|
+
- CHANGELOG.md
|
66
|
+
has_rdoc: true
|
67
|
+
homepage: http://github.com/goncalossilva/dummy_data
|
68
|
+
licenses: []
|
69
|
+
|
70
|
+
post_install_message:
|
71
|
+
rdoc_options: []
|
72
|
+
|
73
|
+
require_paths:
|
74
|
+
- lib
|
75
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
76
|
+
none: false
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
segments:
|
81
|
+
- 0
|
82
|
+
version: "0"
|
83
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
84
|
+
none: false
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
segments:
|
89
|
+
- 1
|
90
|
+
- 3
|
91
|
+
- 7
|
92
|
+
version: 1.3.7
|
93
|
+
requirements: []
|
94
|
+
|
95
|
+
rubyforge_project: dummy_data
|
96
|
+
rubygems_version: 1.3.7
|
97
|
+
signing_key:
|
98
|
+
specification_version: 3
|
99
|
+
summary: Uses dummy to generate data for your models and allows you to import it
|
100
|
+
test_files: []
|
101
|
+
|