expose_association 0.0.1
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 +17 -0
- data/.rspec +1 -0
- data/Gemfile +4 -0
- data/README.md +48 -0
- data/Rakefile +1 -0
- data/expose_association.gemspec +22 -0
- data/lib/expose_association/base.rb +88 -0
- data/lib/expose_association/railtie.rb +9 -0
- data/lib/expose_association/version.rb +3 -0
- data/lib/expose_association.rb +3 -0
- data/spec/expose_association/base_spec.rb +92 -0
- data/spec/spec_helper.rb +18 -0
- metadata +105 -0
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# Expose Association
|
2
|
+
|
3
|
+
|
4
|
+
## Usage:
|
5
|
+
|
6
|
+
Suppose you have this two classes:
|
7
|
+
|
8
|
+
<pre>
|
9
|
+
class Address < ActiveRecord::Base
|
10
|
+
belongs_to :contact
|
11
|
+
end
|
12
|
+
|
13
|
+
class Contact < ActiveRecord::Base
|
14
|
+
|
15
|
+
has_one :primary_address, :class_name => 'Address'
|
16
|
+
expose_association :primary_address, prefix: true, class: Address do
|
17
|
+
expose :address_line_1, :address_line_2, :city
|
18
|
+
expose :phone_number, prefix: false
|
19
|
+
expose :country, prefix: 'primary'
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
</pre>
|
25
|
+
|
26
|
+
You'll be able to use this class like this:
|
27
|
+
|
28
|
+
<pre>
|
29
|
+
contact = Contact.new
|
30
|
+
contact.primary_address_address_line_1 = "122 Fake St."
|
31
|
+
contact.primary_address_address_line_2 = "Apt. 45"
|
32
|
+
contact.primary_address_city = "Beverly Hill"
|
33
|
+
contact.phone_number = "123-123-1234"
|
34
|
+
contact.primary_country = "Argentina"
|
35
|
+
contact.save
|
36
|
+
</pre>
|
37
|
+
|
38
|
+
Specs?
|
39
|
+
|
40
|
+
<pre>
|
41
|
+
contact.primary_address.should be_a_kind_of(Address)
|
42
|
+
...
|
43
|
+
|
44
|
+
# Check te real ones on the spec folder
|
45
|
+
|
46
|
+
</pre>
|
47
|
+
|
48
|
+
MIT License.
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/expose_association/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Lucas Florio"]
|
6
|
+
gem.email = ["lucasefe@gmail.com"]
|
7
|
+
gem.description = %q{Easy exposition of has_one associations at the object
|
8
|
+
level, to avoid nesting.}
|
9
|
+
gem.summary = %q{...}
|
10
|
+
gem.homepage = ""
|
11
|
+
|
12
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
13
|
+
gem.files = `git ls-files`.split("\n")
|
14
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
15
|
+
gem.name = "expose_association"
|
16
|
+
gem.require_paths = ["lib"]
|
17
|
+
gem.version = ExposeAssociation::VERSION
|
18
|
+
gem.add_dependency 'activerecord'
|
19
|
+
gem.add_development_dependency 'rspec'
|
20
|
+
gem.add_development_dependency 'sqlite3'
|
21
|
+
gem.add_development_dependency 'shoulda'
|
22
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module ExposeAssociation
|
2
|
+
module Base
|
3
|
+
def expose_association association_name, options = {}, &block
|
4
|
+
exposer = AssociationExposer.new(self, association_name, options)
|
5
|
+
setup_class_for_exposition(exposer)
|
6
|
+
if block_given?
|
7
|
+
exposer.instance_eval(&block)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def setup_class_for_exposition(exposer)
|
12
|
+
# allowing mass assignment of the has_one association
|
13
|
+
self.attr_accessible exposer.association_attributes
|
14
|
+
self.accepts_nested_attributes_for exposer.association_name
|
15
|
+
self.send :alias_method, "old_#{exposer.association_name}", exposer.association_name
|
16
|
+
self.send :alias_method, "old_#{exposer.association_name}=", "#{exposer.association_name}="
|
17
|
+
self.class_eval <<-CODE
|
18
|
+
def #{exposer.association_name}
|
19
|
+
#{exposer.association_setup_method}
|
20
|
+
end
|
21
|
+
|
22
|
+
def #{exposer.association_setup_method}
|
23
|
+
self.old_#{exposer.association_name} ||= #{exposer.association_class}.new
|
24
|
+
end
|
25
|
+
CODE
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class AssociationExposer
|
30
|
+
|
31
|
+
attr_reader :association_name, :association_class
|
32
|
+
|
33
|
+
def initialize klass, association_name, options = {}
|
34
|
+
@klass = klass
|
35
|
+
options[:class] ||= association_name.to_s.camelize.constantize
|
36
|
+
@association_class = options[:class]
|
37
|
+
@association_name = association_name
|
38
|
+
@options = options
|
39
|
+
end
|
40
|
+
|
41
|
+
def expose *attributes
|
42
|
+
options = attributes.last.is_a?(Hash) ? attributes.pop : {}
|
43
|
+
attributes.each do |attribute|
|
44
|
+
expose_attribute attribute, options
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def expose_attribute name, options = {}
|
49
|
+
@klass.delegate name, "#{name}=",
|
50
|
+
to: association_setup_method,
|
51
|
+
prefix: prefix_for(options[:prefix])
|
52
|
+
|
53
|
+
@klass.attr_accessible attr_name_for(name, options[:prefix])
|
54
|
+
end
|
55
|
+
|
56
|
+
def attr_name_for(name, prefix)
|
57
|
+
case prefix
|
58
|
+
when FalseClass then name.to_s
|
59
|
+
when String, Symbol then
|
60
|
+
"#{prefix.to_s}_#{name}"
|
61
|
+
else
|
62
|
+
"#{association_name}_#{name}"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def prefix_for(prefix_option)
|
67
|
+
case prefix_option
|
68
|
+
when TrueClass, NilClass then association_name.to_s
|
69
|
+
when FalseClass then false
|
70
|
+
when String, Symbol then prefix_option.to_s
|
71
|
+
else # nil
|
72
|
+
raise "Wrong value for prefix: #{prefix_option}"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def association_attributes
|
77
|
+
"#{association_name}_attributes"
|
78
|
+
end
|
79
|
+
|
80
|
+
def association_setup_method
|
81
|
+
"#{setup_method_prefix}_#{@association_name}"
|
82
|
+
end
|
83
|
+
|
84
|
+
def setup_method_prefix
|
85
|
+
@options.fetch(:setup_method_prefix, "setup")
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class Address < ActiveRecord::Base; end
|
4
|
+
class Contact < ActiveRecord::Base
|
5
|
+
extend ExposeAssociation::Base
|
6
|
+
has_one :main_location, :class_name => 'Address'
|
7
|
+
end
|
8
|
+
|
9
|
+
describe 'SomeModel with ExposeAssociation' do
|
10
|
+
|
11
|
+
it "should include the basic functionality" do
|
12
|
+
Contact.should respond_to(:expose_association)
|
13
|
+
end
|
14
|
+
|
15
|
+
context "exposing another object" do
|
16
|
+
|
17
|
+
before :all do
|
18
|
+
Contact.expose_association :main_location, class: Address do
|
19
|
+
expose :city
|
20
|
+
expose :address_line_1, prefix: false
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
let(:contact) { Contact.new }
|
25
|
+
|
26
|
+
it "should setup the association with the provided class" do
|
27
|
+
contact.main_location.should be_a_kind_of(Address)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should add the builder" do
|
31
|
+
contact.should respond_to(:setup_main_location)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should support mass assignment of the other object" do
|
35
|
+
contact.should allow_mass_assignment_of(:main_location_attributes)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should expose a given attribute (with prefix)" do
|
39
|
+
contact.should respond_to("main_location_city")
|
40
|
+
contact.should respond_to("main_location_city=")
|
41
|
+
contact.main_location_city = "Madrid"
|
42
|
+
contact.main_location.city.should == "Madrid"
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should allow mass assignment of the exposed attributes" do
|
46
|
+
contact = Contact.new main_location_city: "Barcelona"
|
47
|
+
contact.main_location.city.should == "Barcelona"
|
48
|
+
end
|
49
|
+
|
50
|
+
context "with no prefix" do
|
51
|
+
it "should expose the attribute directly on the main object" do
|
52
|
+
contact.should respond_to(:address_line_1)
|
53
|
+
contact.address_line_1 = "Freire 1008"
|
54
|
+
contact.main_location.address_line_1.should == "Freire 1008"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
describe ExposeAssociation::AssociationExposer do
|
63
|
+
it "should know the setup method" do
|
64
|
+
exposer = ExposeAssociation::AssociationExposer.new(Contact, :main_location, class: Address)
|
65
|
+
exposer.association_setup_method.should == "setup_main_location"
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should infer the class name of the association if none is provided" do
|
69
|
+
exposer = ExposeAssociation::AssociationExposer.new(Contact, :address)
|
70
|
+
exposer.association_class.should == Address
|
71
|
+
exposer = ExposeAssociation::AssociationExposer.new(Contact, :main_location, class: Address)
|
72
|
+
exposer.association_class.should == Address
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should calculate properly the attr_name for a given prefix" do
|
76
|
+
exposer = ExposeAssociation::AssociationExposer.new(Contact, :address)
|
77
|
+
exposer.attr_name_for(:city, nil).should == 'address_city'
|
78
|
+
exposer.attr_name_for(:city, true).should == 'address_city'
|
79
|
+
exposer.attr_name_for(:city, false).should == 'city'
|
80
|
+
exposer.attr_name_for(:city, "culo").should == 'culo_city'
|
81
|
+
exposer.attr_name_for(:city, :culito).should == 'culito_city'
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should calculate properly the prefix " do
|
85
|
+
exposer = ExposeAssociation::AssociationExposer.new(Contact, :address)
|
86
|
+
exposer.prefix_for("culito").should == 'culito'
|
87
|
+
exposer.prefix_for(true).should == 'address'
|
88
|
+
exposer.prefix_for(nil).should == 'address'
|
89
|
+
exposer.prefix_for(false).should == false
|
90
|
+
exposer.prefix_for(:culito).should == 'culito'
|
91
|
+
end
|
92
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
require 'expose_association'
|
3
|
+
require 'shoulda'
|
4
|
+
|
5
|
+
ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
|
6
|
+
|
7
|
+
ActiveRecord::Schema.define(:version => 1) do
|
8
|
+
create_table :contacts do |t|
|
9
|
+
t.string :first_name, :last_name
|
10
|
+
end
|
11
|
+
|
12
|
+
create_table :addresses do |t|
|
13
|
+
t.string :contactable_type
|
14
|
+
t.integer :contactable_id
|
15
|
+
t.string :address_line_1, :address_line_2
|
16
|
+
t.string :city
|
17
|
+
end
|
18
|
+
end
|
metadata
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: expose_association
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Lucas Florio
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-12-02 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: activerecord
|
16
|
+
requirement: &70160113896320 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70160113896320
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rspec
|
27
|
+
requirement: &70160113895020 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70160113895020
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: sqlite3
|
38
|
+
requirement: &70160113893880 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *70160113893880
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: shoulda
|
49
|
+
requirement: &70160113892360 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *70160113892360
|
58
|
+
description: ! "Easy exposition of has_one associations at the object\n level, to
|
59
|
+
avoid nesting."
|
60
|
+
email:
|
61
|
+
- lucasefe@gmail.com
|
62
|
+
executables: []
|
63
|
+
extensions: []
|
64
|
+
extra_rdoc_files: []
|
65
|
+
files:
|
66
|
+
- .gitignore
|
67
|
+
- .rspec
|
68
|
+
- Gemfile
|
69
|
+
- README.md
|
70
|
+
- Rakefile
|
71
|
+
- expose_association.gemspec
|
72
|
+
- lib/expose_association.rb
|
73
|
+
- lib/expose_association/base.rb
|
74
|
+
- lib/expose_association/railtie.rb
|
75
|
+
- lib/expose_association/version.rb
|
76
|
+
- spec/expose_association/base_spec.rb
|
77
|
+
- spec/spec_helper.rb
|
78
|
+
homepage: ''
|
79
|
+
licenses: []
|
80
|
+
post_install_message:
|
81
|
+
rdoc_options: []
|
82
|
+
require_paths:
|
83
|
+
- lib
|
84
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
85
|
+
none: false
|
86
|
+
requirements:
|
87
|
+
- - ! '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
|
+
none: false
|
92
|
+
requirements:
|
93
|
+
- - ! '>='
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0'
|
96
|
+
requirements: []
|
97
|
+
rubyforge_project:
|
98
|
+
rubygems_version: 1.8.10
|
99
|
+
signing_key:
|
100
|
+
specification_version: 3
|
101
|
+
summary: ! '...'
|
102
|
+
test_files:
|
103
|
+
- spec/expose_association/base_spec.rb
|
104
|
+
- spec/spec_helper.rb
|
105
|
+
has_rdoc:
|