Permalink
Browse files

Initial commit.

  • Loading branch information...
0 parents commit a7abe96eee9f786ffd6740207ebdcad364b28d2a @gabebw committed Jan 29, 2012
18 .gitignore
@@ -0,0 +1,18 @@
+*.gem
+*.rbc
+.bundle
+.config
+.yardoc
+Gemfile.lock
+InstalledFiles
+_yardoc
+coverage
+doc/
+lib/bundler/man
+pkg
+rdoc
+spec/reports
+test/tmp
+test/version_tmp
+tmp
+tags
4 Gemfile
@@ -0,0 +1,4 @@
+source 'https://rubygems.org'
+
+# Specify your gem's dependencies in law_of_demeter_violator.gemspec
+gemspec
22 LICENSE
@@ -0,0 +1,22 @@
+Copyright (c) 2012 Gabe Berke-Williams
+
+MIT License
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
46 README.md
@@ -0,0 +1,46 @@
+# Law Of Demeter Violator
+
+`user.name` is for sissies who think patterns are cool. You and me, we know that
+the Gang of Four were [communists](http://en.wikipedia.org/wiki/Gang_of_four).
+Let's do `user.name.name` instead.
+
+## Installation
+
+Add this line to your application's Gemfile:
+
+ gem 'law_of_demeter_violator'
+
+And then execute:
+
+ $ bundle
+
+Or install it yourself as:
+
+ $ gem install law_of_demeter_violator
+
+## Usage
+
+You have two options.
+
+One: include the `LawOfDemeterViolator` module in your class, at the end:
+
+ class BadIdea
+ def meth1
+ end
+
+ include LawOfDemeterViolator
+ end
+
+Unfortunately, it has to be at the end of the class definition.
+
+Or option 2: use the global `Violate` method:
+
+ Violate(BadIdea)
+
+## Contributing
+
+1. Fork it
+2. Create your feature branch (`git checkout -b my-new-feature`)
+3. Commit your changes (`git commit -am 'Added some feature'`)
+4. Push to the branch (`git push origin my-new-feature`)
+5. Create new Pull Request
8 Rakefile
@@ -0,0 +1,8 @@
+#!/usr/bin/env rake
+require "bundler/gem_tasks"
+
+require "rspec/core/rake_task"
+
+RSpec::Core::RakeTask.new
+
+task :default => :spec
20 law_of_demeter_violator.gemspec
@@ -0,0 +1,20 @@
+# -*- encoding: utf-8 -*-
+require File.expand_path('../lib/law_of_demeter_violator/version', __FILE__)
+
+Gem::Specification.new do |gem|
+ gem.authors = ["Gabe Berke-Williams"]
+ gem.email = ["gabe@thoughtbot.com"]
+ gem.description = %q{Violate the Law of Demeter hardcore with this too-easy-to-use library.}
+ gem.summary = %q{Violate the Law of Demeter hardcore with this too-easy-to-use library.}
+ gem.homepage = ""
+
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
+ gem.files = `git ls-files`.split("\n")
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
+ gem.name = "law_of_demeter_violator"
+ gem.require_paths = ["lib"]
+ gem.version = LawOfDemeterViolator::VERSION
+
+ gem.add_development_dependency("rspec", "~> 2.0")
+ gem.add_development_dependency("bourne", "~> 1.0")
+end
10 lib/law_of_demeter_violator.rb
@@ -0,0 +1,10 @@
+require "law_of_demeter_violator/violator"
+require "law_of_demeter_violator/global_method"
+require "law_of_demeter_violator/version"
+
+module LawOfDemeterViolator
+ def self.included(base)
+ violator = Violator.new(base)
+ violator.violate
+ end
+end
4 lib/law_of_demeter_violator/global_method.rb
@@ -0,0 +1,4 @@
+def Violate(object)
+ violator = LawOfDemeterViolator::Violator.new(object)
+ violator.violate
+end
3 lib/law_of_demeter_violator/version.rb
@@ -0,0 +1,3 @@
+module LawOfDemeterViolator
+ VERSION = "0.0.1"
+end
35 lib/law_of_demeter_violator/violator.rb
@@ -0,0 +1,35 @@
+require "ostruct"
+
+module LawOfDemeterViolator
+ class Violator
+ def initialize(class_to_be_violated)
+ @class_to_be_violated = class_to_be_violated
+ end
+
+ def violate
+ methods_to_violate.each do |original_method_name|
+ unviolated_method_name = :"__unviolated_#{original_method_name}"
+ @class_to_be_violated.class_eval do
+ # First, save the unviolated method.
+ alias_method(unviolated_method_name, original_method_name)
+
+ # # Then, remove the unviolated method. It's still saved via the call to
+ # alias_method.
+ remove_method(original_method_name)
+
+ # Now, define a new method with the same name. It returns an OpenStruct with one key,
+ # the original method name. So obj.user is now obj.user.user.
+ # Pretty sure |*args| is 1.9-only
+ define_method(original_method_name) do |*args|
+ original_value = __send__(unviolated_method_name, *args)
+ OpenStruct.new(original_method_name => original_value)
+ end
+ end
+ end
+ end
+
+ def methods_to_violate
+ @class_to_be_violated.instance_methods(false)
+ end
+ end
+end
16 spec/law_of_demeter_violator/global_method_spec.rb
@@ -0,0 +1,16 @@
+require 'spec_helper'
+
+describe "the global Violate method" do
+ it "violates the given class" do
+ define_class("Greeter") do
+ def hello
+ "hello"
+ end
+ end
+
+ Violate(Greeter)
+ violated_instance = Greeter.new
+
+ violated_instance.hello.hello.should == "hello"
+ end
+end
48 spec/law_of_demeter_violator/violator_spec.rb
@@ -0,0 +1,48 @@
+require "spec_helper"
+
+describe LawOfDemeterViolator::Violator do
+ it "violates methods that don't take arguments" do
+ define_class("Something") do
+ def hello
+ "hello"
+ end
+ end
+
+ violator = LawOfDemeterViolator::Violator.new(Something)
+ violator.violate
+
+ Something.new.hello.hello.should == "hello"
+ end
+
+ it "violates methods that take arguments" do
+ define_class("ClassWithMethodWithArguments") do
+ def name_plus(extra_argument)
+ "Bob #{extra_argument}"
+ end
+ end
+
+ violator = LawOfDemeterViolator::Violator.new(ClassWithMethodWithArguments)
+ violator.violate
+
+ violated_instance = ClassWithMethodWithArguments.new
+ violated_instance.name_plus("Viola").name_plus.should == "Bob Viola"
+ end
+
+ it "does not violate methods from parent classes" do
+ define_class("Parent") do
+ def parent_method
+ "parent"
+ end
+ end
+
+ define_class("Child", Parent) do
+ def child_method
+ end
+ end
+
+ violator = LawOfDemeterViolator::Violator.new(Child)
+ violator.violate
+
+ Child.new.parent_method.should == "parent"
+ end
+end
22 spec/law_of_demeter_violator_spec.rb
@@ -0,0 +1,22 @@
+require "spec_helper"
+
+describe LawOfDemeterViolator do
+ it "violates the Law of Demeter hardcore" do
+ define_class("ViolatedGreeter") do
+ def hello
+ "hello"
+ end
+
+ include LawOfDemeterViolator
+ end
+
+ # include_violator_in(Greeter)
+ violated_instance = ViolatedGreeter.new
+
+ violated_instance.hello.hello.should == "hello"
+ end
+
+ def include_violator_in(a_class)
+ a_class.send(:include, LawOfDemeterViolator)
+ end
+end
11 spec/spec_helper.rb
@@ -0,0 +1,11 @@
+$LOAD_PATH << File.join(File.dirname(__FILE__), "..", "lib")
+
+require "rspec"
+require "law_of_demeter_violator"
+require "bourne"
+
+Dir["spec/support/**/*.rb"].each { |f| require File.expand_path(f) }
+
+RSpec.configure do |config|
+ config.mock_framework = :mocha
+end
43 spec/support/macros/define_constant.rb
@@ -0,0 +1,43 @@
+module DefineConstantMacros
+ def define_class(path, base = Object, &block)
+ namespace, class_name = *constant_path(path)
+ klass = Class.new(base)
+ namespace.const_set(class_name, klass)
+ klass.class_eval(&block) if block_given?
+ @defined_constants << path
+ klass
+ end
+
+ def constant_path(constant_name)
+ names = constant_name.split('::')
+ class_name = names.pop
+ namespace = names.inject(Object) { |result, name| result.const_get(name) }
+ [namespace, class_name]
+ end
+
+ def default_constants
+ @defined_constants ||= []
+ @created_tables ||= []
+ end
+
+ def clear_generated_constants
+ @defined_constants.reverse.each do |path|
+ namespace, class_name = *constant_path(path)
+ namespace.send(:remove_const, class_name)
+ end
+
+ @defined_constants.clear
+ end
+end
+
+RSpec.configure do |config|
+ config.include DefineConstantMacros
+
+ config.before do
+ default_constants
+ end
+
+ config.after do
+ clear_generated_constants
+ end
+end

0 comments on commit a7abe96

Please sign in to comment.