
A lot of my life has been spent on IRC. I got really sick of it. I got tired of the assumptions made by people meeting “anonymous” people for the first time. I include myself here, it seemed inevitable that I’d jump to conclusions without hearing someone out, and in turn made them sick of IRC too.
However, in a recent expedition into making my first clean-coded, well thought out Ruby Gem, I turned to IRC for help.
What I got was much more than I asked for, and it was all awesome-sauce.
We use ActiveRecord::Base.connection.execute occasionally to run really fast read-only queries. Mostly for Dataviz.
I don’t like it. You get a row of string-keyed string-valued hashes back from the database. All that knowledge about the schema and how to handle differently typed columns that ActiveRecord gives us, gone.
Our views and view-helpers were littered with new idioms (bad habbits?) - the “Dealing with direct sql results” idioms.
Not only that, but the SQL queries were in controllers! Ick.
My first cut of this gem is SQLRecord v 0.1.0
It was my first ever DSL in Ruby, and I thought, quite idiomattic.
So I started documenting. It was annoying me that my Yard Index was showing a module ClassMethods rather than showing them as Class Methods of the module that used the include-to-extend idiom.
So I went to #yard at irc.freenode.net channel to ask about indicating that this module’s methods belonged to another. Loren Segal was there and told me there wasn’t any way of munging the docs…
Here’s the beginning of that discussion:
[08:50]
Is there a trick in yard to make Module docs (like ClassMethods) look like those of the parent Module?
[08:51] <@lsegal> you mean like get ClassMethods to be interpreted as a set of class methods? Unfortunately not really
[08:52]yah thats what i meant
[08:53]http://yehudakatz.com/2009/11/12/better-ruby-idioms/ I’m going to re-read this until i get it.
[08:54] <@lsegal> indeed, that’s a good one. I’m not too fond of ClassMethods either
[08:55] <@lsegal> poor docs being one reason why
[08:56] <@lsegal> ClassMethods usually translates to “i have a poorly named mixin”
Yehuda’s post was haunting me but I wasn’t understanding it.
For the next 2 hours, off and on, Loren gave me a code review that changed my world view.
I would say that he improved Ruby by improving one passionate developers view of Ruby.
I now believed that good OO Principles were entirely practicable within this magical wonderland language called Ruby, where all ropes are long and could hang you, your family, and your-pit bull, without breaking a sweat.
Now I had a new perspective, a better Gem and I was pretty sure I was coding what I needed, without over-cooking it.
So one thing that happened as I re-factored, was a mistaken version bump into the 1.0s. Not where I meant to be. I trotted off to #rubygems at irc.freenode.net.
I got an answer about using gem ‘yank’ to remove a gem but then:
<@drbrain> you should not be embarrassed by releasing early and often
Ok then. I broke semantic versioning but I was hitting it hard and “showing my working”. Math education really does have value!
A little later, out of the blue, and not related to my versioning problem, but the code itself, someone said:
doesn’t AR::Base.find_by_sql solve this?
Ok! Here we go, the IRC I know and hate. Someones about to tell me I’m doing it wrong without background information…
Well no not really. Erik Hollensbe, as it turns out, had an abiding interest in what I was doing and made me argue the approach I was using. Moar gravy for my awesome-sauce.
That’s it. Thankyou to the community for being open minded, passionate and brilliant. I’ve had a very good week.
Ruby DSL blocks need access to DSL methods. But which scoping to use?
Farm.new do
animal :kitten
end
or
Farm.new do |farm|
farm.animal :kitten
end
TL;DR? You can support both!
Go read Rubylove’s article
Visfleet is looking for a certain someone to join our Agile team to develop our world class product.
Warning, bullet points follow.
Learn more on this blog, our business blog, and our staff page
Also see our Kanban Board
Is deployed in the cloud.
Want to know more, visit our web site
Want to apply? Contact us on jobs@visfleet.com
In Martin Fowler’s “Refactoring” he introduces the “Extract Class” refactoring. I love this refactoring pattern and I use it all the time.
This refactoring is (unless im sorely mistaken) the most important one for the SRP.
Because of this intimacy with the SRP, I think there is an umbrella refactoring, called “Extract Responsibility” with two concrete methods for achieving it:
You have one class responsible for the work that should be done by two.
Decide if the extra responsibility is encapsulated as a Component (Has-A) or a Process (IoC)
You have one class with an additional responsibility that exhibits compositional behaviour
Create a new class and move the relevant fields and methods from the old class into the new class (lifted directly from Extract Class)
The new class is a component if all it takes to make the refactoring, is to move methods. In my experience you are ‘Extracting A Component’ if the original classes’ data easily fits into a “Has-A” relationship.
You have two methods in subclasses that perform similar steps in the same order, yet the steps are different. (lifted directly from “Form Template Method”)
Create a new class, add process hooks in the new class and use those to call on the original classes actions.
OK so here is the crux of the matter. There are classes that still have too much responsibility, but it’s not in a ‘Has-A’ relationship. These classes have a ‘process’. These classes often present themselves in sets. Usually you will find two classes doing the exact same thing with moderately different behaviour.
The most suitable term for this extraction is “Inversion of Control”. Please don’t think I mean DI. DI, especially automated DI, is “Orthogonal Control” in my mind (topic of another post I must write).
The most common way I’ve seen of inverting control for a class or set of classes is the Form Template Method Refactoring. There are probably quite a few other methods for achieving IoC, including utilizing annotations.
The one I prefer is the Fluent Interface, and a beautiful example of IoC in fluent interfaces is Shaun Smith’s Promise Class.
In general I prefer Fluent Interfaces over Template Methods because they turn an ‘Is-a’ into a ‘Has-a’, providing far less coupling, and allowing for more than one ‘Process’ to be utilized by any given class.
This actually makes the new Fluent class a “has-a” relationship, so how does this differ from ‘Extract Component’? - You are not extracting data, you are extracting common behavior.
There is one downside to Fluent Interfaces: Language support.
In languages like Actionscript 3, you cannot delegate methods with static typing. This means that the callbacks you give your fluent interface are not compile time checked. In my next ‘Actionscript 4’ article I’ll talk more about static typed delegates and how much I think we need them in the language.
My apologies that my writing is not succinct. Writing is a craft I need to take more time to master. So please, feedback, opinions and constructive criticism!
So I had surgery 2 nights ago. Routine Gall bladder thing. But the pain killers, namely morphine, keep me awake, contrary to their typical side effects. This left me up all night, in a hospital room, with such a calm mind and strange thoughts, that my desires for Actionscript began to crystalise.
These are meant to be idea’s to argue over, so please do comment.
Groovy’s Safe Navigation Operator is pretty straight forward:
var a:String = b?.c?.e
If b evaluates to null, a is null. Same for c.
It’s wonderful. I really don’t think I need to use Null Objects as often as I do.
“Alan Kay’s meaning of OOP” and ObjC’s message passing got me thinking about thorough bi-directional decoupling. In the vWork application code, we have a considerable number of modules, which use a notifier bus (Robotlegs Modular context) to pass messages between modules. To properly decouple ‘Module A’ from ‘Module B’, I ended up with something like:
module.a.events.IRaiseThisEventOnTheBus
module.b.events.IExpectThisEventToBeRaised
or to be more concrete:
module.map.workerSelectedEvent
module.workerManager.selectWorkerEvent
and then I have a ‘master’ module manage the coupling. It listens for ‘workerSelectedEvent’ and fires the selectWorkerEvent. Now neither module has an import from the other…
And this is the rub. My master module (usually the application boot module) has knowledge (read: imports) of both modules. Thats not a truly decoupled program.
So the obvious way to get true decoupling is to use untyped messages. Like ruby, or ObjC might do. It works because a module can dissapear, and the other ‘dependent’ module is just listening for a magic string or symbol that never arrives.
I don’t like this approach, because I like type-safety.
My solution is to allow namespaces and classes to be missing. A kind of null-safety if you will. I can only describe it in code:
Describe an event class:
package module.map.events {
import flash.events.*
public class WorkerSelectedEvent extends Event {
... event boilerplate ...
public function get payload():TypeSafeThing {
return _payload;
}
}
}
And ‘Maybe’ consume it:
package module.workerManager.config {
use module.map.events.WorkerSelectedEvent;
use module.map.TypeSafeThing;
public class Config {
... stuff ...
public function configWorkerListener() {
if (WorkerSelectedEvent is _UndefinedClass)
return;
bus.addEventListener(WorkerSelectedEvent.SELECTED, selectedHandler);
}
}
}
Where ‘use’ is defined instead of ‘import’, the compiler will find the Class, but if it cannot, it will replace it with an instance of _UndefinedClass, which has no methods or behaviour, it’s just a marker.
I would quickly replace EventDispatchers with something like Rob Penner’s signals (although I want to see c# style delegates, which I’ll cover in part 2). A signal/delegate message passing system could happily accept an _UndefinedDelegate and make most of this decoupling transparent.
I suspect ‘use’ would be the defacto when specifying message classes/delegates.
Dominic Graefen (@devboy_org) introduces a Flex/Flash extension for Buildr. I recommend Buildr over Maven or other declarative build/dependency systems. Give it a go :)
I’m sharing the following Bootstrap Actor class, which simplifies bootstrap sequencing in Robotlegs.
I figure it has some value because Stray, who has a fairly similar programming ethos to me, see’s some merit in it.
Trigger the bootstrap in your context
override public function startup():void {
commandMap.mapEvent(ContextEvent.STARTUP_COMPLETE, BootstrapCommand, ContextEvent);
super.startup();
}
Which calls the BootstrapCommand
import org.robotlegs.mvcs.Command;
public class BootstrapCommand extends Command {
override public function execute():void {
injector.mapSingleton(ConfigurationSequence);
injector.getInstance(ConfigurationSequence).step();
}
}
Then implement the sequence
public class ConfigurationSequence extends AbstractSequencer {
override protected function configure():void {
addStep(ConfigureWorkersModelCommand);
addStep(ConfigureScalesModelCommand);
... yadda yadda ...
addStep(ConfigureJobDraggingModel);
}
}
What happens is that the line in the bootstrap
injector.getInstance(ConfigurationSequence).step();
instances the Sequencer and tells it to run the next step, which in this case, is the first one.
Each command added with ‘addStep’ is added as a command mapping which responds to a SequenceStepEvent with a named type. This is not conventional behavior for an event, but it works really well in this context.
Finally, a sequenced command looks a bit like this:
public class ConfigureScheduleHeaderViewCommand extends Command {
[Inject]
public var event:SequenceStepEvent;
override public function execute():void {
mediatorMap.mapView(TimelineView, TimelineMediator);
... yadda yadda ...
event.step();
}
}
The event.step causes the next item to be run.
The sequencer gets the addStep() and configure() methods from AbstractSequencer.as.
You also need the SequenceStepEvent.as class
Bootstrap command and context
You could merge BootstrapCommand into the context. I prefer to overdo SRP than underdo it.
Automatically step
Instead of specifying event.step() in each sequence command, you could modify AbstractSequencer#executeNextStep like this:
private function executeNextStep():void {
var classFQN:String = steps.shift();
var event:SequenceStepEvent = new SequenceStepEvent(classFQN, this);
dispatch(event);
step();
}
Our kanbancam runs 24/7 (with archives) at http://www.kanbancam.com/
This is the view of our Kanban wall. To the right of frame you can see our information radiator showing our Hudson CI status.
© 2010 - VisFleet Ltd
No prawns were harmed in
the making of this website
Comments