piggyback 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +3 -0
- data/MIT-LICENSE +21 -0
- data/Manifest +5 -1
- data/README.markdown +110 -0
- data/Rakefile +5 -4
- data/lib/piggyback.rb +76 -62
- data/piggyback.gemspec +16 -7
- data/spec/models.rb +44 -0
- data/spec/piggyback_spec.rb +68 -25
- data/spec/spec_helper.rb +4 -37
- metadata +46 -10
- data/README.rdoc +0 -0
data/CHANGELOG
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
|
2
|
+
Copyright (c) 2011 Andreas Korth
|
3
|
+
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
a copy of this software and associated documentation files (the
|
6
|
+
"Software"), to deal in the Software without restriction, including
|
7
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be
|
13
|
+
included in all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
18
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
19
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
20
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
21
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Manifest
CHANGED
data/README.markdown
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
# Piggyback
|
2
|
+
|
3
|
+
An extension for piggybacking of ActiveRecord™ models.
|
4
|
+
|
5
|
+
### What is Piggybacking?
|
6
|
+
|
7
|
+
Piggybacking refers to the technique of dynamically including attributes from an associated object into the master object. This is achieved by joining the associated object's table in a database query and selecting the attributes that should be included with the parent object.
|
8
|
+
|
9
|
+
This is best illustrated in an example. Consider these models:
|
10
|
+
|
11
|
+
class Author < ActiveRecord::Base
|
12
|
+
has_many :articles
|
13
|
+
end
|
14
|
+
|
15
|
+
class Post < ActiveRecord::Base
|
16
|
+
belongs_to :author
|
17
|
+
end
|
18
|
+
|
19
|
+
ActiveRecord supports piggybacking simply by joining the associated table and selecting columns from it:
|
20
|
+
|
21
|
+
>> post = Post.joins(:author).select('posts.*, author.name AS author_name').first
|
22
|
+
|
23
|
+
>> post.title
|
24
|
+
=> "Why piggybacking in ActiveRecord is flawed"
|
25
|
+
|
26
|
+
>> post.author_name
|
27
|
+
=> "Alec Smart"
|
28
|
+
|
29
|
+
As you can see, the `name` attribute from `Author` is treated as if it were an attribute of `User`. ActiveRecord dynamically determines a model's attributes from the result set returned by the database. Every column in the result set becomes an attribute of the instantiated ActiveRecord objects. Whether the columns originate from the model's own or from a foreign table doesn't make a difference.
|
30
|
+
|
31
|
+
Or so it seems. Actually there is a drawback which becomes obvious when we select non-string columns:
|
32
|
+
|
33
|
+
>> post = Post.joins(:author).select('posts.*, author.birthday AS author_birthday, author.rating AS author_rating').first
|
34
|
+
|
35
|
+
>> post.author_birthday
|
36
|
+
=> "2011-03-01"
|
37
|
+
|
38
|
+
>> post.author_rating
|
39
|
+
=> "4.5"
|
40
|
+
|
41
|
+
Any attributes originating from foreign tables are not automatically type-casted as we would expect. This is because the database always returns results in text format. The model only knows about the data types of the columns in its own table. Since this information is used when ActiveRecord does its type-casting magic, it doesn't work with foreign columns.
|
42
|
+
|
43
|
+
We could work around this by defining attribute reader methods in the `Post` model that implicitly converts the values:
|
44
|
+
|
45
|
+
class Post < ActiveRecord::Base
|
46
|
+
...
|
47
|
+
|
48
|
+
def author_birthday
|
49
|
+
Date.parse(read_attribute('author_birthday'))
|
50
|
+
end
|
51
|
+
|
52
|
+
def author_rating
|
53
|
+
read_attribute('author_rating').to_f
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
However this is tedious, error-prone and repetitive if you do it in many models. The type-casting code shown above isn't solid and would be more complex in a real-life situation in order to deal with `nil` values, for example.
|
58
|
+
|
59
|
+
|
60
|
+
### Piggyback to the rescue!
|
61
|
+
|
62
|
+
Piggyback introduces the `piggybacks` directive which allows us to easily define which attributes we want to piggyback from associated models. Not only does it take care of the type-casting but also provides us with some additional benefits.
|
63
|
+
|
64
|
+
You simply declare which association you want to piggyback and how the attribute names should be mapped:
|
65
|
+
|
66
|
+
class Post < ActiveRecord::Base
|
67
|
+
belongs_to :author
|
68
|
+
|
69
|
+
piggybacks :author, :birthday => :author_birthday, :rating => :author_rating
|
70
|
+
end
|
71
|
+
|
72
|
+
Now you can do the following:
|
73
|
+
|
74
|
+
>> post = Post.piggyback(:author).first
|
75
|
+
|
76
|
+
>> post.author_birthday
|
77
|
+
=> Tue, 01 Mar 2011
|
78
|
+
|
79
|
+
>> post.author_rating
|
80
|
+
=> 4.5
|
81
|
+
|
82
|
+
The type-casting works with any type of attribute, even with serialized ones.
|
83
|
+
|
84
|
+
As you can see, the `piggibacks` statement replaces the `joins` and `select` parts of the query. Using it is optional but makes life easier since you don't have to write the SQL for `select` by hand.
|
85
|
+
|
86
|
+
Of course, `piggibacks` plays nice with Arel and you can add additional `joins`, `select` and other statements as you like, for example:
|
87
|
+
|
88
|
+
Post.select('posts.title, posts.body').piggibacks(:author).joins(:comments).where(:published => true)
|
89
|
+
|
90
|
+
__Please note:__ If you want to restrict the columns selected from the master table as in the example above, you have to do so _before_ the `piggibacks` statement. Otherwise it will insert the select-all wildcard `SELECT posts.*` rendering your column selection useless.
|
91
|
+
|
92
|
+
If you don't need to map the attribute names of the piggybacked model, you can simply do:
|
93
|
+
|
94
|
+
piggyback :author, [:name, :birthday, :rating]
|
95
|
+
|
96
|
+
|
97
|
+
### Computed values
|
98
|
+
|
99
|
+
If you want to use an SQL-expression for selecting an attribute, Piggyback can also help you with that. If `Author` didn't have a single `name` attribute, but `first_name` and `last_name`, you could concatenate them into a single attribute:
|
100
|
+
|
101
|
+
class Post < ActiveRecord::Base
|
102
|
+
belongs_to :author
|
103
|
+
|
104
|
+
piggybacks :author, :author_name => "authors.first_name || ' ' || authors.last_name"
|
105
|
+
end
|
106
|
+
|
107
|
+
>> post.author_name
|
108
|
+
=> "Donald Duck"
|
109
|
+
|
110
|
+
In fact, every value you pass in as a string will be treated as raw SQL in the SELECT clause.
|
data/Rakefile
CHANGED
@@ -1,14 +1,15 @@
|
|
1
|
-
require 'rubygems'
|
2
1
|
require 'rake'
|
3
2
|
require 'echoe'
|
4
3
|
|
5
|
-
Echoe.new('piggyback', '0.
|
6
|
-
p.description = "Piggyback associated models with ActiveRecord"
|
4
|
+
Echoe.new('piggyback', '0.2.0') do |p|
|
5
|
+
p.description = "Piggyback attributes from associated models with ActiveRecord"
|
7
6
|
p.url = "http://github.com/juni0r/piggyback"
|
8
7
|
p.author = "Andreas Korth"
|
9
8
|
p.email = "andreas.korth@gmail.com"
|
10
9
|
p.ignore_pattern = ["tmp/*", "log/*", "Gemfile", "Gemfile.lock"]
|
11
|
-
|
10
|
+
|
11
|
+
p.runtime_dependencies = ["activerecord >=3.0.0"]
|
12
|
+
p.development_dependencies = ["rspec", "sqlite3"]
|
12
13
|
end
|
13
14
|
|
14
15
|
Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each { |ext| load ext }
|
data/lib/piggyback.rb
CHANGED
@@ -1,95 +1,109 @@
|
|
1
1
|
module Piggyback
|
2
2
|
extend ActiveSupport::Concern
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
4
|
+
class Association #:nodoc:
|
5
|
+
|
6
|
+
def initialize(reflection, mappings = {})
|
7
|
+
@reflection = reflection
|
8
|
+
@mappings = mappings
|
9
|
+
end
|
10
|
+
|
11
|
+
def add_mappings(mappings)
|
12
|
+
@mappings.merge!(mappings)
|
13
|
+
end
|
14
|
+
|
15
|
+
def map(&block)
|
16
|
+
@mappings.map(&block)
|
17
|
+
end
|
18
|
+
|
19
|
+
def has_attribute?(attr_name)
|
20
|
+
@mappings.has_key? attr_name
|
21
|
+
end
|
13
22
|
|
14
|
-
|
23
|
+
def column(attr_name)
|
24
|
+
@reflection.klass.columns_hash[@mappings[attr_name]]
|
25
|
+
end
|
26
|
+
|
27
|
+
def serialized_attribute(attr_name)
|
28
|
+
@reflection.klass.serialized_attributes[@mappings[attr_name]]
|
29
|
+
end
|
15
30
|
|
16
|
-
def
|
17
|
-
@reflection
|
31
|
+
def quoted_table_name
|
32
|
+
@reflection.klass.quoted_table_name
|
18
33
|
end
|
19
34
|
end
|
20
35
|
|
21
36
|
module ClassMethods
|
22
|
-
|
23
|
-
def
|
24
|
-
|
25
|
-
table_name = association.reflection.klass.quoted_table_name
|
26
|
-
association.attributes.map do |attribute, mapping|
|
27
|
-
if attribute != mapping
|
28
|
-
"#{table_name}.#{connection.quote_column_name(mapping)} AS #{attribute}"
|
29
|
-
else
|
30
|
-
"#{table_name}.#{connection.quote_column_name(attribute)}"
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
columns.flatten!
|
35
|
-
columns.unshift("#{quoted_table_name}.*") unless scoped.select_values.any?
|
36
|
-
select(columns.join(', ')).joins(*associations)
|
37
|
+
|
38
|
+
def piggyback_associations
|
39
|
+
read_inheritable_attribute(:piggyback_associations) || write_inheritable_attribute(:piggyback_associations, {})
|
37
40
|
end
|
38
41
|
|
42
|
+
def piggyback_association_for_attribute(attr_name)
|
43
|
+
piggyback_associations.each_value.detect{ |association| association.has_attribute? attr_name }
|
44
|
+
end
|
39
45
|
|
40
|
-
def
|
46
|
+
def piggybacks(association, *attributes)
|
41
47
|
|
48
|
+
attributes.flatten!
|
49
|
+
mapped_attributes = attributes.extract_options!
|
42
50
|
reflection = reflect_on_association(association.to_sym)
|
43
|
-
|
44
|
-
unless reflection
|
45
|
-
raise ArgumentError.new("#{self.class.name} has no association named '#{association}'.")
|
46
|
-
end
|
47
|
-
|
48
|
-
unless [:belongs_to, :has_one].include?(reflection.macro)
|
49
|
-
raise ArgumentError.new("Only belongs_to and has_many associations can be used for piggybacking.")
|
50
|
-
end
|
51
|
-
|
52
|
-
mapped_attributes = unmapped_attributes.extract_options!
|
53
|
-
unmapped_attributes.concat(Array(mapped_attributes.delete(:attributes)))
|
54
51
|
|
55
|
-
|
52
|
+
raise ArgumentError, "#{name} has no association named '#{association}'" if reflection.nil?
|
53
|
+
raise ArgumentError, "Piggyback only supports belongs_to and has_one associations" if reflection.collection?
|
54
|
+
raise ArgumentError, "No attributes specified for piggybacking" if attributes.empty?
|
55
|
+
|
56
|
+
mappings = {}
|
56
57
|
|
57
|
-
|
58
|
-
attr_name = attr_name.to_s
|
59
|
-
attributes[attr_name] = attr_name
|
58
|
+
attributes.each do |attr_name|
|
59
|
+
mappings[attr_name.to_s] = attr_name.to_s
|
60
60
|
end
|
61
61
|
|
62
|
-
mapped_attributes.each do |attr_name,
|
63
|
-
|
62
|
+
mapped_attributes.each do |attr_name, mapped_name|
|
63
|
+
mappings[attr_name.to_s] = mapped_name.is_a?(String) ? Arel.sql(mapped_name) : mapped_name.to_s
|
64
64
|
end
|
65
65
|
|
66
|
-
association = Association.new(reflection
|
66
|
+
association = (piggyback_associations[reflection.name] ||= Association.new(reflection))
|
67
|
+
association.add_mappings(mappings)
|
67
68
|
|
68
|
-
|
69
|
-
|
70
|
-
attributes.
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
else
|
76
|
-
send(reflection.name).send(mapping)
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
69
|
+
# These default procs for +columns_hash+ and +serialized_attributes+
|
70
|
+
# allow +read_attribute+ to work transparently and consistently with
|
71
|
+
# attributes from joined models. They rely heavily on the implementation
|
72
|
+
# of +read_attribute+ and related methods. Since ActiveRecord doesn't
|
73
|
+
# provide hooks for overriding this behavior, it is the simplest and
|
74
|
+
# least intrusive way to implement proper type casting for attributes
|
75
|
+
# while avoiding code duplication.
|
80
76
|
|
81
77
|
columns_hash.default_proc = lambda do |hash, attr_name|
|
82
|
-
if association =
|
83
|
-
association.
|
78
|
+
if association = piggyback_association_for_attribute(attr_name)
|
79
|
+
association.column(attr_name)
|
84
80
|
end
|
85
81
|
end
|
86
82
|
|
87
83
|
serialized_attributes.default_proc = lambda do |hash, attr_name|
|
88
|
-
if association =
|
89
|
-
association.
|
84
|
+
if association = piggyback_association_for_attribute(attr_name)
|
85
|
+
association.serialized_attribute(attr_name)
|
90
86
|
end
|
91
87
|
end
|
92
88
|
end
|
89
|
+
|
90
|
+
def piggyback(*associations)
|
91
|
+
columns = piggyback_associations.values_at(*associations).map do |association|
|
92
|
+
association.map do |attr_name, mapped_name|
|
93
|
+
if attr_name != mapped_name
|
94
|
+
if mapped_name.is_a? Arel::Nodes::SqlLiteral
|
95
|
+
"#{mapped_name} AS #{attr_name}"
|
96
|
+
else
|
97
|
+
"#{association.quoted_table_name}.#{connection.quote_column_name(mapped_name)} AS #{attr_name}"
|
98
|
+
end
|
99
|
+
else
|
100
|
+
"#{association.quoted_table_name}.#{connection.quote_column_name(attr_name)}"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
columns.unshift("#{quoted_table_name}.*") unless scoped.select_values.any?
|
105
|
+
select(columns.flatten.join(', ')).joins(*associations)
|
106
|
+
end
|
93
107
|
end
|
94
108
|
end
|
95
109
|
|
data/piggyback.gemspec
CHANGED
@@ -2,28 +2,37 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = %q{piggyback}
|
5
|
-
s.version = "0.
|
5
|
+
s.version = "0.2.0"
|
6
6
|
|
7
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
8
|
s.authors = ["Andreas Korth"]
|
9
|
-
s.date = %q{2011-03-
|
10
|
-
s.description = %q{Piggyback associated models with ActiveRecord}
|
9
|
+
s.date = %q{2011-03-21}
|
10
|
+
s.description = %q{Piggyback attributes from associated models with ActiveRecord}
|
11
11
|
s.email = %q{andreas.korth@gmail.com}
|
12
|
-
s.extra_rdoc_files = ["README.
|
13
|
-
s.files = ["README.
|
12
|
+
s.extra_rdoc_files = ["CHANGELOG", "README.markdown", "lib/piggyback.rb"]
|
13
|
+
s.files = ["CHANGELOG", "MIT-LICENSE", "README.markdown", "Rakefile", "lib/piggyback.rb", "piggyback.gemspec", "spec/models.rb", "spec/piggyback_spec.rb", "spec/spec_helper.rb", "Manifest"]
|
14
14
|
s.homepage = %q{http://github.com/juni0r/piggyback}
|
15
|
-
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Piggyback", "--main", "README.
|
15
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Piggyback", "--main", "README.markdown"]
|
16
16
|
s.require_paths = ["lib"]
|
17
17
|
s.rubyforge_project = %q{piggyback}
|
18
18
|
s.rubygems_version = %q{1.5.2}
|
19
|
-
s.summary = %q{Piggyback associated models with ActiveRecord}
|
19
|
+
s.summary = %q{Piggyback attributes from associated models with ActiveRecord}
|
20
20
|
|
21
21
|
if s.respond_to? :specification_version then
|
22
22
|
s.specification_version = 3
|
23
23
|
|
24
24
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
25
|
+
s.add_runtime_dependency(%q<activerecord>, [">= 3.0.0"])
|
26
|
+
s.add_development_dependency(%q<rspec>, [">= 0"])
|
27
|
+
s.add_development_dependency(%q<sqlite3>, [">= 0"])
|
25
28
|
else
|
29
|
+
s.add_dependency(%q<activerecord>, [">= 3.0.0"])
|
30
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
31
|
+
s.add_dependency(%q<sqlite3>, [">= 0"])
|
26
32
|
end
|
27
33
|
else
|
34
|
+
s.add_dependency(%q<activerecord>, [">= 3.0.0"])
|
35
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
36
|
+
s.add_dependency(%q<sqlite3>, [">= 0"])
|
28
37
|
end
|
29
38
|
end
|
data/spec/models.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
silence_stream(STDOUT) do
|
2
|
+
ActiveRecord::Schema.define(:version => 1) do
|
3
|
+
|
4
|
+
create_table :essentials do |t|
|
5
|
+
t.string :token
|
6
|
+
t.timestamps
|
7
|
+
end
|
8
|
+
|
9
|
+
create_table :details do |t|
|
10
|
+
t.belongs_to :essential
|
11
|
+
|
12
|
+
t.string :first_name
|
13
|
+
t.string :last_name
|
14
|
+
t.string :words
|
15
|
+
t.integer :count
|
16
|
+
t.boolean :checked
|
17
|
+
t.date :birthday
|
18
|
+
t.text :baggage
|
19
|
+
|
20
|
+
t.timestamps
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class Essential < ActiveRecord::Base
|
26
|
+
has_one :detail, :inverse_of => :essential
|
27
|
+
has_many :details
|
28
|
+
|
29
|
+
piggybacks :detail, [:first_name, :last_name, :count, :checked, :birthday, :baggage],
|
30
|
+
:name => "details.first_name || ' ' || details.last_name",
|
31
|
+
:detail_updated_at => :updated_at
|
32
|
+
end
|
33
|
+
|
34
|
+
class Detail < ActiveRecord::Base
|
35
|
+
belongs_to :essential, :inverse_of => :detail
|
36
|
+
|
37
|
+
serialize :baggage
|
38
|
+
|
39
|
+
piggybacks :essential, :token
|
40
|
+
|
41
|
+
def name
|
42
|
+
"#{first_name} #{last_name}"
|
43
|
+
end
|
44
|
+
end
|
data/spec/piggyback_spec.rb
CHANGED
@@ -2,33 +2,76 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe Piggyback do
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
5
|
+
context "directive" do
|
6
|
+
|
7
|
+
def definition_should_raise(message, &block)
|
8
|
+
lambda{ Essential.class_eval(&block) }.should raise_error(ArgumentError, message)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should require the association to be defined" do
|
12
|
+
definition_should_raise "Essential has no association named 'bogus'" do
|
13
|
+
piggybacks :bogus, [:attribute]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should not work with has_many associations" do
|
18
|
+
definition_should_raise "Piggyback only supports belongs_to and has_one associations" do
|
19
|
+
piggybacks :details, [:attribute]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should require attributes" do
|
24
|
+
definition_should_raise "No attributes specified for piggybacking" do
|
25
|
+
piggybacks :detail
|
26
|
+
end
|
27
|
+
end
|
12
28
|
end
|
13
29
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
30
|
+
context "relation" do
|
31
|
+
|
32
|
+
let :essential do
|
33
|
+
Essential.create(:token => "TOKEN").create_detail(
|
34
|
+
:first_name => "John",
|
35
|
+
:last_name => "Doe",
|
36
|
+
:count => 23,
|
37
|
+
:checked => false,
|
38
|
+
:birthday => "18.12.1974",
|
39
|
+
:baggage => {:key => "value"})
|
40
|
+
|
41
|
+
Essential.piggyback(:detail).first
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should not load the association" do
|
45
|
+
essential.should_not_receive(:detail)
|
46
|
+
essential.first_name.should be_present
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should read string attributes" do
|
50
|
+
essential.first_name.should eql("John")
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should read integer attributes" do
|
54
|
+
essential.count.should eql(23)
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should read boolean attributes" do
|
58
|
+
essential.checked.should eql(false)
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should read date attributes" do
|
62
|
+
essential.birthday.should eql(Date.new(1974,12,18))
|
63
|
+
end
|
24
64
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
65
|
+
it "should read serialized attributes" do
|
66
|
+
essential.baggage.should eql({:key => "value"})
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should read a mapped attribute" do
|
70
|
+
essential.detail_updated_at.should be_instance_of(Time)
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should read computed attributes" do
|
74
|
+
essential.name.should eql("John Doe")
|
75
|
+
end
|
33
76
|
end
|
34
77
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,12 +1,8 @@
|
|
1
|
-
|
1
|
+
GEM_ROOT = File.expand_path('../..',__FILE__)
|
2
|
+
$: << File.join(GEM_ROOT,'lib')
|
2
3
|
|
3
|
-
$: << Root.join('lib')
|
4
|
-
|
5
|
-
require 'fileutils'
|
6
4
|
require 'active_record'
|
7
|
-
# require 'active_support/buffered_logger'
|
8
5
|
require 'piggyback'
|
9
|
-
require 'rspec'
|
10
6
|
|
11
7
|
RSpec.configure do |config|
|
12
8
|
config.before do
|
@@ -15,35 +11,6 @@ RSpec.configure do |config|
|
|
15
11
|
end
|
16
12
|
|
17
13
|
ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:", :verbosity => "silent")
|
18
|
-
ActiveRecord::Base.logger = ActiveSupport::BufferedLogger.new(
|
19
|
-
|
20
|
-
silence_stream(STDOUT) do
|
21
|
-
ActiveRecord::Schema.define(:version => 1) do
|
22
|
-
create_table :essentials do |t|
|
23
|
-
t.integer :position
|
24
|
-
t.timestamps
|
25
|
-
end
|
26
|
-
|
27
|
-
create_table :details do |t|
|
28
|
-
t.belongs_to :essential
|
29
|
-
t.string :name
|
30
|
-
t.integer :position
|
31
|
-
t.boolean :awesome
|
32
|
-
t.date :birthday
|
33
|
-
t.text :baggage
|
34
|
-
t.timestamps
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
class Essential < ActiveRecord::Base
|
40
|
-
has_one :detail
|
41
|
-
# piggyback :detail, :attributes => [:name, :awesome, :birthday, :baggage], :rank => :position
|
42
|
-
# piggyback :detail, :name, :awesome, :birthday, :baggage, :rank => :position
|
43
|
-
piggyback :detail, [:name, :awesome, :birthday, :baggage], :rank => :position
|
44
|
-
end
|
14
|
+
ActiveRecord::Base.logger = ActiveSupport::BufferedLogger.new(File.join(GEM_ROOT,'log','test.log'))
|
45
15
|
|
46
|
-
|
47
|
-
belongs_to :essential
|
48
|
-
serialize :baggage
|
49
|
-
end
|
16
|
+
require 'models'
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: piggyback
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.
|
5
|
+
version: 0.2.0
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Andreas Korth
|
@@ -10,27 +10,63 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-03-
|
13
|
+
date: 2011-03-21 00:00:00 +01:00
|
14
14
|
default_executable:
|
15
|
-
dependencies:
|
16
|
-
|
17
|
-
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: activerecord
|
18
|
+
prerelease: false
|
19
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
20
|
+
none: false
|
21
|
+
requirements:
|
22
|
+
- - ">="
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: 3.0.0
|
25
|
+
type: :runtime
|
26
|
+
version_requirements: *id001
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
prerelease: false
|
30
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
31
|
+
none: false
|
32
|
+
requirements:
|
33
|
+
- - ">="
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: "0"
|
36
|
+
type: :development
|
37
|
+
version_requirements: *id002
|
38
|
+
- !ruby/object:Gem::Dependency
|
39
|
+
name: sqlite3
|
40
|
+
prerelease: false
|
41
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: "0"
|
47
|
+
type: :development
|
48
|
+
version_requirements: *id003
|
49
|
+
description: Piggyback attributes from associated models with ActiveRecord
|
18
50
|
email: andreas.korth@gmail.com
|
19
51
|
executables: []
|
20
52
|
|
21
53
|
extensions: []
|
22
54
|
|
23
55
|
extra_rdoc_files:
|
24
|
-
-
|
56
|
+
- CHANGELOG
|
57
|
+
- README.markdown
|
25
58
|
- lib/piggyback.rb
|
26
59
|
files:
|
27
|
-
-
|
60
|
+
- CHANGELOG
|
61
|
+
- MIT-LICENSE
|
62
|
+
- README.markdown
|
28
63
|
- Rakefile
|
29
64
|
- lib/piggyback.rb
|
65
|
+
- piggyback.gemspec
|
66
|
+
- spec/models.rb
|
30
67
|
- spec/piggyback_spec.rb
|
31
68
|
- spec/spec_helper.rb
|
32
69
|
- Manifest
|
33
|
-
- piggyback.gemspec
|
34
70
|
has_rdoc: true
|
35
71
|
homepage: http://github.com/juni0r/piggyback
|
36
72
|
licenses: []
|
@@ -42,7 +78,7 @@ rdoc_options:
|
|
42
78
|
- --title
|
43
79
|
- Piggyback
|
44
80
|
- --main
|
45
|
-
- README.
|
81
|
+
- README.markdown
|
46
82
|
require_paths:
|
47
83
|
- lib
|
48
84
|
required_ruby_version: !ruby/object:Gem::Requirement
|
@@ -63,6 +99,6 @@ rubyforge_project: piggyback
|
|
63
99
|
rubygems_version: 1.5.2
|
64
100
|
signing_key:
|
65
101
|
specification_version: 3
|
66
|
-
summary: Piggyback associated models with ActiveRecord
|
102
|
+
summary: Piggyback attributes from associated models with ActiveRecord
|
67
103
|
test_files: []
|
68
104
|
|
data/README.rdoc
DELETED
File without changes
|