has_meta_data 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.rdoc ADDED
@@ -0,0 +1,6 @@
1
+ == 1.0.0 Updated to Rails 3, RSpec
2
+
3
+ * Converted tests to RSpec
4
+ * Added support for Rails 3.0
5
+
6
+ == 0.1.0 Initial Version
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 [name of plugin creator]
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.rdoc ADDED
@@ -0,0 +1,85 @@
1
+ == Has Meta Data
2
+
3
+ This plugin allows you to extend one of your models with additional supplementary data stored in another table through a has_one relationship. The benefit of this plugin over a simple has_one is that the meta model class is dynamically defined (though extendable via the has_meta_data declaration) and fields on the meta model are automatically delegated to from the main model, allowing you to work solely with your primary model and not worry about the storage of the data in a separate meta table. This plugin also automatically handles creation of the meta model if you set a field on your model that exists in the meta model table.
4
+
5
+ Common use cases for this are database efficiency if only a small number of your records have a certain set of data or in conjunction with STI to accomplish something akin to Class Table Inheritance (CTI).
6
+
7
+ In short, it's a transparent way of splitting up data into two tables while still treating them as one model.
8
+
9
+ == Installation
10
+
11
+ This gem works with Rails 3 only.
12
+
13
+ In your Gemfile:
14
+
15
+ gem "has_meta_data"
16
+
17
+ == Basic Example
18
+
19
+ ## Database Schema
20
+ create_table :articles, :force => true do |t|
21
+ t.string :title
22
+ t.text :body
23
+ end
24
+
25
+ create_table :article_meta, :force => true do |t|
26
+ t.references :article
27
+ t.string :reference_link
28
+ t.text :reference_note
29
+ end
30
+
31
+ ## Model
32
+ class Article < ActiveRecord::Base
33
+ has_meta_data
34
+ end
35
+
36
+ ## Exposed Class Methods & Scopes:
37
+ Article.meta_data_class
38
+ => Article::Meta
39
+ Article.with_meta_data.all
40
+ => (Articles that have associated meta data)
41
+ Article.without_meta_data.all
42
+ => (Articles with no associated meta data)
43
+
44
+ ## Usage Examples:
45
+
46
+ article = Article.create(:title => 'Title', :body => 'Body')
47
+ article.has_meta_data? # => false
48
+
49
+ article.reference_link = 'http://benhughes.name/'
50
+ article.has_meta_data? # => true
51
+ article.meta
52
+ # => #<Article::Meta reference_link: "http://benhughes.name/", reference_note: nil>
53
+
54
+ article = Article.create(:title => 'Title', :body => 'Body', :reference_link => 'http://benhughes.name/')
55
+ article.has_meta_data? # => true
56
+ article.reference_link # => "http://benhughes.name/"
57
+
58
+ == Changing the Defaults
59
+
60
+ You can control the name of the meta class, foreign key, and table name not unlike ActiveRecord associations:
61
+
62
+ class Article < ActiveRecord::Base
63
+ has_meta_data :class_name => 'AdditionalData',
64
+ :foreign_key => 'news_article_id',
65
+ :table_name => 'news_article_additional_data'
66
+ end
67
+
68
+
69
+ == Extending the Meta Data Class
70
+
71
+ Because you don't directly define the meta data class, you can specify a block of code to be run in its
72
+ context by passing a block to has_draft:
73
+
74
+ class Article < ActiveRecord::Base
75
+ belongs_to :user
76
+
77
+ has_meta_data do
78
+ belongs_to :some_author
79
+
80
+ def do_something
81
+ # Something
82
+ end
83
+ end
84
+
85
+ end
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ require "rubygems"
2
+ require "rake"
3
+ require "rake/rdoctask"
4
+
5
+ desc "Generate documentation for the plugin."
6
+ Rake::RDocTask.new(:rdoc) do |rdoc|
7
+ rdoc.rdoc_dir = "rdoc"
8
+ rdoc.title = "has_meta_data"
9
+ rdoc.options << "--line-numbers" << "--inline-source"
10
+ rdoc.rdoc_files.include('README')
11
+ rdoc.rdoc_files.include('lib/**/*.rb')
12
+ end
13
+
14
+ Dir["#{File.dirname(__FILE__)}/lib/tasks/*.rake"].sort.each { |ext| load ext }
data/init.rb ADDED
@@ -0,0 +1,3 @@
1
+ require "has_meta_data"
2
+
3
+ HasMetaData::Railtie.insert
@@ -0,0 +1,69 @@
1
+ module HasMetaData
2
+ def self.included(base)
3
+ base.extend ClassMethods
4
+ end
5
+
6
+ module ClassMethods
7
+ def has_meta_data(options = {}, &block)
8
+ return if self.included_modules.include?(HasMetaData::InstanceMethods)
9
+ include HasMetaData::InstanceMethods
10
+
11
+ cattr_accessor :meta_data_class_name, :meta_data_foreign_key, :meta_data_table_name, :meta_data_columns
12
+
13
+ self.meta_data_class_name = options[:class_name] || 'Meta'
14
+ self.meta_data_foreign_key = options[:foreign_key] || self.to_s.foreign_key
15
+ self.meta_data_table_name = options[:table_name] || "#{table_name_prefix}#{self.name.demodulize.underscore}_meta#{table_name_suffix}"
16
+
17
+
18
+ # Create Relationship to Meta Data
19
+ class_eval do
20
+ has_one :meta,
21
+ :class_name => "#{self.to_s}::#{meta_data_class_name}",
22
+ :foreign_key => meta_data_foreign_key,
23
+ :dependent => :destroy
24
+
25
+ named_scope :with_meta, :include => [:meta], :conditions => "#{meta_data_table_name}.id IS NOT NULL"
26
+ named_scope :without_meta, :include => [:meta], :conditions => "#{meta_data_table_name}.id IS NULL"
27
+ end
28
+
29
+ # Dynamically Create Model::Meta Class
30
+ const_set(meta_data_class_name, Class.new(ActiveRecord::Base))
31
+
32
+ meta_data_class.cattr_accessor :original_class
33
+ meta_data_class.original_class = self
34
+ meta_data_class.set_table_name(meta_data_table_name)
35
+
36
+ # Draft Parent Association
37
+ meta_data_class.belongs_to self.to_s.demodulize.underscore.to_sym, :class_name => "::#{self.to_s}", :foreign_key => meta_data_foreign_key
38
+
39
+ # Block extension
40
+ meta_data_class.class_eval(&block) if block_given?
41
+
42
+ # Finally setup attribute delegation to the meta fields
43
+ self.meta_data_columns = meta_data_class.content_columns.map(&:name)
44
+
45
+ meta_data_columns.each do |field|
46
+ define_method(field.to_sym) do
47
+ meta.send(field.to_sym) if has_meta_data?
48
+ end
49
+
50
+ define_method("#{field}=".to_sym) do |value|
51
+ self.build_meta unless has_meta_data?
52
+ meta.send("#{field}=".to_sym, value)
53
+ end
54
+ end
55
+ end
56
+
57
+ def meta_data_class
58
+ const_get(meta_data_class_name)
59
+ end
60
+ end
61
+
62
+ module InstanceMethods
63
+ def has_meta_data?
64
+ !self.meta.nil?
65
+ end
66
+ end
67
+ end
68
+
69
+ require "has_meta_data/railtie"
@@ -0,0 +1,19 @@
1
+ module HasMetaData
2
+ if defined?(Rails::Railtie)
3
+ require "rails"
4
+
5
+ class Railtie < Rails::Railtie
6
+ initializer "has_draft.extend_active_record" do
7
+ ActiveSupport.on_load(:active_record) do
8
+ HasMetaData::Railtie.insert
9
+ end
10
+ end
11
+ end
12
+ end
13
+
14
+ class Railtie
15
+ def self.insert
16
+ ActiveRecord::Base.send(:include, HasMetaData)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,3 @@
1
+ module HasMetaData
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,21 @@
1
+ sqlite:
2
+ adapter: sqlite
3
+ database: spec/db/test.sqlite
4
+
5
+ sqlite3:
6
+ adapter: sqlite3
7
+ database: spec/db/test.sqlite3
8
+
9
+ postgresql:
10
+ adapter: postgresql
11
+ username: postgres
12
+ password: postgres
13
+ database: has_draft_plugin_test
14
+ min_messages: ERROR
15
+
16
+ mysql:
17
+ adapter: mysql
18
+ host: localhost
19
+ username: root
20
+ password:
21
+ database: has_draft_plugin_test
data/spec/db/schema.rb ADDED
@@ -0,0 +1,14 @@
1
+ ActiveRecord::Schema.define(:version => 0) do
2
+
3
+ create_table :articles, :force => true do |t|
4
+ t.string :title
5
+ t.text :body
6
+ end
7
+
8
+ create_table :article_meta, :force => true do |t|
9
+ t.references :article
10
+ t.string :reference_link
11
+ t.text :reference_note
12
+ end
13
+
14
+ end
Binary file
@@ -0,0 +1,16 @@
1
+ require "factory_girl"
2
+
3
+ Factory.define(:article) do |f|
4
+ f.title { Faker::Lorem.sentence }
5
+ f.body { Faker::Lorem.paragraphs.join("\n\n") }
6
+ end
7
+
8
+ Factory.define(:article_meta, :class => Article::Meta) do |f|
9
+ f.association(:article)
10
+ f.reference_link { "http://" + Faker::Internet.domain_name }
11
+ f.reference_note { Faker::Lorem.paragraph }
12
+ end
13
+
14
+ Factory.define(:article_with_meta_data, :parent => :article) do |f|
15
+ f.association(:meta, :factory => :article_meta)
16
+ end
@@ -0,0 +1,75 @@
1
+ require "spec_helper"
2
+
3
+ describe HasMetaData do
4
+ context "Article model" do
5
+ it "should expose #meta_data_class_name as Meta" do
6
+ Article.meta_data_class_name.should == "Meta"
7
+ end
8
+
9
+ it "should expose #meta_data_class as Article::Meta" do
10
+ Article.meta_data_class.should == Article::Meta
11
+ end
12
+
13
+ it "should default #meta_data_foreign_key to article_id" do
14
+ Article.meta_data_foreign_key.should == "article_id"
15
+ end
16
+
17
+ it "should find #meta_data_columns" do
18
+ Article.meta_data_columns.should == ["reference_link", "reference_note"]
19
+ end
20
+
21
+ context "Meta model" do
22
+ it "should be defined under the Article namespace" do
23
+ Article.constants.should include('Meta')
24
+ end
25
+
26
+ it "should expose #original_class as Article" do
27
+ Article::Meta.original_class.should == Article
28
+ end
29
+ end
30
+ end
31
+
32
+ context "with an article without meta data" do
33
+ before { @article = Factory.create(:article) }
34
+
35
+ it "should not have meta data" do
36
+ @article.should_not have_meta_data
37
+ end
38
+
39
+ it "should return nil for meta data fields" do
40
+ @article.reference_link.should be_nil
41
+ @article.reference_note.should be_nil
42
+ end
43
+ end
44
+
45
+ context "with an article with meta data" do
46
+ before { @article = Factory.create(:article_with_meta_data) }
47
+
48
+ it "should have meta data" do
49
+ @article.should have_meta_data
50
+ end
51
+
52
+ it "should return values for meta data fields" do
53
+ @article.reference_link.should_not be_nil
54
+ @article.reference_note.should_not be_nil
55
+ end
56
+ end
57
+
58
+ context "with an article after setting meta data fields through mutators" do
59
+ before do
60
+ @article = Factory.create(:article)
61
+ @article.reference_link = "http://railsgarden.com/"
62
+ @article.reference_note = "Notes"
63
+ end
64
+
65
+ it "should have meta data" do
66
+ @article.should have_meta_data
67
+ end
68
+
69
+ it "should return values for meta data fields" do
70
+ @article.reference_link.should == "http://railsgarden.com/"
71
+ @article.reference_note.should == "Notes"
72
+ end
73
+ end
74
+
75
+ end
@@ -0,0 +1,25 @@
1
+ require "rubygems"
2
+ require "rspec"
3
+ require "factory_girl"
4
+ require "faker"
5
+ require "rails"
6
+ require "active_record"
7
+ require "active_support"
8
+
9
+ # Establish DB Connection
10
+ config = YAML::load(IO.read(File.join(File.dirname(__FILE__), 'db', 'database.yml')))
11
+ ActiveRecord::Base.configurations = {'test' => config[ENV['DB'] || 'sqlite3']}
12
+ ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations['test'])
13
+
14
+ # Load Test Schema into the Database
15
+ load(File.dirname(__FILE__) + "/db/schema.rb")
16
+
17
+ require File.dirname(__FILE__) + '/../init'
18
+
19
+ # Example has_meta_data Model:
20
+ class Article < ActiveRecord::Base
21
+ has_meta_data
22
+ end
23
+
24
+ # Load Factories:
25
+ Dir[File.join(File.dirname(__FILE__), "factories/**/*.rb")].each {|f| require f}
metadata ADDED
@@ -0,0 +1,113 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: has_meta_data
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease: false
6
+ segments:
7
+ - 1
8
+ - 0
9
+ - 0
10
+ version: 1.0.0
11
+ platform: ruby
12
+ authors:
13
+ - Ben Hughes
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-10-15 00:00:00 +08:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: activesupport
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 7
30
+ segments:
31
+ - 3
32
+ - 0
33
+ - 0
34
+ version: 3.0.0
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: activerecord
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ hash: 7
46
+ segments:
47
+ - 3
48
+ - 0
49
+ - 0
50
+ version: 3.0.0
51
+ type: :runtime
52
+ version_requirements: *id002
53
+ description: Create one model backed by a primary table and a supplementary 'meta' table.
54
+ email: ben@railsgarden.com
55
+ executables: []
56
+
57
+ extensions: []
58
+
59
+ extra_rdoc_files: []
60
+
61
+ files:
62
+ - lib/has_meta_data/railtie.rb
63
+ - lib/has_meta_data/version.rb
64
+ - lib/has_meta_data.rb
65
+ - spec/db/database.yml
66
+ - spec/db/schema.rb
67
+ - spec/db/test.sqlite3
68
+ - spec/factories/article.rb
69
+ - spec/has_meta_data_spec.rb
70
+ - spec/spec_helper.rb
71
+ - CHANGELOG.rdoc
72
+ - LICENSE
73
+ - Rakefile
74
+ - README.rdoc
75
+ - init.rb
76
+ has_rdoc: true
77
+ homepage: http://github.com/rubiety/has_meta_data
78
+ licenses: []
79
+
80
+ post_install_message:
81
+ rdoc_options: []
82
+
83
+ require_paths:
84
+ - lib
85
+ required_ruby_version: !ruby/object:Gem::Requirement
86
+ none: false
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ hash: 3
91
+ segments:
92
+ - 0
93
+ version: "0"
94
+ required_rubygems_version: !ruby/object:Gem::Requirement
95
+ none: false
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ hash: 19
100
+ segments:
101
+ - 1
102
+ - 3
103
+ - 4
104
+ version: 1.3.4
105
+ requirements: []
106
+
107
+ rubyforge_project: has_meta_data
108
+ rubygems_version: 1.3.7
109
+ signing_key:
110
+ specification_version: 3
111
+ summary: Extended attributes in an attached meta table.
112
+ test_files: []
113
+