<?xml version="1.0"?>
<rss version="2.0">
  <channel>
    <title>Advogato blog for dan</title>
    <link>http://www.advogato.org/person/dan/</link>
    <description>Advogato blog for dan</description>
    <language>en-us</language>
    <generator>mod_virgule</generator>
    <pubDate>Fri, 10 Feb 2012 14:26:48 GMT</pubDate>
    <item>
      <pubDate>Wed, 1 Feb 2012 13:38:17 GMT</pubDate>
      <title>ANN Twitling: a Twitter link digest tool</title>
      <link>http://www.advogato.org/person/dan/diary.html?start=159</link>
      <guid>http://ww.telent.net/2012/2/1/ann_twitling_a_twitter_link_digest_tool</guid>
      <description>&lt;p&gt;Problem: I can&#x2019;t keep up with the Internet&lt;/p&gt;
&lt;p&gt;I often check Twitter on my phone.  When I see tweets with links in
them I tend to skip over them intending to return later when I&#x2019;m on a
computer with a full-size screen, and then forget about them either
because I find something else to look at or I can&#x2019;t be bothered with
scrolling all the way down again.  And looking through old tweets is
nearly as bad on the full-size twitter web site as it is in a mobile
client.&lt;/p&gt;
&lt;p&gt;Proposed solution: I need a computer program to read the Internet for me&lt;/p&gt;
&lt;p&gt;Thus, &lt;a href="http://twitling.telent.net/" &gt;Twitling&lt;/a&gt;: a small
script consisting of Ruby and Sinatra and OmniAuth and the Twitter gem
and Typhoeus to grab links in parallel, the function of which is to
read one&#x2019;s timeline and display the resolved &lt;span&gt;URL&lt;/span&gt;, the title and an
excerpt from the text of each link that was posted.  &lt;a href="https://github.com/telent/twitling" &gt;Source code&lt;/a&gt; is on
Github.&lt;/p&gt;
&lt;p&gt;I haven&#x2019;t really used it myself yet in anger: the first thing I notice
while testing it is that there are a whole lot more links in my feed
than I thought there were, and the original plan to produce a 24 hour
digest might become very unwieldy.&lt;/p&gt;
&lt;p&gt;Possible further development ideas include&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;speed it up, by prefetching, better caching, or fetching the links
  asynchronously and client-side&lt;/li&gt;
&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;an &#x201C;older&#x201D; link at the bottom of the page&lt;/li&gt;
&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;Atom/&lt;span&gt;RSS&lt;/span&gt; output so it gets fed to me every so often and I don&#x2019;t have
  to remember to check it&lt;/li&gt;
&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;email output (for the same reason)&lt;/li&gt;
&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;some css gradient fills just to make it look modern (hey, I already
  used text-shadow, what do you want, round button borders?)&lt;/li&gt;
&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;em&gt;your suggestion here&lt;/em&gt;: email dan@telent.net or &lt;a href="https://github.com/telent/twitling/issues" &gt;open an
  issue&lt;/a&gt; on Github.  Bug
  reports too.&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;Try not to break it, please.&lt;/p&gt;</description>
    </item>
    <item>
      <pubDate>Sun, 29 Jan 2012 22:39:22 GMT</pubDate>
      <title>backbone.js 1 0 jQuery</title>
      <link>http://www.advogato.org/person/dan/diary.html?start=158</link>
      <guid>http://ww.telent.net/2012/1/29/backbone_js_1_0_jquery</guid>
      <description>&lt;p&gt;I&#x2019;ve spent a few hours over the last couple of days figuring out how to
use backbone.js, and so far I&#x2019;m impressed by it: it solves a real
problem elegantly and doesn&#x2019;t seem to have an entire religion bolted
onto the side of it.&lt;/p&gt;
&lt;p&gt;5 minute summary: it introduces &lt;strong&gt;models&lt;/strong&gt; (and &lt;strong&gt;collections&lt;/strong&gt; of them)
and &lt;strong&gt;views&lt;/strong&gt; to client-side javascript, and connects them with a
publish/subscribe event notifier system so that when you make changes
to a model all the views of it update without your having to remember
to do anything to them.&lt;/p&gt;
&lt;p&gt;A &lt;code&gt;Model&lt;/code&gt; is an object that knows how to update itself from a Rails-y
&#x201C;&lt;span&gt;REST&lt;/span&gt;&#x201D; server (scare quotes, because as we all know these days &lt;a href="http://intridea.com/2010/4/29/rest-isnt-what-you-think-it-is?blog=company" &gt;&lt;span&gt;REST&lt;/span&gt;
isn&#x2019;t what you think it
is&lt;/a&gt;
), and publishes its attributes using the methods &lt;code&gt;set&lt;/code&gt; and &lt;code&gt;get&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;
	var m=find_me_a_model();
	var selected= (m.has('selected')) ? m.get('selected') : false;
	m.set({selected:  !selected});
&lt;/pre&gt;
&lt;p&gt;Calling &lt;code&gt;set&lt;/code&gt; will, if the value has changed, trigger a &lt;code&gt;changed&lt;/code&gt;
event handler to be called in all objects which have bound to it.
These objects are usually Views.&lt;/p&gt;
&lt;p&gt;A View is an object with a &lt;code&gt;render&lt;/code&gt; method and an &lt;code&gt;el&lt;/code&gt; attribute, and
in which calling the former creates a piece of &lt;span&gt;DOM&lt;/span&gt; tree in the latter,
which you can then attach to your document somewhere&lt;/p&gt;
&lt;pre&gt;
MyApp.Views.ThingView=Backbone.View.extend({
    initialize: function() {
	this.model.bind("all",this.render,this);
	this.render();
    },
    // ... this is not working code - I missed out some important bits ...
    events: {
	"click li" : "do_select",
    },
    do_select: function(e) { ... },
    render: function() {
	var ul=$(this.el);
	ul.html(some_html_for(this.model));
        return this;
    }
})

jQuery(document).ready(function() {
     var myView=new MyApp.Views.ThingView();
     $('#some_element').append(myView.render().el);
});
&lt;/pre&gt;
&lt;p&gt;Collections are provided too.  They come with a large number of
iteration functions (&lt;code&gt;map&lt;/code&gt;, &lt;code&gt;filter&lt;/code&gt;, &lt;code&gt;reduce&lt;/code&gt;, all that stuff) which
makes them really rather useful, and you can build Views of them in
much the same way as you can build views of models.&lt;/p&gt;
&lt;p&gt;(To complete the completion, there&#x2019;s also a Router, which is an
interface for monkeying around with the &lt;span&gt;URL&lt;/span&gt; so you can build
bookmarkable client-side apps.  But I haven&#x2019;t had to use that yet)&lt;/p&gt;
&lt;p&gt;Anyway.  As you see in the example above, the view can also take a
hash of events which is registered with jQuery using its
&lt;a href="http://api.jquery.com/delegate/" &gt;delegate&lt;/a&gt; method.  In this case we&#x2019;re
asking to have &lt;code&gt;do_select&lt;/code&gt; called whenever a &lt;code&gt;click&lt;/code&gt; event is received
on any &lt;code&gt;li&lt;/code&gt; element inside it.  Great!&lt;/p&gt;
&lt;p&gt;Not so great when it unexpectedly doesn&#x2019;t work, though.  Specifically,
&lt;em&gt;jQuery drag/drop events don&#x2019;t work with jQuery&#x2019;s delegate method&lt;/em&gt;, and
there&#x2019;s nothing in the documentation on either page to stop you
wasting an afternoon finding this out.  Way to go.  For more details
on just &lt;em&gt;how much&lt;/em&gt; hysterical raisins mess is involved with jQuery
event handlers, see the pages for &lt;a href="http://api.jquery.com/on/" &gt;on&lt;/a&gt; and
&lt;a href="http://api.jquery.com/live/" &gt;live&lt;/a&gt; &#x2013; look upon these works, ye mighty,
and despair.&lt;/p&gt;
&lt;p&gt;backbone.js is &lt;a href="http://documentcloud.github.com/backbone/" &gt;here&lt;/a&gt;.  There&#x2019;s
a Ruby gem for using it with Rails: add &lt;code&gt;rails-backbone&lt;/code&gt; to your
Gemfile, and you get a handy set of generators which write Coffeescript
for you.  (A brief inspection of the result says that this is a good
thing because there&#x2019;s no way on earth I&#x2019;d want to write that stuff
myself.  But I concede, significant whitespace is a valid personal
preference, just not one of mine.)&lt;/p&gt;</description>
    </item>
    <item>
      <pubDate>Mon, 23 Jan 2012 14:36:02 GMT</pubDate>
      <title>Making reload! work in Pry with Rails 3.2</title>
      <link>http://www.advogato.org/person/dan/diary.html?start=157</link>
      <guid>http://ww.telent.net/2012/1/23/making_reload_work_in_pry_with_rails_3_2</guid>
      <description>&lt;p&gt;As of Pry 0.9.7.4 (the current version according to Bundler at the
time I write this), the setup instructions for replacing irb with Pry
when you run &lt;code&gt;rails console&lt;/code&gt; no longer work fully in Rails 3.2.
Specifically, the Rails team have changed the way they create irb
commands like &lt;code&gt;reload!&lt;/code&gt;: where in earlier version they were added to
&lt;code&gt;Object&lt;/code&gt;, now they are added to &lt;code&gt;IRB::ExtendCommandBundle&lt;/code&gt; to avoid
polluting the global namespace.  Here&#x2019;s the &lt;a href="https://github.com/rails/rails/pull/3509" &gt;github pull request&lt;/a&gt; where the change is described.&lt;/p&gt;
&lt;p&gt;(The cynic here will say &#x201C;Rails?  Namespace pollution?  Lost cause,
mate&#x201D;, but hey, let&#x2019;s not be down on attempts to make it better)&lt;/p&gt;
&lt;p&gt;&lt;span&gt;IRB&lt;/span&gt; already knows how to look in &lt;code&gt;IRB::ExtendCommandBundle&lt;/code&gt;; Pry
doesn&#x2019;t, so what will happen if you have installed Pry in &lt;a href="http://www.dotnetguy.co.uk/post/2011/08/23/replace-the-rails-console-with-pry" &gt;the usually
recommended
way&lt;/a&gt;
by assigning &lt;code&gt;IRB=Pry&lt;/code&gt; is you&#x2019;ll get an error that &lt;code&gt;Pry::ExtendCommandBundle&lt;/code&gt;
doesn&#x2019;t exist.&lt;/p&gt;
&lt;p&gt;(I&#x2019;ve seen &#x2018;fixes&#x2019; for this bug that assign
&lt;code&gt;Pry::ExtendCommandBundle=Pry&lt;/code&gt;.  This will make &lt;code&gt;rail console&lt;/code&gt; start,
but it still doesn&#x2019;t make the commands accessible.  Less than
&lt;em&gt;entirely&lt;/em&gt; useful then)&lt;/p&gt;
&lt;p&gt;So, let&#x2019;s make it.  Here&#x2019;s the relevant bit of my &lt;code&gt;.pryrc&lt;/code&gt;: feel free
to use as inspiration, but don&#x2019;t blame me if copy/paste doesn&#x2019;t work
&lt;/p&gt;&lt;pre&gt;
if Kernel.const_defined?(&#x201C;Rails&#x201D;) then
  require File.join(Rails.root,&#x201C;config&#x201D;,&#x201C;environment&#x201D;)
  require &#x2018;rails/console/app&#x2019;
  require &#x2018;rails/console/helpers&#x2019;
  Pry::RailsCommands.instance_methods.each do |name| 
    Pry::Commands.command name.to_s do 
      Class.new.extend(Pry::RailsCommands).send(name)
    end
  end
end
&lt;/pre&gt;
&lt;p&gt;If you are using a newer version of Pry than me &#x2013; well, first off,
they may have fixed this already and if so you can ignore this whole
post.  But if they haven&#x2019;t, and if &lt;code&gt;Pry::Commands.command&lt;/code&gt; is giving
you trouble, note that the unreleased Pry 0.9.8 is set to include a
new way of defining custom commands and you may need to rewrite this
using the new &lt;code&gt;Pry::Commands.block_command&lt;/code&gt; construct instead.&lt;/p&gt;
&lt;p&gt;
  &lt;span&gt;HTH&lt;/span&gt;
&lt;/p&gt;</description>
    </item>
    <item>
      <pubDate>Mon, 23 Jan 2012 14:36:02 GMT</pubDate>
      <title>Micro setup for minitest in rails</title>
      <link>http://www.advogato.org/person/dan/diary.html?start=156</link>
      <guid>http://ww.telent.net/2012/1/21/micro_setup_for_minitest_in_rails</guid>
      <description>&lt;p&gt;I think this is the bare minimum setup for being able to write
&lt;code&gt;Minitest::Spec&lt;/code&gt; tests in a Rails 3.1 app, and certainly a lot simpler
than all that faffage with &lt;code&gt;minitest-rails&lt;/code&gt; and stuff&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;add the line &lt;code&gt;require 'minitest/spec'&lt;/code&gt; somewhere near the top of &lt;code&gt;test/test_helper.rb&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;write tests that look something like this:
&lt;pre&gt;
require &#x2018;test_helper&#x2019;
require &#x2018;edition&#x2019;
&#xA0;
describe Edition do
  it &#x201C;ex nihilo nihil fit&#x201D; do
    nil.must_be_nil
  end
end
&lt;/pre&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;we don&#x2019;t create generators, but really, why do you need a generator
to add one line of code?  To disable the builtin &lt;code&gt;Test::Unit&lt;/code&gt;
generators &#x2013; which you may as well because in this context they&#x2019;re
useless, add&lt;/li&gt;
&lt;/ul&gt;&lt;pre&gt;
    config.generators do |g|
      g.test_framework nil
    end
&lt;/pre&gt;
&lt;p&gt;inside &lt;code&gt;YourApp::Application&lt;/code&gt; in &lt;code&gt;config/application.rb&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;This is all pretty vanilla &#x2013; it doesn&#x2019;t do spork or any of the
faster-testing-through-not-loading-the-framework stuff, but with those
three simple steps you can run &lt;code&gt;rake test:units&lt;/code&gt; just as you would
with the default Test::Unit stuff.  &lt;code&gt;test:foo&lt;/code&gt; for other values of foo
appears also to work, but I don&#x2019;t have any integration tests in this
project yet so don&#x2019;t take my word for it.&lt;/p&gt;
&lt;h2&gt;Double Trouble&lt;/h2&gt;
&lt;p&gt;I can see no way in minitest to create a partial mock: viz. a real
object with some but not all methods mocked.  In fact I can see no
&lt;em&gt;documented&lt;/em&gt; way in minitest to do any kind of mocking at all.  As the
new fashionable Double Ruby (a.k.a. &lt;a href="https://github.com/btakita/rr" &gt;rr&lt;/a&gt;
) library scores highly on both these counts, I decided to use that
too.&lt;/p&gt;
&lt;p&gt;This is a simple matter of adding &#x201C;rr&#x201D; to the &lt;code&gt;Gemfile&lt;/code&gt;, then amending
&lt;code&gt;test/test_helper.rb&lt;/code&gt; to include the lines
&lt;/p&gt;&lt;pre&gt;
require &#x2018;rr&#x2019;
&lt;/pre&gt;
and
&lt;pre&gt;
class MiniTest::Unit::TestCase
  include RR::Adapters::MiniTest
end
&lt;/pre&gt;</description>
    </item>
    <item>
      <pubDate>Fri, 6 Jan 2012 14:36:14 GMT</pubDate>
      <title>Objections on Rails</title>
      <link>http://www.advogato.org/person/dan/diary.html?start=155</link>
      <guid>http://ww.telent.net/2012/1/5/objections_on_rails</guid>
      <description>&lt;p&gt;It seems to be pick-on-rails week/month/quarter again. I&#x2019;m not sure
that 3 months and two projects really should qualify me to have an
opinion on it, but this is the internet, so here we go anyway.&lt;/p&gt;
&lt;p&gt;Recent influences which have provoked these thoughts:&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;a href="http://lanyrd.com/2011/ru3y-manor/skhmk/" &gt;Tom Stuart&#x2019;s talk&lt;/a&gt; at Ruby
Manor, about which I have &lt;a href="http://ww.telent.net/2011/11/9/in_soviet_russia_activerecord_mocks_you" &gt;previously written&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;Avdi Grimm $5 &lt;a href="http://avdi.org/devblog/2011/11/15/early-access-beta-of-objects-on-rails-now-available-2/" &gt;Objects on
Rails&lt;/a&gt;
booklet&lt;/li&gt;
&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;And &lt;a href="http://blog.steveklabnik.com/posts/2011-12-30-active-record-considered-harmful" &gt;Steve Klabnik&#x2019;s blog&lt;/a&gt;
in which he identifies the problem as ActiveRecord and/ or
ActionController and/or ActionView and/or the whole concept of
&lt;span&gt;MVC&lt;/span&gt;. &lt;span&gt;MVC&lt;/span&gt;. Webdevs, you keep using that word. I do not think it means
what you think it means.&lt;/li&gt;
&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;The veneer of delegating-domain-objects I created in my current
$dayjob project, inspired by the writings above and about which this
post was originally going to be until I realised just how much I was
writing even explaining the problem.  Look out for Part Two&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;I&#x2019;m not going to lay into the V and C of Rails here: to be honest,
although they seem rather unpretty (my controller&#x2019;s instance variables
appear magically in the view? ew) they&#x2019;re perfectly up to the task as
long as you don&#x2019;t try to do the heavy lifting in either of those
layers.  Which is a bad idea anyway.  And actually, after writing an
entire site including the layouts in Erector (if you don&#x2019;t know
Erector, think Markaby), there is a certain pleasure in being able to
write the mostly static &lt;span&gt;HTML&lt;/span&gt; bits in &#x2026; &lt;span&gt;HTML&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;No, instead of generalizing that &lt;span&gt;MVC&lt;/span&gt; is the problem, I an going to
confine myself to the ActiveRecord arena and generalize that M,
specifically M built on &lt;span&gt;ORM&lt;/span&gt;, is the problem.  The two problems.&lt;/p&gt;
&lt;p&gt;Here is the first problem.  Object-orientated modelling is about
defining the responsibilities (or behaviours, if you prefer) of the
objects in your system.  Object-relational modelling is about
specifying their attributes.  Except in the trivially simple cases
(&#x201C;an Elf is responsible for knowing what its name is&#x201D;) the two are not
the same: you define an Elf with a method which tells him to don his
pointy shoes, not with direct access to his feet so you can do it
yourself.  So that&#x2019;s the first problem: the objects you end up with
when you design with ActiveRecord have accessors where their instance
variables would be in a sensible universe.&lt;/p&gt;
&lt;p&gt;(Compounding this they also have all the AR methods like &lt;code&gt;#find&lt;/code&gt; wich
in no way correspond to domain requirements, but really I think that&#x2019;s
a secondary issue: forcing you to inherit baggage from your ancestors
is one thing, but this pattern actively encourages you to create more
of it yourself.  This is the kind of thing that drives Philip Larkin
to poetry)&lt;/p&gt;
&lt;p&gt;Here is the second problem.  We&#x2019;re wimping out on &lt;a href="http://en.wikipedia.org/wiki/Relational_model" &gt;relations&lt;/a&gt;.
For the benefit of readers who equate &lt;span&gt;RDMBS&lt;/span&gt; with &lt;span&gt;SQL&lt;/span&gt; with punishment
visited on us by our forebears in 1960s mainframe data processing
departments, I&#x2019;m going to expound briefly on the nature of relational
algebra: why it&#x2019;s cool, and what the &#x201C;object-relational impedance
mismatch&#x201D; really is.  It&#x2019;s not about the difference between &lt;code&gt;String&lt;/code&gt;
and &lt;code&gt;VARCHAR&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;Digression: a brief guide to relational algebra&lt;/h2&gt;
&lt;p&gt;A relation is a collection of tuples.  A tuple is a collection of
named attributes.&lt;/p&gt;
&lt;p&gt;(You can map these to &lt;span&gt;SQL&lt;/span&gt; database terminology if you put your tuples
in a grid with one column per attribute name.  Then attribute=column,
row=tuple, and relation=table.  Approximately, at least)&lt;/p&gt;
&lt;p&gt;An operation takes relation(s) as arguments and returns a relation as
result.  Operators are things like&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;em&gt;select&lt;/em&gt; (a.k.a. &lt;em&gt;restrict&lt;/em&gt;), which selects tuples from a relation
  according to some criteria and forms a new relation containing those
  selected.  If you view the relation as a grid, this operation makes
  the grid shorter&lt;/li&gt;
&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;em&gt;project&lt;/em&gt;, which selects attributes from each tuple by name (make
  the grid narrower)&lt;/li&gt;
&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;em&gt;rename&lt;/em&gt;, which renames one or more attributes in each tuple (change
  the column titles)&lt;/li&gt;
&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;em&gt;set difference&lt;/em&gt; and &lt;em&gt;set intersection&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;some kind of &lt;em&gt;join&lt;/em&gt;: for example the cross join, which takes two
  relations A (m rows tall) and B (n rows tall) and returns an m*n row
  relation R in which for each row Ai in A there are n rows each
  consisting of all attributes in Ai plus all attributes in some row
  Bj in B.  Usually followed by some kind of selection which picks out
  the rows where primary and foreign key values match, otherwise
  usually done accidentally.&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;Here&#x2019;s an example to illustrate for &lt;span&gt;SQL&lt;/span&gt; folk: when you write&lt;/p&gt;
&lt;pre&gt;select a,b,c from foo f join bar b on b.foo_id=f.id where a&amp;gt;1
&lt;/pre&gt;
&lt;p&gt;this is mathematically a cross join of &lt;code&gt;foo&lt;/code&gt; with &lt;code&gt;bar&lt;/code&gt;, followed by
a selection of the rows where &lt;code&gt;b.foo_id=f.id&lt;/code&gt;, followed by a projection
down to attributes &lt;code&gt;a,b,c&lt;/code&gt;, followed by a selection of rows where &lt;code&gt;a&amp;gt;1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Now here&#x2019;s the important bit:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;the tuple isn&#x2019;t in itself a
representation of some real-world object: it&#x2019;s &lt;em&gt;an assertion&lt;/em&gt; that
some object with the given attributes exists.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Why is this important? It makes a difference when we look at
operations that throw away data.  If Santa has a relation with rows
representing two elves with the same name but different shoe sizes,
and he projects this relation to remove &lt;code&gt;shoe_size&lt;/code&gt;, he doesn&#x2019;t say
&#x201C;oh shit, we can&#x2019;t differentiate those two elves any more, how do we
know which is which?&#x201D;, because he doesn&#x2019;t have records of two elves
and has never had records of two elves &#x2013; he has two assertions that at
least one elf of that name exists. There might be one or two or n
different elves with that name and we&#x2019;ve thrown away the information
that previously let us deduce there were at least two of them, but we
haven&#x2019;t broken our database &#x2013; we&#x2019;ve just deleted data from it.
&lt;em&gt;Relational systems fundamentally don&#x2019;t and can&#x2019;t have object
identity&lt;/em&gt;, because they don&#x2019;t contain objects.  They record facts
about objects that have an existence of their own.  If you delete some
of those facts your database is not screwed.  &lt;strong&gt;You&lt;/strong&gt; might be screwed,
if you needed to know those facts, but your convention that a relation
row uniquely identifies a real-world object is your convention, not
the database&#x2019;s rule.&lt;/p&gt;
&lt;p&gt;(Aside: the relational algebra says we can&#x2019;t have two identical rows:
&lt;span&gt;SQL&lt;/span&gt; says we can.  I say it makes no difference either way because both
rows represent the same truth and you have to violate the abstraction
using internal row identifiers to differentiate between them)&lt;/p&gt;
&lt;h2&gt;Back in the room&lt;/h2&gt;
&lt;p&gt;The reason I&#x2019;ve spent this expended all those words explaining the relational model
instead of just saying &#x201C;ActiveRecord has poor support for sticking
arbitrary bits of &lt;span&gt;SQL&lt;/span&gt; into the code&#x201D; is to impress on you that it&#x2019;s a
beautiful, valuable, and legitimate way to look at the data.  And that
by imposing the requirement that the resulting relation has to be
turned back into an object, we limit ourselves.  Consider&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;As a present fulfillment agent, Santa wants a list of delivery
postcodes so that he can put them in his satnav. Do you (a) select all
the children and iterate over them, or (b) &lt;code&gt;select distinct postcode
from children where nice&lt;/code&gt; (he does the coal lumps in a separate pass)?&lt;/li&gt;
&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;As a financial controller, Mrs Claus wants to know the total cost of
presents in each of 2011, 2010 and 2009, broken down by year and by
country of recipient, so that she can submit her tax returns on time.&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;We wave #select, #map and #inject around on our in-memory Ruby arrays
like a Timelord looking for something to use his sonic screwdriver on.
When it comes to doing the same thing for our persistent data:
performing set operations on collections instead of iterating over
them like some kind of VB programmer, why do we get a sense of shame
from &#x201C;going behind&#x201D; the object layer and &#x201C;dropping into&#x201D; &lt;span&gt;SQL&lt;/span&gt;?  It&#x2019;s
not an efficiency hack, we&#x2019;re using the relational model how it was
intended.&lt;/p&gt;
&lt;p&gt;And although we &lt;em&gt;can&lt;/em&gt; do this in Rails (in fairness, it gets a lot
easier now we have Arel and Sequel), I think we need a little bit of
infrastructure support (for example, conventions for putting relations
into views, or for adding presenters/decorators to them) to legitimise
it.&lt;/p&gt;
&lt;h2&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;Summary: (1) our &lt;span&gt;ORM&lt;/span&gt;-derived objects expose their internal state, and
this is bad.  (2) we don&#x2019;t have good conventions for looking at our
state except by bundling up small parcels of it and creating objects
from them, and this is limiting us because sometimes we want to see a
summary composed of parts of several objects.  Summary of the summary:
(1) exposing state is bad; (2) we can&#x2019;t see all the state in the
combinations we&#x2019;d like.&lt;/p&gt;
&lt;p&gt;Yes, I realise the apparent contradiction here, and no, I&#x2019;m not sure
how it resolves.  I think there&#x2019;s a distinction to be drawn between
the parts of the sytem that allow mutation according to business
requirements, and the &#x201C;reporting&#x201D; parts that just let us view
information in different ways.  I also think we&#x2019;re putting behaviour
in the wrong places, but that&#x2019;s a topic for Part Three&lt;/p&gt;
&lt;p&gt;If you have read all the way to the end, Part Two, &#x201C;Objective in
Rails&#x201D;, will be a run through of my current progress (with code!
honest!) for coping with with the first problem and parts of the
second.  Part Three will be a probably-quite-handwavey look at
&lt;a href="http://www.artima.com/articles/dci_vision.html" &gt;&lt;span&gt;DCI&lt;/span&gt;&lt;/a&gt; and how it might
provide a way of looking at things which makes &lt;em&gt;both&lt;/em&gt; problems go away.&lt;/p&gt;</description>
    </item>
    <item>
      <pubDate>Wed, 9 Nov 2011 18:04:53 GMT</pubDate>
      <title>In Soviet Russia, ActiveRecord mocks YOU!</title>
      <link>http://www.advogato.org/person/dan/diary.html?start=154</link>
      <guid>http://ww.telent.net/2011/11/9/in_soviet_russia_activerecord_mocks_you</guid>
      <description>&lt;p&gt;A week ago I attended the &lt;a href="http://rubymanor.org/3/" &gt;Ru3y Manor&lt;/a&gt;
conference, which was Really Cool.  Educational, entertaining,
excellent value for money.&lt;/p&gt;
&lt;p&gt;One of the talks was by &lt;a href="http://www.twitter.com/mortice" &gt;Tom Stuart&lt;/a&gt; on
&lt;a href="http://lanyrd.com/2011/ru3y-manor/skhmk/" &gt;Rails vs object-oriented design&lt;/a&gt; 
which could be
summarised as a run through the &lt;a href="http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod" &gt;&lt;span&gt;SOLID&lt;/span&gt; principles&lt;/a&gt; and a
description of how well (or how badly) the affordances in Rails
encourage adherence to each principle.&lt;/p&gt;
&lt;p&gt;ActiveRecord came in for some stick.  The primary offence is against
the Single Responsibility Principle, which says that a class should
have only one reason to change &#x2013; or in the vernacular, should do only
one thing.  This is because AR is both an implementation of a
persistence pattern &lt;em&gt;and&lt;/em&gt; (usually, in most projects) a place to dump
all the business logic and often a lot of the presentation logic as
well.&lt;/p&gt;
&lt;p&gt;Divesting the presentation logic is usually pretty simple.  Decorators
(Tom plugged the &lt;a href="https://github.com/jcasimir/draper" &gt;Draper&lt;/a&gt; gem,
which I haven&#x2019;t yet tried but looks pretty cool in the screencast)
seem well-equipped to fix that.&lt;/p&gt;
&lt;p&gt;But I wish he&#x2019;d said more about persistence, because it&#x2019;s a mess.  And
the root cause of the mess is, I conjecture, that an AR object is
actually two things (although only one at a time).  First, it reifies
a database row &#x2013; it provides a convenient set of OO-ey accessors to
some tuples in a relational database, allowing mutation of the
underlying attributes and following of relations.  Second, it provides
a container for some data that &lt;em&gt;might&lt;/em&gt; some day appear in some
database &#x2013; or on the other hand, might not even be valid.  I refer of
course to the unsaved objects.  They might not pass validation, the
result of putting them in associations is ambiguous, they don&#x2019;t have
IDs &#x2026; really, they&#x2019;re &lt;em&gt;not actually the same thing&lt;/em&gt; as a real
AR::Model object.  But because saving is expensive (network round
trips to the database, disk writes, etc) people use them e.g. when
writing tests and then get surprised when they don&#x2019;t honour the same
contract that real saved db-backed AR objects do.  So, the clear
answer there is &#x201C;don&#x2019;t do that then&#x201D;.&lt;/p&gt;
&lt;p&gt;Ideally, I think, there would be a separate layer for business
functionality which uses the AR stuff just for talkum-to-database and
can have that dependency neatly replaced by e.g. a Hash when all you
want to do is test your business methods.  I suggest this is the way
to go because my experiences with testing AR-based classes have not
been uniformly painless: when I want to test object A and mock B, and
each time I run the test I find a new internal ActiveRecord method on
B that needs stubbing, someone somewhere is Doing Something Wrong.
Me, most likely.  But what?  I should be using Plain Old Ruby Objects
which might delegate some stuff to the AR instances: then I should
decide whether all those &lt;span&gt;CRUD&lt;/span&gt; pages should be using my objects or the
AR backing, then I should decide how to represent associations (as
objects or arrays of objects or using some kind of lazy on-demand
reference to avoid loading the entire object graph on each request,
and will there need to be a consistent syntax for searching or will I
just end up with a large number of methods &lt;code&gt;orders_in_last_week&lt;/code&gt;,
&lt;code&gt;orders_in_last_month&lt;/code&gt;, &lt;code&gt;open_orders&lt;/code&gt; each of which does some query or
other and then wraps each returned AR object in the appropriate domain
object) and whether the semantic distinction between an &#x201C;aggregation&#x201D;
relation and a &#x201C;references&#x201D; relation (an Order has many OrderLines,
but a Country doesn&#x2019;t have many People &#x2013; people can emigrate) has
practical relevance.  The length of the preceding sentence suggests
that there&#x2019;s a fair amount to consider.  I don&#x2019;t know of any good
discussion of this in Ruby, and the prospect of wading through all the
Java/.&lt;span&gt;NET&lt;/span&gt; limitations-imposed-by-insufficiently-expressive-languages
shit to find it in &#x201C;enterprise&#x201D; languages is not one I look forward
to.  Surely someone must have answers already?&lt;/p&gt;
&lt;p&gt;There&#x2019;s other stuff.  Saving objects is expensive.  Saving objects on
every single update is expensive and wasteful when there&#x2019;s probably
another update imminent, so there&#x2019;s some kind of case to be made for
inventing a &#x201C;to be saved&#x201D; queue of AR objects which is eventually
flushed by saving them &lt;em&gt;once each at most&lt;/em&gt;.  The flush method could be
called from some suitable post-request method in the controller, or
wherever the analogous &#x201C;all done now&#x201D; point is in a non-Web
application.  That would probably be a fairly easy task, although it
would be no help for the initial object creation, because until we
have an &lt;code&gt;id&lt;/code&gt; field &#x2013; and we need to ask the database to get a
legitimate value for it &#x2013; the behaviour of associations is officially
anybody&#x2019;s guess.&lt;/p&gt;
&lt;p&gt;Rant over, and I apologise for the length but I am running out of time
in which to make it shorter.  In happier news:
&lt;a href="http://pry.github.com/" &gt;Pry&lt;/a&gt; &#x2013; a replacement ruby toplevel that does
useful stuff and that can be invoked from inside code.  It&#x2019;s like what
Ruby developers would come up with after seeing &lt;span&gt;SLIME&lt;/span&gt;.&lt;/p&gt;</description>
    </item>
    <item>
      <pubDate>Tue, 25 Oct 2011 12:07:42 GMT</pubDate>
      <title>Inanely great</title>
      <link>http://www.advogato.org/person/dan/diary.html?start=153</link>
      <guid>http://ww.telent.net/2011/10/25/inanely_great</guid>
      <description>&lt;p&gt;A lot has been written &#x2013; and I expect a lot more is yet to be written
- about the attention to detail and unique grasp of design aesthetic
that Steve Jobs exerted on Apple product development.  A reasonable
observation and not a new one.  But the implication that goes with it
which I find curious is that those slacker open source/free software
people who are threatening to eat his lunch with Android or (perhaps
less convincingly) with Ubuntu have no hope of ever replicating this
setup because as they&#x2019;re volunteer-based they have to spend too much
time being nice to their contributors.&lt;/p&gt;
&lt;p&gt;Ignoring the quibble that Android&#x2019;s not actually a very good exemplar
of open source development style (development directions are quite
obviously set by Google, and at the time I write this there have been
two major releases since they even pushed any open source stuff out at
all) this argument falls down because it&#x2019;s simply not true.  Free
software projects can be very good indeed at maintaining exacting
standards in areas that they care about, and not apparently caring too
much whose toes they tread on in the process &#x2013; it&#x2019;s just that the
areas they care about are much more related to code quality and
maintainability than typography and &lt;a href="http://www.geek.com/articles/apple/steve-jobs-as-ceo-he-cared-about-a-shade-of-yellow-on-a-sunday-20110825/" &gt;exact shades of yellow&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Taking the Linux kernel for an example, the particular story that
prompted this observation was the &lt;a href="http://lwn.net/Articles/456762/" &gt;Broadcom wireless drivers&lt;/a&gt;
contribution, but I could add to that: Reiserfs, nvidia ethernet,
Intel ethernet drivers, Android wake locks, and a zillion other less
high-profile cases where badly coded patches have not been accepted,
even when the rejection is due to something as trivial as whitespace[*].
(OK, maybe I was wrong to say they don&#x2019;t care about typography ;-) So,
the social/organisational structures exist for an open source project
to be quite incredibly demanding of high standards and yet remain
successful &#x2013; &lt;em&gt;the question of why they don&#x2019;t extend these standards to external factors and &#x201C;UX&#x201D; probably has to remain open&lt;/em&gt;.  And don&#x2019;t tell
me it&#x2019;s because they don&#x2019;t appreciate good design when it is on offer,
because the number of Macs I see at conferences invalidates that
hypothesis straight off.&lt;/p&gt;
&lt;p&gt;[*] I am reasonably sure this is not an exaggeration, although I can
no longer find the mail from when it happened to me so I may be
misremembering.&lt;/p&gt;</description>
    </item>
    <item>
      <pubDate>Wed, 28 Sep 2011 22:07:29 GMT</pubDate>
      <title>Pluto</title>
      <link>http://www.advogato.org/person/dan/diary.html?start=152</link>
      <guid>http://ww.telent.net/2011/9/26/pluto</guid>
      <description>&lt;p&gt;My &lt;a href="http://www.advogato.org/social-notworking" &gt;previous entry&lt;/a&gt; was not just a retro whinge
about today&#x2019;s centralised and balkanised Internet, but also a run up
to a description of how things could be different.  My efforts on and
off over the last few weeks to make that difference have recently been
blocked by too-much-$&lt;span&gt;DAYJOB&lt;/span&gt;, so maybe this is a good time to stop
coding and talk about it a bit.&lt;/p&gt;
&lt;p&gt;When I was first playing around with the idea of a &lt;a href="http://ww.telent.net/2011/3/2/the_neighbour_net_proxy_protocol" &gt;distributed social
network&lt;/a&gt;
my focus was on duplicating the interesting bits of Facebook, and one
of the reasons I concluded it wasn&#x2019;t really ever worth pursuing
was that Facebook already exists and nobody (to a first approximation)
needs an empty duplicate of it.  If you want a network where you can
tell your friends what you had for breakfast and post cat videos, you
want it to be the network that your friends are on.&lt;/p&gt;
&lt;p&gt;But in the course of thinking about how to implement it and reading
about &lt;a href="http://tools.ietf.org/html/rfc5023b" &gt;Atompub&lt;/a&gt; , I realised that
it showed the way to something subtly different.  And when I thought
about that a bit more I realised I&#x2019;d reinvented the blog aggregator.
Um.  But this is the &lt;em&gt;threaded&lt;/em&gt; blog aggregator, which is better.&lt;/p&gt;
&lt;h2&gt;The Embrace&lt;/h2&gt;
&lt;p&gt;Well, the logic is unassailable: there are already lots of people on
the internet publishing their thoughts using Atom (or its gelatinous
structural isomorph &lt;span&gt;RSS&lt;/span&gt;): what we need is an app that sucks all their
posts, sorts them into categories (which we are calling &#x201C;channels&#x201D;),
and allows the user to post his own articles (either &lt;em&gt;ab initio&lt;/em&gt; or in
reply to those they read) into the same channels.  You can notify the
people you&#x2019;re replying to by sending them a copy of your reply (as
Atom &lt;span&gt;POST&lt;/span&gt; to their published feed url, falling back to
&lt;a href="http://en.wikipedia.org/wiki/Trackback" &gt;Trackback&lt;/a&gt; or
&lt;a href="http://www.hixie.ch/specs/pingback/pingback" &gt;Pingback&lt;/a&gt; or
&lt;a href="http://www.advogato.org//www.elegantfootwear.co.uk/slingback_shoes.html" &gt;Slingback&lt;/a&gt; or
&lt;a href="http://www.amazon.co.uk/Stickle-Bricks-Sticklebricks-Giant-Set/dp/B004F77ZPM/ref=sr_1_11?ie=UTF8&amp;amp;qid=1317245237&amp;amp;sr=8-11" &gt;Stickleback&lt;/a&gt;
or whatever if that doesn&#x2019;t work), and you can incorporate their
replies to your articles in the same way when they come in.  Stick a
UI on the front that presents a trn-style threaded view of all unread
articles by all authors in the channel, et viola, you&#x2019;ve just created
a conversational view of stuff that&#x2019;s out there already.  And by and
large it&#x2019;s much &lt;em&gt;better&lt;/em&gt; stuff than &#x201C;paste this as your status and tag
three people&#x201D;.&lt;/p&gt;
&lt;h2&gt;The Extension&lt;/h2&gt;
&lt;p&gt;How do we turn that into a distributed resilient blah system like Usenet?&lt;/p&gt;
&lt;p&gt;The key bit of
&lt;a href="http://ww.telent.net/2011/3/2/the_neighbour_net_proxy_protocol" &gt;&lt;span&gt;NNPP&lt;/span&gt;&lt;/a&gt;
was that each node answers proxy requests on behalf of its neighbours,
for articles it&#x2019;s loaded from its neighbours.  So, if one of your
usual feed sources is offline, you can fetch their articles from
someone else who reads them.  Combine that with
&lt;a href="http://code.google.com/apis/pubsubhubbub/" &gt;PubSubHubbub&lt;/a&gt; and add some
yet-to-be-decided peer-to-peer negotiation protocol so that a group of
nodes can decide between themselves which will be the hub and which
will subscribe to it.&lt;/p&gt;
&lt;p&gt;This does make the issue of identity a bit more pressing: what&#x2019;s to
stop node B altering articles published by A, or even introducing
entirely new ones that purport to come from A?  Crypto, that&#x2019;s what.
I don&#x2019;t give a stuff whether the name you go by is what your
government calls you, but I do want to know whether, when someone with
your moniker is claiming to have written article &lt;em&gt;N&lt;/em&gt;, it is the same
someone who prevously wrote articles &lt;em&gt;1,2,3,&#x2026; N-1&lt;/em&gt;.  So, you
get a &lt;a href="http://tools.ietf.org/html/rfc4880" &gt;&lt;span&gt;PGP&lt;/span&gt;&lt;/a&gt; key (or some other asymmetric peer-to-peer public-key
encryption system that doesn&#x2019;t depend on a centralised certification
authority).  Then if the key associated with your feed changes without
prior notification, my client shows me a big red warning that says you
probably aren&#x2019;t who you say you are.  &lt;a href="http://tools.ietf.org/id/draft-gutmann-keycont-01.txt" &gt;Key management by key continuity&lt;/a&gt; a.k.a &#x201C;what ssh does&#x201D;.
Perhaps once you&#x2019;ve been posting
stuff I like for a while I&#x2019;ll sign your key as well, and other people
- at least, other people who like what &lt;em&gt;I&lt;/em&gt; post &#x2013; will be more likely
to trust you as a result.&lt;/p&gt;
&lt;p&gt;(&lt;span&gt;NNPP&lt;/span&gt; also contains an outline sketch of a &lt;span&gt;DNS&lt;/span&gt; protocol replacement.  I
presently think this is an optional extra, but that depends on how
offensive you plan to be to deep-pocket corporates who will complain
to your naming authority)&lt;/p&gt;
&lt;p&gt;Spam?  No magic solutions, I&#x2019;m afraid, but the &#x201C;trusted introducer&#x201D;
thing goes some way.  If people that you don&#x2019;t already read send you
articles that aren&#x2019;t signed by keys you have a trust relationship
with, they pile up in your &#x201C;slush pile&#x201D; (the analogue of the G+
Incoming feed) until you decide to look at them &#x2013; you might decide to
apply spam filtering tools of the same kind as we use for email, or
you might just decide to junk it sight unread.&lt;/p&gt;
&lt;h2&gt;The End&lt;/h2&gt;
&lt;p&gt;It&#x2019;s called Pluto.  Because Planet is &#x201C;a feed aggregator application
designed to collect posts from the weblogs of members of an Internet
community and display them on a single page&#x201D; (thank you, Wikipedia)
and Pluto is a dwarf planet.  Sometime soon, I hope, there will be
code on Github.&lt;/p&gt;
&lt;p&gt;Catchy summary points:&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;we care about &lt;strong&gt;content and conversation&lt;/strong&gt; &#x2013; I&#x2019;m happy to let Facebook
  and Twitter corner the market in ephemera: this is for keepers&lt;/li&gt;
&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;protocols not platforms&lt;/strong&gt; &#x2013; we interoperate on equal terms with
  anything that speaks Atompub (and intend to provide adaptors for &lt;span&gt;RSS&lt;/span&gt;
  or Facebook or scraped content or even an email-to-pluto gateway) -
  all the other authentication and distribution stuff is strictly
  opt-in&lt;/li&gt;
&lt;/ul&gt;</description>
    </item>
    <item>
      <pubDate>Mon, 19 Sep 2011 16:04:49 GMT</pubDate>
      <title>Social notworking</title>
      <link>http://www.advogato.org/person/dan/diary.html?start=151</link>
      <guid>http://ww.telent.net/2011/9/18/social_notworking</guid>
      <description>&lt;p&gt;After a bit over a month using Google Plus (with admittedly decreasing
enthusiasm over the course of that time) I have no firm conclusions
about what it&#x2019;s good for, except that it&#x2019;s incredibly good at
reminding me how much I miss Usenet.&lt;/p&gt;
&lt;p&gt;I could compare it with the other networks that people consider it
&#x201C;competition&#x201D; for: it doesn&#x2019;t replace Facebook &#x2013; for me anyway -
because the whole world isn&#x2019;t on it, and that means I can&#x2019;t use it to
stay in touch with friends and family.  It doesn&#x2019;t replace Twitter as
the the lack of a message length limit means it&#x2019;s useless for epigrams
(which I like) and not much cop for status updates either (which I can
live without) &#x2013; though it does work as &#x201C;source of interesting links&#x201D;
which in my opinion is the third arm of Twitter utility.  And Google
will, probably, be disappointed to learn that it doesn&#x2019;t replace
LinkedIn be cause despite the best efforts of the 
&lt;a href="http://boingboing.net/2011/08/20/understanding-the-nym-wars.html" &gt;Real Names policy&lt;/a&gt;
enforcers, it still isn&#x2019;t quite boring enough. Yet, anyway.&lt;/p&gt;
&lt;p&gt;But that&#x2019;s enough about Google+, what about Usenet?&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;The unit of discussion was an article.  Not a two-line throwaway
  comment or a single bit of &#x201C;me likes this&#x201D; information.  When you
  read something on Usenet that you felt strongly enough to reply to,
  you hit &#x2018;r&#x2019;, you got the scary warning about &#x201C;hundreds if not
  thousands of dollars&#x201D;, and it dumped you in a full screen text
  editor where you could compose your pearl of wisdom.  Sure, so you
  could alternatively compose your &#x201C;ME &lt;span&gt;TOO&lt;/span&gt;&lt;img src="http://ww.telent.net/!!1" alt=""/&gt;!&#x201D;, but it wasn&#x2019;t a
  teeny text widget which practically demands the latter response: the
  affordances were there for writing something with meat&lt;/li&gt;
&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;It was decentralised.  No capricious site owner could take your
  comment down because someone might find it offensive, or ban all
  discussion of certain topics, or refuse to allow you to post links
  to other places, or even that he was going to pull the plug
  completely and delete all your words.  You might be reading this and
  thinking 
 &lt;a href="http://en.wikipedia.org/wiki/Godfrey_v_Demon_Internet_Service" &gt;Godfrey vs Demon&lt;/a&gt;
  and you&#x2019;d be entirely correct that it wasn&#x2019;t completely uncensored
  in practice &#x2013; nor, I contend, should it have been &#x2013; but there was at
  least a &lt;strong&gt;bit&lt;/strong&gt; more effort involved in getting a post removed than
  clicking the &#x2018;I am offended by this picture of a breast-feeding
  woman&#x2019; button, and that made potential complainants think a bit more
  carefully about whether it was worth it&lt;/li&gt;
&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;It had user interfaces that didn&#x2019;t get in the way.  Really.  I could
  sit in front of my computer for &lt;em&gt;hours&lt;/em&gt; pressing only the space bar
  (maybe alternating with the &#x2018;n&#x2019; key in less interesting groups) and
  it would keep the content coming.  (And I did.  I would blame my
  degree class on Usenet, if it weren&#x2019;t that the time I spent
  &lt;a href="http://ftp.armedslack.org/slackware/slackware-3.1/docs/ELF-HOWTO" &gt;fiddling with Linux&lt;/a&gt;
  was in itself sufficient to leave approximately 0 time for studying.  But i digress.)&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;The reasons it&#x2019;s dead are well-rehearsed, and boil down to this: it
couldn&#x2019;t cope with universal access.  It was built back in the days
when people had access through their institutions or employers,
and for the most part knew they could lose it by acting like jerks -
or at least by acting like jerks consistently enough and outrageously
enough.  Come the personal internet revolution &#x2013; the Endless September
- it had no protection against or meaningful sanctions for spammers
and trolls, and so blogs/web forums sucked away most of the people who
wanted to just talk, leaving behind people who were by and large too
much concerned with the minutiae of meta and much less enthused about
the actual posting of content.&lt;/p&gt;
&lt;p&gt;But it did do stuff that nobody else has replicated since.&lt;/p&gt;
&lt;p&gt;Other people:&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;a href="http://reddragdiva.dreamwidth.org/566555.html" &gt;Why we don&#x2019;t have an &lt;span&gt;NNTP&lt;/span&gt; back end for web forums&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;James Adam &lt;a href="http://interblah.net/on-commenting" &gt;on commenting&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
    </item>
    <item>
      <pubDate>Wed, 22 Jun 2011 12:05:30 GMT</pubDate>
      <title>Openwrt "backfire" first impressions</title>
      <link>http://www.advogato.org/person/dan/diary.html?start=150</link>
      <guid>http://ww.telent.net/2011/6/22/openwrt_backfire_first_impressions</guid>
      <description>&lt;p&gt;Some notes on my first impressions of Openwrt 10.03 &#x201C;Backfire&#x201D;&lt;/p&gt;
&lt;p&gt;Having happily run a Draytek Vigor 2600 in my last home for 2-3 years,
the obvious thing to do when my exchange was upgraded to 21CN (that&#x2019;s
ADSL2+ to readers outside the UK) was to buy the same brand again and
this time go for a model that supports the newer standard. I bought a
2700 on ebay on the basis that comparing the model numbers indicated
it should be better by at least 64 (octal, right?).  It
wasn&#x2019;t. Although I can&#x2019;t prove that it&#x2019;s the router&#x2019;s fault it drops
out twice a week (we also moved house at about the same time, it could
be the line), I can say it&#x2019;s not a mark of quality that when I access
its web interface (e.g. to force a redial) I get an &lt;span&gt;HTTP&lt;/span&gt; timeout on at
least one of the three frames in the frameset &#x2013; if you&#x2019;re going to use
framesets for your router admin interface, it would probably be smart
to give it a web server that can answer more than two queries at the
same time.  And its syslog client has an approach to the standards
which is most charitably described as &#x201C;improvisational&#x201D;, . And I&#x2019;ve
talked before about the missing options for &lt;a href="http://ww.telent.net/2010/5/" &gt;second subnet support&lt;/a&gt; that aren&#x2019;t really missing.&lt;/p&gt;
&lt;p&gt;Push eventually came to shove last month when my OfflineIMAP process
decided that 2GB a day was a reasonable amount of traffic to incur
checking my email (I disagree, for the record) and I hit my &lt;span&gt;ISP&lt;/span&gt;
monthly download allowance, and the router offered absolutely no help
whatever in finding the source of the problem (between one wired
computer, three wireless laptops, assorted smartphones and ipod, and a
&lt;a href="http://www.withings.com/en/bodyscale" &gt;wifi-enabled weighing scale&lt;/a&gt; it could really have been anywhere). So it was time to
shove it, preferably in favour of something that would run Linux.
Like an early &lt;span&gt;WRT&lt;/span&gt;-54G won on ebay, coupled with a lightly hacked BT
Voyager 220V left behind in a previous flat by a previous previous
tenant and configured in bridge mode for the &lt;span&gt;ADSL&lt;/span&gt; hookup.&lt;/p&gt;
&lt;p&gt;Openwrt seems to be the most Debian-like of the popular Linux-based
router firmwares (that&#x2019;s intended as a compliment), in that it has a
package manager, and it likes to be configured from the command line
by editing files. My observations based on about 4 hours playing with
it:&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;the documentation is fragmented and lacks any clear sense of order
or editorial control.  This is listed first not because it&#x2019;s most
important (it isn&#x2019;t) but because it&#x2019;s the first thing I noticed.
Seriously, a Wiki is not a substitute for a user manual, and I say
that as someone who&#x2019;s written one.  When resorting to Google you will
find that a lot of what you read there is out of date. For example,
there is no longer an &lt;code&gt;ipkg&lt;/code&gt; command, but &lt;code&gt;opkg&lt;/code&gt; seems to replace it.&lt;/li&gt;
&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;It has a web interface called Luci.  It&#x2019;s a bit slow and clunky -
though still better than the Vigor router&#x2019;s was &#x2013; but it&#x2019;s helpful for
getting started.  I was confused by the interaction between the
various &#x2018;Save&#x2019;, &#x2018;Apply&#x2019;, &#x2018;Save and Apply&#x2019; buttons at the bottom of the
page and the &#x2018;Unsaved Changes&#x2019; link in the menu bar at the top: on the
&#x2018;Firewall&#x2019; page, for example, clicking &#x2018;Save&#x2019; at the bottom causes the
status at the top to go from &#x2018;Changes: 0&#x2019; to &#x2018;Unsaved Changes: 1&#x2019;.  To
my way of thinking, clicking Save should reduce the number of unsaved
changes not increase them, but this is probably just bad labelling.&lt;/li&gt;
&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;I say it likes to be configured by editing files: it is however
fussy about &lt;strong&gt;which&lt;/strong&gt; files. If there&#x2019;s a file under &lt;code&gt;/etc/config&lt;/code&gt; with
a relevant-looking setting in it, edit that in preference to whatever
the upstream app&#x2019;s usual config file would be, then run &lt;code&gt;uci commit&lt;/code&gt; -
although actually you might not need to run &lt;code&gt;uci commit&lt;/code&gt; &#x2013; see &lt;a href="https://forum.openwrt.org/viewtopic.php?id=30428" &gt;this
lovely thread&lt;/a&gt; for
the full confusing detail &#x2013; then run the relevant &lt;code&gt;/etc/init.d/foo&lt;/code&gt;
scripts to restart services as needed.  I am not sure if there&#x2019;s a
clear rule for gets overridden or overwritten if you edit config files
directly and conflict with &lt;span&gt;UCI&lt;/span&gt;, but I suspect it&#x2019;s pretty ad hoc.&lt;/li&gt;
&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;the hardware doesn&#x2019;t speak &lt;span&gt;ADSL&lt;/span&gt;, hence the need for a separate box
to do that.  I set the Voyager up to do PPPoE and the &lt;span&gt;WRT&lt;/span&gt; likewise: in
Luci look for Network &#x2192; Interfaces &#x2192; &lt;span&gt;WAN&lt;/span&gt; and set the Protocol to
PPPoE: this should get you Username and Password boxes in which you
put whatever your &lt;span&gt;ISP&lt;/span&gt; told you to.&lt;/li&gt;
&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;the wifi did not work no matter what I did in Luci, but eventually I found
the problem was in &lt;code&gt;/etc/config/wireless&lt;/code&gt; which had an entirely bogus mac
address in its definition of &lt;code&gt;radio1&lt;/code&gt;: I replaced it with the address printed
by &lt;code&gt;ifconfig wlan0&lt;/code&gt; and suddenly everything started working.&lt;/li&gt;
&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;it runs an ssh daemon, which is nice. Although it will authenticate
using keys, it &lt;strong&gt;won&#x2019;t&lt;/strong&gt; look at &lt;code&gt;/root/.ssh/authorized_keys&lt;/code&gt; as openssh
does. I used the web interface to add my key, which worked fine.&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;Summary: although not currently suitable for the non-technical end user,
if you have some Linux experience and a few hours to screw around
with Google, it all eventually works fine. And I can run &lt;code&gt;tcpdump&lt;/code&gt; on
it, which more than makes up for all these minor problems 64 times
over.  Get in.&lt;/p&gt;
&lt;p&gt;More on the BT Voyager in a later blog entry, but I leave you with
some &lt;a href="http://jcsu.jesus.cam.ac.uk/~acw43/projects/voyager/" &gt;instructions for unlocking it&lt;/a&gt; which you may need if you are
sensible enough to use an &lt;span&gt;ISP&lt;/span&gt; that isn&#x2019;t BT Retail.&lt;/p&gt;</description>
    </item>
  </channel>
</rss>

