mongoid_denormalize 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Logan Raarup
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,100 @@
1
+ Mongoid::Denormalize
2
+ ====================
3
+
4
+ Helper module for denormalizing association attributes in Mongoid models. Why denormalize? Read *[A Note on Denormalization](http://www.mongodb.org/display/DOCS/MongoDB+Data+Modeling+and+Rails#MongoDBDataModelingandRails-ANoteonDenormalization)*.
5
+
6
+
7
+ Installation
8
+ ------------
9
+
10
+ Add the gem to your Bundler `Gemfile`:
11
+
12
+ gem 'mongoid_denormalize'
13
+
14
+ Or install with RubyGems:
15
+
16
+ $ gem install mongoid_denormalize
17
+
18
+
19
+ Usage
20
+ -----
21
+
22
+ In your model:
23
+
24
+ # Include the helper method
25
+ include Mongoid::Denormalize
26
+
27
+ # Define your denormalized fields
28
+ denormalize :title, :from => :post
29
+ denormalize :name, :avatar, :from => :user
30
+
31
+
32
+
33
+ Example
34
+ -------
35
+
36
+ def User
37
+ include Mongoid::Document
38
+ include Mongoid::Denormalize
39
+
40
+ references_many :posts
41
+
42
+ field :name
43
+ field :avatar
44
+ end
45
+
46
+ def Post
47
+ include Mongoid::Document
48
+ include Mongoid::Denormalize
49
+
50
+ referenced_in :user
51
+
52
+ field :title
53
+ denormalize :name, :avatar, :from => :user
54
+ end
55
+
56
+ >> user = User.create(:name => "John Doe", :avatar => "http://url/to/avatar.png")
57
+ >> post = Post.create(:title => "Blog post", :user => user)
58
+ >> post.user_name
59
+ "John Doe"
60
+ >> post.user_avatar
61
+ "http://url/to/avatar.png"
62
+ >> user.update_attributes(:name => "Bill")
63
+ >> post.save
64
+ >> post.user_name
65
+ "Bill"
66
+
67
+
68
+ Options
69
+ -------
70
+
71
+ Denormalization can happen using an associated object as illustrated above, or using a block. The field type for denormalized fields must
72
+ be explicitly set if it is not a `String` value. Examples:
73
+
74
+ # Basic denormalization. Will set the user_name attribute with the user name.
75
+ denormalize :name, :from => :user
76
+
77
+ # Override denormalized field name. Will set the from_email attribute with the user email.
78
+ denormalize :email, :from => :user, :to => :from_email
79
+
80
+ # Specify denormalized field type. Will set the post_created_at attribute as a Time object.
81
+ denormalize :created_at, :type => Time, :from => :post
82
+
83
+ # Multiple denormalization fields. Will set the user_name and user_email attributes with values from user.
84
+ denormalize :name, :email, :from => :user
85
+
86
+ # Block denormalization. Will set the comment_count attribute with the blocks return value.
87
+ # The block receives the current instance as the first argument.
88
+ denormalize(:comment_count, :type => Integer) { |post| post.comments.count }
89
+
90
+ # Block denormalization with multiple fields. Will set the post_titles and post_dates attributes with the blocks return value.
91
+ # The block receives the current instance as the first argument and the name of the denormalized field as the second argument.
92
+ denormalize :post_titles, :post_dates, :type => Array do |user, field|
93
+ field == :post_titles ? user.posts.collect(&:title) : user.posts.collect(&:created_at)
94
+ end
95
+
96
+
97
+ Credits
98
+ -------
99
+
100
+ Copyright (c) 2010 Logan Raarup, released under the MIT license.
@@ -0,0 +1,63 @@
1
+ # = Mongoid::Denormalize
2
+ #
3
+ # Helper module for denormalizing association attributes in Mongoid models.
4
+ module Mongoid::Denormalize
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ cattr_accessor :denormalize_definitions
9
+ end
10
+
11
+ module ClassMethods
12
+ # Set a field or a number of fields to denormalize. Specify the associated object using the :from option.
13
+ #
14
+ # def Post
15
+ # include Mongoid::Document
16
+ # include Mongoid::Denormalize
17
+ #
18
+ # referenced_in :user
19
+ # references_many :comments
20
+ #
21
+ # denormalize :name, :avatar, :from => :user
22
+ #
23
+ # denormalize :email, :from => :user, :to => :from_email
24
+ #
25
+ # denormalize :comment_count, :type => Integer do |post|
26
+ # post.comments.count
27
+ # end
28
+ # end
29
+ def denormalize(*fields, &block)
30
+ options = fields.pop
31
+
32
+ (self.denormalize_definitions ||= []) << { :fields => fields, :options => options, :block => block}
33
+
34
+ # Define schema
35
+ fields.each do |name|
36
+ denormalized_name = if block_given?
37
+ name
38
+ else
39
+ options[:to] ? options[:to] : "#{options[:from]}_#{name}"
40
+ end
41
+
42
+ field denormalized_name, :type => options[:type]
43
+ end
44
+
45
+ before_validation :denormalize_fields
46
+ end
47
+ end
48
+
49
+ private
50
+ def denormalize_fields
51
+ self.denormalize_definitions.each do |definition|
52
+ definition[:fields].each do |name|
53
+ if definition[:block]
54
+ value = (definition[:fields].length > 1 ? definition[:block].call(self, name) : definition[:block].call(self))
55
+ self.send("#{name}=", value)
56
+ else
57
+ attribute_name = (definition[:options][:to] ? definition[:options][:to] : "#{definition[:options][:from]}_#{name}")
58
+ self.send("#{attribute_name}=", self.send(definition[:options][:from]).try(name))
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,13 @@
1
+ class Comment
2
+ include Mongoid::Document
3
+ include Mongoid::Denormalize
4
+
5
+ field :body
6
+
7
+ referenced_in :post
8
+ referenced_in :user
9
+
10
+ denormalize :name, :from => :user
11
+ denormalize :email, :from => :user, :to => :from_email
12
+ denormalize :created_at, :type => Time, :from => :post
13
+ end
@@ -0,0 +1,14 @@
1
+ class Post
2
+ include Mongoid::Document
3
+ include Mongoid::Denormalize
4
+
5
+ field :title
6
+ field :body
7
+ field :created_at, :type => Time
8
+
9
+ referenced_in :user
10
+ references_many :comments
11
+
12
+ denormalize :name, :email, :from => :user
13
+ denormalize(:comment_count, :type => Integer) { |post| post.comments.count }
14
+ end
@@ -0,0 +1,14 @@
1
+ class User
2
+ include Mongoid::Document
3
+ include Mongoid::Denormalize
4
+
5
+ field :name
6
+ field :email
7
+
8
+ references_many :posts
9
+ references_many :comments
10
+
11
+ denormalize :post_titles, :post_dates, :type => Array do |user, field|
12
+ field == :post_titles ? user.posts.collect(&:title) : user.posts.collect(&:created_at).collect { |t| t + 300 }
13
+ end
14
+ end
@@ -0,0 +1,96 @@
1
+ require "spec_helper"
2
+
3
+ describe Mongoid::Denormalize do
4
+ before(:all) do
5
+ Mongoid.master.collections.each do |c|
6
+ c.drop rescue nil
7
+ end
8
+
9
+ @user = User.create!(:name => "John Doe", :email => "john@doe.com")
10
+ @post = Post.create!(:title => "Blog post", :body => "Lorem ipsum...", :created_at => Time.parse("Jan 1 2010 12:00"), :user => @user)
11
+ @comment = Comment.create!(:body => "This is the comment", :post => @post, :user => @user)
12
+
13
+ @other_user = User.create!(:name => "Bill", :email => "bill@doe.com")
14
+ end
15
+
16
+ context "denormalize associated object" do
17
+ it "should define multiple fields for association" do
18
+ @post.fields.should have_key "user_name"
19
+ @post.fields.should have_key "user_email"
20
+ end
21
+
22
+ it "should override the name of the denormalized field" do
23
+ @comment.fields.should have_key "from_email"
24
+ end
25
+
26
+ it "should default to string field type for associated fields" do
27
+ @post.fields["user_name"].type.should eql String
28
+ end
29
+
30
+ it "should allow setting the field type for associated fields" do
31
+ @comment.fields["post_created_at"].type.should eql Time
32
+ end
33
+
34
+ it "should allow multiple declarations for the same association" do
35
+ @comment.fields.should have_key "user_name"
36
+ @comment.fields.should have_key "from_email"
37
+ end
38
+
39
+ it "should denormalize fields without specified type" do
40
+ @comment.user_name.should eql @user.name
41
+ @comment.from_email.should eql @user.email
42
+ @post.user_name.should eql @user.name
43
+ @post.user_email.should eql @user.email
44
+ end
45
+
46
+ it "should denormalize fields with specified type" do
47
+ @comment.post_created_at.should eql @post.created_at
48
+ end
49
+
50
+ it "should update denormalized values if changed" do
51
+ @other_user = User.create!(:name => "Bill", :email => "bill@doe.com")
52
+
53
+ @comment.user = @other_user
54
+ @comment.save!
55
+
56
+ @comment.user_name.should eql @other_user.name
57
+ @comment.from_email.should eql @other_user.email
58
+ end
59
+ end
60
+
61
+ context "denormalization with block" do
62
+ it "should accept block for denormalization" do
63
+ @post.fields.should have_key "comment_count"
64
+ end
65
+
66
+ it "should accept multiple fields for block" do
67
+ @user.fields.should have_key "post_titles"
68
+ @user.fields.should have_key "post_dates"
69
+ end
70
+
71
+ it "should allow setting the field type" do
72
+ @user.fields["post_titles"].type.should eql Array
73
+ @post.fields["comment_count"].type.should eql Integer
74
+ end
75
+
76
+ it "should denormalize fields using block" do
77
+ @post.save!
78
+ @post.comment_count.should eql 1
79
+
80
+ @user.save!
81
+ @user.post_titles.should eql ["Blog post"]
82
+ @user.post_dates.should eql [Time.parse("Jan 1 2010 12:00") + 300]
83
+ end
84
+
85
+ it "should update denormalized values if changed" do
86
+ @post.user = @other_user
87
+ @post.save!
88
+
89
+ @user.save!
90
+ @other_user.save!
91
+
92
+ @user.post_titles.should eql []
93
+ @other_user.post_titles.should eql ["Blog post"]
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,16 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+ require 'mongoid'
4
+ require 'yaml'
5
+
6
+ Mongoid.configure do |config|
7
+ settings = YAML.load(File.read(File.join(File.dirname(__FILE__), "database.yml")))
8
+
9
+ db = Mongo::Connection.new(settings['host'], settings['port']).db(settings['database'])
10
+ db.authenticate(settings['username'], settings['password'])
11
+
12
+ config.master = db
13
+ end
14
+
15
+ require File.expand_path("../../lib/mongoid_denormalize", __FILE__)
16
+ Dir["#{File.dirname(__FILE__)}/models/*.rb"].each { |f| require f }
metadata ADDED
@@ -0,0 +1,95 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mongoid_denormalize
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Logan Raarup
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-07-09 00:00:00 +02:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: mongoid
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ hash: -1848230043
30
+ segments:
31
+ - 2
32
+ - 0
33
+ - 0
34
+ - beta9
35
+ version: 2.0.0.beta9
36
+ type: :runtime
37
+ version_requirements: *id001
38
+ description: Helper module for denormalizing association attributes in Mongoid models.
39
+ email: logan@logan.dk
40
+ executables: []
41
+
42
+ extensions: []
43
+
44
+ extra_rdoc_files:
45
+ - LICENSE
46
+ - README.md
47
+ files:
48
+ - LICENSE
49
+ - README.md
50
+ - lib/mongoid_denormalize.rb
51
+ - spec/models/comment.rb
52
+ - spec/models/post.rb
53
+ - spec/models/user.rb
54
+ - spec/mongoid_denormalize_spec.rb
55
+ - spec/spec_helper.rb
56
+ has_rdoc: true
57
+ homepage: http://github.com/logandk/mongoid_denormalize
58
+ licenses: []
59
+
60
+ post_install_message:
61
+ rdoc_options:
62
+ - --charset=UTF-8
63
+ require_paths:
64
+ - lib
65
+ required_ruby_version: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ hash: 3
71
+ segments:
72
+ - 0
73
+ version: "0"
74
+ required_rubygems_version: !ruby/object:Gem::Requirement
75
+ none: false
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ hash: 3
80
+ segments:
81
+ - 0
82
+ version: "0"
83
+ requirements: []
84
+
85
+ rubyforge_project:
86
+ rubygems_version: 1.3.7
87
+ signing_key:
88
+ specification_version: 3
89
+ summary: Mongoid denormalization helper.
90
+ test_files:
91
+ - spec/models/comment.rb
92
+ - spec/models/post.rb
93
+ - spec/models/user.rb
94
+ - spec/mongoid_denormalize_spec.rb
95
+ - spec/spec_helper.rb