friendly-attributes 0.7.1.1 → 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|