Skip to content

a little more s.o.l.i.d., a little less responsibilities into one place.

Notifications You must be signed in to change notification settings

caelum/rails-and-solid

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

19 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

# What is this?

This is a gem that changes Rails behavior to allow you to code more freely, without coupling
yourself to inheritance issues. Take a look at the examples to see what it changes.

# Installing

Configure the gem in your Gemfile:

  gem 'rails-and-solid'

# Configuring

In order not to refactor Rails completely, the easiest solution is to just trick it
and make it believe you have the controller he wants, just trick him:

  class ApplicationController < ActionController::Base
    
    solidify
  
  end

# Inheritance coupling in controllers

Ok. Time for some goodies. First example, let's unpimp :

[code]
RailsAndSolid.pimp :clients

class ClientsControl
  
  def show(id)
    @client = Client.find(id)
  end
  
end
[/code]

Notice that *id* is the parameter *id* from your http request. No inheritance required.
Same old behavior. No http params appeared. You can unit test for real, without all
its inherited behavior.

Why? Am I really coupled to something when inheriting?

[code]
rails console
> ActionController::Base.instance_methods.size
374
> (ActionController::Base.instance_methods + ActionController::Base.instance_methods - Object.instance_methods - Object.private_instance_methods).size
560
> ActionController::Base.methods.size
353
> ActionController::Base.ancestors.size
36
[/code]

By default, when creating a new controller in Rails, you are coupled
with 374 public methods (*542 in total*), inheriting behavior from *36 different* sources.

# Session parameters

[code]
class ClientsControl
  
  def show(current_user, id)
	# if session[:current_user] exists, guess who is here?
  end
  
end
[/code]

Again, unit testable. Non-http intrusive.

# Session parameters

[code]
class ClientsControl
  
  def show(current_user, id)
	# if session[:current_user] exists, guess who is here?
  end
  
end
[/code]

# What about creating a client?

Well, just create it:

[code]
class ClientsControl
  
  def create(client)
	client.save
  end
  
end
[/code]

# But you can't load, right?

I don't think so:

[code]
class ClientsControl
  
	def show(loaded_client)
		@client = loaded_client
	end
  
end
[/code]

# But what about responses? Sessions? Http stuff?

Just bind to what you need, nothing more than that:

[code]
def logout(session, redirect_to, flash)
  session[:current_user] = nil
  flash[:message] = "Logged out..."
  redirect_to.root_path
end
[/code]

# Internally: Helpers

Helpers are the classes that help passing parameters to those
methods by using a key based lookup algorithm. For example
if a 'json' parameter is received, one will use the Json helper
to return it:

[code]
class ApplicationController < ActionController::Base
  
  protect_from_forgery
  solidify

  class JsonHelper
    def initialize(controller)
      @controller = controller
    end
    def message(x)
      @controller.render :json => {'message' => x}
    end
  end

  KEYS["json"] = JsonHelper
  
end
[/code]

Now you can receive json and use the message:

[code]
def some_action(json)
	json.message("Well done!")
end
[/code]

You can use those auxiliary objects to compose any kind of behavior (not just rendering)
by just receiving the expected coupled semantic value instead of inheriting everything.

# Handlers

Handlers are not key-fixed parameter providers. For instance, the param lookup:

[code]
module RailsAndSolid
  module Handler
    class Param
      def extract(controller, name)
        controller.params[name]
      end
      def handles?(controller, name)
        controller.params[name]
      end
    end
  end
end[/code]

It will be asked whether it can handle this parameter and the provide it.

# TODO List

Help?

First, simple unit tests.

Units are split and behavior is composed. This was only to
show that its possible to achieve the same (and better results) with less coupling and a different
OO design approach. So what about start unit testing, providing new handlers, new helpers or
just doing a blog post on it? Refactoring trick_him would also be nice. Removing the
pimp method would be even better but that requires more refactoring on Rails internals,
not just tricking it.

Results from handlers "handles?" could be cached.

You can also implement DI support by correctly implementing di_instantiate(type) at TrickHim.

Finally, feel you can support named parameters in 1.8.7+ if not yet supported.

Everything is quite simple so far as this is just a concept

# Why not changing Rails internally?

One needs to know how all the mixin of Rails interact with each other (i.e. the 36 ancestors
of ActionController) in order to make safe changes. That is the coupling this proof of concept
is trying show how to avoid.

About

a little more s.o.l.i.d., a little less responsibilities into one place.

Resources

Stars

Watchers

Forks

Packages

No packages published

Languages