HNHacker News
TopNewBestAskShowJobs

urbanautomaton

138 karma · joined June 28, 2011

All-purpose developer and C++ refugee, currently molesting Ruby on behalf of Tribesports.
submissionscomments
urbanautomaton··on Tinder sued for sexual harassment by former co-founder
Well, I'm not sure I agree that not much harassment goes on in more established companies, but you're absolutely right that at least they're more likely to have procedures in place for dealing with it.

This is why I'm astonished to see such strenuous effort go into confecting explanations why there might not really be a problem in startup culture. You've got young, inexperienced founders and a tendency to dispense with formal HR practises in favour of "cultural fit" - the ingredients are all there for horrible abuses of power, and yet when said abuses inevitably surface, there's an almost desperate effort to hand-wave them away as isolated incidents. It's bewildering, it really is.

Model View Culture have been publishing some superb work in this area, most relevantly in this article:

http://modelviewculture.com/pieces/hr-antipatterns-at-startu...

urbanautomaton··on Tinder sued for sexual harassment by former co-founder
Call me naive, but when harrassment this egregious is exposed at a well-known tech company, I don't feel the need to stroke my chin and wonder if there's a media conspiracy to make it appear worse than it is. I don't even know how this could be made to look worse than it is. What possible spin could a publisher put on this that is not already present in the allegation and evidence?
urbanautomaton··on Gittip, Year Two
I have no time for someone who claims to be quoting "the legal documents" when he is in fact parroting one side's case in a domestic violence dispute to which he is not a party. Thanks for clarifying what you are, though; that's useful, albeit hardly surprising.
urbanautomaton··on Gittip, Year Two
> If she doesn't like it, she can go back to actually working for a living, like the rest of us.

The rest of us turn up for 8 hours on a good day, turn in shitty code on our horrifically-factored, monolithic Rails app that some other poor shlub will have to maintain, break off at 2pm on a Friday for craft beer and convince ourselves we're changing the goddamn world with our latest social widget.

"Working for a living, like the rest of us"? Don't make me laugh.

The most half-assed Ruby dev, and oh my goodness there's some competition for that title, would easily be earning an integer multiple of what Ashe gets on gittip. You quite clearly know absolutely zilch about how much work Ashe does, and you so very obviously have no idea whatsoever how to value it, so how about you quieten down and go take a look at yourself, eh?

urbanautomaton··on Gittip, Year Two
And as a guy not on the margins, who has been repeatedly educated and informed by these "spoiled princesses", I can't say I agree with him either. I find their work incredibly valuable, so I'd be interested to hear why joaren believes they're setting back their own cause.

Well, maybe "interested" is overstating it a bit. "Morbidly curious"?

urbanautomaton··on Goodbye, Firefox Marketplace
I would be ecstatic if the businesses I chose to deal with had such principles, yes, and would gladly join their boycott. We are all involved in the civil rights "debate", whether we like it or not.
urbanautomaton··on Goodbye, Firefox Marketplace
Brendan Eich chose to make his personal belief public by paying for it to be written into law, and his customers, who include publishers on his platform, are absolutely free to hold him accountable for that.

What his employers choose to do in response to that public knowledge and action is up to them, within the limits of employment law. It is my understanding that homophobes are not a protected class under said law, and long may that continue.

Any further inferences you draw from my posts are unwarranted, although I'll tell you for free that any company that asks potential employees what their sexual orientation is can get stuffed as far as I'm concerned.

urbanautomaton··on Goodbye, Firefox Marketplace
Weighed in the balance:

1. the right of gay people to get married

2. our personal right to pay money to bigots using specific money-transfer services

I'll be honest, I keep re-rolling, and it keeps coming up 1. What am I doing wrong?

urbanautomaton··on Goodbye, Firefox Marketplace
Well, let me help you out. Brendan Eich choosing to pay actual money (taking a direct action) to oppose the existence of gay marriage (alienating a large community) is what I'm referring to.
urbanautomaton··on Goodbye, Firefox Marketplace
> That's a very very very VERY unprofessional stance.

I do not recognise this definition of professionalism, and am happy to be called unprofessional if it is the accepted one.

Mozilla is an organisation that is dependent on community goodwill and support, and if the person they choose as a figurehead is one who has taken direct action to alienate a large segment of that community, then it seems to me entirely proper for that community to react as they see fit.

urbanautomaton··on Rails autoloading – how it works and when it doesn't
I totally agree. And this is before we even consider what ubiquitous autoloading encourages us to do to our code in terms of coupling. Our classes are available everywhere, so the tendency to create many hard dependencies on a wide range of classes is very strong. Want to run a SQL query right in a view? Nothing stopping you. Want to instantiate a controller in a model so you can get at the path helpers? I will hunt you-- er, sorry, I mean, "nothing stopping you" (except your pride).

Like you, I find it very hard to rubbish the auto-reloading, but even this encourages bad habits, I think. Rather than write tests for the behaviour we just implemented, or the bug we just fixed, we hit F5. Didn't work? Change something, hit F5. This at least isn't really Rails' fault; it's our job as programmers to do the professional thing. But it does act as something of a siren call to the rocks of cowboy programming. And like you say, there are other, more insidious issues with divergent dev/production environments.

urbanautomaton··on Rails autoloading – how it works and when it doesn't
Yep - if you pass a path the behaviour is as normal, but it's still overloaded to support the path inference behaviour. I guess it's not really monkeypatching though as `Module` itself is not modified, and you have to deliberately choose to use `ActiveSupport::Autoload`, so I reluctantly retract my parenthetical snark. :-)
urbanautomaton··on Rails autoloading – how it works and when it doesn't
Rails does overload Ruby's autoload (naturally - no core functionality can remain un-monkeypatched. ;-)), but not for the on-demand autoloading the article describes, which is what most users contend with most frequently, and was what I most wanted to demystify. Thanks for the clarification though - I'll try to make the distinction clearer.
urbanautomaton··on A Mysterious Patch Of Light Shows Up In The North Dakota Dark
Just to make sure we're on the same page: I'm talking about the externality of global warming, which at present is completely absent from the oil pricing system.

As a result, I'm not sure it's as simple as a straightforward efficiency calculation, because under a system where the consumer bears the currently externalised costs of burning fossil fuels, both the cost of flaring would go up, and the oil value captured by the drilling company would go down. I have no idea what a sane level for a carbon tax would be, and I'm certainly not economist enough to predict the ramifications for global fuels pricing; like I say, it may well work out that flaring remains cost-effective. But the costs of an action should be borne by the party that benefits, otherwise the incentives are, as I say, totally screwed.

urbanautomaton··on A Mysterious Patch Of Light Shows Up In The North Dakota Dark
The incentives are completely screwed, because flaring off that gas represents a mere opportunity cost to the oil company, yet imposes a heavy externality on the rest of the world. The waste is only "less expensive than the alternative" because the person doing the wasting isn't the one paying the price.

So yes, absolutely: impose a carbon tax, where the person burning any fossil fuel pays a price that actually reflects the externalities of their choice. That's not arbitrary at all. If it still makes financial sense to flare rather than capture, fine. But there's no pretending that everything is all right with the existing equation.

urbanautomaton··on "DCI" in Ruby is completely broken
I took a stab at separating out the method dispatch costs from the cost of the #extend call here https://gist.github.com/4436640. I also tried making a call against an unmodified instance of a completely different class to demonstrate that the global method cache is indeed flushed in MRI by doing a runtime #extend on any object instance.

Subtracting the results of different runs like this is pretty questionable, though (the errors accumulate), so the above is to be taken with a pinch of salt.

urbanautomaton··on "DCI" in Ruby is completely broken
It doesn't seem to be widely acknowledged amongst people promoting this particular style of DCI as a good way to write Rails app, so I'd say the point is valid, even if the title is overstated. There are also quite a few other very popular gems that make heavy use of #extend at runtime for non-DCI purposes, including Haml and Paperclip, so I don't think it's fair to say that this is common knowledge (at least in the Ruby community).
urbanautomaton··on Fizz Buzz codegolf challenge in 15 languages
"You Scored: 56.00000000000001". :-)
urbanautomaton··on How deep are your unit tests?
You're quite right, query builder statements are error-prone and difficult to stub; that's why you shouldn't sprinkle them throughout your app. Rather than write things like

  User.active.in_group(x).not_replied.each { ... }
all over the place, write scopes on your User model (e.g. User.awaiting_response_for_group(x)) that represent each use case, test them thoroughly against the database, and then stub them out when testing client code. That way you only have to test the queries in one place, and you don't couple the rest of your code to your query builder.
urbanautomaton··on Applying the Unix Philosophy to Object-Oriented Design
> It feels like we're on totally separate universes.

It does indeed. Never mind, it's happened before and will no doubt happen again. :-)

If all you're proposing is a coding convention, then I don't see why you're surprised/annoyed that the OP doesn't obey it; it's vanishingly rare. You're placing a significant structural constraint on your code that conveys only optional semantics, and ones that will apply to virtually no third-party code. Why not adopt a more rubyish convention and use bangs to denote methods that do have side-effects (c.f. String#gsub vs. String#gsub!)? This way you're not imposing any structural constraints on your code, only a minor naming restriction.

Re: refactoring, constructor params are only one possible change we might make. Our checker might store previously-seen content in an ivar, so that the first time it sees the content "hey sexy, why not call me?" it allows it to pass, but if it sees 1000 such messages in quick succession, it flags them as spam.

However: yes, changes to object instantiation will require changes to client code (although, as I pointed out earlier, it's easy to provide access to a default instance via a class method, providing both convenience and flexibility). In something other than a Rails application this would be mitigated using dependency injection, so that merely being a client of a particular object does not mean you have to know how to instantiate it. Unfortunately as Rails monopolises object construction for its own infernal purposes this isn't really possible, so you end up with unpalatable things like this knocking around all over the place:

    class GuestBookEntry < ActiveRecord::Base
      def spam_checker
        @spam_checker ||= UserContentSpamChecker.new(...)
      end
    end
While not ideal, this at least localises changes to one site in each client class, and one whose concern is directly related to the change you're making.
urbanautomaton··on Applying the Unix Philosophy to Object-Oriented Design
> As a client I like to know if a method is purely functional or if it's going to have side-effects because I care to know if the operation is going to be re-entrant or not.

But your implementation doesn't convey this information. This simply isn't a guarantee you're going to obtain with Ruby; you have to inspect the code. A class method can do pretty much anything it likes; it could rewrite Object#method_missing if it wanted to. It can certainly store state, as I demonstrated. What's worse, it's global state, because every client is accessing a shared instance via a global constant.

As for refactoring, search and replace isn't difficult (although it is error-prone); the important point is that it's introducing unnecessary change. All that's changing is the internal implementation of your spam checker, and yet that change propagates to every single place your spam checker is used, all because of the way you implemented it originally. This doesn't seem like a good trade-off to me, given that we're not even obtaining a functional guarantee in return.

urbanautomaton··on Applying the Unix Philosophy to Object-Oriented Design
> I think that we agree on the bottom-line: UserContentSpamChecker has no need for a state and is just some kind of namespace.

I'm not sure that we do. I agree that the current implementation of `UserContentSpamChecker#is_spam?` doesn't need state, but I don't agree with your conclusion that this distinction should be made obvious to clients, who surely just care that they have a thing that will check for spam, to which they can pass content. They don't care if it's stateful or not, as long as it accepts strings and returns booleans. Why are we trying to tell them that this particular method is stateless?

After all, even that offers a false guarantee. In your implementation, `#is_spam?` is just another method on an object instance - in this case an instance of class Module, referred to by the global constant UserContentSpamChecker. I can happily use instance variables in such a method:

    module Stateless
      extend self
      def no_state_here!
        @thing ||= 0
        @thing += 1
      end
    end
    
    > Stateless.no_state_here!
    => 1
    > Stateless.no_state_here!
    => 2
    > Stateless.no_state_here!
    => 3
Your implementation resists future refactoring, forces clients to create a hard dependency on a global constant, and doesn't make the guarantee you intend it to convey. We agree that the current implementation doesn't need an object instance, but can you explain what is actually better about avoiding one? Ruby is an object-oriented language, after all; objects are its common currency. It's not like we're introducing the Strategy pattern for a four-line method, we're just using Ruby as she is wrote. :-)

p.s. sorry vidarh, I see you've covered some of this already - serves me right for half-composing a reply then wandering off...

urbanautomaton··on Applying the Unix Philosophy to Object-Oriented Design
I see this assertion increasingly frequently, and it puzzles me: that if we're not currently using an object to encapsulate state, we should prefer class methods. What's the justification? Other than four characters saved (".new"), what's the benefit in this approach?

State encapsulation is just one feature of objects. We may not be using it right now, but the only thing you achieve with the above code is removing future flexibility. It costs us nothing to allow for the future possibility of encapsulated state, so why rule it out? Adding complexity when you don't yet need it, fine, I quite understand objecting to that; but here you're actually putting in effort to make a future modification more difficult.

Funnily enough, Code Climate's previous blog entry was on precisely this topic, and is worth a read:

http://blog.codeclimate.com/blog/2012/11/14/why-ruby-class-m...

For what it's worth, I don't see the point in instantiating a whole new spam checker for every piece of content, so I'd probably change the OP's example to read:

    class UserContentSpamChecker

      TRIGGER_KEYWORDS = %w(viagra acne adult loans xrated).to_set

      def is_spam?(content)
        flagged_words(content).present?
      end

      protected

      def flagged_words(content)
        TRIGGER_KEYWORDS & content.split
      end
    end
If I really really wanted access to a default spam checker via a global constant I can always add the following:

    class UserContentSpamChecker
      def self.is_spam?(content)
        new.is_spam?(content)
      end
    end
At least then if my UserContentSpamChecker class ever has to change (perhaps it starts to use an external spam-checking service that's injected through the constructor), then I only need to change code in one place. And other clients that might want to inject a different spam-checking service (or a test double) are perfectly able to.
urbanautomaton··on HAML vs ERB vs SLIM in terms of render speed
Yes, some configuration detail would be helpful. For instance, I can't find any custom Haml config, suggesting it's running with the defaults - in production mode this would mean that the output HTML is indented, while it wouldn't be in development or test. This has a noticeable performance effect, and it looks as though the same is true of Slim.
urbanautomaton··on Is it OK to hold credit card numbers in cookies, Santander?
The one almost inexcusable omission from their online banking is any way to download your statements. You have to scrape the data from the pages themselves, and infuriatingly, they display the page of most recent items in a slightly different format to older pages, making it a pain to merge data scraped from different pages.

In lesser annoyances, they make you do a stupid amount of typing to log in, about the only bit of which is actually secure is two randomly selected digits from your 4-digit PIN. All other info is insecure: account number, sort code, first school attended, yada yada yada. All in all they make you enter something like 30 characters spread across 3-5 input boxes and two pages, all for about 6 bits of actual entropy. Unless, of course, you're the sort of person who when asked to tell your bank what your first school was, replies "8EOHzxdO6QnJ".

urbanautomaton··on Pub/Sub system events for post-action tasks in Rails
I agree that using Observers (at least as embodied in Rails' implementation) can lead to a fearful (and hard-to-debug) mess. I feel that our approach differs, though, in forcing the notification to be explicit, as opposed to tied to object lifecycle callbacks. It's plain to see in our controller what event is being announced, and trivial to find all listeners that respond to that event. No familiarity with the model being manipulated is required, because it's got nothing to do with either the event announcement or the responses to it. And by providing a centralised event dispatch system, there's a simple point of entry for debugging.

We did consider the service object approach, as detailed early in the blog - for me, though, it feels wrong to end up with a bunch of service objects, each with a semi-random grab-bag of dependencies and responsibilities. One of our main aims was to reduce the random scattering throughout our app of various calls, because the coupling was making it hard to reliably effect change when necessary. Service objects don't address this, unfortunately. We still make use of them, but to my mind a CommentCreationService should only be concerned with creating valid comments - not with emailing or rewarding users.

urbanautomaton··on Pub/Sub system events for post-action tasks in Rails
It's a trade-off, really. As mentioned in the blog post, we had considerably more complex actions, and many more types of post-action task than were shown.

You're quite right, in the original code it's easier to see at a glance what happens in that particular action. However, over the span of the whole application it's much more difficult to see, say, all circumstances in which an activity update is triggered, or all the ways a user might get a gamification reward for doing something on the site. It was precisely the growing complexity of our app that motivated this change; we were losing the ability to keep control of (and test) whole important features of our site, such as mailout logic, gamification, etc. and so forth.

As I say at the end, I certainly don't recommend this as a catch-all approach; you've got to know your app and be aware of the advantages and disadvantages.

urbanautomaton··on RSpec Best Practices
But if you're having to create lots of mock objects, that's a huge signal that your objects are tightly coupled, and that not only are you writing a bad test, you've written a bad subject-under-test.

Yes, you still have to write integration tests. But the discipline of testing your objects in isolation and outlining the objects they collaborate with is a major benefit of using test doubles, not a downside. If you can't reason about your objects in isolation, how can you expect to reason about them in aggregate?

urbanautomaton··on Version your RESTful API responses
I don't think "media type" is synonymous with "data format". For example, IANA considers atomcat+xml and atomsvc+xml to be distinct media types [1], despite them both being XML, and there are 406 other XML-based media types in that list.

[1] http://www.iana.org/assignments/media-types/application/inde...

urbanautomaton··on Version your RESTful API responses
I personally prefer this approach - certainly if the changes you're making are only to the representation of your resources, then (assuming they can't be made purely additively, i.e. without breaking existing clients) simply making a new content type available seems the natural way to go.

If it's of interest, I wrote a couple of articles about how we implemented exactly this sort of versioning in Rails for our app's API:

http://techblog.tribesports.com/blog/2011/09/24/versioning-t... http://techblog.tribesports.com/blog/2011/09/24/separating-a...

I think these demonstrate the advantages of content negotiation as a versioning mechanism. To introduce a new API representation, we simply have to define a new set of decorators for our exposed resources, define a new MIME type and we're set. No monkeying with our routing, no messing around in controllers; a view-level change entails only view-level code alterations.

Page 1 of 2Next →