active_record-acts_as 1.0.0.pre

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