midas-has_associative_facades 1.0.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/History.txt +3 -0
- data/Manifest.txt +16 -0
- data/PostInstall.txt +7 -0
- data/README.rdoc +110 -0
- data/Rakefile +28 -0
- data/has_associative_facades.gemspec +38 -0
- data/lib/has_associative_facades.rb +15 -0
- data/lib/has_associative_facades/acts_methods.rb +13 -0
- data/lib/has_associative_facades/instance_methods.rb +52 -0
- data/script/console +10 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/spec/has_associative_facades_spec.rb +11 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +10 -0
- data/tasks/rspec.rake +21 -0
- metadata +92 -0
data/History.txt
ADDED
data/Manifest.txt
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
History.txt
|
2
|
+
Manifest.txt
|
3
|
+
PostInstall.txt
|
4
|
+
README.rdoc
|
5
|
+
Rakefile
|
6
|
+
has_associative_facades.gemspec
|
7
|
+
lib/has_associative_facades.rb
|
8
|
+
lib/has_associative_facades/acts_methods.rb
|
9
|
+
lib/has_associative_facades/instance_methods.rb
|
10
|
+
script/console
|
11
|
+
script/destroy
|
12
|
+
script/generate
|
13
|
+
spec/has_associative_facades_spec.rb
|
14
|
+
spec/spec.opts
|
15
|
+
spec/spec_helper.rb
|
16
|
+
tasks/rspec.rake
|
data/PostInstall.txt
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
= has_associative_facades
|
2
|
+
|
3
|
+
http://github.com/midas/has_associative_facades/tree/master
|
4
|
+
|
5
|
+
|
6
|
+
== DESCRIPTION:
|
7
|
+
|
8
|
+
A Rails gem that creates getters and setters for associated object's attributes to aid in not breaking Demeter's Law.
|
9
|
+
|
10
|
+
== WHY
|
11
|
+
|
12
|
+
From http://en.wikipedia.org/wiki/Law_of_Demeter, the "Law of Demeter for Functions/Methods” (LoD-F) states:
|
13
|
+
|
14
|
+
An object A can request a service (call a method) of an object instance B, but object A cannot “reach through” object
|
15
|
+
B to access yet another object, C, to request its services. Doing so would mean that object A implicitly requires greater
|
16
|
+
knowledge of object B’s internal structure. Instead, B’s class should be modified if necessary so that object A can simply
|
17
|
+
make the request directly of object B, and then let object B propagate the request to any relevant subcomponents. Or A should
|
18
|
+
have a direct reference to object C and make the call directly. If the law is followed, only object B knows its own internal
|
19
|
+
structure.
|
20
|
+
|
21
|
+
In particular, an object should avoid invoking methods of a member object returned by another method. For many modern object
|
22
|
+
oriented languages that use a dot as field identifier, the law can be stated simply as "use only one dot". That is, the code
|
23
|
+
"a.b.Method()" breaks the law where "a.Method()" does not.
|
24
|
+
|
25
|
+
As a simple example, when one wants to walk a dog, it would be folly to command the dog’s legs to walk directly; instead one
|
26
|
+
commands the dog and lets it take care of its legs itself
|
27
|
+
|
28
|
+
The advantage of following the Law of Demeter is that the resulting software tends to be more maintainable and adaptable. Since
|
29
|
+
objects are less dependent on the internal structure of other objects, object containers can be changed without reworking their
|
30
|
+
callers.
|
31
|
+
|
32
|
+
A disadvantage of the Law of Demeter is that it sometimes requires writing a large number of small “wrapper” methods (sometimes
|
33
|
+
referred to as Demeter Transmogrifiers) to propagate method calls to the components. Furthermore, a class’s interface can become
|
34
|
+
bulky as it hosts methods for contained classes resulting in a class without a cohesive interface.
|
35
|
+
|
36
|
+
This is where has_associative_facades steps in. Instead of writing this large number of wrapper methods, you can just call the
|
37
|
+
methods and they will be defined on an as needed basis, remaining in memory until the object is destroyed. So now you can avoid
|
38
|
+
coupling your objects together without the extra work. Only the interface to your object need remain the same. You can change
|
39
|
+
the internal implementation of methods to your heart;s content without breaking your project.
|
40
|
+
|
41
|
+
|
42
|
+
== FEATURES:
|
43
|
+
|
44
|
+
* Automatically creates facade getter methods for has one and belongs to associations.
|
45
|
+
|
46
|
+
|
47
|
+
== TO IMPLEMENT:
|
48
|
+
|
49
|
+
* Automatically creates facade setter methods for has one and belongs to associations.
|
50
|
+
* Possibly provide a static way to define through macro type singleton methods (like attr_accessor, attr_reader, etc).
|
51
|
+
|
52
|
+
|
53
|
+
== USAGE:
|
54
|
+
|
55
|
+
For:
|
56
|
+
|
57
|
+
class Account < ActiveRecord::Base
|
58
|
+
# has name attribute
|
59
|
+
end
|
60
|
+
|
61
|
+
class User
|
62
|
+
has_associative_facades
|
63
|
+
|
64
|
+
belongs_to :account
|
65
|
+
end
|
66
|
+
|
67
|
+
You can now:
|
68
|
+
|
69
|
+
user = User.first # or User.new, User.find, etc
|
70
|
+
user.account_name # is the same as user.account.name
|
71
|
+
|
72
|
+
|
73
|
+
== REQUIREMENTS:
|
74
|
+
|
75
|
+
* Rails (ActiveRecord)
|
76
|
+
|
77
|
+
|
78
|
+
== INSTALL:
|
79
|
+
|
80
|
+
sudo gem install midas-has_associative_facades
|
81
|
+
|
82
|
+
In your Rails environment file:
|
83
|
+
|
84
|
+
config.gem 'midas-has_associative_facades', :version => '1.0.0', :lib => 'has_associative_facades', :source => 'http://gems.github.com'
|
85
|
+
|
86
|
+
|
87
|
+
== LICENSE:
|
88
|
+
|
89
|
+
(The MIT License)
|
90
|
+
|
91
|
+
Copyright (c) 2009 C. Jason Harrelson (midas)
|
92
|
+
|
93
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
94
|
+
a copy of this software and associated documentation files (the
|
95
|
+
'Software'), to deal in the Software without restriction, including
|
96
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
97
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
98
|
+
permit persons to whom the Software is furnished to do so, subject to
|
99
|
+
the following conditions:
|
100
|
+
|
101
|
+
The above copyright notice and this permission notice shall be
|
102
|
+
included in all copies or substantial portions of the Software.
|
103
|
+
|
104
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
105
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
106
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
107
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
108
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
109
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
110
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
%w[rubygems rake rake/clean fileutils newgem rubigen].each { |f| require f }
|
2
|
+
require File.dirname(__FILE__) + '/lib/has_associative_facades'
|
3
|
+
|
4
|
+
# Generate all the Rake tasks
|
5
|
+
# Run 'rake -T' to see list of generated tasks (from gem root directory)
|
6
|
+
$hoe = Hoe.new('has_associative_facades', HasAssociativeFacades::VERSION) do |p|
|
7
|
+
p.developer('C. Jason Harrelson', 'jason@lookforwardwnterprises.com')
|
8
|
+
p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
|
9
|
+
p.post_install_message = 'PostInstall.txt' # TODO remove if post-install message not required
|
10
|
+
p.rubyforge_name = p.name # TODO this is default value
|
11
|
+
# p.extra_deps = [
|
12
|
+
# ['activesupport','>= 2.0.2'],
|
13
|
+
# ]
|
14
|
+
p.extra_dev_deps = [
|
15
|
+
['newgem', ">= #{::Newgem::VERSION}"]
|
16
|
+
]
|
17
|
+
|
18
|
+
p.clean_globs |= %w[**/.DS_Store tmp *.log]
|
19
|
+
path = (p.rubyforge_name == p.name) ? p.rubyforge_name : "\#{p.rubyforge_name}/\#{p.name}"
|
20
|
+
p.remote_rdoc_dir = File.join(path.gsub(/^#{p.rubyforge_name}\/?/,''), 'rdoc')
|
21
|
+
p.rsync_args = '-av --delete --ignore-errors'
|
22
|
+
end
|
23
|
+
|
24
|
+
require 'newgem/tasks' # load /tasks/*.rake
|
25
|
+
Dir['tasks/**/*.rake'].each { |t| load t }
|
26
|
+
|
27
|
+
# TODO - want other tests/tasks run by default? Add them to the list
|
28
|
+
# task :default => [:spec, :features]
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{has_associative_facades}
|
5
|
+
s.version = "1.0.2"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["C. Jason Harrelson"]
|
9
|
+
s.date = %q{2009-06-28}
|
10
|
+
s.description = %q{A Rails gem that creates getters and setters for associated object's attributes to aid in not breaking Demeter's Law.}
|
11
|
+
s.email = ["jason@lookforwardwnterprises.com"]
|
12
|
+
s.extra_rdoc_files = ["History.txt", "Manifest.txt", "PostInstall.txt", "README.rdoc"]
|
13
|
+
s.files = ["History.txt", "Manifest.txt", "PostInstall.txt", "README.rdoc", "Rakefile", "has_associative_facades.gemspec", "lib/has_associative_facades.rb", "lib/has_associative_facades/acts_methods.rb", "lib/has_associative_facades/instance_methods.rb", "script/console", "script/destroy", "script/generate", "spec/has_associative_facades_spec.rb", "spec/spec.opts", "spec/spec_helper.rb", "tasks/rspec.rake"]
|
14
|
+
s.has_rdoc = true
|
15
|
+
s.homepage = %q{http://github.com/midas/has_associative_facades/tree/master}
|
16
|
+
s.post_install_message = %q{PostInstall.txt}
|
17
|
+
s.rdoc_options = ["--main", "README.rdoc"]
|
18
|
+
s.require_paths = ["lib"]
|
19
|
+
s.rubyforge_project = %q{has_associative_facades}
|
20
|
+
s.rubygems_version = %q{1.3.1}
|
21
|
+
s.summary = %q{A Rails gem that creates getters and setters for associated object's attributes to aid in not breaking Demeter's Law.}
|
22
|
+
|
23
|
+
if s.respond_to? :specification_version then
|
24
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
25
|
+
s.specification_version = 2
|
26
|
+
|
27
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
28
|
+
s.add_development_dependency(%q<newgem>, [">= 1.3.0"])
|
29
|
+
s.add_development_dependency(%q<hoe>, [">= 1.8.0"])
|
30
|
+
else
|
31
|
+
s.add_dependency(%q<newgem>, [">= 1.3.0"])
|
32
|
+
s.add_dependency(%q<hoe>, [">= 1.8.0"])
|
33
|
+
end
|
34
|
+
else
|
35
|
+
s.add_dependency(%q<newgem>, [">= 1.3.0"])
|
36
|
+
s.add_dependency(%q<hoe>, [">= 1.8.0"])
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
2
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
3
|
+
|
4
|
+
require 'has_associative_facades/acts_methods'
|
5
|
+
require 'has_associative_facades/instance_methods'
|
6
|
+
|
7
|
+
module HasAssociativeFacades
|
8
|
+
VERSION = '1.0.2'
|
9
|
+
|
10
|
+
def self.included( base )
|
11
|
+
base.extend ActsMethods
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
ActiveRecord::Base.send( :include, HasAssociativeFacades ) if defined?( ActiveRecord::Base )
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module HasAssociativeFacades
|
2
|
+
module ActsMethods
|
3
|
+
|
4
|
+
# Use this method to declare the associated facade behavior on your ActiveRecord class.
|
5
|
+
#
|
6
|
+
def has_associative_facades
|
7
|
+
unless included_modules.include? InstanceMethods
|
8
|
+
include InstanceMethods
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module HasAssociativeFacades
|
2
|
+
module InstanceMethods
|
3
|
+
|
4
|
+
# When the method being called is the case of "#{association_name}_#{attribute_or_method}" a getter method
|
5
|
+
# will be defined and called returning its value. This works for an ActiveRecord object created through the
|
6
|
+
# new operator having all attributes set to nil or an find of some sort having all attributes populated.
|
7
|
+
#
|
8
|
+
# If the associated object is nil but has an attribute or method with the given name a nil is reurned. Otherwise,
|
9
|
+
# the NoMethodError is thrown.
|
10
|
+
#
|
11
|
+
def method_missing( method, *args )
|
12
|
+
method_name = method.to_s
|
13
|
+
super( method, *args ) # allow super to win if it has the method
|
14
|
+
rescue NoMethodError => ex
|
15
|
+
association = nil
|
16
|
+
association_reflection = nil
|
17
|
+
associateds = self.class.inheritable_attributes[:reflections]
|
18
|
+
associateds.each do |associated|
|
19
|
+
if /#{associated[0]}_/.match( method_name )
|
20
|
+
association_reflection = associated
|
21
|
+
association = associated[0]
|
22
|
+
break
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
if match = /#{association}_([_a-zA-Z]\w*)/.match( method_name )
|
27
|
+
throw ex if match.captures.empty? || match.captures.size > 2
|
28
|
+
attribute = match.captures[0].to_sym
|
29
|
+
|
30
|
+
assoc_klass = nil
|
31
|
+
begin
|
32
|
+
assoc_klass = association.to_s.classify.constantize
|
33
|
+
rescue
|
34
|
+
assoc_klass = association_reflection[1].options[:class_name].constantize
|
35
|
+
end
|
36
|
+
throw ex unless assoc_klass.columns_hash.has_key?( attribute.to_s ) || assoc_klass.public_instance_methods.include?( attribute.to_s )
|
37
|
+
|
38
|
+
self.class.class_eval <<-END
|
39
|
+
def #{method_name}
|
40
|
+
return self.send( :#{association} ).send( :#{attribute} ) unless self.send( :#{association} ).nil?
|
41
|
+
nil
|
42
|
+
end
|
43
|
+
END
|
44
|
+
|
45
|
+
return self.send( method_name )
|
46
|
+
end
|
47
|
+
|
48
|
+
throw ex
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
data/script/console
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# File: script/console
|
3
|
+
irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
|
4
|
+
|
5
|
+
libs = " -r irb/completion"
|
6
|
+
# Perhaps use a console_lib to store any extra methods I may want available in the cosole
|
7
|
+
# libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
|
8
|
+
libs << " -r #{File.dirname(__FILE__) + '/../lib/has_associative_facades.rb'}"
|
9
|
+
puts "Loading has_associative_facades gem"
|
10
|
+
exec "#{irb} #{libs} --simple-prompt"
|
data/script/destroy
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/destroy'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
+
RubiGen::Scripts::Destroy.new.run(ARGV)
|
data/script/generate
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/generate'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
+
RubiGen::Scripts::Generate.new.run(ARGV)
|
data/spec/spec.opts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour
|
data/spec/spec_helper.rb
ADDED
data/tasks/rspec.rake
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
begin
|
2
|
+
require 'spec'
|
3
|
+
rescue LoadError
|
4
|
+
require 'rubygems'
|
5
|
+
require 'spec'
|
6
|
+
end
|
7
|
+
begin
|
8
|
+
require 'spec/rake/spectask'
|
9
|
+
rescue LoadError
|
10
|
+
puts <<-EOS
|
11
|
+
To use rspec for testing you must install rspec gem:
|
12
|
+
gem install rspec
|
13
|
+
EOS
|
14
|
+
exit(0)
|
15
|
+
end
|
16
|
+
|
17
|
+
desc "Run the specs under spec/models"
|
18
|
+
Spec::Rake::SpecTask.new do |t|
|
19
|
+
t.spec_opts = ['--options', "spec/spec.opts"]
|
20
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
21
|
+
end
|
metadata
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: midas-has_associative_facades
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- C. Jason Harrelson
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-06-28 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: newgem
|
17
|
+
type: :development
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 1.3.0
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: hoe
|
27
|
+
type: :development
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.8.0
|
34
|
+
version:
|
35
|
+
description: A Rails gem that creates getters and setters for associated object's attributes to aid in not breaking Demeter's Law.
|
36
|
+
email:
|
37
|
+
- jason@lookforwardwnterprises.com
|
38
|
+
executables: []
|
39
|
+
|
40
|
+
extensions: []
|
41
|
+
|
42
|
+
extra_rdoc_files:
|
43
|
+
- History.txt
|
44
|
+
- Manifest.txt
|
45
|
+
- PostInstall.txt
|
46
|
+
- README.rdoc
|
47
|
+
files:
|
48
|
+
- History.txt
|
49
|
+
- Manifest.txt
|
50
|
+
- PostInstall.txt
|
51
|
+
- README.rdoc
|
52
|
+
- Rakefile
|
53
|
+
- has_associative_facades.gemspec
|
54
|
+
- lib/has_associative_facades.rb
|
55
|
+
- lib/has_associative_facades/acts_methods.rb
|
56
|
+
- lib/has_associative_facades/instance_methods.rb
|
57
|
+
- script/console
|
58
|
+
- script/destroy
|
59
|
+
- script/generate
|
60
|
+
- spec/has_associative_facades_spec.rb
|
61
|
+
- spec/spec.opts
|
62
|
+
- spec/spec_helper.rb
|
63
|
+
- tasks/rspec.rake
|
64
|
+
has_rdoc: true
|
65
|
+
homepage: http://github.com/midas/has_associative_facades/tree/master
|
66
|
+
post_install_message: PostInstall.txt
|
67
|
+
rdoc_options:
|
68
|
+
- --main
|
69
|
+
- README.rdoc
|
70
|
+
require_paths:
|
71
|
+
- lib
|
72
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: "0"
|
77
|
+
version:
|
78
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: "0"
|
83
|
+
version:
|
84
|
+
requirements: []
|
85
|
+
|
86
|
+
rubyforge_project: has_associative_facades
|
87
|
+
rubygems_version: 1.2.0
|
88
|
+
signing_key:
|
89
|
+
specification_version: 2
|
90
|
+
summary: A Rails gem that creates getters and setters for associated object's attributes to aid in not breaking Demeter's Law.
|
91
|
+
test_files: []
|
92
|
+
|