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 ADDED
@@ -0,0 +1,3 @@
1
+ == 1.0.0 2009-06-25
2
+
3
+ * Initial release
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
@@ -0,0 +1,7 @@
1
+
2
+ For more information on has_associative_facades, see http://has_associative_facades.rubyforge.org
3
+
4
+ NOTE: Change this information in PostInstall.txt
5
+ You can also delete it if you don't want it.
6
+
7
+
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)
@@ -0,0 +1,11 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ # Time to add your specs!
4
+ # http://rspec.info/
5
+ describe "Place your specs here" do
6
+
7
+ it "find this spec in spec directory" do
8
+ violated "Be sure to write your specs"
9
+ end
10
+
11
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1 @@
1
+ --colour
@@ -0,0 +1,10 @@
1
+ begin
2
+ require 'spec'
3
+ rescue LoadError
4
+ require 'rubygems'
5
+ gem 'rspec'
6
+ require 'spec'
7
+ end
8
+
9
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
10
+ require 'has_associative_facades'
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
+