attr_inherited 1.0.0

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.
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ test/debug.log
18
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in attr_inherited.gemspec
4
+ gemspec
@@ -0,0 +1,23 @@
1
+ Copyright (c) Christos Zisopoulos ( [@christos](http://twitter.com/christos) )
2
+ ========================================================================================
3
+
4
+ The "attr_inherited_" plugin is released under the **MIT LICENSE**
5
+ --------------------------------------------------------------------
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
@@ -0,0 +1,145 @@
1
+ ## Description
2
+
3
+ Attribute inheritance for `ActiveRecord` models
4
+
5
+ Using `attr_inherited` allows your `ActiveRecord` models to inherit any of their attributes from an associated, or parent model.
6
+
7
+
8
+
9
+ ## Usage
10
+
11
+ [![Build Status](https://secure.travis-ci.org/christos/attr_inherited.png)](http://travis-ci.org/christos/attr_inherited)
12
+
13
+ ### Inheriting attributes from an associated model
14
+
15
+ Given a `Post` model and the following `Version` model
16
+
17
+ class Version < ActiveRecord::Base
18
+ belongs_to :post
19
+
20
+ attr_inherited :title, :synopsis, from: :post
21
+ end
22
+
23
+ ..then `Version` will now inherit `title` and `synopsis` from the parent post, if either attributes are `nil`
24
+
25
+ 1.9.3p194 :001 > post = Post.create!(title: "First post", synopsis: "First post synopsis")
26
+ 1.9.3p194 :002 > version = Version.create!(post: post)
27
+
28
+ 1.9.3p194 :003 > version.title
29
+ => "First post"
30
+
31
+ 1.9.3p194 :004 > version.body
32
+ => "First post body"
33
+
34
+ You can override any of the inherited attributes by setting its a value to anything other than `nil`
35
+
36
+ 1.9.3p194 :005 > version.update_attributes(description: "Version synopsis")
37
+
38
+ 1.9.3p194 :006 > version.synopsis
39
+ => "Version description"
40
+
41
+ ### Renaming inherited attributes
42
+
43
+ If you want the inherited attribute to have a name other than that of the associated model's attribute, you can use the `as:` option to rename it. For example:
44
+
45
+ class Version < ActiveRecord::Base
46
+ belongs_to :post
47
+
48
+ attr_inherited :synopsis, from: :post, as: :description
49
+ end
50
+
51
+ 1.9.3p194 :001 > post = Post.create!(synopsis: "First post synopsis")
52
+ 1.9.3p194 :002 > version = Version.create!(post: post)
53
+
54
+ 1.9.3p194 :003 > version.description
55
+ => "First post synopsis"
56
+
57
+ Note that `description` must be an attribute of `Version` for the above to work.
58
+
59
+ ### Inheriting attributes even when they are not `nil`
60
+
61
+ You might want to inherit an attribute not only when it is `nil` but even when it is an empty string `""`. You can do this by passing a predicate with the `when:` option that would test if the attribute should be inherited.
62
+
63
+ class Version < ActiveRecord::Base
64
+ belongs_to :post
65
+
66
+ attr_inherited :title, from: :post, when: :blank?
67
+ end
68
+
69
+ 1.9.3p194 :001 > post = Post.create!(title: "First post")
70
+ 1.9.3p194 :002 > version = Version.create!(post: post)
71
+
72
+ 1.9.3p194 :003 > version.title
73
+ => "First post"
74
+
75
+ 1.9.3p194 :004 > version.update_attributes(title: " ")
76
+ 1.9.3p194 :005 > version.title
77
+ => "First post"
78
+
79
+ ### Using the `when:` option to fake has_many association inheritance
80
+
81
+ Until real association inheritance is implemented you can fake it like this:
82
+
83
+ class Version < ActiveRecord::Base
84
+ has_many :comments, as: :commentable
85
+ end
86
+
87
+ class Version < ActiveRecord::Base
88
+ belongs_to :post
89
+ has_many :comments, as: :commentable
90
+
91
+ attr_inherited :comments, from: :post, when: :empty?
92
+ end
93
+
94
+ ...which makes `comments` behave almost as if it was inherited from `Post`:
95
+
96
+ 1.9.3p194 :001 > post = Post.create!(title: "First post")
97
+ 1.9.3p194 :002 > post.comments.create(text: "Post comment")
98
+
99
+ 1.9.3p194 :003 > version = Version.create!(post: post)
100
+ 1.9.3p194 :004 > version.comments.first.text
101
+ => "Post comment"
102
+
103
+ 1.9.3p194 :005 > version.comments = [Comment.create(text: "Version comment")]
104
+ 1.9.3p194 :006 > version.comments.first.text
105
+ => "Version comment"
106
+
107
+ **Caveat**: You can't call any other association methods on `version.comments` when it is inherited, such as `<<`, `.create`, `.build`
108
+
109
+ ## Installation
110
+
111
+ *Requires Rails 3.1+*
112
+
113
+ Add this line to your application's Gemfile:
114
+
115
+ gem 'attr_inherited'
116
+
117
+ And then execute:
118
+
119
+ $ bundle
120
+
121
+ Or install it yourself as:
122
+
123
+ $ gem install attr_inherited
124
+
125
+ ## Changelog
126
+
127
+ **1.0.0** Initial relase (2012-10-02)
128
+
129
+ ## To Do
130
+
131
+ * Test assertion of valid parameters
132
+ * Write rdocs
133
+ * Document or remove default `parent` option for `from:` option
134
+ * Properly support inheriting associations
135
+ * Investigate support for virtual attributes (:attr_accessor)
136
+
137
+ ## Contributing
138
+
139
+ 1. Fork it
140
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
141
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
142
+ 4. Push to the branch (`git push origin my-new-feature`)
143
+ 5. Create new Pull Request
144
+
145
+ Copyright (c) Christos Zisopoulos, released under the MIT license
@@ -0,0 +1,15 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require "bundler/gem_tasks"
4
+
5
+ desc 'Default: run unit tests.'
6
+ task :default => :test
7
+
8
+ desc 'Test the attr_inherited gem.'
9
+ Rake::TestTask.new(:test) do |t|
10
+ t.libs << 'lib'
11
+ t.libs << 'test'
12
+ t.pattern = 'test/**/*_test.rb'
13
+ t.verbose = true
14
+ end
15
+
@@ -0,0 +1,29 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'attr_inherited/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "attr_inherited"
8
+ gem.version = AttrInherited::VERSION
9
+
10
+ gem.authors = ["Christos Zisopoulos"]
11
+ gem.email = ["christos@mac.com"]
12
+ gem.description = "Attribute inheritance for ActiveRecord models"
13
+ gem.summary = gem.description
14
+ gem.homepage = "https://github.com/christos/attr_inherited"
15
+
16
+ gem.rubyforge_project = "attr_inherited"
17
+
18
+ gem.files = `git ls-files`.split($/)
19
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
20
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
21
+ gem.require_paths = ["lib"]
22
+
23
+ gem.add_development_dependency "rake"
24
+ gem.add_development_dependency "sqlite3"
25
+
26
+ gem.add_dependency "activesupport"
27
+ gem.add_dependency "activerecord"
28
+
29
+ end
@@ -0,0 +1,51 @@
1
+ require "attr_inherited/version"
2
+ require 'active_record'
3
+
4
+ module AttrInherited
5
+ def self.included(base) #:nodoc:
6
+ base.extend(AttrInheritedMacro)
7
+ end
8
+
9
+ module AttrInheritedMacro
10
+
11
+ def attr_inherited(*attrs)
12
+ if attrs.last.is_a? Hash
13
+ options = attrs.pop
14
+ end
15
+
16
+ _parent = options[:from] || 'parent'
17
+ _predicate = options[:when] && options[:when].to_s || 'nil?'
18
+
19
+ raise ArgumentError.new("Can't specify :as for multiple attributes in a single line") if attrs.size > 1 && options[:as].present?
20
+
21
+ attrs.each do |attr|
22
+ _alias = options[:as] || attr
23
+
24
+ class_eval <<-END, __FILE__, __LINE__
25
+
26
+ def #{_alias}_before_type_cast
27
+ _super = super
28
+ !_super.#{_predicate} && super || #{_parent} && #{_parent}.#{attr}_before_type_cast
29
+ end
30
+
31
+ def #{_alias}
32
+ _super = super
33
+ !_super.#{_predicate} && _super || #{_parent} && #{_parent}.#{attr} && !#{_parent}.#{attr}.#{_predicate} && #{_parent}.#{attr} || _super
34
+ end
35
+
36
+ def #{_alias}=(value)
37
+ super(value) if #{_parent}.nil? || #{_parent}.#{attr}.#{_predicate} || (#{_parent} && #{_parent}.#{attr} && #{_parent}.#{attr} != value)
38
+ end
39
+
40
+ def #{_alias}_inherited?
41
+ #{_parent}.#{attr} == self.#{attr}
42
+ end
43
+
44
+ END
45
+ end
46
+ end
47
+ end
48
+
49
+ end
50
+
51
+ ActiveRecord::Base.send(:include, AttrInherited)
@@ -0,0 +1,3 @@
1
+ module AttrInherited
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,84 @@
1
+ require 'test_helper'
2
+
3
+ class AttributeChoicesTest < ActiveSupport::TestCase
4
+
5
+ class Post < ActiveRecord::Base
6
+ has_many :comments, as: :commentable
7
+ end
8
+
9
+ class Version < ActiveRecord::Base
10
+ belongs_to :post
11
+ has_many :comments, as: :commentable
12
+
13
+ attr_inherited :title, :body, from: :post
14
+ attr_inherited :synopsis, from: :post, as: :description
15
+
16
+ attr_inherited :comments, from: :post, when: :empty?
17
+ end
18
+
19
+ class Comment < ActiveRecord::Base
20
+ belongs_to :commentable, polymorphic: true
21
+ end
22
+
23
+ test "It should add a class method to all ActiveRecord objects" do
24
+ assert_respond_to ActiveRecord::Base, :attr_inherited
25
+ assert_respond_to Post, :attr_inherited
26
+ assert_respond_to Version, :attr_inherited
27
+ assert_respond_to Comment, :attr_inherited
28
+ end
29
+
30
+ test "It does not inherit attributes if its parent object is nil" do
31
+ version = Version.create! author: "Christos Zisopoulos", title: "First post"
32
+
33
+ assert_equal version.title, "First post"
34
+ assert_equal version.body, nil
35
+ assert_equal version.author, "Christos Zisopoulos"
36
+ end
37
+
38
+ test "It inherits attributes from the specified parent association when attribute is nil" do
39
+ post = Post.create! title: "First post", body: "First post body"
40
+ version = Version.create! author: "Christos Zisopoulos", post: post
41
+
42
+ assert_equal version.title, "First post"
43
+ assert_equal version.body, "First post body"
44
+ assert_equal version.author, "Christos Zisopoulos"
45
+ end
46
+
47
+ test "Inherited attribute's name can be aliased" do
48
+ post = Post.create! synopsis: "First post synopsis"
49
+ version = Version.create! post_id: post.id
50
+
51
+ assert_equal version.description, "First post synopsis"
52
+ end
53
+
54
+ test "Overrding the values of inherited attributes" do
55
+ post = Post.create! title: "First post", body: "First post body"
56
+ version = Version.create! do |v|
57
+ v.post_id = post.id
58
+ v.title = "Version title"
59
+ v.body = "Version body"
60
+ v.description = "Version description"
61
+ v.author = "Christos Zisopoulos"
62
+ end
63
+
64
+ assert_equal version.title, "Version title"
65
+ assert_equal version.body, "Version body"
66
+ assert_equal version.description, "Version description"
67
+ assert_equal version.author, "Christos Zisopoulos"
68
+ end
69
+
70
+ test "Using a user defined predicate to decide when to inherit" do
71
+ post = Post.create! title: "First post", body: "First post body"
72
+ post.comments.create! body: "Comment 1"
73
+ version = Version.create! post_id: post.id
74
+
75
+ assert_equal version.comments, post.comments
76
+
77
+ version_comment = Comment.create! body: "Comment 3"
78
+ version.comments = [version_comment]
79
+
80
+ assert_equal version.comments, [version_comment]
81
+ end
82
+
83
+ end
84
+
@@ -0,0 +1,3 @@
1
+ sqlite3mem:
2
+ :adapter: sqlite3
3
+ :database: ":memory:"
@@ -0,0 +1,45 @@
1
+ # This file is auto-generated from the current state of the database. Instead of editing this file,
2
+ # please use the migrations feature of Active Record to incrementally modify your database, and
3
+ # then regenerate this schema definition.
4
+ #
5
+ # Note that this schema.rb definition is the authoritative source for your database schema. If you need
6
+ # to create the application database on another system, you should be using db:schema:load, not running
7
+ # all the migrations from scratch. The latter is a flawed and unsustainable approach (the more migrations
8
+ # you'll amass, the slower it'll run and the greater likelihood for issues).
9
+ #
10
+ # It's strongly recommended to check this file into your version control system.
11
+
12
+ ActiveRecord::Schema.define(:version => 20090418155608) do
13
+
14
+ create_table "posts", :force => true do |t|
15
+ t.string "title"
16
+ t.text "synopsis"
17
+ t.text "body"
18
+ t.datetime "created_at"
19
+ t.datetime "updated_at"
20
+ end
21
+
22
+ create_table "versions", :force => true do |t|
23
+ t.integer "post_id"
24
+
25
+ t.string "title"
26
+ t.text "description"
27
+ t.text "body"
28
+ t.string "author"
29
+
30
+ t.datetime "created_at"
31
+ t.datetime "updated_at"
32
+ end
33
+
34
+ create_table "comments", :force => true do |t|
35
+ t.integer "commentable_id"
36
+ t.string "commentable_type"
37
+
38
+ t.text "body"
39
+ t.string "author"
40
+
41
+ t.datetime "created_at"
42
+ t.datetime "updated_at"
43
+ end
44
+
45
+ end
@@ -0,0 +1,19 @@
1
+ ENV["RAILS_ENV"] = "test"
2
+
3
+ require 'rubygems'
4
+
5
+ require 'attr_inherited'
6
+
7
+ require 'test/unit'
8
+
9
+ require 'active_support/test_case'
10
+ require 'active_record/fixtures'
11
+
12
+
13
+ config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
14
+ ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/debug.log")
15
+ ActiveRecord::Base.establish_connection(config[ENV['DB'] || 'sqlite3mem'])
16
+
17
+ ActiveRecord::Migration.verbose = false
18
+ load(File.dirname(__FILE__) + "/schema.rb")
19
+
metadata ADDED
@@ -0,0 +1,125 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: attr_inherited
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Christos Zisopoulos
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-10-01 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: sqlite3
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: activesupport
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: activerecord
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ description: Attribute inheritance for ActiveRecord models
79
+ email:
80
+ - christos@mac.com
81
+ executables: []
82
+ extensions: []
83
+ extra_rdoc_files: []
84
+ files:
85
+ - .gitignore
86
+ - Gemfile
87
+ - LICENSE.txt
88
+ - README.md
89
+ - Rakefile
90
+ - attr_inherited.gemspec
91
+ - lib/attr_inherited.rb
92
+ - lib/attr_inherited/version.rb
93
+ - test/attr_inherited_test.rb
94
+ - test/database.yml
95
+ - test/schema.rb
96
+ - test/test_helper.rb
97
+ homepage: https://github.com/christos/attr_inherited
98
+ licenses: []
99
+ post_install_message:
100
+ rdoc_options: []
101
+ require_paths:
102
+ - lib
103
+ required_ruby_version: !ruby/object:Gem::Requirement
104
+ none: false
105
+ requirements:
106
+ - - ! '>='
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ required_rubygems_version: !ruby/object:Gem::Requirement
110
+ none: false
111
+ requirements:
112
+ - - ! '>='
113
+ - !ruby/object:Gem::Version
114
+ version: '0'
115
+ requirements: []
116
+ rubyforge_project: attr_inherited
117
+ rubygems_version: 1.8.24
118
+ signing_key:
119
+ specification_version: 3
120
+ summary: Attribute inheritance for ActiveRecord models
121
+ test_files:
122
+ - test/attr_inherited_test.rb
123
+ - test/database.yml
124
+ - test/schema.rb
125
+ - test/test_helper.rb