Posted by Sam on Feb 27, 2012 at 07:45 AM UTC - 5 hrs
Here's a 35 minute recording of the presentation which I gave to
houstonrb on February 21, 2012. It is a practice run I did before the live presentation, so you won't get the discussion, but hopefully you'll find it useful anyway.
How to avoid becoming a formerly-employed Rails developer standing in line at the OOP Kitchen from Sammy Larbi on Vimeo.
You can find the slides here:
Slides for the Rails OOP presentation
There is also reference to a project whose purpose is to eventually be a full-scale demonstration of the techniques:
Project for the Rails OOP presentation
Let me know what you think in the comments below.
Updated to use HTML5 player at Vimeo.
Hey! Why don't you make your life easier and subscribe to the full post
or short blurb RSS feed? I'm so confident you'll love my smelly pasta plate
wisdom that I'm offering a no-strings-attached, lifetime money back guarantee!
Last modified on Mar 01, 2012 at 06:10 AM UTC - 5 hrs
Posted by Sam on Feb 22, 2012 at 08:24 AM UTC - 5 hrs
Someone rip this idea apart:
When you write an
if statement in one class that's using data from an object of a different class to make a decision, ask yourself:
- Would this code be more appropriate in the other object?
- Would it be better to introduce a new object, whose purpose it is to do this?
Thoughts appreciated.
Posted by Sam on Feb 08, 2012 at 06:51 AM UTC - 5 hrs
My take at this concept was too long for twitter, and I was too lazy to pare it down:
In the future, the only companies that sell physical goods* will be the ones that figure out how to mine atoms from raw materials which can be used as "ink" in 3d printers which people use to print their own products from (probably pirated) plans they found on the internet.
* This excludes non-mass-produced art
Thoughts?
Posted by Sam on Feb 01, 2012 at 05:41 AM UTC - 5 hrs
You know when you see code like this:
class CompulsionsController < ApplicationController
# ... standard actions above here
def update
if params[:obsessions].include?(ObsessionsTypes[:murdering_small_animals])
handle_sociopathic_obsessions
redirect_to socio_path and return
elsif params[:obsessions]
handle_normal_obsessions
redirect_to standard_obsessions_path and return
end
# normal update for compulsions
@compulsion = Compulsions.find(params[:id])
if(@compulsion.update_attributes(params[:compulsion]))
# ... remainder of the standard actions below here
end
and the phrase "WTF were they thinking?" runs through your mind?
More...
I have a theory about that little "pass a flag in the url to skip over the real action and perform a different one"
trick I see so often (and have been guilty of using myself).
It's because you've got this omniscient file that knows everything about where to route requests
that's not part of your editing routine, so finding and opening it breaks your train of thought.
It's a pain to open routes.rb when you suddenly realize you need a new route.
That got me thinking:
Should controllers route themselves? Would it make more sense for a controller to tell the router
how each of it's actions should be reached?
In the
second edition of Code Complete (that's an affiliate link), Steve McConnell writes about using
the Principle of Proximity (page 352) as a way to think about organizing code.
Keep related actions together.
From that point of view, it
certainly would be easier to follow along when you're questioning "how do I get to this action?"
Further, I think it would help solve the "pass a flag to an action to perform a different one" problem I illustrated in the code snippet above.
It was on my mind over the weekend, so I put together this little
experiment to see what controllers routing themselves in Rails would look like.
In that repository is a one-controller Rails project which specifies routes to itself using a gem you'll find in
vendor/gems/route.
One major drawback to doing routing in this style has to do with nested routes: should a controller that's
part of a nested route know who it's parents are? Should a higher-in-the-nest controller know about its child
routes? And if you choose one or the other, how would you specify it? What if there are conflicting routes -- who wins out?
It leads to a lot of questions for which I have no immediate answers.
Anyway, what do you think? Would this help solve the problem of recycled routes? Is that even a problem?
What are the drawbacks of such an approach? Do you see any merits?
Last modified on Feb 01, 2012 at 05:44 AM UTC - 5 hrs
Posted by Sam on Jan 27, 2012 at 06:01 AM UTC - 5 hrs
Most online developer API services that I've used are set up as if the customer is also the software developer.
That should change.
As the software developer, I don't want to be the owner of my customer's accounts, and I don't
want to worry about trying to figure out how to transfer ownership (if your service allows it, that is).
Because of that, theres a lot of waste that goes on: wastes of my time,
which wastes my customer's or my company's money.
I'm saying "customer" here, but you might substitute that with "the person who really needs / cares about the account,"
because that person, in my estimation, is rarely the software developer. Unless I'm developing an app for myself,
I only care about that API because someone else needs me to. And even when I'm developing for myself, I hope it gets to
a point where I need to hire someone to care about it on my behalf, so I can focus on more important things.
The typical signup process for me goes like this:
More...
- Me: If I already have an account, sign out.
- Me: Sign up for the service. Find what I need to integrate with the API. Note the steps I perform.
- Customer: Signs up for the service, with me providing the steps they'll need to take.
- Me: Ask my customer to give me the info I need, or grant me access if that option is available.
- Customer: "I can't figure it out, here's my login info, find it please."
- Me: I find the info, and tell the customer to reset their password. I doubt they ever do.
You're offering the service, yet there's no "you" in that process.
Here's the process I'd like to see:
- You: Tell me I can easily transfer the account (or project or access keys) before I bother to sign up
- Me: Sign up for an account for myself.
- Me: Get everything working.
- Me and You: If I know my customer already has an account, provide a place for me to enter my customer's email address. If you offer more than one thing I'll need access to, let me indicate what I need for this project.
- You: Send detailed instructions of to grant access to my needs.
- You: If my customer does not have an account, you send them detailed instructions for how to sign up and what they'll need to know to get value from your service.
- Customer: signs up for their account, indicating they are actually working with me.
- You: Hook up our accounts, with me transferring ownership of that "project" to my customer.
There's a lot less of me and my customer in that process, and a lot more of you.
It's certainly more complex, but it's simpler for the people who matter: your users.
Remember, the software developer is often the one who makes recommendations as to which products to use. Make
it easier for me, and I'll be more likely to choose your service over a competing one.
post scriptum side note: I used Lucid Chart to do the flow charts. I really
like it, but given how rarely I've wanted to diagram online, I don't see myself becoming a paid
subscriber. What do you use?
Posted by Sam on Dec 21, 2011 at 08:49 AM UTC - 5 hrs
Type Casing is the act of using case statements
in a program to determine what to do with an object based on what type of object it is. It's an OO fail, often
hoping to implement
Multiple Dispatch. (See also
Case Statements Considered Harmful)
Here are three passive-aggressive ways to feel like you're getting back at typecasers.
More...
The first tactic turns your object into an
everything, so it's whatever the typecaser was looking for. I've called it
OmniObject.
module OmniObject
def is_a?(*)
true
end
def kind_of?(*)
true
end
def nil?
true
end
end
foo = "hello, world!"
foo.extend OmniObject
puts "Is foo a Fixnum? #{foo.is_a?(Fixnum) ? 'yes' : 'no'}"
puts "Is foo a Kernel? #{foo.is_a?(Kernel) ? 'yes' : 'no'}"
puts "Is foo a NilClass? #{foo.kind_of?(NilClass) ? 'yes' : 'no'}"
puts "foo.nil? => #{foo.nil?}"
The next one makes your object unable to decide what it is, turning it into a
FickleTeenager. If he has to check more than once,
the typecaser is going to have a tough time with a kid who can't make up his mind.
module FickleTeenager
def is_a?(*)
sorta
end
def kind_of?(*)
sorta
end
def nil?
sorta
end
def sorta
truish
end
def truish
rand < 0.5
end
end
foo = "hello, world!"
foo.extend FickleTeenager
3.times{ puts "Is foo a String? #{foo.kind_of?(String) ? 'yes' : 'no'}"}
Finally, we have the
AntisocialPrivacyAdvocate. When the typecaser asks him what he is, he tells them like it is: It's none of your damn business!
class WhatBusinessOfItIsYoursError < StandardError; end;
module AntisocialPrivacyAdvocate
def is_a?(*)
raise WhatBusinessOfItIsYoursError
end
def kind_of?(*)
is_a?
end
def nil?
is_a?(NilClass)
end
end
foo = "hello, world!"
foo.extend AntisocialPrivacyAdvocate
result = foo.kind_of?(String) rescue "#{$!} OMG, How Rude!"
puts result
Posted by Sam on Dec 09, 2011 at 06:13 AM UTC - 5 hrs
Suppose you have some awesome analytics tool that provides great value to a bank's customer, but
they need to interact with it through the bank's website, and you need to host the tool.
You already have the data you need for the analytics to work, and the only missing piece you're left
to consider is "how do I know to whom to show which data?"
The data is private, so you need to ensure you're not showing it to someone who's not authorized to see it.
More...
Photo by pbkwee
Some more constraints:
-
You don't want to generate thousands of usernames and passwords that would create more burden for the
users (and force them to log in twice)
-
You can't hook into the bank's user system. (nor would you want to, since they'd still have to log in twice)
-
You want this to be reasonably simple for the bank to implement whatever they need to do to make it
work on their end.
To show it on their site, you decide to create a template-less app that they'll embed in an iframe.
So what authorization mechanism do you use to know who should see which data?
One idea I had was to simply restrict which IP addresses (to those of the bank) the app would respond to.
Then they could just request a page via HTTP and serve up our response. But in order to get that to work,
there's too much that needs to be done: we have to coordinate all URLs with whatever the bank decides
they should be, and they have to figure out whether to GET, POST, PUT, etc. back to us.
The next idea was to have a single page that is restricted by IP address (to those of the bank), where
they will make an HTTP request to receive a token that expires after some length of time (or at the bank's
request, if they hit another URL to invalidate the token). The bank includes the token in the iframe src
URL which lets you know whose data to show.
But I'm really interested in hearing your thoughts.
How would you do it? Is there a
standard way of doing this already?
Posted by Sam on Mar 25, 2007 at 10:33 AM UTC - 5 hrs
Update: Ruby 1.9.3 adds the ability to use ranges as arguments to rand, which produces more obvious code. So if you're using it, instead of using "magic offsets" like I did in the original post (as Joni Orponen mentions in the comments below), it would be better to use rand(1..6) to simulate a die roll.
So to summarize: if you need a percentage between 0 and 1, just call rand. If you need an integer between 0 and x (not including x), you can still call rand(x). Finally, if you need a number in a specific range, just call rand(x..y) where x is the lower bound of the range, and y is the higher end.
(And recall that if you want a non-inclusive range, you can use 3 periods, like rand(1...100) to get numbers from 1 to 99. Although if you're typing the number out, it's certainly better to use 1..99, if you had used a variable in the higher part of the range, the 3rd period is preferable to 1..(x-1), in my opinion.)
The original post follows:
Another quick note today... I surprisingly have yet to need a random number in Ruby up to this point (or forgot if I did), so I went through a little hassle trying to find out how. Turns out, you can simply use rand(int). So, if you needed a random integer to simulate a roll of a six-sided die, you'd use: 1 + rand(6). A roll in craps could be simulated with 2 + rand(6) + rand(6).
Finally, if you just need a random float, just call rand with no arguments. After that, you can modify the range as you normally would in anything else (i.e., using addition and multiplication).
I guess I used some crappy search terms, because I couldn't find this via google, and I didn't see it skimming the usual suspects in the Ruby docs.
Last modified on Feb 15, 2012 at 05:58 AM UTC - 5 hrs