Skip to content

Commit

Permalink
Initial commit.
Browse files Browse the repository at this point in the history
  • Loading branch information
Gabe Berke-Williams committed Jan 29, 2012
0 parents commit a7abe96
Show file tree
Hide file tree
Showing 15 changed files with 310 additions and 0 deletions.
18 changes: 18 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -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 changes: 4 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
source 'https://rubygems.org'

# Specify your gem's dependencies in law_of_demeter_violator.gemspec
gemspec
22 changes: 22 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -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 changes: 46 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -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 changes: 8 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -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 changes: 20 additions & 0 deletions law_of_demeter_violator.gemspec
Original file line number Diff line number Diff line change
@@ -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 changes: 10 additions & 0 deletions lib/law_of_demeter_violator.rb
Original file line number Diff line number Diff line change
@@ -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 changes: 4 additions & 0 deletions lib/law_of_demeter_violator/global_method.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
def Violate(object)
violator = LawOfDemeterViolator::Violator.new(object)
violator.violate
end
3 changes: 3 additions & 0 deletions lib/law_of_demeter_violator/version.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module LawOfDemeterViolator
VERSION = "0.0.1"
end
35 changes: 35 additions & 0 deletions lib/law_of_demeter_violator/violator.rb
Original file line number Diff line number Diff line change
@@ -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 changes: 16 additions & 0 deletions spec/law_of_demeter_violator/global_method_spec.rb
Original file line number Diff line number Diff line change
@@ -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 changes: 48 additions & 0 deletions spec/law_of_demeter_violator/violator_spec.rb
Original file line number Diff line number Diff line change
@@ -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 changes: 22 additions & 0 deletions spec/law_of_demeter_violator_spec.rb
Original file line number Diff line number Diff line change
@@ -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 changes: 11 additions & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
@@ -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 changes: 43 additions & 0 deletions spec/support/macros/define_constant.rb
Original file line number Diff line number Diff line change
@@ -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.