friendly-attributes 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/Gemfile ADDED
@@ -0,0 +1,15 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gem 'activerecord', '~> 2.3.5'
4
+ gem 'friendly', '~> 0.6.0'
5
+ gem 'yajl-ruby', '~> 0.7.7'
6
+ gem 'memcached', '~> 0.20.1'
7
+
8
+ group :development do
9
+ gem 'mysql', '~> 2.8.1'
10
+ gem 'rspec', '~> 2.1.0'
11
+ gem 'bundler', '~> 1.0.0'
12
+ gem 'jeweler', '~> 1.5.1'
13
+ gem 'rcov', '>= 0'
14
+ gem 'database_cleaner', '~> 0.5.0'
15
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,49 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ activerecord (2.3.5)
5
+ activesupport (= 2.3.5)
6
+ activesupport (2.3.5)
7
+ database_cleaner (0.5.2)
8
+ diff-lcs (1.1.2)
9
+ friendly (0.6.0)
10
+ activesupport (= 2.3.5)
11
+ rufus-json (= 0.2.3)
12
+ sequel (>= 3.7.0)
13
+ will_paginate
14
+ git (1.2.5)
15
+ jeweler (1.5.1)
16
+ bundler (~> 1.0.0)
17
+ git (>= 1.2.5)
18
+ rake
19
+ memcached (0.20.1)
20
+ mysql (2.8.1)
21
+ rake (0.8.7)
22
+ rcov (0.9.9)
23
+ rspec (2.1.0)
24
+ rspec-core (~> 2.1.0)
25
+ rspec-expectations (~> 2.1.0)
26
+ rspec-mocks (~> 2.1.0)
27
+ rspec-core (2.1.0)
28
+ rspec-expectations (2.1.0)
29
+ diff-lcs (~> 1.1.2)
30
+ rspec-mocks (2.1.0)
31
+ rufus-json (0.2.3)
32
+ sequel (3.17.0)
33
+ will_paginate (2.3.15)
34
+ yajl-ruby (0.7.8)
35
+
36
+ PLATFORMS
37
+ ruby
38
+
39
+ DEPENDENCIES
40
+ activerecord (~> 2.3.5)
41
+ bundler (~> 1.0.0)
42
+ database_cleaner (~> 0.5.0)
43
+ friendly (~> 0.6.0)
44
+ jeweler (~> 1.5.1)
45
+ memcached (~> 0.20.1)
46
+ mysql (~> 2.8.1)
47
+ rcov
48
+ rspec (~> 2.1.0)
49
+ yajl-ruby (~> 0.7.7)
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Istvan Hoka
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,21 @@
1
+ = friendly-attributes
2
+
3
+ Pattern to add fields to ActiveRecord models, using an associated document, without needing schema migrations.
4
+
5
+ Uses http://friendlyorm.com/
6
+
7
+ == Contributing to friendly-attributes
8
+
9
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
10
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
11
+ * Fork the project
12
+ * Start a feature/bugfix branch
13
+ * Commit and push until you are happy with your contribution
14
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
15
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
16
+
17
+ == Copyright
18
+
19
+ Copyright (c) 2010 Istvan Hoka. See LICENSE.txt for
20
+ further details.
21
+
data/Rakefile ADDED
@@ -0,0 +1,58 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'rake'
11
+
12
+ require 'jeweler'
13
+ Jeweler::Tasks.new do |gem|
14
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
15
+ gem.name = "friendly-attributes"
16
+ gem.homepage = "http://github.com/ihoka/friendly-attributes"
17
+ gem.license = "MIT"
18
+ gem.summary = %Q{Extend ActiveRecord models using Friendly ORM delegate models}
19
+ gem.description = %Q{Pattern to add fields to ActiveRecord models, using an associated document, without needing schema migrations.}
20
+ gem.email = "istvan.hoka@gmail.com"
21
+ gem.authors = ["Istvan Hoka"]
22
+ gem.add_runtime_dependency 'activerecord', '~> 2.3.5'
23
+ gem.add_runtime_dependency 'friendly', '~> 0.6.0'
24
+ gem.add_runtime_dependency 'yajl-ruby', '~> 0.7.7'
25
+ gem.add_runtime_dependency 'memcached', '~> 0.20.1'
26
+
27
+ gem.add_development_dependency 'mysql', '~> 2.8.1'
28
+ gem.add_development_dependency 'rspec', '~> 2.1.0'
29
+ gem.add_development_dependency 'bundler', '~> 1.0.0'
30
+ gem.add_development_dependency 'jeweler', '~> 1.5.1'
31
+ gem.add_development_dependency 'rcov', '>= 0'
32
+ gem.add_development_dependency 'database_cleaner', '~> 0.5.0'
33
+ end
34
+ Jeweler::RubygemsDotOrgTasks.new
35
+
36
+ require 'rspec/core'
37
+ require 'rspec/core/rake_task'
38
+ RSpec::Core::RakeTask.new(:spec) do |spec|
39
+ spec.pattern = FileList['spec/**/*_spec.rb']
40
+ end
41
+
42
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
43
+ spec.pattern = FileList['spec/**/*_spec.rb']
44
+ spec.rcov = true
45
+ spec.rcov_opts = %w{-I spec:lib --exclude gems\/,spec\/}
46
+ end
47
+
48
+ task :default => :spec
49
+
50
+ require 'rake/rdoctask'
51
+ Rake::RDocTask.new do |rdoc|
52
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
53
+
54
+ rdoc.rdoc_dir = 'rdoc'
55
+ rdoc.title = "friendly-attributes #{version}"
56
+ rdoc.rdoc_files.include('README*')
57
+ rdoc.rdoc_files.include('lib/**/*.rb')
58
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,130 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{friendly-attributes}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Istvan Hoka"]
12
+ s.date = %q{2010-11-20}
13
+ s.description = %q{Pattern to add fields to ActiveRecord models, using an associated document, without needing schema migrations.}
14
+ s.email = %q{istvan.hoka@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ "Gemfile",
22
+ "Gemfile.lock",
23
+ "LICENSE.txt",
24
+ "README.rdoc",
25
+ "Rakefile",
26
+ "VERSION",
27
+ "friendly-attributes.gemspec",
28
+ "lib/friendly-attributes.rb",
29
+ "lib/friendly_attributes.rb",
30
+ "lib/friendly_attributes/class_methods.rb",
31
+ "lib/friendly_attributes/details.rb",
32
+ "lib/friendly_attributes/details_delegator.rb",
33
+ "lib/friendly_attributes/instance_methods.rb",
34
+ "spec/config.yml.example",
35
+ "spec/friendly_attributes/class_methods_spec.rb",
36
+ "spec/friendly_attributes/details_delegator_spec.rb",
37
+ "spec/friendly_attributes/details_spec.rb",
38
+ "spec/friendly_attributes/instance_methods_spec.rb",
39
+ "spec/friendly_attributes_spec.rb",
40
+ "spec/spec_helper.rb",
41
+ "spec/support/active_record_fake.rb",
42
+ "spec/support/database_cleaner_helpers.rb"
43
+ ]
44
+ s.homepage = %q{http://github.com/ihoka/friendly-attributes}
45
+ s.licenses = ["MIT"]
46
+ s.require_paths = ["lib"]
47
+ s.rubygems_version = %q{1.3.7}
48
+ s.summary = %q{Extend ActiveRecord models using Friendly ORM delegate models}
49
+ s.test_files = [
50
+ "spec/friendly_attributes/class_methods_spec.rb",
51
+ "spec/friendly_attributes/details_delegator_spec.rb",
52
+ "spec/friendly_attributes/details_spec.rb",
53
+ "spec/friendly_attributes/instance_methods_spec.rb",
54
+ "spec/friendly_attributes_spec.rb",
55
+ "spec/spec_helper.rb",
56
+ "spec/support/active_record_fake.rb",
57
+ "spec/support/database_cleaner_helpers.rb"
58
+ ]
59
+
60
+ if s.respond_to? :specification_version then
61
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
62
+ s.specification_version = 3
63
+
64
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
65
+ s.add_runtime_dependency(%q<activerecord>, ["~> 2.3.5"])
66
+ s.add_runtime_dependency(%q<friendly>, ["~> 0.6.0"])
67
+ s.add_runtime_dependency(%q<yajl-ruby>, ["~> 0.7.7"])
68
+ s.add_runtime_dependency(%q<memcached>, ["~> 0.20.1"])
69
+ s.add_development_dependency(%q<mysql>, ["~> 2.8.1"])
70
+ s.add_development_dependency(%q<rspec>, ["~> 2.1.0"])
71
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
72
+ s.add_development_dependency(%q<jeweler>, ["~> 1.5.1"])
73
+ s.add_development_dependency(%q<rcov>, [">= 0"])
74
+ s.add_development_dependency(%q<database_cleaner>, ["~> 0.5.0"])
75
+ s.add_runtime_dependency(%q<activerecord>, ["~> 2.3.5"])
76
+ s.add_runtime_dependency(%q<friendly>, ["~> 0.6.0"])
77
+ s.add_runtime_dependency(%q<yajl-ruby>, ["~> 0.7.7"])
78
+ s.add_runtime_dependency(%q<memcached>, ["~> 0.20.1"])
79
+ s.add_development_dependency(%q<mysql>, ["~> 2.8.1"])
80
+ s.add_development_dependency(%q<rspec>, ["~> 2.1.0"])
81
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
82
+ s.add_development_dependency(%q<jeweler>, ["~> 1.5.1"])
83
+ s.add_development_dependency(%q<rcov>, [">= 0"])
84
+ s.add_development_dependency(%q<database_cleaner>, ["~> 0.5.0"])
85
+ else
86
+ s.add_dependency(%q<activerecord>, ["~> 2.3.5"])
87
+ s.add_dependency(%q<friendly>, ["~> 0.6.0"])
88
+ s.add_dependency(%q<yajl-ruby>, ["~> 0.7.7"])
89
+ s.add_dependency(%q<memcached>, ["~> 0.20.1"])
90
+ s.add_dependency(%q<mysql>, ["~> 2.8.1"])
91
+ s.add_dependency(%q<rspec>, ["~> 2.1.0"])
92
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
93
+ s.add_dependency(%q<jeweler>, ["~> 1.5.1"])
94
+ s.add_dependency(%q<rcov>, [">= 0"])
95
+ s.add_dependency(%q<database_cleaner>, ["~> 0.5.0"])
96
+ s.add_dependency(%q<activerecord>, ["~> 2.3.5"])
97
+ s.add_dependency(%q<friendly>, ["~> 0.6.0"])
98
+ s.add_dependency(%q<yajl-ruby>, ["~> 0.7.7"])
99
+ s.add_dependency(%q<memcached>, ["~> 0.20.1"])
100
+ s.add_dependency(%q<mysql>, ["~> 2.8.1"])
101
+ s.add_dependency(%q<rspec>, ["~> 2.1.0"])
102
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
103
+ s.add_dependency(%q<jeweler>, ["~> 1.5.1"])
104
+ s.add_dependency(%q<rcov>, [">= 0"])
105
+ s.add_dependency(%q<database_cleaner>, ["~> 0.5.0"])
106
+ end
107
+ else
108
+ s.add_dependency(%q<activerecord>, ["~> 2.3.5"])
109
+ s.add_dependency(%q<friendly>, ["~> 0.6.0"])
110
+ s.add_dependency(%q<yajl-ruby>, ["~> 0.7.7"])
111
+ s.add_dependency(%q<memcached>, ["~> 0.20.1"])
112
+ s.add_dependency(%q<mysql>, ["~> 2.8.1"])
113
+ s.add_dependency(%q<rspec>, ["~> 2.1.0"])
114
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
115
+ s.add_dependency(%q<jeweler>, ["~> 1.5.1"])
116
+ s.add_dependency(%q<rcov>, [">= 0"])
117
+ s.add_dependency(%q<database_cleaner>, ["~> 0.5.0"])
118
+ s.add_dependency(%q<activerecord>, ["~> 2.3.5"])
119
+ s.add_dependency(%q<friendly>, ["~> 0.6.0"])
120
+ s.add_dependency(%q<yajl-ruby>, ["~> 0.7.7"])
121
+ s.add_dependency(%q<memcached>, ["~> 0.20.1"])
122
+ s.add_dependency(%q<mysql>, ["~> 2.8.1"])
123
+ s.add_dependency(%q<rspec>, ["~> 2.1.0"])
124
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
125
+ s.add_dependency(%q<jeweler>, ["~> 1.5.1"])
126
+ s.add_dependency(%q<rcov>, [">= 0"])
127
+ s.add_dependency(%q<database_cleaner>, ["~> 0.5.0"])
128
+ end
129
+ end
130
+
@@ -0,0 +1 @@
1
+ require 'friendly_attributes'
@@ -0,0 +1,14 @@
1
+ require 'friendly'
2
+ require 'active_record'
3
+
4
+ module FriendlyAttributes
5
+ autoload :ClassMethods, 'friendly_attributes/class_methods'
6
+ autoload :InstanceMethods, 'friendly_attributes/instance_methods'
7
+ autoload :Details, 'friendly_attributes/details'
8
+ autoload :DetailsDelegator, 'friendly_attributes/details_delegator'
9
+
10
+ def self.included(base)
11
+ base.extend ClassMethods
12
+ base.send(:include, InstanceMethods)
13
+ end
14
+ end
@@ -0,0 +1,16 @@
1
+ module FriendlyAttributes
2
+ module ClassMethods
3
+ def friendly_details(klass, &block)
4
+ DetailsDelegator.new(klass, self, &block)
5
+ end
6
+
7
+ def friendly_mount_uploader(name, klass)
8
+ mount_uploader name, klass
9
+
10
+ instance_eval do
11
+ alias_method :read_uploader, :read_friendly_attribute
12
+ alias_method :write_uploader, :write_friendly_attribute
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,9 @@
1
+ module FriendlyAttributes
2
+ class Details
3
+ class << self
4
+ def find_or_build_by_active_record_id(active_record_id)
5
+ active_record_id && first(:active_record_id => active_record_id) || new(:active_record_id => active_record_id)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,42 @@
1
+ module FriendlyAttributes
2
+ class DetailsDelegator
3
+ attr_reader :friendly_model, :ar_model
4
+
5
+ delegate :attribute, :indexes, :to => :friendly_model
6
+
7
+ def initialize(friendly_model, ar_model, &block)
8
+ @ar_model = ar_model
9
+ @friendly_model = friendly_model
10
+
11
+ friendly_model.instance_eval do
12
+ include Friendly::Document
13
+
14
+ attribute :active_record_id, Integer
15
+ indexes :active_record_id
16
+ end
17
+
18
+ ar_model.class_eval do
19
+ after_save :update_friendly_details
20
+ after_destroy :destroy_friendly_details
21
+
22
+ define_method(:details) do
23
+ @details ||= friendly_model.find_or_build_by_active_record_id(id)
24
+ end
25
+ end
26
+
27
+ self.instance_eval(&block)
28
+ end
29
+
30
+ def delegated_attribute(name, klass)
31
+ attribute name, klass
32
+ delegated_method(:"#{name}")
33
+ delegated_method(:"#{name}=")
34
+ end
35
+
36
+ def delegated_method(name)
37
+ ar_model.class_eval do
38
+ delegate :"#{name}", :to => :details
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,25 @@
1
+ module FriendlyAttributes
2
+ module InstanceMethods
3
+ def read_friendly_attribute(column)
4
+ details.send(column)
5
+ end
6
+
7
+ def write_friendly_attribute(column, instance)
8
+ details.send(:"#{column}=", instance)
9
+ end
10
+
11
+ def details_present?
12
+ @details.present?
13
+ end
14
+
15
+ def update_friendly_details
16
+ return unless details_present?
17
+ details.active_record_id = id unless details.active_record_id == id
18
+ details.save if details.changed?
19
+ end
20
+
21
+ def destroy_friendly_details
22
+ details.destroy
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,6 @@
1
+ test:
2
+ :adapter: "mysql"
3
+ :host: "localhost"
4
+ :user: "root"
5
+ :password:
6
+ :database: "friendly_attributes_test"
@@ -0,0 +1,65 @@
1
+ require 'spec_helper'
2
+
3
+ describe FriendlyAttributes::ClassMethods do
4
+ let(:friendly_model) { Class.new }
5
+ let(:ar_model) {
6
+ Class.new {
7
+ include ActiveRecordFake
8
+ extend FriendlyAttributes::ClassMethods
9
+ }
10
+ }
11
+ let(:initializer) { proc {} }
12
+ let(:details_delegator) { mock(FriendlyAttributes::DetailsDelegator) }
13
+
14
+ class FakeUploader; end
15
+
16
+ describe ".friendly_details" do
17
+ it "instantiates a new DetailsDelegator" do
18
+ FriendlyAttributes::DetailsDelegator.should_receive(:new).with(friendly_model, ar_model, &initializer).and_return(details_delegator)
19
+ ar_model.friendly_details(friendly_model, &initializer).should == details_delegator
20
+ end
21
+ end
22
+
23
+ describe ".friendly_mount_uploader" do
24
+ let(:foo_value) { mock("Friendly attribute value") }
25
+
26
+ before(:each) do
27
+ ar_model.stub(:mount_uploader => nil)
28
+
29
+ foo = foo_value
30
+
31
+ ar_model.class_eval do
32
+ define_method(:read_friendly_attribute) do |attribute|
33
+ attribute.should == :foo
34
+ foo
35
+ end
36
+
37
+ define_method(:write_friendly_attribute) do |attribute, value|
38
+ value.tap do
39
+ attribute.should == :foo
40
+ value.should == foo
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ it "mounts the uploader" do
47
+ ar_model.should_receive(:mount_uploader).with(:foo, FakeUploader)
48
+ ar_model.friendly_mount_uploader :foo, FakeUploader
49
+ end
50
+
51
+ it "aliases #read_friendly_attribute to #read_uploader" do
52
+ ar_model.friendly_mount_uploader :foo, FakeUploader
53
+ ar_object = ar_model.new
54
+
55
+ ar_object.read_uploader(:foo).should == foo_value
56
+ end
57
+
58
+ it "aliases #read_friendly_attribute to #read_uploader" do
59
+ ar_model.friendly_mount_uploader :foo, FakeUploader
60
+ ar_object = ar_model.new
61
+
62
+ ar_object.write_uploader(:foo, foo_value).should == foo_value
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,118 @@
1
+ require 'spec_helper'
2
+
3
+ describe FriendlyAttributes::DetailsDelegator do
4
+ let(:details_delegator) { FriendlyAttributes::DetailsDelegator.new(friendly_model, ar_model, &initializer) }
5
+ let(:initializer) { proc {} }
6
+
7
+ let(:friendly_model) { Class.new }
8
+ let(:ar_model) { Class.new { include ActiveRecordFake } }
9
+
10
+ let(:ar_instance) { ar_model.new(:id => 42) }
11
+ let(:friendly_instance) { mock(friendly_model) }
12
+
13
+ describe "initialization" do
14
+ context "the Friendly model" do
15
+ before(:each) do
16
+ details_delegator
17
+ end
18
+
19
+ it "includes Friendly::Document" do
20
+ friendly_model.ancestors.should include(Friendly::Document)
21
+ end
22
+
23
+ it "adds the active_record_id attribute" do
24
+ friendly_model.attributes.should include(:active_record_id)
25
+ end
26
+
27
+ it "adds an index to active_record_id" do
28
+ friendly_model.storage_proxy.index_for_fields([:active_record_id]).should be_an_instance_of(Friendly::Index)
29
+ end
30
+ end
31
+
32
+ context "the ActiveRecord model" do
33
+ it "installs the update_friendly_details callback after_save" do
34
+ ar_model.should_receive(:after_save).with(:update_friendly_details)
35
+ details_delegator
36
+ end
37
+
38
+ it "installs the destroy_friendly_details callback after_destroy" do
39
+ ar_model.should_receive(:after_destroy).with(:destroy_friendly_details)
40
+ details_delegator
41
+ end
42
+
43
+ context ".details" do
44
+ before(:each) do
45
+ details_delegator
46
+ end
47
+
48
+ it "is defined" do
49
+ ar_instance.should respond_to(:details)
50
+ end
51
+
52
+ it "finds and memoizes the associated Friendly model" do
53
+ friendly_model.should_receive(:find_or_build_by_active_record_id).with(ar_instance.id).once.and_return(friendly_instance)
54
+ ar_instance.details.should == friendly_instance
55
+ ar_instance.details.should == friendly_instance
56
+ end
57
+ end
58
+ end
59
+
60
+ context "the initialization block" do
61
+ def yielded_inside(instance)
62
+ @yielded_instance = instance
63
+ end
64
+
65
+ let(:initializer) do
66
+ example = self
67
+
68
+ proc {
69
+ example.yielded_inside(self)
70
+ }
71
+ end
72
+
73
+ it "is instance evaled" do
74
+ details_delegator
75
+ @yielded_instance.should == details_delegator
76
+ end
77
+ end
78
+ end
79
+
80
+ describe "#delegated_method" do
81
+ before(:each) do
82
+ details_delegator
83
+ details_delegator.delegated_method(:some_method)
84
+ ar_instance.stub(:details => friendly_instance)
85
+ end
86
+
87
+ it "delegates the method to Friendly model" do
88
+ bar = mock
89
+ friendly_instance.should_receive(:some_method).with(:foo).and_return(bar)
90
+ ar_instance.some_method(:foo).should == bar
91
+ end
92
+ end
93
+
94
+ describe "#delegated_attribute" do
95
+ before(:each) do
96
+ details_delegator
97
+ details_delegator.delegated_attribute(:some_attribute, String)
98
+ ar_instance.stub(:details => friendly_instance)
99
+ end
100
+
101
+ it "adds an attribute to the Friendly model" do
102
+ friendly_model.attributes.should include(:some_attribute)
103
+ friendly_model.attributes[:some_attribute].type.should == String
104
+ end
105
+
106
+ it "delegates the reader to Friendly model" do
107
+ bar = mock
108
+ friendly_instance.should_receive(:some_attribute).and_return(bar)
109
+ ar_instance.some_attribute.should == bar
110
+ end
111
+
112
+ it "delegates the writer to Friendly model" do
113
+ bar = mock
114
+ friendly_instance.should_receive(:some_attribute=).with(:value)
115
+ ar_instance.some_attribute = :value
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ describe FriendlyAttributes::Details do
4
+ use_database_cleanup
5
+
6
+ describe ".find_or_build_by_active_record_id" do
7
+ context "when the record does not exist" do
8
+ it "builds a new record with the active_record_id" do
9
+ details = UserDetails.find_or_build_by_active_record_id(42)
10
+ details.should be_new_record
11
+ details.active_record_id.should == 42
12
+ end
13
+ end
14
+
15
+ context "when the record exists" do
16
+ let(:details) { UserDetails.create(:active_record_id => 42) }
17
+
18
+ before(:each) do
19
+ details.should_not be_new_record
20
+ end
21
+
22
+ it "finds the record" do
23
+ UserDetails.find_or_build_by_active_record_id(42).should == details
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,113 @@
1
+ require 'spec_helper'
2
+
3
+ describe FriendlyAttributes::InstanceMethods do
4
+ let(:object) { klass.new.tap { |o| o.stub(:details => details) } }
5
+ let(:klass) {
6
+ Class.new {
7
+ include ActiveRecordFake
8
+ include FriendlyAttributes::InstanceMethods
9
+ }
10
+ }
11
+ let(:details) { mock("details") }
12
+
13
+ describe "#read_friendly_attribute" do
14
+ it "reads the attribute from details" do
15
+ foo = mock
16
+
17
+ details.should_receive(:foo).and_return(foo)
18
+ object.read_friendly_attribute(:foo).should == foo
19
+ end
20
+ end
21
+
22
+ describe "#write_friendly_attribute" do
23
+ it "writes the attribute to details" do
24
+ foo = mock
25
+
26
+ details.should_receive(:foo=).with(:value)
27
+ object.write_friendly_attribute(:foo, :value)
28
+ end
29
+ end
30
+
31
+ describe "#details_present?" do
32
+ subject { object.details_present? }
33
+
34
+ context "when @details is present" do
35
+ before(:each) do
36
+ object.instance_variable_set(:"@details", mock)
37
+ end
38
+
39
+ it { should be_true }
40
+ end
41
+
42
+ context "when @details is not present" do
43
+ it { should be_false }
44
+ end
45
+ end
46
+
47
+ describe "#update_friendly_details" do
48
+ context "details are present" do
49
+ before(:each) do
50
+ object.id = 42
51
+ object.should_receive(:details_present?).and_return(true)
52
+ details.stub(:changed? => false, :active_record_id => object.id)
53
+ end
54
+
55
+ context "details.active_record_id does not equal object.id" do
56
+ before(:each) do
57
+ details.should_receive(:active_record_id).and_return(nil)
58
+ end
59
+
60
+ it "sets the active_record_id" do
61
+ details.should_receive(:active_record_id=).with(object.id)
62
+ object.update_friendly_details
63
+ end
64
+ end
65
+
66
+ context "details.active_record_id equals object.id" do
67
+ before(:each) do
68
+ details.should_receive(:active_record_id).and_return(object.id)
69
+ end
70
+
71
+ it "sets the active_record_id" do
72
+ details.should_not_receive(:active_record_id=)
73
+ object.update_friendly_details
74
+ end
75
+ end
76
+
77
+ context "the details have changed" do
78
+ before(:each) do
79
+ details.should_receive(:changed?).and_return(true)
80
+ end
81
+
82
+ it "saves the details" do
83
+ details.should_receive(:save)
84
+ object.update_friendly_details
85
+ end
86
+ end
87
+
88
+ context "the details have NOT changed" do
89
+ before(:each) do
90
+ details.should_receive(:changed?).and_return(false)
91
+ end
92
+
93
+ it "saves the details" do
94
+ details.should_not_receive(:save)
95
+ object.update_friendly_details
96
+ end
97
+ end
98
+ end
99
+
100
+ context "details are NOT present" do
101
+ before(:each) do
102
+ object.should_receive(:details_present?).and_return(false)
103
+ end
104
+ end
105
+ end
106
+
107
+ describe "#destroy_friendly_details" do
108
+ it "destroys the details" do
109
+ details.should_receive(:destroy)
110
+ object.destroy_friendly_details
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,70 @@
1
+ require 'spec_helper'
2
+
3
+ describe FriendlyAttributes do
4
+ use_database_cleanup
5
+
6
+ describe "creating" do
7
+ context "with Friendly attributes" do
8
+ let(:user) { User.create(:name => "Stan", :email => "stan@example.com") }
9
+
10
+ it "creates an associated Details model with the AR model" do
11
+ expect do
12
+ user
13
+ end.to change { UserDetails.count({}) }
14
+
15
+ user_detail = user.details
16
+ user_detail.name.should == "Stan"
17
+ end
18
+ end
19
+
20
+ context "without Friendly attributes" do
21
+ let(:user) { User.create(:email => "stan@example.com") }
22
+
23
+ it "does not create an associated Details model" do
24
+ expect do
25
+ user
26
+ end.to_not change { UserDetails.count({}) }
27
+ end
28
+ end
29
+ end
30
+
31
+ describe "updating" do
32
+ context "with Friendly attributes" do
33
+ let(:user) { User.create(:name => "Stan", :email => "stan@example.com") }
34
+
35
+ it "updates the attributes" do
36
+ user.update_attributes(:name => "Eric")
37
+ UserDetails.first({}).name.should == "Eric"
38
+ end
39
+ end
40
+
41
+ context "without Friendly attributes" do
42
+ let(:user) { User.create(:email => "stan@example.com") }
43
+
44
+ it "does not create an associated Details model, if no delegated attributes are updated" do
45
+ expect do
46
+ user.update_attributes(:email => "eric@example.com")
47
+ end.to_not change { UserDetails.count({}) }
48
+ end
49
+
50
+ it "creates an associated Details model, if delegated attributes are updated" do
51
+ expect do
52
+ user.update_attributes(:name => "Eric")
53
+ end.to change { UserDetails.count({}) }
54
+ user.name.should == "Eric"
55
+ end
56
+ end
57
+ end
58
+
59
+ describe "destroying" do
60
+ let(:user) { User.create(:name => "Stan", :email => "stan@example.com") }
61
+
62
+ it "destroys the associated Details model" do
63
+ user # create it first
64
+
65
+ expect do
66
+ user.destroy
67
+ end.to change { UserDetails.count({}) }.by(-1)
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,56 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+ require 'rspec'
4
+ require 'memcached'
5
+ require 'yajl'
6
+ require 'friendly_attributes'
7
+ require 'database_cleaner'
8
+
9
+ # Requires supporting files with custom matchers and macros, etc,
10
+ # in ./support/ and its subdirectories.
11
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
12
+
13
+ RSpec.configure do |config|
14
+ yaml = YAML.load(File.read(File.dirname(__FILE__) + "/config.yml"))
15
+ Friendly.configure yaml['test']
16
+
17
+ $db = Friendly.db
18
+
19
+ %w[user_details index_user_details_on_active_record_id].each do |table|
20
+ $db.drop_table(table) if $db.table_exists?(table)
21
+ end
22
+
23
+ datastore = Friendly::DataStore.new($db)
24
+ Friendly.datastore = datastore
25
+ $cache = Memcached.new
26
+ Friendly.cache = Friendly::Memcached.new($cache)
27
+
28
+ ActiveRecord::Base.logger = Friendly.db.logger = Logger.new(File.dirname(__FILE__) + "/../log/test.log")
29
+
30
+ ActiveRecord::Base.configurations = yaml
31
+ ActiveRecord::Base.establish_connection 'test'
32
+ ActiveRecord::Schema.define do
33
+ create_table :users, :force => true do |t|
34
+ t.string :email
35
+ end
36
+ end
37
+
38
+ class ::UserDetails < FriendlyAttributes::Details
39
+ end
40
+
41
+ class ::User < ActiveRecord::Base
42
+ include FriendlyAttributes
43
+
44
+ friendly_details UserDetails do
45
+ delegated_attribute :name, String
46
+ end
47
+ end
48
+
49
+ Friendly::Document.create_tables!
50
+
51
+ DatabaseCleaner.strategy = :truncation
52
+ DatabaseCleaner.clean_with(:truncation)
53
+
54
+ config.extend DatabaseCleanerHelpers
55
+ end
56
+
@@ -0,0 +1,18 @@
1
+ module ActiveRecordFake
2
+ def self.included(base)
3
+ base.instance_eval do
4
+ attr_accessor :id
5
+ end
6
+
7
+ (class << base; self; end).class_eval do
8
+ def after_save(*args); end
9
+ def after_destroy(*args); end
10
+ end
11
+ end
12
+
13
+ def initialize(attributes={})
14
+ attributes.each do |k, v|
15
+ send(:"#{k}=", v)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,11 @@
1
+ module DatabaseCleanerHelpers
2
+ def use_database_cleanup
3
+ before(:each) do
4
+ DatabaseCleaner.start
5
+ end
6
+
7
+ after(:each) do
8
+ DatabaseCleaner.clean
9
+ end
10
+ end
11
+ end
metadata ADDED
@@ -0,0 +1,412 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: friendly-attributes
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Istvan Hoka
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-11-20 00:00:00 +02:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ prerelease: false
23
+ type: :runtime
24
+ name: activerecord
25
+ version_requirements: &id001 !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ~>
29
+ - !ruby/object:Gem::Version
30
+ hash: 9
31
+ segments:
32
+ - 2
33
+ - 3
34
+ - 5
35
+ version: 2.3.5
36
+ requirement: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ prerelease: false
39
+ type: :runtime
40
+ name: friendly
41
+ version_requirements: &id002 !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ~>
45
+ - !ruby/object:Gem::Version
46
+ hash: 7
47
+ segments:
48
+ - 0
49
+ - 6
50
+ - 0
51
+ version: 0.6.0
52
+ requirement: *id002
53
+ - !ruby/object:Gem::Dependency
54
+ prerelease: false
55
+ type: :runtime
56
+ name: yajl-ruby
57
+ version_requirements: &id003 !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ~>
61
+ - !ruby/object:Gem::Version
62
+ hash: 13
63
+ segments:
64
+ - 0
65
+ - 7
66
+ - 7
67
+ version: 0.7.7
68
+ requirement: *id003
69
+ - !ruby/object:Gem::Dependency
70
+ prerelease: false
71
+ type: :runtime
72
+ name: memcached
73
+ version_requirements: &id004 !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ~>
77
+ - !ruby/object:Gem::Version
78
+ hash: 77
79
+ segments:
80
+ - 0
81
+ - 20
82
+ - 1
83
+ version: 0.20.1
84
+ requirement: *id004
85
+ - !ruby/object:Gem::Dependency
86
+ prerelease: false
87
+ type: :development
88
+ name: mysql
89
+ version_requirements: &id005 !ruby/object:Gem::Requirement
90
+ none: false
91
+ requirements:
92
+ - - ~>
93
+ - !ruby/object:Gem::Version
94
+ hash: 45
95
+ segments:
96
+ - 2
97
+ - 8
98
+ - 1
99
+ version: 2.8.1
100
+ requirement: *id005
101
+ - !ruby/object:Gem::Dependency
102
+ prerelease: false
103
+ type: :development
104
+ name: rspec
105
+ version_requirements: &id006 !ruby/object:Gem::Requirement
106
+ none: false
107
+ requirements:
108
+ - - ~>
109
+ - !ruby/object:Gem::Version
110
+ hash: 11
111
+ segments:
112
+ - 2
113
+ - 1
114
+ - 0
115
+ version: 2.1.0
116
+ requirement: *id006
117
+ - !ruby/object:Gem::Dependency
118
+ prerelease: false
119
+ type: :development
120
+ name: bundler
121
+ version_requirements: &id007 !ruby/object:Gem::Requirement
122
+ none: false
123
+ requirements:
124
+ - - ~>
125
+ - !ruby/object:Gem::Version
126
+ hash: 23
127
+ segments:
128
+ - 1
129
+ - 0
130
+ - 0
131
+ version: 1.0.0
132
+ requirement: *id007
133
+ - !ruby/object:Gem::Dependency
134
+ prerelease: false
135
+ type: :development
136
+ name: jeweler
137
+ version_requirements: &id008 !ruby/object:Gem::Requirement
138
+ none: false
139
+ requirements:
140
+ - - ~>
141
+ - !ruby/object:Gem::Version
142
+ hash: 1
143
+ segments:
144
+ - 1
145
+ - 5
146
+ - 1
147
+ version: 1.5.1
148
+ requirement: *id008
149
+ - !ruby/object:Gem::Dependency
150
+ prerelease: false
151
+ type: :development
152
+ name: rcov
153
+ version_requirements: &id009 !ruby/object:Gem::Requirement
154
+ none: false
155
+ requirements:
156
+ - - ">="
157
+ - !ruby/object:Gem::Version
158
+ hash: 3
159
+ segments:
160
+ - 0
161
+ version: "0"
162
+ requirement: *id009
163
+ - !ruby/object:Gem::Dependency
164
+ prerelease: false
165
+ type: :development
166
+ name: database_cleaner
167
+ version_requirements: &id010 !ruby/object:Gem::Requirement
168
+ none: false
169
+ requirements:
170
+ - - ~>
171
+ - !ruby/object:Gem::Version
172
+ hash: 11
173
+ segments:
174
+ - 0
175
+ - 5
176
+ - 0
177
+ version: 0.5.0
178
+ requirement: *id010
179
+ - !ruby/object:Gem::Dependency
180
+ prerelease: false
181
+ type: :runtime
182
+ name: activerecord
183
+ version_requirements: &id011 !ruby/object:Gem::Requirement
184
+ none: false
185
+ requirements:
186
+ - - ~>
187
+ - !ruby/object:Gem::Version
188
+ hash: 9
189
+ segments:
190
+ - 2
191
+ - 3
192
+ - 5
193
+ version: 2.3.5
194
+ requirement: *id011
195
+ - !ruby/object:Gem::Dependency
196
+ prerelease: false
197
+ type: :runtime
198
+ name: friendly
199
+ version_requirements: &id012 !ruby/object:Gem::Requirement
200
+ none: false
201
+ requirements:
202
+ - - ~>
203
+ - !ruby/object:Gem::Version
204
+ hash: 7
205
+ segments:
206
+ - 0
207
+ - 6
208
+ - 0
209
+ version: 0.6.0
210
+ requirement: *id012
211
+ - !ruby/object:Gem::Dependency
212
+ prerelease: false
213
+ type: :runtime
214
+ name: yajl-ruby
215
+ version_requirements: &id013 !ruby/object:Gem::Requirement
216
+ none: false
217
+ requirements:
218
+ - - ~>
219
+ - !ruby/object:Gem::Version
220
+ hash: 13
221
+ segments:
222
+ - 0
223
+ - 7
224
+ - 7
225
+ version: 0.7.7
226
+ requirement: *id013
227
+ - !ruby/object:Gem::Dependency
228
+ prerelease: false
229
+ type: :runtime
230
+ name: memcached
231
+ version_requirements: &id014 !ruby/object:Gem::Requirement
232
+ none: false
233
+ requirements:
234
+ - - ~>
235
+ - !ruby/object:Gem::Version
236
+ hash: 77
237
+ segments:
238
+ - 0
239
+ - 20
240
+ - 1
241
+ version: 0.20.1
242
+ requirement: *id014
243
+ - !ruby/object:Gem::Dependency
244
+ prerelease: false
245
+ type: :development
246
+ name: mysql
247
+ version_requirements: &id015 !ruby/object:Gem::Requirement
248
+ none: false
249
+ requirements:
250
+ - - ~>
251
+ - !ruby/object:Gem::Version
252
+ hash: 45
253
+ segments:
254
+ - 2
255
+ - 8
256
+ - 1
257
+ version: 2.8.1
258
+ requirement: *id015
259
+ - !ruby/object:Gem::Dependency
260
+ prerelease: false
261
+ type: :development
262
+ name: rspec
263
+ version_requirements: &id016 !ruby/object:Gem::Requirement
264
+ none: false
265
+ requirements:
266
+ - - ~>
267
+ - !ruby/object:Gem::Version
268
+ hash: 11
269
+ segments:
270
+ - 2
271
+ - 1
272
+ - 0
273
+ version: 2.1.0
274
+ requirement: *id016
275
+ - !ruby/object:Gem::Dependency
276
+ prerelease: false
277
+ type: :development
278
+ name: bundler
279
+ version_requirements: &id017 !ruby/object:Gem::Requirement
280
+ none: false
281
+ requirements:
282
+ - - ~>
283
+ - !ruby/object:Gem::Version
284
+ hash: 23
285
+ segments:
286
+ - 1
287
+ - 0
288
+ - 0
289
+ version: 1.0.0
290
+ requirement: *id017
291
+ - !ruby/object:Gem::Dependency
292
+ prerelease: false
293
+ type: :development
294
+ name: jeweler
295
+ version_requirements: &id018 !ruby/object:Gem::Requirement
296
+ none: false
297
+ requirements:
298
+ - - ~>
299
+ - !ruby/object:Gem::Version
300
+ hash: 1
301
+ segments:
302
+ - 1
303
+ - 5
304
+ - 1
305
+ version: 1.5.1
306
+ requirement: *id018
307
+ - !ruby/object:Gem::Dependency
308
+ prerelease: false
309
+ type: :development
310
+ name: rcov
311
+ version_requirements: &id019 !ruby/object:Gem::Requirement
312
+ none: false
313
+ requirements:
314
+ - - ">="
315
+ - !ruby/object:Gem::Version
316
+ hash: 3
317
+ segments:
318
+ - 0
319
+ version: "0"
320
+ requirement: *id019
321
+ - !ruby/object:Gem::Dependency
322
+ prerelease: false
323
+ type: :development
324
+ name: database_cleaner
325
+ version_requirements: &id020 !ruby/object:Gem::Requirement
326
+ none: false
327
+ requirements:
328
+ - - ~>
329
+ - !ruby/object:Gem::Version
330
+ hash: 11
331
+ segments:
332
+ - 0
333
+ - 5
334
+ - 0
335
+ version: 0.5.0
336
+ requirement: *id020
337
+ description: Pattern to add fields to ActiveRecord models, using an associated document, without needing schema migrations.
338
+ email: istvan.hoka@gmail.com
339
+ executables: []
340
+
341
+ extensions: []
342
+
343
+ extra_rdoc_files:
344
+ - LICENSE.txt
345
+ - README.rdoc
346
+ files:
347
+ - .document
348
+ - Gemfile
349
+ - Gemfile.lock
350
+ - LICENSE.txt
351
+ - README.rdoc
352
+ - Rakefile
353
+ - VERSION
354
+ - friendly-attributes.gemspec
355
+ - lib/friendly-attributes.rb
356
+ - lib/friendly_attributes.rb
357
+ - lib/friendly_attributes/class_methods.rb
358
+ - lib/friendly_attributes/details.rb
359
+ - lib/friendly_attributes/details_delegator.rb
360
+ - lib/friendly_attributes/instance_methods.rb
361
+ - spec/config.yml.example
362
+ - spec/friendly_attributes/class_methods_spec.rb
363
+ - spec/friendly_attributes/details_delegator_spec.rb
364
+ - spec/friendly_attributes/details_spec.rb
365
+ - spec/friendly_attributes/instance_methods_spec.rb
366
+ - spec/friendly_attributes_spec.rb
367
+ - spec/spec_helper.rb
368
+ - spec/support/active_record_fake.rb
369
+ - spec/support/database_cleaner_helpers.rb
370
+ has_rdoc: true
371
+ homepage: http://github.com/ihoka/friendly-attributes
372
+ licenses:
373
+ - MIT
374
+ post_install_message:
375
+ rdoc_options: []
376
+
377
+ require_paths:
378
+ - lib
379
+ required_ruby_version: !ruby/object:Gem::Requirement
380
+ none: false
381
+ requirements:
382
+ - - ">="
383
+ - !ruby/object:Gem::Version
384
+ hash: 3
385
+ segments:
386
+ - 0
387
+ version: "0"
388
+ required_rubygems_version: !ruby/object:Gem::Requirement
389
+ none: false
390
+ requirements:
391
+ - - ">="
392
+ - !ruby/object:Gem::Version
393
+ hash: 3
394
+ segments:
395
+ - 0
396
+ version: "0"
397
+ requirements: []
398
+
399
+ rubyforge_project:
400
+ rubygems_version: 1.3.7
401
+ signing_key:
402
+ specification_version: 3
403
+ summary: Extend ActiveRecord models using Friendly ORM delegate models
404
+ test_files:
405
+ - spec/friendly_attributes/class_methods_spec.rb
406
+ - spec/friendly_attributes/details_delegator_spec.rb
407
+ - spec/friendly_attributes/details_spec.rb
408
+ - spec/friendly_attributes/instance_methods_spec.rb
409
+ - spec/friendly_attributes_spec.rb
410
+ - spec/spec_helper.rb
411
+ - spec/support/active_record_fake.rb
412
+ - spec/support/database_cleaner_helpers.rb