mongoid_denormalize 0.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.
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