mm-embeddable 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Michael Bleigh
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.
@@ -0,0 +1,66 @@
1
+ = MongoMapper Embeddable
2
+
3
+ Because MongoDB doesn't have joins but does have the ability to embed documents in other documents, it is often the case that for query minimization purposes it is useful to provide a few key properties of a document when it's embedded in another one. MongoMapper Embeddable is a plugin for MongoMapper to do just that.
4
+
5
+ == Installation
6
+
7
+ gem install mm-embeddable
8
+
9
+ == Usage
10
+
11
+ To declare an embeddable version of a MongoMapper document, simply call <tt>embeds</tt> after all of your <tt>key</tt> declarations and specify the keys that should be embedded in the "compacted" version of the model. This will automatically generate a <tt>YourDocumentClass::Embeddable</tt> class that can be embedded in other documents.
12
+
13
+ === Example
14
+
15
+ require 'mongo_mapper'
16
+ require 'mm-embeddable'
17
+
18
+ class User
19
+ include MongoMapper::Document
20
+
21
+ key :name, String
22
+ key :profile_photo, String
23
+ key :bio, String
24
+ key :interests, Array
25
+
26
+ embeds :name, :profile_photo
27
+ end
28
+
29
+ class Post
30
+ include MongoMapper::Document
31
+
32
+ key :author, User::Embeddable
33
+ key :title, String
34
+ key :body, String
35
+ end
36
+
37
+ This creates an embeddable version of the <tt>User</tt> model that only contains the name and profile picture. If that's all I access, then no additional queries are called. Example:
38
+
39
+ <h1><%= @post.title %></h1>
40
+ <span class='author'>
41
+ <img src='<%= @post.author.profile_photo %>'/> <%= @post.author.name %>
42
+ </span>
43
+ <div class='body'><%= @post.body %></div>
44
+
45
+ But if we need other attributes that haven't been stored in the embeddable model, we can do so seamlessly:
46
+
47
+ # no additional query is performed
48
+ @post.author.name
49
+ # the full document is seamlessly fetched in the background
50
+ @post.author.bio
51
+
52
+ In this way we take full advantage of Mongo's embedded documents while still being able to easily fall back to the full document when necessary.
53
+
54
+ == Note on Patches/Pull Requests
55
+
56
+ * Fork the project.
57
+ * Make your feature addition or bug fix.
58
+ * Add tests for it. This is important so I don't break it in a
59
+ future version unintentionally.
60
+ * Commit, do not mess with rakefile, version, or history.
61
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
62
+ * Send me a pull request. Bonus points for topic branches.
63
+
64
+ == Copyright
65
+
66
+ Copyright (c) 2010 Michael Bleigh. See LICENSE for details.
@@ -0,0 +1,51 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "mm-embeddable"
8
+ gem.summary = %Q{Create compact, embeddable versions of your MongoMapper documents.}
9
+ gem.description = %Q{For query minimization purposes in MongoDB it is useful to provide a few key properties of a document when it's embedded in another one. MongoMapper Embeddable is a plugin for MongoMapper to do just that.}
10
+ gem.email = "michael@intridea.com"
11
+ gem.homepage = "http://github.com/intridea/mm-embeddable"
12
+ gem.add_dependency 'mongo_mapper', '>= 0.7.0'
13
+ gem.authors = ["Michael Bleigh"]
14
+ gem.add_development_dependency "rspec", ">= 1.2.9"
15
+ gem.add_development_dependency 'mocha'
16
+ gem.add_development_dependency 'machinist'
17
+ gem.add_development_dependency 'machinist_mongo'
18
+ gem.add_development_dependency 'faker'
19
+
20
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
21
+ end
22
+ Jeweler::GemcutterTasks.new
23
+ rescue LoadError
24
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
25
+ end
26
+
27
+ require 'spec/rake/spectask'
28
+ Spec::Rake::SpecTask.new(:spec) do |spec|
29
+ spec.libs << 'lib' << 'spec'
30
+ spec.spec_files = FileList['spec/**/*_spec.rb']
31
+ end
32
+
33
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
34
+ spec.libs << 'lib' << 'spec'
35
+ spec.pattern = 'spec/**/*_spec.rb'
36
+ spec.rcov = true
37
+ end
38
+
39
+ task :spec => :check_dependencies
40
+
41
+ task :default => :spec
42
+
43
+ require 'rake/rdoctask'
44
+ Rake::RDocTask.new do |rdoc|
45
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
46
+
47
+ rdoc.rdoc_dir = 'rdoc'
48
+ rdoc.title = "mm_compactable #{version}"
49
+ rdoc.rdoc_files.include('README*')
50
+ rdoc.rdoc_files.include('lib/**/*.rb')
51
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
@@ -0,0 +1,81 @@
1
+ require 'mongo_mapper'
2
+
3
+ module MongoMapper
4
+ module Plugins
5
+ module Embeddable
6
+ class EmbeddableDocument #:nodoc:
7
+ include ::MongoMapper::EmbeddedDocument
8
+
9
+ class << self
10
+ attr_writer :full_class
11
+ end
12
+
13
+ def self.from_full(full_document)
14
+ d = self.new
15
+ keys.keys.each do |k|
16
+ d.send("#{k}=".to_sym, full_document.send(k.to_sym))
17
+ end
18
+ d
19
+ end
20
+
21
+ def self.full_class
22
+ @full_class or raise NotImplementedError, 'This embed has no full class.'
23
+ end
24
+
25
+ def expand!
26
+ @expanded = true
27
+ @expansion = self.class.full_class.find(self.id)
28
+ end
29
+
30
+ def method_missing(*args)
31
+ if !@expanded
32
+ expand!
33
+ @expansion.send(*args)
34
+ else
35
+ super
36
+ end
37
+ end
38
+
39
+ def self.to_mongo(instance)
40
+ case instance
41
+ when full_class
42
+ from_full(instance).to_mongo
43
+ else
44
+ instance.to_mongo
45
+ end
46
+ end
47
+ end
48
+
49
+ module ClassMethods
50
+ # Tells MongoMapper that this document can be embedded. Pass
51
+ # in an array of the keys that should be persisted to the
52
+ # embedded document (<tt>_id</tt> is automatically persisted
53
+ # as a reference to the full document).
54
+ def embeds(*ckeys)
55
+ @embeddable_keys ||= []
56
+ @embeddable_keys.push(*ckeys)
57
+
58
+ self.const_set(:Embeddable, Class.new(::MongoMapper::Plugins::Embeddable::EmbeddableDocument))
59
+
60
+ self.const_get(:Embeddable).full_class = self
61
+ self.const_get(:Embeddable).key :_id, ObjectId
62
+
63
+ ckeys.each do |k|
64
+ self.const_get(:Embeddable).key k
65
+ self.const_get(:Embeddable).keys[k.to_s] = self.keys[k.to_s].dup
66
+ end
67
+
68
+ include MongoMapper::Plugins::Embeddable::EmbeddableMethods
69
+ end
70
+ end
71
+
72
+ module EmbeddableMethods
73
+ def to_embeddable
74
+ self.class.const_get(:Embeddable).from_full(self)
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+
81
+ MongoMapper::Document.append_extensions(MongoMapper::Plugins::Embeddable::ClassMethods)
@@ -0,0 +1,22 @@
1
+ require 'machinist/mongo_mapper'
2
+ require 'sham'
3
+ require 'faker'
4
+
5
+ Sham.name { Faker::Name.name }
6
+ Sham.login { Faker::Internet.user_name }
7
+ Sham.bio { Faker::Lorem.sentence(30) }
8
+ Sham.subject { Faker::Lorem.sentence(10) }
9
+ Sham.body { Faker::Lorem.paragraphs(4).join("\n\n") }
10
+
11
+ TestUser.blueprint do
12
+ name
13
+ login
14
+ bio
15
+ end
16
+
17
+ TestMessage.blueprint do
18
+ sender { TestUser.make }
19
+ receivers { [TestUser.make, TestUser.make] }
20
+ subject
21
+ body
22
+ end
@@ -0,0 +1,47 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "MongoMapper::Plugins::Embeddable" do
4
+ it { TestUser.should be_respond_to(:embeds) }
5
+
6
+ it 'should define the Embedded subclass' do
7
+ defined?(TestUser::Embeddable).should be_true
8
+ TestUser::Embeddable.should < MongoMapper::Plugins::Embeddable::EmbeddableDocument
9
+ end
10
+
11
+ it 'should be able to convert to embedded' do
12
+ TestUser.new.to_embeddable.should be_kind_of(TestUser::Embeddable)
13
+ end
14
+
15
+ describe '::EmbeddedDocument' do
16
+ it 'should set attributes from a document' do
17
+ mid = Mongo::ObjectID.new
18
+ c = TestUser::Embeddable.from_full(stub(:_id => mid, :login => 'abc', :name => 'Bob Bobson', :bio => "A great dude."))
19
+ c.login.should == 'abc'
20
+ c.name.should == 'Bob Bobson'
21
+ c._id.should == mid
22
+ lambda{c.bio}.should raise_error(NoMethodError)
23
+ end
24
+
25
+ it 'should expand when an attribute is missing' do
26
+ mid = Mongo::ObjectID.new
27
+ u = TestUser.new(:_id => mid, :name => "Frank", :bio => 'Once upon a time.')
28
+ TestUser.expects(:find).with(mid).returns(u)
29
+ TestUser::Embeddable.from_full(u).bio.should == 'Once upon a time.'
30
+ end
31
+ end
32
+
33
+ describe ' in another model' do
34
+ it 'should coerce as a compact document' do
35
+ u1 = TestUser.make
36
+ u2 = TestUser.make
37
+ u3 = TestUser.make
38
+
39
+ t = TestMessage.make(:sender => u1, :receivers => [u2, u3])
40
+
41
+ t.reload
42
+ t.sender.should be_kind_of(TestUser::Embeddable)
43
+ t.sender._id.should == u1._id
44
+ t.receivers.first.should be_kind_of(TestUser::Embeddable)
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,2 @@
1
+ --color
2
+ --backtrace
@@ -0,0 +1,41 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+
4
+ require 'rubygems'
5
+ gem 'rspec', '1.3.0'
6
+
7
+ require 'mm-embeddable'
8
+ require 'spec'
9
+ require 'spec/autorun'
10
+ require 'spec/mocks'
11
+ require 'mocha'
12
+
13
+ class TestUser
14
+ include MongoMapper::Document
15
+
16
+ key :login, String
17
+ key :name, String
18
+ key :bio, String
19
+
20
+ embeds :login, :name
21
+ end
22
+
23
+ class TestMessage
24
+ include MongoMapper::Document
25
+
26
+ key :sender, TestUser::Embeddable
27
+ many :receivers, :class_name => 'TestUser::Embeddable'
28
+
29
+ key :subject, String
30
+ key :body, String
31
+ end
32
+
33
+ require File.expand_path(File.dirname(__FILE__) + "/blueprints")
34
+
35
+ MongoMapper.database = 'mm_plugin_test'
36
+
37
+ Spec::Runner.configure do |config|
38
+ config.mock_with :mocha
39
+ config.before(:all) { Sham.reset(:before_all) }
40
+ config.before(:each) { Sham.reset(:before_each); }#MongoMapper.connection.drop_database('mm_plugin_test') }
41
+ end
metadata ADDED
@@ -0,0 +1,150 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mm-embeddable
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - Michael Bleigh
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-03-29 00:00:00 -04:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: mongo_mapper
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 0
29
+ - 7
30
+ - 0
31
+ version: 0.7.0
32
+ type: :runtime
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: rspec
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ segments:
42
+ - 1
43
+ - 2
44
+ - 9
45
+ version: 1.2.9
46
+ type: :development
47
+ version_requirements: *id002
48
+ - !ruby/object:Gem::Dependency
49
+ name: mocha
50
+ prerelease: false
51
+ requirement: &id003 !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ segments:
56
+ - 0
57
+ version: "0"
58
+ type: :development
59
+ version_requirements: *id003
60
+ - !ruby/object:Gem::Dependency
61
+ name: machinist
62
+ prerelease: false
63
+ requirement: &id004 !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ segments:
68
+ - 0
69
+ version: "0"
70
+ type: :development
71
+ version_requirements: *id004
72
+ - !ruby/object:Gem::Dependency
73
+ name: machinist_mongo
74
+ prerelease: false
75
+ requirement: &id005 !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ segments:
80
+ - 0
81
+ version: "0"
82
+ type: :development
83
+ version_requirements: *id005
84
+ - !ruby/object:Gem::Dependency
85
+ name: faker
86
+ prerelease: false
87
+ requirement: &id006 !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ segments:
92
+ - 0
93
+ version: "0"
94
+ type: :development
95
+ version_requirements: *id006
96
+ description: For query minimization purposes in MongoDB it is useful to provide a few key properties of a document when it's embedded in another one. MongoMapper Embeddable is a plugin for MongoMapper to do just that.
97
+ email: michael@intridea.com
98
+ executables: []
99
+
100
+ extensions: []
101
+
102
+ extra_rdoc_files:
103
+ - LICENSE
104
+ - README.rdoc
105
+ files:
106
+ - .document
107
+ - .gitignore
108
+ - LICENSE
109
+ - README.rdoc
110
+ - Rakefile
111
+ - VERSION
112
+ - lib/mm-embeddable.rb
113
+ - spec/blueprints.rb
114
+ - spec/mm_embeddable_spec.rb
115
+ - spec/spec.opts
116
+ - spec/spec_helper.rb
117
+ has_rdoc: true
118
+ homepage: http://github.com/intridea/mm-embeddable
119
+ licenses: []
120
+
121
+ post_install_message:
122
+ rdoc_options:
123
+ - --charset=UTF-8
124
+ require_paths:
125
+ - lib
126
+ required_ruby_version: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ segments:
131
+ - 0
132
+ version: "0"
133
+ required_rubygems_version: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ segments:
138
+ - 0
139
+ version: "0"
140
+ requirements: []
141
+
142
+ rubyforge_project:
143
+ rubygems_version: 1.3.6
144
+ signing_key:
145
+ specification_version: 3
146
+ summary: Create compact, embeddable versions of your MongoMapper documents.
147
+ test_files:
148
+ - spec/blueprints.rb
149
+ - spec/mm_embeddable_spec.rb
150
+ - spec/spec_helper.rb