activerecord-fusion 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG ADDED
@@ -0,0 +1,3 @@
1
+ = 0.1.0 / 2012-12-18
2
+
3
+ Derived from piggyback
data/Guardfile ADDED
@@ -0,0 +1,7 @@
1
+ guard 'rspec' do
2
+ watch(%r{^spec/.+_spec\.rb$})
3
+ watch(%r{^lib/}) { "spec" }
4
+ watch('spec/spec_helper.rb') { "spec" }
5
+ watch('spec/models.rb') { "spec" }
6
+ end
7
+
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 ADDED
@@ -0,0 +1,11 @@
1
+ CHANGELOG
2
+ Guardfile
3
+ MIT-LICENSE
4
+ Manifest
5
+ README.markdown
6
+ Rakefile
7
+ lib/active_record/fusion.rb
8
+ spec/app.rb
9
+ spec/fusion_spec.rb
10
+ spec/schema.rb
11
+ spec/spec_helper.rb
data/README.markdown ADDED
@@ -0,0 +1,82 @@
1
+ # Fusion
2
+
3
+ An extension for merging of ActiveRecord™ models.
4
+
5
+ ### Piggybacking
6
+
7
+ Piggybacking refers to the technique of dynamically including attributes from an associated model. This is achieved by joining the associated model 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 User < ActiveRecord::Base
12
+ has_many :posts
13
+ end
14
+
15
+ class Post < ActiveRecord::Base
16
+ belongs_to :user
17
+ end
18
+
19
+ ActiveRecord supports piggybacking simply by joining the associated table and selecting columns from it:
20
+
21
+ post = Post.select('posts.*, user.name AS user_name') \
22
+ .joins("JOIN users ON posts.user_id = users.id") \
23
+ .first
24
+
25
+ post.title # => "Why piggybacking in ActiveRecord is flawed"
26
+ post.user_name # => "Alec Smart"
27
+
28
+ As you can see, the `name` attribute from `User` is treated as if it were an attribute of `Post`. 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.
29
+
30
+ Or so it seems. Actually there is a drawback which becomes obvious when we select non-string columns:
31
+
32
+ post = Post.select('posts.*, user.birthday AS user_birthday, user.rating AS user_rating') \
33
+ .joins("JOIN users ON posts.user_id = users.id") \
34
+ .first
35
+
36
+ post.user_birthday # => "2011-03-01"
37
+ post.user_rating # => "4.5"
38
+
39
+ Any attributes originating from the `users` table are treated as strings instead of being automatically type-casted as we would expect. The database returns result sets as plain text and ActiveRecord needs to obtain type information separately from the table schema in order to do its type-casting magic. Unfortunately, a model only knows about the columns types in its own table, so type-casting doesn't work with columns selected from foreign tables.
40
+
41
+ We could work around this by defining attribute reader methods in the `Post` model that implicitly convert the values:
42
+
43
+ class Post < ActiveRecord::Base
44
+ belongs_to :user
45
+
46
+ def user_birthday
47
+ Date.parse(read_attribute(:user_birthday))
48
+ end
49
+
50
+ def user_rating
51
+ read_attribute(:user_rating).to_f
52
+ end
53
+ end
54
+
55
+ 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 quickly become more complex in a real-life application. In its current state it won't handle `nil` values properly, for example.
56
+
57
+
58
+ ### Fusion to the rescue!
59
+
60
+ The `fusion` declaration specifies the attributes we want to fuse from associated models. Not only does it take care of the type-casting but also provides us with some additional benefits.
61
+
62
+ You simply declare which association and attributes you want to fuse:
63
+
64
+ class Post < ActiveRecord::Base
65
+ belongs_to :user
66
+
67
+ fusion :user, :only => [:name, :email, :rating, :confirmed_at]
68
+ end
69
+
70
+ Now you can do the following:
71
+
72
+ post = Post.fuse.first
73
+
74
+ post.name # => "John Doe"
75
+ post.rating # => 4.5
76
+ post.confirmed_at # => Tue, 01 Mar 2011
77
+
78
+ The type-casting works with any type of attribute, even with serialized ones.
79
+
80
+ Fusion uses `OUTER JOIN` in order to include both records that have an associated record and ones that don't.
81
+
82
+ Of course, Fusion plays nice with Arel and you can add additional `joins`, `select` and other statements as you like.
data/Rakefile ADDED
@@ -0,0 +1,38 @@
1
+ require 'rake'
2
+ require 'echoe'
3
+
4
+ Echoe.new('activerecord-fusion', '0.1.0') do |p|
5
+
6
+ p.description = "Fuse attributes from associated models with ActiveRecord"
7
+ p.url = "http://github.com/juni0r/activerecord-fusion"
8
+ p.author = "Andreas Korth"
9
+ p.email = "andreas.korth@gmail.com"
10
+
11
+ p.retain_gemspec = true
12
+
13
+ p.ignore_pattern = %w{
14
+ Gemfile
15
+ Gemfile.lock
16
+ vendor/**/*
17
+ tmp/*
18
+ log/*
19
+ *.tmproj
20
+ }
21
+
22
+ p.runtime_dependencies = [ "activerecord >=3.1.0" ]
23
+ p.development_dependencies = [ "echoe", "sqlite3", "rspec", "guard-rspec", "rb-fsevent", "growl" ]
24
+ end
25
+
26
+ require 'rspec/core'
27
+ require 'rspec/core/rake_task'
28
+
29
+ RSpec::Core::RakeTask.new(:spec) do |spec|
30
+ spec.pattern = FileList['spec/**/*_spec.rb']
31
+ end
32
+
33
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
34
+ spec.pattern = 'spec/**/*_spec.rb'
35
+ spec.rcov = true
36
+ end
37
+
38
+ task default: :spec
@@ -0,0 +1,50 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = "activerecord-fusion"
5
+ s.version = "0.1.0"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Andreas Korth"]
9
+ s.date = "2012-12-16"
10
+ s.description = "Fuse attributes from associated models with ActiveRecord"
11
+ s.email = "andreas.korth@gmail.com"
12
+ s.extra_rdoc_files = ["CHANGELOG", "README.markdown", "lib/active_record/fusion.rb"]
13
+ s.files = ["CHANGELOG", "Guardfile", "MIT-LICENSE", "Manifest", "README.markdown", "Rakefile", "lib/active_record/fusion.rb", "spec/app.rb", "spec/fusion_spec.rb", "spec/schema.rb", "spec/spec_helper.rb", "activerecord-fusion.gemspec"]
14
+ s.homepage = "http://github.com/juni0r/activerecord-fusion"
15
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Activerecord-fusion", "--main", "README.markdown"]
16
+ s.require_paths = ["lib"]
17
+ s.rubyforge_project = "activerecord-fusion"
18
+ s.rubygems_version = "1.8.23"
19
+ s.summary = "Fuse attributes from associated models with ActiveRecord"
20
+
21
+ if s.respond_to? :specification_version then
22
+ s.specification_version = 3
23
+
24
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
25
+ s.add_runtime_dependency(%q<activerecord>, [">= 3.1.0"])
26
+ s.add_development_dependency(%q<echoe>, [">= 0"])
27
+ s.add_development_dependency(%q<sqlite3>, [">= 0"])
28
+ s.add_development_dependency(%q<rspec>, [">= 0"])
29
+ s.add_development_dependency(%q<guard-rspec>, [">= 0"])
30
+ s.add_development_dependency(%q<rb-fsevent>, [">= 0"])
31
+ s.add_development_dependency(%q<growl>, [">= 0"])
32
+ else
33
+ s.add_dependency(%q<activerecord>, [">= 3.1.0"])
34
+ s.add_dependency(%q<echoe>, [">= 0"])
35
+ s.add_dependency(%q<sqlite3>, [">= 0"])
36
+ s.add_dependency(%q<rspec>, [">= 0"])
37
+ s.add_dependency(%q<guard-rspec>, [">= 0"])
38
+ s.add_dependency(%q<rb-fsevent>, [">= 0"])
39
+ s.add_dependency(%q<growl>, [">= 0"])
40
+ end
41
+ else
42
+ s.add_dependency(%q<activerecord>, [">= 3.1.0"])
43
+ s.add_dependency(%q<echoe>, [">= 0"])
44
+ s.add_dependency(%q<sqlite3>, [">= 0"])
45
+ s.add_dependency(%q<rspec>, [">= 0"])
46
+ s.add_dependency(%q<guard-rspec>, [">= 0"])
47
+ s.add_dependency(%q<rb-fsevent>, [">= 0"])
48
+ s.add_dependency(%q<growl>, [">= 0"])
49
+ end
50
+ end
@@ -0,0 +1,137 @@
1
+ module ActiveRecord
2
+ module Fusion
3
+
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ class_attribute :fusion_associations
8
+ self.fusion_associations = {}
9
+ end
10
+
11
+ module ClassMethods
12
+
13
+ def fusion(name, options = nil)
14
+ reflection = reflect_on_association(name)
15
+
16
+ if reflection.nil?
17
+ raise ArgumentError, "#{self.name} has no association #{name.inspect}"
18
+ end
19
+
20
+ if reflection.collection?
21
+ raise ArgumentError, "Only belongs_to and has_one associations can be fused"
22
+ end
23
+
24
+ assoc = Association.new(reflection, options)
25
+
26
+ self.fusion_associations = fusion_associations.merge(name => assoc)
27
+ end
28
+
29
+ def fuse(*assocs)
30
+ context = scoped
31
+
32
+ if context.select_values.empty?
33
+ context = context.select(arel_table[Arel::star])
34
+ end
35
+
36
+ assocs = if assocs.any?
37
+ fusion_associations.values_at(*assocs)
38
+ else
39
+ fusion_associations.values
40
+ end
41
+
42
+ assocs.each do |assoc|
43
+ context = context.select(assoc.selects).joins(assoc.joins)
44
+ end
45
+
46
+ context
47
+ end
48
+
49
+ def define_attribute_methods
50
+ @attribute_methods_mutex.synchronize do
51
+ return if attribute_methods_generated?
52
+
53
+ fusion_associations.each_value do |assoc|
54
+ assoc.each_column do |name, column, serialized|
55
+ columns_hash[name] = column
56
+ serialized_attributes[name] = serialized if serialized
57
+ define_fusion_attribute_methods(assoc.name, name)
58
+ end
59
+ end
60
+ end
61
+
62
+ super
63
+ end
64
+
65
+ def define_fusion_attribute_methods(assoc_name, attr_name)
66
+ generated_attribute_methods.module_eval <<-RUBY, __FILE__, __LINE__ + 1
67
+ def __temp__
68
+ if @attributes.has_key?("#{attr_name}")
69
+ (v=@attributes["#{attr_name}"]) && #{attribute_cast_code(attr_name)}
70
+ else
71
+ #{assoc_name}.#{attr_name}
72
+ end
73
+ end
74
+ alias_method :"#{attr_name}", :__temp__
75
+ undef_method :__temp__
76
+
77
+ def __temp__
78
+ !!send(:"#{attr_name}")
79
+ end
80
+
81
+ alias_method '#{attr_name}?', :__temp__
82
+ undef_method :__temp__
83
+ RUBY
84
+ end
85
+ end
86
+
87
+ class Association #:nodoc:
88
+
89
+ def initialize(reflection, options = nil)
90
+ @reflection = reflection
91
+ @options = options.is_a?(Hash) ? options : { only: options }
92
+ @options.assert_valid_keys(:only, :except)
93
+ end
94
+
95
+ def method_missing(method, *args, &block)
96
+ if @reflection.respond_to? method
97
+ @reflection.send(method, *args, &block)
98
+ else
99
+ super
100
+ end
101
+ end
102
+
103
+ def attributes
104
+ unless @attributes
105
+ only = Array.wrap(@options[:only]).map(&:to_s)
106
+ except = Array.wrap(@options[:except]).map(&:to_s)
107
+
108
+ @attributes = klass.attribute_names
109
+ @attributes &= only if only.any?
110
+ @attributes -= active_record.attribute_names
111
+ @attributes -= [foreign_key] unless belongs_to?
112
+ @attributes -= except if except.any?
113
+ end
114
+ @attributes
115
+ end
116
+
117
+ def each_column
118
+ attributes.each do |name|
119
+ yield(name, klass.columns_hash[name], klass.serialized_attributes[name])
120
+ end
121
+ end
122
+
123
+ def selects
124
+ @select ||= attributes.map{ |name| klass.arel_table[name] }
125
+ end
126
+
127
+ def joins
128
+ dep = ActiveRecord::Associations::JoinDependency.new(active_record, name, [])
129
+ dep.join_associations.each{ |a| a.join_type = Arel::OuterJoin }
130
+ end
131
+ end
132
+ end
133
+ end
134
+
135
+ ActiveRecord::Base.class_eval do
136
+ include ActiveRecord::Fusion
137
+ end
data/spec/app.rb ADDED
@@ -0,0 +1,27 @@
1
+ require 'schema'
2
+
3
+ class Essential < ActiveRecord::Base
4
+ has_many :details
5
+ has_one :detail, inverse_of: :essential
6
+
7
+ has_many :features
8
+ has_one :feature, conditions:{ active:true }
9
+
10
+ fusion :detail, [:first_name, :count, :checked, :birthday, :baggage]
11
+
12
+ fusion :feature, except: :active
13
+ end
14
+
15
+
16
+ class Detail < ActiveRecord::Base
17
+ belongs_to :essential, inverse_of: :detail
18
+
19
+ fusion :essential, :token
20
+
21
+ serialize :baggage
22
+ end
23
+
24
+
25
+ class Feature < ActiveRecord::Base
26
+ belongs_to :essential, inverse_of: :detail
27
+ end
@@ -0,0 +1,90 @@
1
+ require 'spec_helper'
2
+
3
+ describe ActiveRecord::Fusion do
4
+
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 :bogus" do
13
+ fusion :bogus
14
+ end
15
+ end
16
+
17
+ it "should not work with has_many associations" do
18
+ definition_should_raise "Only belongs_to and has_one associations can be fused" do
19
+ fusion :details
20
+ end
21
+ end
22
+ end
23
+
24
+ it "should respect association options" do
25
+ essential = Essential.create!
26
+ Feature.create!(essential_id:essential.id, value:23, active:false)
27
+ Feature.create!(essential_id:essential.id, value:42, active:true)
28
+ Feature.create!(essential_id:essential.id, value:45, active:false)
29
+
30
+ essential = Essential.fuse(:detail).first
31
+ essential.value.should eql(42)
32
+ end
33
+
34
+ context "relation" do
35
+
36
+ before do
37
+ Essential.create!(:token => "ESSENTIAL").create_detail(
38
+ :first_name => "John",
39
+ :last_name => "Doe",
40
+ :count => 23,
41
+ :checked => false,
42
+ :birthday => "18.12.1974",
43
+ :baggage => {:key => "value"})
44
+ end
45
+
46
+ it "should work with belongs_to associations" do
47
+ Detail.fuse(:essential).first.token.should eql("ESSENTIAL")
48
+ Detail.create!
49
+ Detail.fuse(:essential).should have(2).items
50
+ end
51
+
52
+ it "should include records without an association" do
53
+ Essential.fuse(:detail).create!
54
+ Essential.should have(2).items
55
+ end
56
+
57
+ it "should include all attributes from the specified associations" do
58
+ essential = Essential.fuse(:detail, :feature).first
59
+ essential.attributes.keys.should =~
60
+ %w{ id token created_at updated_at first_name count checked birthday baggage value }
61
+ end
62
+
63
+ context "attribute methods" do
64
+
65
+ let :essential do
66
+ Essential.fuse(:detail).first
67
+ end
68
+
69
+ it "should read string attributes" do
70
+ essential.first_name.should eql("John")
71
+ end
72
+
73
+ it "should read integer attributes" do
74
+ essential.count.should eql(23)
75
+ end
76
+
77
+ it "should read boolean attributes" do
78
+ essential.checked.should eql(false)
79
+ end
80
+
81
+ it "should read date attributes" do
82
+ essential.birthday.should eql(Date.new(1974,12,18))
83
+ end
84
+
85
+ it "should read serialized attributes" do
86
+ essential.baggage.should eql({:key => "value"})
87
+ end
88
+ end
89
+ end
90
+ end
data/spec/schema.rb ADDED
@@ -0,0 +1,36 @@
1
+ require 'active_record'
2
+
3
+ ActiveRecord::Base.establish_connection(
4
+ :adapter => "sqlite3",
5
+ :database => ":memory:",
6
+ :verbosity => "silent"
7
+ )
8
+
9
+ silence_stream(STDOUT) do
10
+ ActiveRecord::Schema.define(:version => 1) do
11
+
12
+ create_table :essentials do |t|
13
+ t.string :token
14
+ t.timestamps
15
+ end
16
+
17
+ create_table :details do |t|
18
+ t.belongs_to :essential
19
+
20
+ t.string :first_name
21
+ t.string :last_name
22
+ t.integer :count
23
+ t.boolean :checked
24
+ t.date :birthday
25
+ t.text :baggage
26
+
27
+ t.timestamps
28
+ end
29
+
30
+ create_table :features do |t|
31
+ t.belongs_to :essential
32
+ t.integer :value
33
+ t.boolean :active
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,16 @@
1
+ GEM_ROOT = File.expand_path('../..',__FILE__)
2
+ $: << File.join(GEM_ROOT,'lib')
3
+
4
+ require 'active_record'
5
+ require 'active_record/fusion'
6
+ require 'app'
7
+
8
+ ActiveRecord::Base.logger = ActiveSupport::BufferedLogger.new(File.join(GEM_ROOT,'log','test.log'))
9
+ ActiveRecord::Base::include_root_in_json = false
10
+
11
+ RSpec.configure do |config|
12
+ config.before do
13
+ ActiveRecord::Base::descendants.each{ |model| model.delete_all }
14
+ end
15
+ end
16
+
metadata ADDED
@@ -0,0 +1,177 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: activerecord-fusion
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Andreas Korth
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-12-16 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activerecord
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 3.1.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 3.1.0
30
+ - !ruby/object:Gem::Dependency
31
+ name: echoe
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: sqlite3
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
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: rspec
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
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
+ - !ruby/object:Gem::Dependency
79
+ name: guard-rspec
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: rb-fsevent
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: growl
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ description: Fuse attributes from associated models with ActiveRecord
127
+ email: andreas.korth@gmail.com
128
+ executables: []
129
+ extensions: []
130
+ extra_rdoc_files:
131
+ - CHANGELOG
132
+ - README.markdown
133
+ - lib/active_record/fusion.rb
134
+ files:
135
+ - CHANGELOG
136
+ - Guardfile
137
+ - MIT-LICENSE
138
+ - Manifest
139
+ - README.markdown
140
+ - Rakefile
141
+ - lib/active_record/fusion.rb
142
+ - spec/app.rb
143
+ - spec/fusion_spec.rb
144
+ - spec/schema.rb
145
+ - spec/spec_helper.rb
146
+ - activerecord-fusion.gemspec
147
+ homepage: http://github.com/juni0r/activerecord-fusion
148
+ licenses: []
149
+ post_install_message:
150
+ rdoc_options:
151
+ - --line-numbers
152
+ - --inline-source
153
+ - --title
154
+ - Activerecord-fusion
155
+ - --main
156
+ - README.markdown
157
+ require_paths:
158
+ - lib
159
+ required_ruby_version: !ruby/object:Gem::Requirement
160
+ none: false
161
+ requirements:
162
+ - - ! '>='
163
+ - !ruby/object:Gem::Version
164
+ version: '0'
165
+ required_rubygems_version: !ruby/object:Gem::Requirement
166
+ none: false
167
+ requirements:
168
+ - - ! '>='
169
+ - !ruby/object:Gem::Version
170
+ version: '1.2'
171
+ requirements: []
172
+ rubyforge_project: activerecord-fusion
173
+ rubygems_version: 1.8.23
174
+ signing_key:
175
+ specification_version: 3
176
+ summary: Fuse attributes from associated models with ActiveRecord
177
+ test_files: []