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.
@@ -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.
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- mongo_populator (0.1.1)
4
+ mongo_populator (1.0.0)
5
5
  mongo (~> 1.3.1)
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # MongoPopulator
2
2
 
3
- `mongo_populator` 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 is not tied to any particular framework.
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 "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.
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
- ### JSON-specific structures
68
+ ### Mongo-specific values
69
69
 
70
- 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.
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
- Setting an attribute to nil prevents that attribute being set.
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
- So, to support conditional setting of an attribute, pass it an array with one or more nils as elements.
100
+ To persist a static dictionary to your document, use `#dictionary`.
83
101
 
84
- ...
85
- user.creds = ['M.D.', 'J.D.', 'N.D.', nil, nil]
86
- ...
102
+ MongoPopulator.dictionary(:name => "Mongo", :type => "db")
103
+
104
+ #### Embedded Documents
87
105
 
88
- ~40% of users will not have credentials.
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
- ## TODO
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
- * Support singular and multiple embedded documents
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 highly derivative of, and heavily reuses, the work of Ryan Bates [via Populator](https://github.com/ryanb/populator/). Thanks, Ryan.
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
@@ -0,0 +1,2 @@
1
+ Write MongoDictionary
2
+ Write support for singular and multiple embedded documents
@@ -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
@@ -0,0 +1,5 @@
1
+ module MongoPopulator
2
+
3
+ # MongoArrays can be passed directly to attributes and will be saved as arrays in mongo.
4
+ class MongoDictionary < Hash; end
5
+ end
@@ -0,0 +1,4 @@
1
+ module MongoPopulator
2
+
3
+ class MongoSkip; end
4
+ 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
- (1..interpret_value(total)).map do
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
- return out
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?('=') && args.first
23
- @attributes[name.sub('=', '').to_sym] = MongoPopulator.interpret_value(args.first)
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 nil" do
24
+ it "should not set an attribute if passed MongoPopulator.skip" do
25
25
  @collection.populate(1) do |record|
26
- record.monkey = nil
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::MongoArray do
4
+ it 'should basically be an array' do
5
+ ma = MongoPopulator::MongoArray.new
6
+ ma.should be_a(Array)
7
+ MongoPopulator::MongoArray.methods.count.should equal Array.methods.count
8
+ end
9
+ 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
@@ -0,0 +1,8 @@
1
+ require "spec_helper"
2
+
3
+ describe MongoPopulator::MongoSkip do
4
+ it 'should be instantiable' do
5
+ mn = MongoPopulator::MongoSkip.new
6
+ mn.should be_an_instance_of(MongoPopulator::MongoSkip)
7
+ end
8
+ 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.2.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-09-15 00:00:00.000000000Z
12
+ date: 2011-10-01 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: mongo
16
- requirement: &70136711178880 !ruby/object:Gem::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: *70136711178880
24
+ version_requirements: *70105499047780
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rspec
27
- requirement: &70136711178220 !ruby/object:Gem::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: *70136711178220
35
+ version_requirements: *70105499047300
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: mocha
38
- requirement: &70136711177400 !ruby/object:Gem::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: *70136711177400
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: