mongo_populator 0.2.0 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +20 -2
- data/Gemfile.lock +1 -1
- data/README.md +41 -20
- data/TODO.md +2 -0
- data/lib/mongo_populator.rb +2 -0
- data/lib/mongo_populator/factory.rb +1 -1
- data/lib/mongo_populator/mongo_dictionary.rb +5 -0
- data/lib/mongo_populator/mongo_skip.rb +4 -0
- data/lib/mongo_populator/random.rb +51 -2
- data/lib/mongo_populator/record.rb +4 -3
- data/spec/mongo_populator/collection_additions_spec.rb +37 -2
- data/spec/mongo_populator/mongo_array_spec.rb +9 -0
- data/spec/mongo_populator/mongo_dictionary_spec.rb +9 -0
- data/spec/mongo_populator/mongo_skip_spec.rb +8 -0
- data/spec/mongo_populator/random_spec.rb +35 -0
- data/spec/mongo_populator/record_spec.rb +20 -0
- metadata +14 -8
data/CHANGELOG.md
CHANGED
@@ -1,11 +1,29 @@
|
|
1
|
+
# 1.0.1 (September 30th, 2011)
|
2
|
+
|
3
|
+
* Fixed bug where using #items without an array in parameters caused error.
|
4
|
+
|
5
|
+
# 1.0.0 (September 30th, 2011)
|
6
|
+
|
7
|
+
* Embedded documents. There is a known issue that generated data (e.g. MongoPopulator.words()) is only generated once per set of embedded documents.
|
8
|
+
|
9
|
+
* Static arrays.
|
10
|
+
|
11
|
+
* Static dictionaries.
|
12
|
+
|
13
|
+
* Backwards-compat breaking API change: to conform better to the mongo gem API, attributes set to `nil` will be set to NULL in the resulting document. To suppress an attribute from a document, use MongoPopulator#skip (see README).
|
14
|
+
|
15
|
+
# 0.2.1 (September 14th, 2011)
|
16
|
+
|
17
|
+
* MongoArrays are now guaranteed unique.
|
18
|
+
|
1
19
|
# 0.2.0 (September 14th, 2011)
|
2
20
|
|
3
21
|
* an attribute set to nil (directly or randomly via an array) will not appear in the resulting document.
|
4
22
|
|
5
23
|
# 0.1.1 (September 14th, 2011)
|
6
24
|
|
7
|
-
* remove required minimum RubyGems version
|
25
|
+
* remove required minimum RubyGems version.
|
8
26
|
|
9
27
|
# 0.1.0 (September 14th, 2011)
|
10
28
|
|
11
|
-
* initial release based on fork of http://github.com/ryanb/populator
|
29
|
+
* initial release based on fork of http://github.com/ryanb/populator.
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# MongoPopulator
|
2
2
|
|
3
|
-
|
3
|
+
MongoPopulator populates a MongoDB database with placeholder data. It is built upon [Populator](https://github.com/ryanb/populator) by Ryan Bates, but it works with the `mongo` gem in standalone scripts, and therefore is not tied to any particular framework.
|
4
4
|
|
5
5
|
## Installation
|
6
6
|
|
@@ -8,7 +8,7 @@
|
|
8
8
|
|
9
9
|
## Usage
|
10
10
|
|
11
|
-
This gem adds a
|
11
|
+
This gem adds a `#populate` method to a `Mongo::Collection`. Pass the number of documents you want to create along with a block. In the block you can set the field values for each document.
|
12
12
|
|
13
13
|
require 'rubygems'
|
14
14
|
require 'mongo_populator'
|
@@ -65,31 +65,56 @@ If you need to generate fake data, there are a few methods to do this.
|
|
65
65
|
|
66
66
|
For fancier data generation, try the [Faker gem](http://faker.rubyforge.org).
|
67
67
|
|
68
|
-
###
|
68
|
+
### Mongo-specific values
|
69
69
|
|
70
|
-
|
70
|
+
Setting an attribute with the `#skip` method prevents that attribute being set. This is useful when you only want a field to appear in *some* documents
|
71
|
+
|
72
|
+
...
|
73
|
+
address.state = MongoPopulator.skip if address.country != "United States"
|
74
|
+
...
|
75
|
+
|
76
|
+
So, to support conditional setting of an attribute, pass it an array with one or more MongoPopulator.skip as elements.
|
77
|
+
|
78
|
+
...
|
79
|
+
user.creds = ['M.D.', 'J.D.', 'N.D.', MongoPopulator.skip, MongoPopulator.skip]
|
80
|
+
...
|
81
|
+
|
82
|
+
~40% of users will not have the "cred" field.
|
83
|
+
|
84
|
+
If you actually want a field in your document to be set to NULL, pass `nil` as the value.
|
85
|
+
|
86
|
+
...
|
87
|
+
user.style = nil # 'style' key will not be in resulting document
|
88
|
+
...
|
89
|
+
|
90
|
+
To persist arrays in your documents, use either `#items` to save a certain number of items randomly selected from a set, or `#array` to save a specific array.
|
71
91
|
|
72
92
|
MongoPopulator.items(1..5, %w(ape bear cat dog elephant firefox)) # populates array with provided terms
|
73
93
|
MongoPopulator.items(10..20) # populates array with random words
|
74
94
|
MongoPopulator.array('red', 'green', 'blue') # saves `['red', 'green', 'blue']` exactly
|
75
95
|
|
76
|
-
|
96
|
+
Note that you cannot pass `#skip` in `#items`, `#dictionary`, or `#array`. Doing so will result in an error.
|
77
97
|
|
78
|
-
|
79
|
-
address.state = nil if address.country != "United States"
|
80
|
-
...
|
98
|
+
user.fruit = MongoPopulator.items(3, ['apple', 'banana', 'kiwi', MongoPopulator.skip]) #=> Error
|
81
99
|
|
82
|
-
|
100
|
+
To persist a static dictionary to your document, use `#dictionary`.
|
83
101
|
|
84
|
-
|
85
|
-
|
86
|
-
|
102
|
+
MongoPopulator.dictionary(:name => "Mongo", :type => "db")
|
103
|
+
|
104
|
+
#### Embedded Documents
|
87
105
|
|
88
|
-
|
106
|
+
To embed documents, use `#embed`. It takes a dictionary template, which accepts any of the Populator constructs. See note below in "Known Issues."
|
89
107
|
|
90
|
-
|
108
|
+
@collection.populate(1) do |parent|
|
109
|
+
parent.name = "Bunny Sr."
|
110
|
+
parent.kids = MongoPopulator.embed(10..20, {:name => ["Bunny Jr.","Fluffy","Other Fluffy"], :age => (1..20), :tattoos => ["butterfly", "banjo frog", MongoPopulator.skip]})
|
111
|
+
end
|
112
|
+
|
113
|
+
The above code generates a record with 10 to 20 embedded documents, roughly one-third of which have a 'tattoo' field.
|
114
|
+
|
115
|
+
## Known issues
|
91
116
|
|
92
|
-
*
|
117
|
+
* Data generation methods like `#words` or Faker methods will not return varying data within a set when used in embedded document templates. Instead a random value will be picked the first time, and used for the entire set.
|
93
118
|
|
94
119
|
## Development
|
95
120
|
|
@@ -97,8 +122,4 @@ Problems or questions? Add an [issue on GitHub](https://github.com/bak/mongo_pop
|
|
97
122
|
|
98
123
|
## Special Thanks
|
99
124
|
|
100
|
-
MongoPopulator is
|
101
|
-
|
102
|
-
## Special Thanks for the original Populator
|
103
|
-
|
104
|
-
Special thanks to Zach Dennis for his ar-extensions gem which some of this code is based on. Also many thanks to the [contributors](https://github.com/ryanb/populator/contributors). See the [CHANGELOG](https://github.com/ryanb/populator/blob/master/CHANGELOG.rdoc) for the full list.
|
125
|
+
MongoPopulator is built upon the work of Ryan Bates [via Populator](https://github.com/ryanb/populator/). Thanks, Ryan.
|
data/TODO.md
ADDED
data/lib/mongo_populator.rb
CHANGED
@@ -5,6 +5,8 @@ require 'mongo_populator/collection_additions'
|
|
5
5
|
require 'mongo_populator/factory'
|
6
6
|
require 'mongo_populator/record'
|
7
7
|
require 'mongo_populator/mongo_array'
|
8
|
+
require 'mongo_populator/mongo_dictionary'
|
9
|
+
require 'mongo_populator/mongo_skip'
|
8
10
|
require 'mongo_populator/random'
|
9
11
|
|
10
12
|
# MongoPopulator is made up of several parts. To start, see MongoPopulator::ModelAdditions.
|
@@ -50,7 +50,7 @@ module MongoPopulator
|
|
50
50
|
# index = last_id_in_database + @records.size + 1
|
51
51
|
record = Record.new(@collection)
|
52
52
|
block.call(record) if block
|
53
|
-
@records << record.attributes
|
53
|
+
@records << record.attributes.delete_if {|k,v| v.is_a?(MongoSkip) }
|
54
54
|
save_records
|
55
55
|
end
|
56
56
|
end
|
@@ -39,19 +39,68 @@ module MongoPopulator
|
|
39
39
|
|
40
40
|
# Generate a given number of items, or for a range, generate a random number of
|
41
41
|
# items within that range, using values in array, or random words. Returns MongoArray.
|
42
|
+
# Resulting items should be a unique set, therefore if minimum number requested exceeds
|
43
|
+
# number of items available, provide fewer items.
|
42
44
|
def items(total, arr=nil)
|
45
|
+
if arr && arr.map{|e| e.class}.include?(MongoSkip)
|
46
|
+
raise StandardError, "#skip method is not permitted in the #items array argument"
|
47
|
+
end
|
48
|
+
|
49
|
+
# limit returned size to arr size if arr is not large enough
|
50
|
+
min = total.is_a?(Range) ? total.first : total
|
51
|
+
if arr
|
52
|
+
total = (min <= arr.size) ? total : arr.size
|
53
|
+
end
|
54
|
+
|
43
55
|
out = MongoArray.new
|
44
|
-
|
56
|
+
target = interpret_value(total)
|
57
|
+
until out.size == target do
|
45
58
|
out << (arr ? arr[rand(arr.size)] : words(1))
|
59
|
+
out.uniq!
|
46
60
|
end
|
47
|
-
|
61
|
+
out
|
48
62
|
end
|
49
63
|
|
50
64
|
# Simply pass the values back out as a MongoArray
|
51
65
|
def array(*values)
|
66
|
+
if values.map {|e| e.class}.include?(MongoSkip)
|
67
|
+
raise StandardError, "#skip method is not a permitted argument to #array"
|
68
|
+
end
|
52
69
|
MongoArray.new(values)
|
53
70
|
end
|
54
71
|
|
72
|
+
# Simply pass the values back out as a MongoDictionary
|
73
|
+
def dictionary(dict)
|
74
|
+
if dict.values.map {|e| e.class}.include?(MongoSkip)
|
75
|
+
raise StandardError, "#skip method is not a permitted value in #dictionary"
|
76
|
+
end
|
77
|
+
md = MongoDictionary.new()
|
78
|
+
dict.each {|k,v| md[k]=v}
|
79
|
+
md
|
80
|
+
end
|
81
|
+
|
82
|
+
# Because the mongo gem sets NULL for a value of `nil` instead of skipping the field
|
83
|
+
# altogether, we need a way to suppress a field from a doc so we don't surprise anyone.
|
84
|
+
# See #build_records in factory.rb for how this is done in parent documents, and
|
85
|
+
# #embed in this file for how it is done in embedded documents.
|
86
|
+
def skip()
|
87
|
+
MongoSkip.new
|
88
|
+
end
|
89
|
+
|
90
|
+
# Create n embedded documents from a template hash
|
91
|
+
def embed(total, template)
|
92
|
+
out = MongoArray.new
|
93
|
+
(1..interpret_value(total)).map do
|
94
|
+
md = MongoDictionary.new
|
95
|
+
template.each_pair { |k,v|
|
96
|
+
iv = interpret_value(v)
|
97
|
+
md[k] = iv unless iv.is_a?(MongoSkip)
|
98
|
+
}
|
99
|
+
out << md
|
100
|
+
end
|
101
|
+
out
|
102
|
+
end
|
103
|
+
|
55
104
|
# If an array or range is passed, a random value will be selected to match.
|
56
105
|
# All other values are simply returned.
|
57
106
|
def interpret_value(value)
|
@@ -11,7 +11,7 @@ module MongoPopulator
|
|
11
11
|
def attributes=(values_hash)
|
12
12
|
values_hash.each_pair do |key, value|
|
13
13
|
value = value.call if value.is_a?(Proc)
|
14
|
-
self.send((key.to_s + "=").to_sym, value)
|
14
|
+
self.send((key.to_s + "=").to_sym, value)
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
@@ -19,8 +19,9 @@ module MongoPopulator
|
|
19
19
|
|
20
20
|
def method_missing(sym, *args, &block)
|
21
21
|
name = sym.to_s
|
22
|
-
if name.include?('=')
|
23
|
-
|
22
|
+
if name.include?('=')
|
23
|
+
rtn = MongoPopulator.interpret_value(args.first)
|
24
|
+
@attributes[name.sub('=', '').to_sym] = rtn
|
24
25
|
else
|
25
26
|
@attributes[sym]
|
26
27
|
end
|
@@ -21,13 +21,48 @@ describe MongoPopulator::CollectionAdditions do
|
|
21
21
|
@collection.distinct('name').last.should == "foo"
|
22
22
|
end
|
23
23
|
|
24
|
-
it "should not set an attribute if passed
|
24
|
+
it "should not set an attribute if passed MongoPopulator.skip" do
|
25
25
|
@collection.populate(1) do |record|
|
26
|
-
record.monkey =
|
26
|
+
record.monkey = MongoPopulator.skip
|
27
27
|
end
|
28
28
|
@collection.distinct('monkey').should be_empty
|
29
29
|
end
|
30
30
|
|
31
|
+
it "should set a NULL value if passed nil" do
|
32
|
+
@collection.populate(1) do |record|
|
33
|
+
record.monkey = nil
|
34
|
+
end
|
35
|
+
@collection.distinct('monkey').should_not be_empty
|
36
|
+
end
|
37
|
+
|
38
|
+
# TODO: there is a chance that this will legitimately fail (if 1 is always picked).
|
39
|
+
it "should set an attribute only sometimes if MongoPopulator.skip is part of set" do
|
40
|
+
@collection.populate(10) do |record|
|
41
|
+
record.monkey = [1, MongoPopulator.skip]
|
42
|
+
end
|
43
|
+
count = 0; @collection.find().collect {|r| count += 1 if r.keys.include?('monkey')}
|
44
|
+
count.should <= 9
|
45
|
+
end
|
46
|
+
|
47
|
+
context "when generating embedded documents" do
|
48
|
+
before do
|
49
|
+
@collection.populate(1) do |parent|
|
50
|
+
parent.name = "Abraham"
|
51
|
+
parent.kids = MongoPopulator.embed(30, {:name => ["River","Willow","Swan"], :age => (1..20), :tattoos => ["butterfly", MongoPopulator.skip]})
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should generate within the value provided" do
|
56
|
+
@collection.find_one()['kids'].should have(30).items
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should not set a field when value is MongoPopulator.skip" do
|
60
|
+
# above, tattoos is set to MongoPopulator.skip approx 50% of the time
|
61
|
+
count = 0; @collection.find_one()['kids'].collect {|r| count += 1 if r.keys.include?('tattoos')}
|
62
|
+
count.should <= 29
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
31
66
|
after(:each) do
|
32
67
|
@collection.drop
|
33
68
|
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe MongoPopulator::MongoDictionary do
|
4
|
+
it 'should basically be a hash' do
|
5
|
+
md = MongoPopulator::MongoDictionary.new
|
6
|
+
md.should be_a(Hash)
|
7
|
+
MongoPopulator::MongoDictionary.methods.count.should equal Hash.methods.count
|
8
|
+
end
|
9
|
+
end
|
@@ -46,4 +46,39 @@ describe MongoPopulator::Random do
|
|
46
46
|
it "should generate 3 random paragraphs" do
|
47
47
|
MongoPopulator.paragraphs(3).split(/\n\n/).should have(3).records
|
48
48
|
end
|
49
|
+
|
50
|
+
it "should generate an array of requested size from set" do
|
51
|
+
MongoPopulator.items(5..7, %w(a b c d e f g h i j k)).should have_at_least(5).records
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should generate an array of requested size without a set" do
|
55
|
+
MongoPopulator.items(5..7).should have_at_least(5).records
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should raise an error if trying to skip within #items" do
|
59
|
+
lambda { MongoPopulator.items(2, ['money','cash',MongoPopulator.skip]) }.should raise_error(StandardError)
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should generate a unique array" do
|
63
|
+
res = [ ]
|
64
|
+
100.times do # we can be reasonably certain...
|
65
|
+
set = MongoPopulator.items(3, %w(a b c d))
|
66
|
+
res << (set == set.uniq)
|
67
|
+
end
|
68
|
+
res.should_not include(false)
|
69
|
+
end
|
70
|
+
|
71
|
+
context "when the set is not large enough to accomodate a uniq array of the size requested" do
|
72
|
+
before do
|
73
|
+
@set = MongoPopulator.items(4..5, %w(a b c))
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should generate a smaller array than asked for" do
|
77
|
+
@set.size.should == 3
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should still be unique" do
|
81
|
+
@set.should include("a","b","c")
|
82
|
+
end
|
83
|
+
end
|
49
84
|
end
|
@@ -31,6 +31,26 @@ describe MongoPopulator::Record do
|
|
31
31
|
record.stock.should == 15
|
32
32
|
end
|
33
33
|
|
34
|
+
it "should persist a dictionary as-is" do
|
35
|
+
record = MongoPopulator::Record.new(@collection)
|
36
|
+
record.info = MongoPopulator.dictionary(:name => "mongo", :type => "db")
|
37
|
+
record.info[:name].should == "mongo"
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should raise an error if trying to skip within #dictionary" do
|
41
|
+
lambda { MongoPopulator.dictionary(:name => "mongo", :type => MongoPopulator.skip) }.should raise_error(StandardError)
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should persist an array as-is" do
|
45
|
+
record = MongoPopulator::Record.new(@collection)
|
46
|
+
record.info = MongoPopulator.array("mongo", "db")
|
47
|
+
record.info.should == ["mongo","db"]
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should raise an error if trying to skip within #array" do
|
51
|
+
lambda { MongoPopulator.array("mongo", MongoPopulator.skip) }.should raise_error(StandardError)
|
52
|
+
end
|
53
|
+
|
34
54
|
after(:each) do
|
35
55
|
@collection.drop
|
36
56
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mongo_populator
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-
|
12
|
+
date: 2011-10-01 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: mongo
|
16
|
-
requirement: &
|
16
|
+
requirement: &70105499047780 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: 1.3.1
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70105499047780
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: rspec
|
27
|
-
requirement: &
|
27
|
+
requirement: &70105499047300 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ~>
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: 2.6.0
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70105499047300
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: mocha
|
38
|
-
requirement: &
|
38
|
+
requirement: &70105499046840 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ~>
|
@@ -43,7 +43,7 @@ dependencies:
|
|
43
43
|
version: 0.10.0
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *70105499046840
|
47
47
|
description: Mass populate MongoDB.
|
48
48
|
email: ben.kerney -> gmail.com
|
49
49
|
executables: []
|
@@ -53,11 +53,16 @@ files:
|
|
53
53
|
- lib/mongo_populator/collection_additions.rb
|
54
54
|
- lib/mongo_populator/factory.rb
|
55
55
|
- lib/mongo_populator/mongo_array.rb
|
56
|
+
- lib/mongo_populator/mongo_dictionary.rb
|
57
|
+
- lib/mongo_populator/mongo_skip.rb
|
56
58
|
- lib/mongo_populator/random.rb
|
57
59
|
- lib/mongo_populator/record.rb
|
58
60
|
- lib/mongo_populator.rb
|
59
61
|
- spec/mongo_populator/collection_additions_spec.rb
|
60
62
|
- spec/mongo_populator/factory_spec.rb
|
63
|
+
- spec/mongo_populator/mongo_array_spec.rb
|
64
|
+
- spec/mongo_populator/mongo_dictionary_spec.rb
|
65
|
+
- spec/mongo_populator/mongo_skip_spec.rb
|
61
66
|
- spec/mongo_populator/random_spec.rb
|
62
67
|
- spec/mongo_populator/record_spec.rb
|
63
68
|
- spec/README.md
|
@@ -68,6 +73,7 @@ files:
|
|
68
73
|
- LICENSE
|
69
74
|
- Rakefile
|
70
75
|
- README.md
|
76
|
+
- TODO.md
|
71
77
|
homepage: http://github.com/bak/mongo_populator
|
72
78
|
licenses: []
|
73
79
|
post_install_message:
|