active_record-acts_as 1.0.0.pre

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0e30b0cf3d5bebc22c5d3e9a63f81bced680e579
4
+ data.tar.gz: 11a3af36ca6331fe13cf84a962e6ba1728f9572a
5
+ SHA512:
6
+ metadata.gz: 64f7cc31a9033eb4954981f24b6ae5edd5ac8c73ca05de836d2a01d62b5f8c59c41983caa91cf0e76b365a35e7d32ed96f5e02567ad125ef2d23be046ee0f496
7
+ data.tar.gz: 97c3f506fa35cfb4fa391b16f5ca3636db662b1f6963b8fc74d4f9e4fe02f8b50585f31142efe1c6f99b9aa7f06aafc222b8ebedc55f2e0b28f4f085cb21d82e
data/.gitignore ADDED
@@ -0,0 +1,22 @@
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
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
data/.rspec ADDED
@@ -0,0 +1,4 @@
1
+ --color
2
+ --warnings
3
+ --format progress
4
+ --require spec_helper
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.1
4
+ - 2.0.0
5
+ - 1.9.3
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in active_record-acts_as.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Hassan Zamani
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,159 @@
1
+ [![Build Status](https://travis-ci.org/hzamani/active_record-acts_as.svg)](https://travis-ci.org/hzamani/active_record-acts_as)
2
+ [![Code Climate](https://codeclimate.com/github/hzamani/active_record-acts_as.png)](https://codeclimate.com/github/hzamani/active_record-acts_as)
3
+
4
+ # ActiveRecord::ActsAs
5
+
6
+ This is a refactor of [`acts_as_relation`](https://github.com/hzamani/acts_as_relation)
7
+
8
+ Simulates multiple-table-inheritance (MTI) for ActiveRecord models.
9
+ By default, ActiveRecord only supports single-table inheritance (STI).
10
+ MTI gives you the benefits of STI but without having to place dozens of empty fields into a single table.
11
+
12
+ Take a traditional e-commerce application for example:
13
+ A product has common attributes (`name`, `price`, `image` ...),
14
+ while each type of product has its own attributes:
15
+ for example a `pen` has `color`, a `book` has `author` and `publisher` and so on.
16
+ With multiple-table-inheritance you can have a `products` table with common columns and
17
+ a seprate table for each product type, i.e. a `pens` table with `color` column.
18
+
19
+ ## Requirements
20
+
21
+ `AciveRecord ~> 4`
22
+
23
+ ## Installation
24
+
25
+ Add this line to your application's Gemfile:
26
+
27
+ gem 'active_record-acts_as'
28
+
29
+ And then execute:
30
+
31
+ $ bundle
32
+
33
+ Or install it yourself as:
34
+
35
+ $ gem install active_record-acts_as
36
+
37
+ ## Usage
38
+
39
+ Back to example above, all you have to do is to mark `Product` as `actable` and all product type models as `acts_as :product`:
40
+
41
+ ```Ruby
42
+ class Product < ActiveRecord::Base
43
+ actable
44
+
45
+ validates_presence_of :name, :price
46
+
47
+ def display
48
+ "#{name} $#{price}"
49
+ end
50
+ end
51
+
52
+ class Pen < ActiveRecord::Base
53
+ acts_as :product
54
+ end
55
+
56
+ class Book < ActiveRecord::Base
57
+ acts_as :product
58
+ end
59
+
60
+ class Store < ActiveRecord::Base
61
+ has_many :products
62
+ end
63
+ ```
64
+
65
+ and add foreign key and type columns to products table as in a polymorphic relation.
66
+ You may prefere using a migration:
67
+
68
+ ```Ruby
69
+ change_table :products do |t|
70
+ t.integer :actable_id
71
+ t.string :actable_type
72
+ end
73
+ ```
74
+
75
+ or use shortcut `actable`
76
+
77
+ ```Ruby
78
+ change_table :products do |t|
79
+ t.actable
80
+ end
81
+ ```
82
+
83
+ Now `Pen` and `Book` *acts as* `Product`, i.e. they inherit `Product`s *attributes*,
84
+ *methods* and *validations*. Now you can do things like these:
85
+
86
+ ```Ruby
87
+ Pen.create name: 'Penie!', price: 0.8, color: 'red'
88
+ # => #<Pen id: 1, color: "red">
89
+ Pen.where price: 0.8
90
+ # => [#<Pen id: 1, color: "red">]
91
+ Product.where price: 0.8
92
+ # => [#<Product id: 1, name: "Penie!", price: 0.8, store_id: nil, actable_id: 1, actable_type: "Pen">]
93
+ pen = Pen.new
94
+ pen.valid?
95
+ # => false
96
+ pen.errors.full_messages
97
+ # => ["Name can't be blank", "Price can't be blank", "Color can't be blank"]
98
+ Pen.first.display
99
+ # => "Penie! $0.8"
100
+ ```
101
+
102
+ On the other hand you can always access a specific object from its parent by calling `specific` method on it:
103
+
104
+ ```Ruby
105
+ Product.first.specific
106
+ # => #<Pen ...>
107
+ ```
108
+
109
+ In +has_many+ case you can use subclasses:
110
+
111
+ ```Ruby
112
+ store = Store.create
113
+ store.products << Pen.create
114
+ store.products.first
115
+ # => #<Product: ...>
116
+ ```
117
+
118
+ You can give a name to all methods in `:as` option:
119
+
120
+ ```Ruby
121
+ class Product < ActiveRecord::Base
122
+ actable as: producible
123
+ end
124
+
125
+ class Pen < ActiveRecord::Base
126
+ acts_as :product, as: :producible
127
+ end
128
+
129
+ change_table :products do |t|
130
+ t.actable as: :producible
131
+ end
132
+ ```
133
+
134
+ `acts_as` support all `has_one` options, where defauls are there:
135
+ `as: :actable, dependent: :destroy, validate: false, autosave: true`
136
+
137
+ Make sure you know what you are doing when ovrewriting `validate` or `autodave` options.
138
+
139
+ You can pass scope to `acts_as` as in `has_one`:
140
+
141
+ ```Ruby
142
+ acts_as :person, -> { includes(:friends) }
143
+ ```
144
+
145
+ `actable` support all `belongs_to` options, where defaults are these:
146
+ `polymorphic: true, dependent: :delete, autosave: true`
147
+
148
+ Make sure you know what you are doing when ovrewriting `polymorphic` option.
149
+
150
+
151
+ ## Contributing
152
+
153
+ 1. Fork it ( https://github.com/hzamani/active_record-acts_as/fork )
154
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
155
+ 3. Test changes don't breake anything (`rspec`)
156
+ 4. Add specs for your new feature
157
+ 5. Commit your changes (`git commit -am 'Add some feature'`)
158
+ 6. Push to the branch (`git push origin my-new-feature`)
159
+ 7. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ task default: :spec
5
+
6
+ desc 'Run specs'
7
+ RSpec::Core::RakeTask.new
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'active_record/acts_as/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "active_record-acts_as"
8
+ spec.version = ActiveRecord::ActsAs::VERSION
9
+ spec.authors = ["Hassan Zamani"]
10
+ spec.email = ["hsn.zamani@gmail.com"]
11
+ spec.summary = %q{Simulate multi-table inheritance for activerecord models}
12
+ spec.description = %q{Simulate multi-table inheritance for activerecord models using a plymorphic association}
13
+ spec.homepage = "http://github.com/hzamani/active_record-acts_as"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.required_ruby_version = ">= 1.9"
22
+
23
+ spec.add_development_dependency "sqlite3", "~> 1.3"
24
+ spec.add_development_dependency "bundler", "~> 1.6"
25
+ spec.add_development_dependency "rspec", "~> 3"
26
+ spec.add_development_dependency "rake", "~> 10"
27
+
28
+ spec.add_dependency "activesupport", "~> 4"
29
+ spec.add_dependency "activerecord", "~> 4"
30
+ end
@@ -0,0 +1,56 @@
1
+
2
+ module ActiveRecord
3
+ module ActsAs
4
+ module InstanceMethods
5
+ def acting_as?(other = nil)
6
+ self.class.acting_as? other
7
+ end
8
+
9
+ def is_a?(klass)
10
+ super || acting_as?(klass)
11
+ end
12
+
13
+ def actable_must_be_valid
14
+ unless acting_as.valid?
15
+ acting_as.errors.each do |att, message|
16
+ errors.add(att, message)
17
+ end
18
+ end
19
+ end
20
+ protected :actable_must_be_valid
21
+
22
+ def read_attribute(attr_name, *args, &block)
23
+ if attribute_method?(attr_name)
24
+ super
25
+ else
26
+ acting_as.read_attribute(attr_name, *args, &block)
27
+ end
28
+ end
29
+
30
+ def write_attribute(attr_name, value, *args, &block)
31
+ if attribute_method?(attr_name)
32
+ super
33
+ else
34
+ acting_as.send(:write_attribute, attr_name, value, *args, &block)
35
+ end
36
+ end
37
+ private :write_attribute
38
+
39
+ def attributes
40
+ acting_as.attributes.except(acting_as_reflection.type, acting_as_reflection.foreign_key).merge(super)
41
+ end
42
+
43
+ def respond_to?(name, include_private = false)
44
+ super || acting_as.respond_to?(name)
45
+ end
46
+
47
+ def method_missing(method, *args, &block)
48
+ if acting_as.respond_to?(method)
49
+ acting_as.send(method, *args, &block)
50
+ else
51
+ super
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,24 @@
1
+
2
+ module ActiveRecord
3
+ module ActsAs
4
+ module Migration
5
+ module TableDefinition
6
+ def actable(options = {})
7
+ name = options.delete(:as) || :actable
8
+ options[:polymorphic] = true
9
+ references(name, options)
10
+ end
11
+ end
12
+
13
+ module Table
14
+ include TableDefinition
15
+
16
+ def remove_actable(options = {})
17
+ name = options.delete(:as) || :actable
18
+ options[:polymorphic] = true
19
+ @base.remove_reference(@table_name, name, options)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,23 @@
1
+
2
+ module ActiveRecord
3
+ module ActsAs
4
+ module Querying
5
+ def where(opts = :chain, *rest)
6
+ if opts.is_a? Hash
7
+ opts, acts_as_opts = opts.partition { |k,v| attribute_names.include?(k.to_s) }
8
+ opts, acts_as_opts = Hash[opts], Hash[acts_as_opts]
9
+ opts[acting_as_model.table_name] = acts_as_opts
10
+ end
11
+ super(opts, *rest)
12
+ end
13
+
14
+ def find_by(*args)
15
+ where(*args).take
16
+ end
17
+
18
+ def find_by!(*args)
19
+ where(*args).take!
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,47 @@
1
+
2
+ module ActiveRecord
3
+ module ActsAs
4
+ module Relation
5
+ extend ActiveSupport::Concern
6
+
7
+ module ClassMethods
8
+ def acts_as(name, scope = nil, options = {})
9
+ options, scope = scope, nil if Hash === scope
10
+ options = {as: :actable, dependent: :destroy, validate: false, autosave: true}.merge options
11
+
12
+ reflections = has_one name, scope, options
13
+ default_scope -> { eager_load(name) }
14
+ validate :actable_must_be_valid
15
+
16
+ cattr_reader(:acting_as_reflection) { reflections[name.to_sym] }
17
+ cattr_reader(:acting_as_name) { name.to_s }
18
+ cattr_reader(:acting_as_model) { name.to_s.camelize.constantize }
19
+ class_eval "def acting_as() #{name} || build_#{name} end"
20
+
21
+ include ActsAs::InstanceMethods
22
+ extend ActsAs::Querying
23
+ end
24
+
25
+ def acting_as?(other = nil)
26
+ if respond_to? :acting_as_name
27
+ other.nil? || acting_as_name == other.to_s.underscore
28
+ else
29
+ false
30
+ end
31
+ end
32
+
33
+ def is_a?(klass)
34
+ super || acting_as?(klass)
35
+ end
36
+
37
+ def actable(options = {})
38
+ name = options.delete(:as) || :actable
39
+
40
+ belongs_to name, {polymorphic: true, dependent: :delete, autosave: true}.merge(options)
41
+
42
+ alias_method :specific, name
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,6 @@
1
+
2
+ module ActiveRecord
3
+ module ActsAs
4
+ VERSION = "1.0.0.pre"
5
+ end
6
+ end
@@ -0,0 +1,24 @@
1
+ require 'active_record/acts_as/version'
2
+ require 'active_record/acts_as/relation'
3
+ require 'active_record/acts_as/migration'
4
+ require 'active_record/acts_as/instance_methods'
5
+ require 'active_record/acts_as/querying'
6
+
7
+ module ActiveRecord
8
+ class Base
9
+ include ActsAs::Relation
10
+ end
11
+
12
+ module ConnectionAdapters
13
+ class TableDefinition
14
+ include ActsAs::Migration::TableDefinition
15
+ end
16
+ end
17
+
18
+ module ConnectionAdapters
19
+ class Table
20
+ include ActsAs::Migration::Table
21
+ end
22
+ end
23
+ end
24
+
@@ -0,0 +1,37 @@
1
+ require 'models'
2
+
3
+ RSpec.describe "ActiveRecord::Base subclass with #actable" do
4
+ subject { Product }
5
+
6
+ let(:pen_attributes) { {name: 'pen', price: 0.8, color: 'red'} }
7
+ let(:pen) { Pen.new pen_attributes }
8
+
9
+ it "has a polymorphic belongs_to :actable relation" do
10
+ association = subject.reflect_on_all_associations.find { |r| r.name == :actable }
11
+ expect(association).to_not be_nil
12
+ expect(association.macro).to eq(:belongs_to)
13
+ expect(association).to be_polymorphic
14
+ end
15
+
16
+ describe ".specific" do
17
+ it "return specific submodel" do
18
+ pen.save
19
+ expect(Product.find(pen.acting_as.id).specific).to eq(pen)
20
+ end
21
+ end
22
+
23
+ it "deletes specific subclass on destroy" do
24
+ pen.save
25
+ pen.product.destroy
26
+ expect { pen.reload }.to raise_error(ActiveRecord::RecordNotFound)
27
+ end
28
+
29
+ it "saves submodel on save" do
30
+ pen.save
31
+ product = pen.acting_as
32
+ product.specific.color = 'blue'
33
+ product.save
34
+ pen.reload
35
+ expect(pen.color).to eq('blue')
36
+ end
37
+ end
@@ -0,0 +1,21 @@
1
+ require 'active_record'
2
+ require 'active_record/acts_as'
3
+
4
+ class Model < ActiveRecord::Base
5
+ end
6
+
7
+ RSpec.describe ActiveRecord, "Model" do
8
+ subject { Model }
9
+
10
+ it { is_expected.to respond_to(:acts_as) }
11
+ it { is_expected.to respond_to(:actable) }
12
+ it { is_expected.to respond_to(:acting_as?) }
13
+
14
+ describe "#acting_as?" do
15
+ it "returns false with any arg" do
16
+ expect(subject.acting_as?).to be false
17
+ expect(subject.acting_as?(String)).to be false
18
+ expect(subject.acting_as?(:product)).to be false
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,162 @@
1
+ require 'models'
2
+
3
+ RSpec.describe "ActiveRecord::Base model with #acts_as called" do
4
+ subject { Pen }
5
+
6
+ let(:pen_attributes) { {name: 'pen', price: 0.8, color: 'red'} }
7
+ let(:pen) { Pen.new pen_attributes }
8
+ let(:store) { Store.new name: 'biggerman' }
9
+
10
+ it "has a has_one relation" do
11
+ association = subject.reflect_on_all_associations.find { |r| r.name == :product }
12
+ expect(association).to_not be_nil
13
+ expect(association.macro).to eq(:has_one)
14
+ expect(association.options).to have_key(:as)
15
+ end
16
+
17
+ describe "#acting_as?" do
18
+ it "returns true for supermodel class and name" do
19
+ expect(Pen.acting_as? :product).to be true
20
+ expect(Pen.acting_as? Product).to be true
21
+ end
22
+
23
+ it "returns false for anything other than supermodel" do
24
+ expect(Pen.acting_as? :model).to be false
25
+ expect(Pen.acting_as? String).to be false
26
+ end
27
+ end
28
+
29
+ describe ".acting_as?" do
30
+ it "returns true for supermodel class and name" do
31
+ expect(pen.acting_as? :product).to be true
32
+ expect(pen.acting_as? Product).to be true
33
+ end
34
+
35
+ it "returns false for anything other than supermodel" do
36
+ expect(pen.acting_as? :model).to be false
37
+ expect(pen.acting_as? String).to be false
38
+ end
39
+ end
40
+
41
+ describe "#is_a?" do
42
+ it "responds true when supermodel passed to" do
43
+ expect(Pen.is_a? Product).to be true
44
+ expect(Pen.is_a? Object).to be true
45
+ expect(Pen.is_a? String).to be false
46
+ end
47
+ end
48
+
49
+ describe ".is_a?" do
50
+ it "responds true when supermodel passed to" do
51
+ expect(pen.is_a? Product).to be true
52
+ expect(pen.is_a? Object).to be true
53
+ expect(pen.is_a? String).to be false
54
+ end
55
+ end
56
+
57
+ describe "#acting_as_name" do
58
+ it "return acts_as model name" do
59
+ expect(pen.acting_as_name).to eq('product')
60
+ end
61
+ end
62
+
63
+ describe "#acting_as" do
64
+ it "returns autobuilded acts_as model" do
65
+ expect(pen.acting_as).to_not be_nil
66
+ expect(pen.acting_as).to be_instance_of(Product)
67
+ end
68
+ end
69
+
70
+ it "have supermodel attributes accessible on creation" do
71
+ expect{Pen.create(pen_attributes)}.to_not raise_error
72
+ end
73
+
74
+ context "instance" do
75
+ it "responds to supermodel methods" do
76
+ %w(name name= name? name_change name_changed? name_was name_will_change! price color).each do |name|
77
+ expect(pen).to respond_to(name)
78
+ end
79
+ expect(pen.present).to eq("pen - $0.8")
80
+ end
81
+
82
+ it "saves supermodel attributes on save" do
83
+ pen.save
84
+ pen.reload
85
+ expect(pen.name).to eq('pen')
86
+ expect(pen.price).to eq(0.8)
87
+ expect(pen.color).to eq('red')
88
+ end
89
+
90
+ it "raises NoMethodEror on unexisting method call" do
91
+ expect { pen.unexisted_method }.to raise_error(NoMethodError)
92
+ end
93
+
94
+ it "destroies Supermodel on destroy" do
95
+ pen.save
96
+ product_id = pen.product.id
97
+ pen.destroy
98
+ expect { Product.find(product_id) }.to raise_error(ActiveRecord::RecordNotFound)
99
+ end
100
+
101
+ it "validates supermodel attribures upon validation" do
102
+ p = Pen.new
103
+ expect(p).to be_invalid
104
+ expect(p.errors.keys).to include(:name, :price, :color)
105
+ p.name = 'testing'
106
+ expect(p).to be_invalid
107
+ p.color = 'red'
108
+ expect(p).to be_invalid
109
+ p.price = 0.8
110
+ expect(p).to be_valid
111
+ end
112
+
113
+ it "can set assosications defined in supermodel" do
114
+ store.save
115
+ pen.store = store
116
+ pen.save
117
+ pen.reload
118
+ expect(pen.store).to eq(store)
119
+ expect(pen.product.store).to eq(store)
120
+ end
121
+
122
+ it "should be appendable in an has_many relation using << operator" do
123
+ store.save
124
+ store.products << pen
125
+ expect(pen.store).to eq(store)
126
+ end
127
+
128
+ it "includes supermodel attributes in .to_json responce" do
129
+ expect(pen.to_json).to eq('{"id":null,"name":"pen","price":0.8,"store_id":null,"color":"red"}')
130
+ end
131
+ end
132
+
133
+ context "Querying" do
134
+ before(:each) { clear_database }
135
+
136
+ it ".where works with supermodel attributes" do
137
+ red_pen = Pen.create(name: 'red pen', price: 0.8, color: 'red')
138
+ blue_pen = Pen.create(name: 'blue pen', price: 0.8, color: 'blue')
139
+ black_pen = Pen.create(name: 'black pen', price: 0.9, color: 'black')
140
+
141
+ expect{Pen.where(price: 0.8)}.to_not raise_error
142
+ expect(Pen.where(price: 0.8)).to include(red_pen, blue_pen)
143
+ expect(Pen.where(price: 0.8)).to_not include(black_pen)
144
+ end
145
+
146
+ it "find_by works with supermodel attributes" do
147
+ red_pen = Pen.create(name: 'red pen', price: 0.8, color: 'red')
148
+ blue_pen = Pen.create(name: 'blue pen', price: 0.8, color: 'blue')
149
+ black_pen = Pen.create(name: 'black pen', price: 0.9, color: 'black')
150
+
151
+ expect(Pen.find_by(name: 'red pen')).to eq(red_pen)
152
+ expect(Pen.find_by(name: 'blue pen')).to eq(blue_pen)
153
+ expect(Pen.find_by(name: 'black pen')).to eq(black_pen)
154
+ end
155
+
156
+ it "includes supermodel attributes in Relation.scope_for_create" do
157
+ relation = Pen.where(name: 'new name')
158
+ expect(relation.scope_for_create.keys).to include(:name)
159
+ expect(relation.scope_for_create[:name]).to eq('new name')
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,26 @@
1
+ require 'active_record'
2
+
3
+ def connect_to_databse
4
+ ActiveRecord::Base.establish_connection adapter: 'sqlite3', database: ':memory:'
5
+ end
6
+
7
+ def clear_database
8
+ ActiveRecord::Base.descendants.each do |model|
9
+ model.delete_all if model.table_exists?
10
+ end
11
+ end
12
+
13
+ def reset_database
14
+ ActiveRecord::Base.descendants.map(&:reset_column_information)
15
+ ActiveRecord::Base.connection.disconnect!
16
+ connect_to_databse
17
+ end
18
+
19
+ def initialize_database(&block)
20
+ reset_database
21
+ ActiveRecord::Schema.define(&block)
22
+ end
23
+
24
+ I18n.enforce_available_locales = false
25
+ ActiveRecord::Migration.verbose = false
26
+ connect_to_databse
@@ -0,0 +1,25 @@
1
+ require 'database_helper'
2
+ require 'active_record/acts_as'
3
+
4
+
5
+ class Product < ActiveRecord::Base
6
+ end
7
+
8
+
9
+ RSpec.describe ".actable" do
10
+ context "in .create_table block" do
11
+ context "with :as options" do
12
+ it "creates plymorphic reference columns with given name" do
13
+ initialize_database { create_table(:products) { |t| t.actable(as: :produceable) } }
14
+ expect(Product.column_names).to include('produceable_id', 'produceable_type')
15
+ end
16
+ end
17
+
18
+ context "with no args" do
19
+ it "creates plymorphic reference columns" do
20
+ initialize_database { create_table(:products) { |t| t.actable } }
21
+ expect(Product.column_names).to include('actable_id', 'actable_type')
22
+ end
23
+ end
24
+ end
25
+ end
data/spec/models.rb ADDED
@@ -0,0 +1,39 @@
1
+ require 'database_helper'
2
+ require 'active_record/acts_as'
3
+
4
+ class Product < ActiveRecord::Base
5
+ actable
6
+ belongs_to :store
7
+ validates_presence_of :name, :price
8
+
9
+ def present
10
+ "#{name} - $#{price}"
11
+ end
12
+ end
13
+
14
+ class Pen < ActiveRecord::Base
15
+ acts_as :product
16
+
17
+ validates_presence_of :color
18
+ end
19
+
20
+ class Store < ActiveRecord::Base
21
+ has_many :products
22
+ end
23
+
24
+ initialize_database do
25
+ create_table :pens do |t|
26
+ t.string :color
27
+ end
28
+
29
+ create_table :products do |t|
30
+ t.string :name
31
+ t.float :price
32
+ t.integer :store_id
33
+ t.actable
34
+ end
35
+
36
+ create_table :stores do |t|
37
+ t.string :name
38
+ end
39
+ end
@@ -0,0 +1,4 @@
1
+
2
+ RSpec.configure do |config|
3
+ config.disable_monkey_patching!
4
+ end
metadata ADDED
@@ -0,0 +1,157 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: active_record-acts_as
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0.pre
5
+ platform: ruby
6
+ authors:
7
+ - Hassan Zamani
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-07-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: sqlite3
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.6'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.6'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10'
69
+ - !ruby/object:Gem::Dependency
70
+ name: activesupport
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '4'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '4'
83
+ - !ruby/object:Gem::Dependency
84
+ name: activerecord
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '4'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '4'
97
+ description: Simulate multi-table inheritance for activerecord models using a plymorphic
98
+ association
99
+ email:
100
+ - hsn.zamani@gmail.com
101
+ executables: []
102
+ extensions: []
103
+ extra_rdoc_files: []
104
+ files:
105
+ - ".gitignore"
106
+ - ".rspec"
107
+ - ".travis.yml"
108
+ - Gemfile
109
+ - LICENSE.txt
110
+ - README.md
111
+ - Rakefile
112
+ - active_record-acts_as.gemspec
113
+ - lib/active_record/acts_as.rb
114
+ - lib/active_record/acts_as/instance_methods.rb
115
+ - lib/active_record/acts_as/migration.rb
116
+ - lib/active_record/acts_as/querying.rb
117
+ - lib/active_record/acts_as/relation.rb
118
+ - lib/active_record/acts_as/version.rb
119
+ - spec/actable_spec.rb
120
+ - spec/active_record_spec.rb
121
+ - spec/acts_as_spec.rb
122
+ - spec/database_helper.rb
123
+ - spec/migrations_spec.rb
124
+ - spec/models.rb
125
+ - spec/spec_helper.rb
126
+ homepage: http://github.com/hzamani/active_record-acts_as
127
+ licenses:
128
+ - MIT
129
+ metadata: {}
130
+ post_install_message:
131
+ rdoc_options: []
132
+ require_paths:
133
+ - lib
134
+ required_ruby_version: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '1.9'
139
+ required_rubygems_version: !ruby/object:Gem::Requirement
140
+ requirements:
141
+ - - ">"
142
+ - !ruby/object:Gem::Version
143
+ version: 1.3.1
144
+ requirements: []
145
+ rubyforge_project:
146
+ rubygems_version: 2.2.2
147
+ signing_key:
148
+ specification_version: 4
149
+ summary: Simulate multi-table inheritance for activerecord models
150
+ test_files:
151
+ - spec/actable_spec.rb
152
+ - spec/active_record_spec.rb
153
+ - spec/acts_as_spec.rb
154
+ - spec/database_helper.rb
155
+ - spec/migrations_spec.rb
156
+ - spec/models.rb
157
+ - spec/spec_helper.rb