mongo_populator 0.2.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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: