friendly-attributes 0.7.1.1 → 0.7.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +45 -0
- data/.ruby-version +1 -0
- data/.travis.yml +6 -0
- data/.yardopts +1 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +45 -0
- data/README.md +3 -0
- data/Rakefile +25 -0
- data/VERSION +1 -0
- data/friendly-attributes.gemspec +30 -0
- data/spec/config.sample.yml +6 -0
- data/spec/friendly_attributes/base_spec.rb +122 -0
- data/spec/friendly_attributes/class_methods_spec.rb +124 -0
- data/spec/friendly_attributes/configuration_spec.rb +99 -0
- data/spec/friendly_attributes/details_delegator_spec.rb +210 -0
- data/spec/friendly_attributes/instance_methods_spec.rb +240 -0
- data/spec/friendly_attributes_spec.rb +152 -0
- data/spec/spec_helper.rb +81 -0
- data/spec/support/active_record_fake.rb +18 -0
- data/spec/support/database_cleaner_helpers.rb +11 -0
- data/spec/support/friendly_fake.rb +15 -0
- metadata +129 -376
data/.gitignore
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
# rcov generated
|
2
|
+
coverage
|
3
|
+
|
4
|
+
# rdoc generated
|
5
|
+
rdoc
|
6
|
+
|
7
|
+
# yard generated
|
8
|
+
doc
|
9
|
+
.yardoc
|
10
|
+
|
11
|
+
# bundler
|
12
|
+
.bundle
|
13
|
+
|
14
|
+
# jeweler generated
|
15
|
+
pkg
|
16
|
+
|
17
|
+
# Have editor/IDE/OS specific files you need to ignore? Consider using a global gitignore:
|
18
|
+
#
|
19
|
+
# * Create a file at ~/.gitignore
|
20
|
+
# * Include files you want ignored
|
21
|
+
# * Run: git config --global core.excludesfile ~/.gitignore
|
22
|
+
#
|
23
|
+
# After doing this, these files will be ignored in all your git projects,
|
24
|
+
# saving you from having to 'pollute' every project you touch with them
|
25
|
+
#
|
26
|
+
# Not sure what to needs to be ignored for particular editors/OSes? Here's some ideas to get you started. (Remember, remove the leading # of the line)
|
27
|
+
#
|
28
|
+
# For MacOS:
|
29
|
+
#
|
30
|
+
#.DS_Store
|
31
|
+
#
|
32
|
+
# For TextMate
|
33
|
+
#*.tmproj
|
34
|
+
#tmtags
|
35
|
+
#
|
36
|
+
# For emacs:
|
37
|
+
#*~
|
38
|
+
#\#*
|
39
|
+
#.\#*
|
40
|
+
#
|
41
|
+
# For vim:
|
42
|
+
#*.swp
|
43
|
+
spec/config.yml
|
44
|
+
log
|
45
|
+
friendly-attributes.tmproj
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby-1.9.3
|
data/.travis.yml
ADDED
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--no-private lib/**/*.rb - CHANGELOG.md LICENSE.txt
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
friendly-attributes (0.7.2)
|
5
|
+
activerecord (~> 2.3.18)
|
6
|
+
ihoka-friendly (~> 0.7.0)
|
7
|
+
memcached (~> 0.20.1)
|
8
|
+
yajl-ruby (~> 0.7.7)
|
9
|
+
|
10
|
+
GEM
|
11
|
+
remote: http://rubygems.org/
|
12
|
+
specs:
|
13
|
+
activerecord (2.3.18)
|
14
|
+
activesupport (= 2.3.18)
|
15
|
+
activesupport (2.3.18)
|
16
|
+
database_cleaner (0.5.2)
|
17
|
+
diff-lcs (1.1.3)
|
18
|
+
ihoka-friendly (0.7.0)
|
19
|
+
activesupport (~> 2.3.18)
|
20
|
+
rufus-json (~> 0.2.3)
|
21
|
+
sequel (~> 3.16.0)
|
22
|
+
will_paginate (~> 2.3.15)
|
23
|
+
memcached (0.20.1)
|
24
|
+
mysql (2.8.1)
|
25
|
+
rspec (2.5.0)
|
26
|
+
rspec-core (~> 2.5.0)
|
27
|
+
rspec-expectations (~> 2.5.0)
|
28
|
+
rspec-mocks (~> 2.5.0)
|
29
|
+
rspec-core (2.5.2)
|
30
|
+
rspec-expectations (2.5.0)
|
31
|
+
diff-lcs (~> 1.1.2)
|
32
|
+
rspec-mocks (2.5.0)
|
33
|
+
rufus-json (0.2.7)
|
34
|
+
sequel (3.16.0)
|
35
|
+
will_paginate (2.3.16)
|
36
|
+
yajl-ruby (0.7.9)
|
37
|
+
|
38
|
+
PLATFORMS
|
39
|
+
ruby
|
40
|
+
|
41
|
+
DEPENDENCIES
|
42
|
+
database_cleaner (~> 0.5.0)
|
43
|
+
friendly-attributes!
|
44
|
+
mysql (~> 2.8.1)
|
45
|
+
rspec (~> 2.5.0)
|
data/README.md
CHANGED
data/Rakefile
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
|
3
|
+
require 'rspec/core'
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
6
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
7
|
+
end
|
8
|
+
|
9
|
+
RSpec::Core::RakeTask.new(:rcov) do |spec|
|
10
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
11
|
+
spec.rcov = true
|
12
|
+
spec.rcov_opts = %w{-I spec:lib --exclude gems\/,spec\/}
|
13
|
+
end
|
14
|
+
|
15
|
+
task :default => :spec
|
16
|
+
|
17
|
+
require 'rdoc/task'
|
18
|
+
Rake::RDocTask.new do |rdoc|
|
19
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
20
|
+
|
21
|
+
rdoc.rdoc_dir = 'rdoc'
|
22
|
+
rdoc.title = "friendly-attributes #{version}"
|
23
|
+
rdoc.rdoc_files.include('README*')
|
24
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
25
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.7.2
|
@@ -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
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = %q{friendly-attributes}
|
7
|
+
s.version = "0.7.1.1"
|
8
|
+
|
9
|
+
s.authors = ["Istvan Hoka"]
|
10
|
+
s.date = %q{2011-04-10}
|
11
|
+
s.description = %q{Pattern to add fields to ActiveRecord models, using an associated document, without needing schema migrations.}
|
12
|
+
s.email = %q{istvan.hoka@gmail.com}
|
13
|
+
s.homepage = %q{http://github.com/ihoka/friendly-attributes}
|
14
|
+
s.licenses = ["MIT"]
|
15
|
+
s.summary = %q{Extend ActiveRecord models using Friendly ORM delegate models}
|
16
|
+
s.version = File.read(File.dirname(__FILE__) + '/VERSION')
|
17
|
+
|
18
|
+
s.add_development_dependency 'mysql', '~> 2.8.1'
|
19
|
+
s.add_development_dependency 'rspec', '~> 2.5.0'
|
20
|
+
s.add_development_dependency 'database_cleaner', '~> 0.5.0'
|
21
|
+
s.add_dependency 'ihoka-friendly', '~> 0.7.0'
|
22
|
+
s.add_dependency 'activerecord', '~> 2.3.18'
|
23
|
+
s.add_dependency 'yajl-ruby', '~> 0.7.7'
|
24
|
+
s.add_dependency 'memcached', '~> 0.20.1'
|
25
|
+
|
26
|
+
s.files = `git ls-files`.split("\n")
|
27
|
+
s.test_files = `git ls-files -- spec/*`.split("\n")
|
28
|
+
s.require_paths = ["lib"]
|
29
|
+
end
|
30
|
+
|
@@ -0,0 +1,122 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe FriendlyAttributes::Base do
|
4
|
+
use_database_cleanup
|
5
|
+
|
6
|
+
let(:friendly_model) {
|
7
|
+
Class.new(FriendlyAttributes::Base) do
|
8
|
+
cattr_accessor :active_record_key
|
9
|
+
end.tap { |c| c.active_record_key = active_record_key }
|
10
|
+
}
|
11
|
+
let(:active_record_key) { :ar_id }
|
12
|
+
let(:details) { mock(FriendlyAttributes::Base) }
|
13
|
+
|
14
|
+
describe ".find_or_build_by_active_record_id" do
|
15
|
+
|
16
|
+
context "when the record does not exist" do
|
17
|
+
before(:each) do
|
18
|
+
friendly_model.should_receive(:first).with(:ar_id => 42).and_return(nil)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "builds a new record with the active_record_id" do
|
22
|
+
friendly_model.should_receive(:new).with(:ar_id => 42).and_return(details)
|
23
|
+
friendly_model.find_or_build_by_active_record_id(42).should == details
|
24
|
+
end
|
25
|
+
|
26
|
+
it "with options present, it builds a new record with the options and active_record_id" do
|
27
|
+
friendly_model.should_receive(:new).with(:ar_id => 42, :foo => "foo").and_return(details)
|
28
|
+
friendly_model.find_or_build_by_active_record_id(42, :foo => "foo").should == details
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context "when the record exists" do
|
33
|
+
it "finds the record" do
|
34
|
+
friendly_model.should_receive(:first).with(:ar_id => 42).and_return(details)
|
35
|
+
friendly_model.find_or_build_by_active_record_id(42).should == details
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "#write_active_record_id" do
|
41
|
+
let(:friendly_instance) { friendly_model.new }
|
42
|
+
|
43
|
+
it "sets the active_record_id using the configured attribute writer" do
|
44
|
+
friendly_instance.should_receive(:"#{active_record_key}=").with(42).and_return(42)
|
45
|
+
friendly_instance.write_active_record_id(42).should == 42
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "#read_active_record_id" do
|
50
|
+
let(:friendly_instance) { friendly_model.new }
|
51
|
+
|
52
|
+
it "sets the active_record_id using the configured attribute writer" do
|
53
|
+
friendly_instance.should_receive(active_record_key).and_return(42)
|
54
|
+
friendly_instance.read_active_record_id.should == 42
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "#update_if_changed_with_model" do
|
59
|
+
let(:friendly_instance) { friendly_model.new }
|
60
|
+
|
61
|
+
before(:each) do
|
62
|
+
friendly_instance.stub(:save => false, :changed? => false, :read_active_record_id => 42)
|
63
|
+
end
|
64
|
+
|
65
|
+
context "when the active_record_id is not set" do
|
66
|
+
before(:each) do
|
67
|
+
friendly_instance.should_receive(:read_active_record_id).and_return(nil)
|
68
|
+
end
|
69
|
+
|
70
|
+
it "sets the active_record_id" do
|
71
|
+
friendly_instance.should_receive(:write_active_record_id).with(42)
|
72
|
+
friendly_instance.update_if_changed_with_model(42)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context "when the active_record_id is set" do
|
77
|
+
before(:each) do
|
78
|
+
friendly_instance.should_receive(:read_active_record_id).and_return(42)
|
79
|
+
end
|
80
|
+
|
81
|
+
it "does not reset the active_record_id" do
|
82
|
+
friendly_instance.should_not_receive(:write_active_record_id)
|
83
|
+
friendly_instance.update_if_changed_with_model(42)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
context "when changed" do
|
88
|
+
before(:each) do
|
89
|
+
friendly_instance.should_receive(:changed?).and_return(true)
|
90
|
+
end
|
91
|
+
|
92
|
+
it "saves the record" do
|
93
|
+
friendly_instance.should_receive(:save).and_return(true)
|
94
|
+
friendly_instance.update_if_changed_with_model(42).should be_true
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
context "when not changed" do
|
99
|
+
before(:each) do
|
100
|
+
friendly_instance.should_receive(:changed?).and_return(false)
|
101
|
+
end
|
102
|
+
|
103
|
+
it "saves the record" do
|
104
|
+
friendly_instance.should_not_receive(:save)
|
105
|
+
friendly_instance.update_if_changed_with_model(42).should be_false
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
describe "#attributes" do
|
111
|
+
let(:details) { UserDetails.create(attributes) }
|
112
|
+
let(:attributes) { { :name => "Foo", :birth_year => 1970, :shoe_size => 42, :subscribed => true, UserDetails.active_record_key => 55 } }
|
113
|
+
|
114
|
+
it "returns a hash of all the model's attributes" do
|
115
|
+
actual = details.attributes
|
116
|
+
actual.should include(attributes)
|
117
|
+
actual[:created_at].should be_an_instance_of(Time)
|
118
|
+
actual[:updated_at].should be_an_instance_of(Time)
|
119
|
+
actual[:id].should be_an_instance_of(Friendly::UUID)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe FriendlyAttributes::ClassMethods do
|
4
|
+
let(:friendly_model) { mock_friendly_model }
|
5
|
+
let(:ar_model) {
|
6
|
+
Class.new {
|
7
|
+
include ActiveRecordFake
|
8
|
+
extend FriendlyAttributes::ClassMethods
|
9
|
+
}
|
10
|
+
}
|
11
|
+
|
12
|
+
let(:details_delegator) { mock(FriendlyAttributes::DetailsDelegator) }
|
13
|
+
|
14
|
+
class FakeUploader; end
|
15
|
+
|
16
|
+
describe ".friendly_details" do
|
17
|
+
let(:attributes) do
|
18
|
+
{
|
19
|
+
String => :foo,
|
20
|
+
Integer => [:bar, :baz]
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
before(:each) do
|
25
|
+
details_delegator.stub(:setup_delegated_attributes)
|
26
|
+
end
|
27
|
+
|
28
|
+
context "with an initializer block" do
|
29
|
+
def yielded_inside(instance)
|
30
|
+
@yielded_instance = instance
|
31
|
+
end
|
32
|
+
|
33
|
+
let(:initializer) do
|
34
|
+
example = self
|
35
|
+
|
36
|
+
proc {
|
37
|
+
example.yielded_inside(self)
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
it "instance_evals the initializer block on the DetailsDelegator instance" do
|
42
|
+
FriendlyAttributes::DetailsDelegator.
|
43
|
+
should_receive(:new).
|
44
|
+
with(friendly_model, ar_model, attributes, {}).
|
45
|
+
and_return(details_delegator)
|
46
|
+
|
47
|
+
ar_model.friendly_details(friendly_model, attributes, &initializer).should == details_delegator
|
48
|
+
@yielded_instance.should == details_delegator
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context "with attributes and options" do
|
53
|
+
let(:options) { { :active_record_key => :user_id } }
|
54
|
+
|
55
|
+
before(:each) do
|
56
|
+
FriendlyAttributes::DetailsDelegator.stub(:new => details_delegator)
|
57
|
+
end
|
58
|
+
|
59
|
+
def do_details
|
60
|
+
ar_model.friendly_details(friendly_model, attributes, options).should == details_delegator
|
61
|
+
end
|
62
|
+
|
63
|
+
it "instantiates a new DetailsDelegator" do
|
64
|
+
FriendlyAttributes::DetailsDelegator.should_receive(:new).with(friendly_model, ar_model, attributes, options).and_return(details_delegator)
|
65
|
+
do_details
|
66
|
+
end
|
67
|
+
|
68
|
+
it "sets up the delegated attributes" do
|
69
|
+
details_delegator.should_receive(:setup_delegated_attributes)
|
70
|
+
do_details
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
context "without a block" do
|
75
|
+
it "instantiates a new DetailsDelegator" do
|
76
|
+
FriendlyAttributes::DetailsDelegator.should_receive(:new).with(friendly_model, ar_model, attributes, {}).and_return(details_delegator)
|
77
|
+
ar_model.friendly_details(friendly_model, attributes).should == details_delegator
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe ".friendly_mount_uploader" do
|
83
|
+
let(:foo_value) { mock("Friendly attribute value") }
|
84
|
+
|
85
|
+
before(:each) do
|
86
|
+
ar_model.stub(:mount_uploader => nil)
|
87
|
+
|
88
|
+
foo = foo_value
|
89
|
+
|
90
|
+
ar_model.class_eval do
|
91
|
+
define_method(:read_friendly_attribute) do |attribute|
|
92
|
+
attribute.should == :foo
|
93
|
+
foo
|
94
|
+
end
|
95
|
+
|
96
|
+
define_method(:write_friendly_attribute) do |attribute, value|
|
97
|
+
value.tap do
|
98
|
+
attribute.should == :foo
|
99
|
+
value.should == foo
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
it "mounts the uploader" do
|
106
|
+
ar_model.should_receive(:mount_uploader).with(:foo, FakeUploader)
|
107
|
+
ar_model.friendly_mount_uploader :foo, FakeUploader
|
108
|
+
end
|
109
|
+
|
110
|
+
it "aliases #read_friendly_attribute to #read_uploader" do
|
111
|
+
ar_model.friendly_mount_uploader :foo, FakeUploader
|
112
|
+
ar_object = ar_model.new
|
113
|
+
|
114
|
+
ar_object.read_uploader(:foo).should == foo_value
|
115
|
+
end
|
116
|
+
|
117
|
+
it "aliases #read_friendly_attribute to #read_uploader" do
|
118
|
+
ar_model.friendly_mount_uploader :foo, FakeUploader
|
119
|
+
ar_object = ar_model.new
|
120
|
+
|
121
|
+
ar_object.write_uploader(:foo, foo_value).should == foo_value
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe FriendlyAttributes::Configuration do
|
4
|
+
subject { configuration }
|
5
|
+
|
6
|
+
let(:configuration) { FriendlyAttributes::Configuration.new(active_record_model) }
|
7
|
+
let(:active_record_model) { User }
|
8
|
+
|
9
|
+
describe "#initialize" do
|
10
|
+
its(:model) { should == active_record_model }
|
11
|
+
its(:details_delegators) { should == [] }
|
12
|
+
its(:attributes) { should == {} }
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "#add" do
|
16
|
+
let(:delegator) { mock(FriendlyAttributes::DetailsDelegator, :delegated_attributes => delegated_attributes, :friendly_model => UserDetails) }
|
17
|
+
let(:delegated_attributes) { { :foo => Integer, :bar => String } }
|
18
|
+
|
19
|
+
it "adds a delegator to the details_delegators collection" do
|
20
|
+
configuration.add(delegator)
|
21
|
+
configuration.details_delegators.should == [delegator]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "#add_attribute" do
|
26
|
+
it "adds the attribute to the attributes collection" do
|
27
|
+
configuration.add_attribute(:foo, UserDetails)
|
28
|
+
configuration.attributes.should == { :foo => UserDetails }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "Configuration with delegators added" do
|
33
|
+
let(:friendly_models) { [UserDetails, UserSecondDetails] }
|
34
|
+
let(:delegated_attributes) {
|
35
|
+
{
|
36
|
+
UserDetails => { :foo => Integer, :bar => String },
|
37
|
+
UserSecondDetails => { :baz => Date }
|
38
|
+
}
|
39
|
+
}
|
40
|
+
let(:details_delegators) {
|
41
|
+
friendly_models.map do |fm|
|
42
|
+
mock(FriendlyAttributes::DetailsDelegator,
|
43
|
+
:friendly_model => fm,
|
44
|
+
:delegated_attributes => delegated_attributes[fm])
|
45
|
+
end
|
46
|
+
}
|
47
|
+
|
48
|
+
before(:each) do
|
49
|
+
details_delegators.each do |delegator|
|
50
|
+
configuration.add(delegator)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe "#model_for_attribute" do
|
55
|
+
it "returns the FriendlyAttributes model associated with the attribute" do
|
56
|
+
configuration.add_attribute(:foo, UserDetails)
|
57
|
+
configuration.model_for_attribute(:foo).should == UserDetails
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe "#friendly_models" do
|
62
|
+
its(:friendly_models) { should == friendly_models }
|
63
|
+
end
|
64
|
+
|
65
|
+
describe "#map_models" do
|
66
|
+
let(:map_model_map) {
|
67
|
+
{}.tap do |h|
|
68
|
+
friendly_models.each_with_index do |m, i|
|
69
|
+
h[m] = i
|
70
|
+
end
|
71
|
+
end
|
72
|
+
}
|
73
|
+
|
74
|
+
it "maps over the friendly models" do
|
75
|
+
configuration.map_models do |model|
|
76
|
+
map_model_map[model]
|
77
|
+
end.sort.should == map_model_map.values.sort
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe "#each_model" do
|
82
|
+
let(:each_model_map) {
|
83
|
+
{}.tap do |h|
|
84
|
+
friendly_models.each_with_index do |m, i|
|
85
|
+
h[m] = i
|
86
|
+
end
|
87
|
+
end
|
88
|
+
}
|
89
|
+
|
90
|
+
it "iterates over the friendly models" do
|
91
|
+
result = []
|
92
|
+
configuration.each_model do |model|
|
93
|
+
result << each_model_map[model]
|
94
|
+
end
|
95
|
+
result.sort.should == each_model_map.values.sort
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|