<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom">
 
  <title>upstream agile - software</title>
  <link href="https://upstre.am/blog/"/>
  <link type="application/atom+xml" rel="self" href="https://upstre.am/feed/atom/"/>
  <updated>2024-07-11T12:12:36+00:00</updated>
  <id>https://upstre.am/</id>

  
  <entry>
    <id>https://upstre.am/blog/2012/09/ueber-tests-fast-isolated-integration-tests-for-distributed-apps/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2012/09/ueber-tests-fast-isolated-integration-tests-for-distributed-apps/"/>
    <title>Über Tests: fast, isolated integration tests for distributed apps</title>
    <updated>2012-09-15T00:00:00+00:00</updated>
    <author>
      <name>Alexander Lang</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;&lt;strong&gt;&lt;em&gt;TL;DR:&lt;/em&gt;&lt;/strong&gt; when you split up your monolithic (web) app, you can either write integration tests spanning all the parts, which makes them slow and hard to set up, or you test each part in isolation and lose test coverage around the edges of your components. Über Tests try to solve the problem by testing those edges.&lt;/p&gt;

&lt;p&gt;Suppose you have written a nice little Rails app (they all start out that innocent don’t they). Over time you add more and more features and suddenly you are looking at 15k LOC and a test suite (of course you have unit and integration tests for everything, don’t you) that takes 20 minutes to run.&lt;/p&gt;

&lt;p&gt;You decide to go with the trend and split up your code base, say into one or more API apps and a frontend app. The backend(s) now expose a number of endpoints via HTTP and the frontend app uses those instead of directly talking to the database. This  reduces coupling between the different modules and as your test suite gets split up with the app, you now only have to run the tests for the part of the app where you were making changes. Sweet.&lt;/p&gt;

&lt;p&gt;There’s one problem though: what do you do with your integration tests?  Keep them all in one place? Split them up?&lt;/p&gt;

&lt;p&gt;After some headscratching you come up with three ideas:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;http://airbrake.io&quot;&gt;Airbake&lt;/a&gt; - if it crashes you will get an email. Err, maybe not the best idea.&lt;/li&gt;
  &lt;li&gt;Write integration tests across the entire system. In addition to firing up your database, you simply run all your API services, too. The clear benefit here is that you don’t even have to change your existing tests. On the other hand those tests will run even slower now, with the added overhead of multiple processes and possibly databases. Didn’t we say we want fast tests? Also, setting those up will be a nightmare.&lt;/li&gt;
  &lt;li&gt;Test each piece of your app in isolation. When a feature you are testing requires a certain service/API you just stub it using for example &lt;a href=&quot;https://github.com/bblimke/webmock&quot;&gt;WebMock&lt;/a&gt;. This approach allows you to split up your test suite, so you don’t have to run all the tests all the time. In addition they will run faster, too, as your stubbbed API will respond a lot quicker than a full stack. The downside here is that you are not testing the edges of your components anymore, e.g. how do you make sure your frontend app stays in sync with the backend API? If you change the API, will the frontend still work?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here’s an example. Suppose you have a signup API endpoint and it returns the following JSON:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;{&quot;id&quot;: 1, &quot;email&quot;: &quot;joe@example.com&quot;, &quot;access_token&quot;: &quot;12345&quot;}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You now want to change that API to return other data than just the user:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;{
  &quot;user&quot;: {&quot;id&quot;: 1, &quot;email&quot;: &quot;joe@example.com&quot;, &quot;access_token&quot;: &quot;12345&quot;},
  &quot;links&quot;: [
    {&quot;rel&quot;: &quot;settings&quot;, &quot;href&quot;: &quot;https://api.example.com/account/settings&quot;}
  ]
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Which parts of your frontend do you have to change now? Are you not going to make any mistakes when stubbing out the new API? (yes you will).&lt;/p&gt;

&lt;p&gt;Here’s where the idea of Über Tests™®© comes in: we take approach three, which gives us the advantage of fast and decoupled tests and add another suite of tests. These tests will make sure that the different components of our distributed system still work together. Here’s how it works:&lt;/p&gt;

&lt;p&gt;First we write a small client for our API, something like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;class SignupClient
  def initialize(http_client)
    @http_client = http_client # bear with me
  end

  def call(params)
    response = @http_client.post('/signups', {user: params})
    response.success?
  end
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Step 2, we use that client for testing our backend (RSpec integration test example):&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;describe 'The signup API' do
  let(:signup_client) { SignupClient.new(http_client) }
  let(:http_client) {
    Faraday.new {|connection|
      connection.request :url_encoded
      connection.adapter :rack, app
    }
  }

  it 'signals success when sending valid user attributes' do
    expect(signup_client.call({email: 'joe@example.com'})).to be_success # let's not discuss RSpec syntax here
  end
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As you can see we are using &lt;a href=&quot;https://github.com/technoweenie/faraday&quot;&gt;Faraday&lt;/a&gt; as our HTTP client. The cool thing about this is that it allows us to specify a Rack endpoint, and hence our integration test session (referenced by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;app&lt;/code&gt;) as the HTTP adapter. When calling the client in the test it will in turn call the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;post&lt;/code&gt; method of the integration test session. Nifty eh?&lt;/p&gt;

&lt;p&gt;Okay, what do we have so far? We have a client for our API and since we are testing it along our backend we know those two are going to play along like BFFs. What’s left? We use that same client in the frontend:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;class SignupsController
  def create
    if client.call(params[:user])
      redirect_to account_path, notice: 'Konichiwa!'
    else
      render 'new'
    end
  end

  def client
    SignupClient.new(Config.http_client)
  end
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;(By now we should probably put that client into its own gem so we can use the same code in the frontend app and for the backend tests.)&lt;/p&gt;

&lt;p&gt;We set up the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Config&lt;/code&gt; constant in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;environments/production.rb&lt;/code&gt; like so:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Config = OpenStruct.new http_client: Faraday.new {|f| f.adapter :net_http}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;and for our test environment:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Config = OpenStruct.new
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Lastly we add another test (using RSpec/Capybara) for our frontend:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;describe 'Signing up' do
  let(:http_client) { stub(:http_client) }

  before(:each) do
    Config.stub(:http_client) { http_client }
    http_client.stub(:post) { {&quot;user&quot;: {&amp;lt;...&amp;gt;}, &quot;links&quot;: [...] }
  end

  it 'redirects to the account page on success' do
    visit signup_path
    fill_in 'Email', with: 'joe@example.com'
    click_button 'Sign up'

    expect(current_url).to eql('/account')
  end
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In that test we use our API client but we stub the HTTP client, so that the test does not rely on the actual API endpoint to be running, but uses the code that we have already made sure works with it.&lt;/p&gt;

&lt;p&gt;Now when we change our API there are two possible scenarios:&lt;/p&gt;

&lt;p&gt;The change only affects the API Client. In this case we have to change the client’s code, which causes our frontend tests to break because the stubbed HTTP response don’t match the API client’s expectations anymore, so we have to fix only those.&lt;/p&gt;

&lt;p&gt;The change causes the API client’s method signatures to change. In that case the same tests in the frontend app will fail where the frontend code calls the API client in an incorrect way.&lt;/p&gt;

&lt;p&gt;In both cases our Über Tests catch the error and we can fix the code and tests. The rest of our integration tests reside in the different parts of our app: We have a bunch of backend tests that test the API endpoints and the stack behind it, plus a set of tests for the frontend app that only test that app using a stubbed API.&lt;/p&gt;

&lt;p&gt;When you change something in the frontend you don’t need to run your backend tests again, as there’s no way you could have broken anything there. When you change your backend but nothing in the client you only have to run those tests. If you change the client run the client’s tests and the frontend tests, which are fast now because the run against a stubbed backend. Happy end.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2012/05/12-rules-for-writing-web-apps-in-2012/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2012/05/12-rules-for-writing-web-apps-in-2012/"/>
    <title>12 Rules for Writing Web Apps in 2012</title>
    <updated>2012-05-12T00:00:00+00:00</updated>
    <author>
      <name>Alexander Lang</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;The following is a list of ideas I pulled out of my head over the course of today. While it was slapped together rather spontaneously it is based on six years of writing my own web apps, looking at others’ code and basically a lot of headscratching and crying.&lt;/p&gt;

&lt;p&gt;None of these ideas are new, others have written about them over and over. Still most of the world around me is stuck in uncomprehensible code, coffee breaks for running tests and banging their head against the wall.&lt;/p&gt;

&lt;p&gt;The intention of this post is not to piss on anyone’s coding style but to lay a foundation for writing better apps in the future – for myself and hopefully for others, too. The code examples are in Ruby but most of it should apply to other OO languages as well.&lt;/p&gt;

&lt;h3 id=&quot;business-models&quot;&gt;Business Models&lt;/h3&gt;

&lt;p&gt;Your business models are the core of your application. They model your business domain and nothing else.&lt;/p&gt;

&lt;h4 id=&quot;1-models-are-plain-old-ruby-objects&quot;&gt;1. Models are plain old Ruby objects.&lt;/h4&gt;

&lt;p&gt;They don’t extend a framework base class like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveRecord::Base&lt;/code&gt;.&lt;/p&gt;

&lt;h4 id=&quot;2-your-models-do-not-persist-themselves&quot;&gt;2. Your models do not persist themselves.&lt;/h4&gt;

&lt;p&gt;Instead they use delegation. This decouples them from your persistence framework and makes them unit testable.&lt;/p&gt;

&lt;p&gt;Don’t do this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;class User::ActiveRecord::Base
  def update_profile(attributes)
    ...
    save
  end
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Instead do this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;class User
  def update_profile(attributes)
    storage.load(user_id).update_attributes(attributes)
  end
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;3-all-execution-flow-is-explicit&quot;&gt;3. All execution flow is explicit.&lt;/h4&gt;

&lt;p&gt;No Callbacks. The various lifecycle callbacks most persistence frameworks now offer make your life easier if you have four models with 10 lines of code each. In larger apps they lead to unpredictable chains of code execution and tight coupling.&lt;/p&gt;

&lt;p&gt;Don’t do this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;class User
  after_save :create_welcome_message

  def create_welcome_message
    Message.create! text: 'Welcome User!'
  end
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Instead do this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;class Signup
  def complete
    user.confirm
    Mailbox.create_welcome_message_for(user)
  end
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;4-models-do-not-expose-their-properties-publicly&quot;&gt;4. Models do not expose their properties publicly.&lt;/h4&gt;

&lt;p&gt;Instead they offer an API for others to use.&lt;/p&gt;

&lt;p&gt;Don’t do this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Comment.create author: user.name
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Instead do this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;user.sign comment
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;5-each-model-encapsulates-its-persistent-data&quot;&gt;5. Each model encapsulates its persistent data.&lt;/h4&gt;

&lt;p&gt;If other objects want to access that data they have to ask the respective model.&lt;/p&gt;

&lt;p&gt;Don’t do this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;class Comment
  def create
    ...
    storage.load_user(author_id).comment_count += 1
  end
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Instead do this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;class Comment
  def create
    ...
    user.notify(:comment_created, self)
  end
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;persistence&quot;&gt;Persistence&lt;/h3&gt;

&lt;h4 id=&quot;6-use-whatever-database-or-service-is-best-for-the-current-job&quot;&gt;6. Use whatever database or service is best for the current job.&lt;/h4&gt;

&lt;p&gt;Don’t be afraid to use more than one. Don’t use a relational DB for full text search. Use a full text search engine. Don’t store logging data in your main database, use a logging service. And don’t &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kill -9&lt;/code&gt; your MongoDB.&lt;/p&gt;

&lt;p&gt;Some of you data may be write heavy, other read heavy and in need of caching. Research your options, don’t just rely on what’s the default. It will make your life easier and your app faster.&lt;/p&gt;

&lt;h3 id=&quot;front-end&quot;&gt;Front-end&lt;/h3&gt;

&lt;h4 id=&quot;7-your-front-end-is-a-restful-json-api&quot;&gt;7. Your front-end is a RESTful JSON API.&lt;/h4&gt;

&lt;p&gt;The internet is meant to be connected. Data must be free. Apps need to communicate. You want to build your service on top of others. Others will want to build on top of you.&lt;/p&gt;

&lt;p&gt;A proper RESTful JSON API is easy to explore and understand. REST scales, it’s what the internet is made of.&lt;/p&gt;

&lt;p&gt;Your human facing front-end is either a client side JavaScript app or another server side app.&lt;/p&gt;

&lt;h4 id=&quot;8-your-app-provides-oauth2-authentication&quot;&gt;8. Your app provides OAuth2 authentication.&lt;/h4&gt;

&lt;p&gt;OAuth2 is easy and open. It has security built in. It builds the basis for others to use your API.&lt;/p&gt;

&lt;h3 id=&quot;testing&quot;&gt;Testing&lt;/h3&gt;

&lt;h4 id=&quot;9-your-api-is-fully-tested-with-integration-tests&quot;&gt;9. Your API is fully tested with integration tests.&lt;/h4&gt;

&lt;p&gt;This is 2012.&lt;/p&gt;

&lt;h4 id=&quot;10-your-business-models-are-unit-tested&quot;&gt;10. Your business models are unit tested.&lt;/h4&gt;

&lt;p&gt;As in real unit tests, no database access in tests. There will be many, they must run fast.&lt;/p&gt;

&lt;h4 id=&quot;11-your-tests-run-within-two-minutes&quot;&gt;11. Your tests run within two minutes.&lt;/h4&gt;

&lt;p&gt;If they are slower make them faster. If you can’t make them faster split your code base. There is not enough going on on Twitter and Hacker News to wait for slow tests. More importantly your customer shouldn’t pay you waiting.&lt;/p&gt;

&lt;h3 id=&quot;frameworks&quot;&gt;Frameworks&lt;/h3&gt;

&lt;h4 id=&quot;12-prefer-small-and-simple&quot;&gt;12. Prefer small and simple&lt;/h4&gt;

&lt;p&gt;Rails is great, but do you really need all of it? Since 3.0 it has been split into smaller parts. Maybe Sinatra + ActiveRecord will do the job? A 50 line &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Gemfile&lt;/code&gt; may be impressive but it sure hosts a ton of bugs. Less code means faster,  simpler, error free-er.&lt;/p&gt;

&lt;h3 id=&quot;the-bottom-line&quot;&gt;The bottom line&lt;/h3&gt;

&lt;p&gt;I can’t promise you anything but these are the effects I’m trying to achieve.&lt;/p&gt;

&lt;h4 id=&quot;testability&quot;&gt;Testability&lt;/h4&gt;

&lt;p&gt;By decoupling objects they can be truly unit tested individually. Your tests will be easy to write and they will run fast.&lt;/p&gt;

&lt;h4 id=&quot;scalability&quot;&gt;Scalability:&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;Of your code base – decoupling means you can split your code base into smaller apps as it grows. Avoid the big spaghetti monster.&lt;/li&gt;
  &lt;li&gt;Of your team – instead of one large project you now have many small ones that more people can work on simultaneously without stepping on each others’ toes.&lt;/li&gt;
  &lt;li&gt;Of your storage layer – by using the right tool for the job your tools will get your further. A lot further than abusing MySQL into doing things it was never meant to do.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;flexibility&quot;&gt;Flexibility&lt;/h4&gt;

&lt;p&gt;Your main app being an API means you can add mobile apps and other clients easily. Your components being independent means you can swap backends or frameworks and even programming languages.&lt;/p&gt;

&lt;h4 id=&quot;happiness&quot;&gt;Happiness&lt;/h4&gt;

&lt;p&gt;Fast tests, easy to understand relations, small code base – pain free coding, short development cycles, bring out the unicorns.&lt;/p&gt;

&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;I have tried these ideas out to variying degrees. I will continue to do so over the next months. Maybe I’ll start crying again. Then it’ll be time for another post. Until then, let’s move forward, let’s write better software. For the unicorns.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Follow me on Twitter: &lt;a href=&quot;http://langalex.org&quot;&gt;@langalex&lt;/a&gt;, Github: &lt;a href=&quot;https://github.com/langalex&quot;&gt;https://github.com/langalex&lt;/a&gt;&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2011/10/http-caching-assets-in-rails-3-1/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2011/10/http-caching-assets-in-rails-3-1/"/>
    <title>Http Caching assets in Rails 3.1</title>
    <updated>2011-10-20T00:00:00+00:00</updated>
    <author>
      <name>Alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;I’ve been wrestling with this for the past few hours and finally got it working so I’m going to share this, hoping it might save others a few hours of their lives.&lt;/p&gt;

&lt;p&gt;I’m currently working on a Rails 3.1 app that is being deployed to &lt;a href=&quot;http://heroku.com&quot;&gt;Heroku&lt;/a&gt;. We use the asset pipeline to serve all our assets and we want those to load fast. By default Rails sets ETag headers, which means that for every (web page) request the browser checks on all the assets, usually resulting in a &lt;em&gt;304 Not Modifed&lt;/em&gt; response from the server. It’s faster than serving the assets each time but it still means one round trip per asset. We can avoid this by setting an &lt;em&gt;Expires&lt;/em&gt; headers with a far future date.&lt;/p&gt;

&lt;p&gt;To do this add the following lines to your &lt;em&gt;production.rb&lt;/em&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;config.serve_static_assets = true
config.static_cache_control = &quot;public, max-age=#{1.year.to_i}&quot;
config.assets.compile = true
config.assets.digest = true
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This will precompile the assets when you push the app to Heroku and set an &lt;em&gt;Expires&lt;/em&gt; header with an expiry date 1 year in the future.&lt;/p&gt;

&lt;p&gt;Don’t forget that you must reference your assets through the Rails asset helpers like &lt;code&gt;asset_tag&lt;/code&gt; and &lt;code&gt;image_tag&lt;/code&gt; (in &lt;em&gt;*.scss&lt;/em&gt; files you can use &lt;code&gt;image-url()&lt;/code&gt; etc. instead of the &lt;code&gt;url()&lt;/code&gt; call that comes with CSS).&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2011/08/ruby-tdd-workshop/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2011/08/ruby-tdd-workshop/"/>
    <title>Ruby TDD Workshop</title>
    <updated>2011-08-10T00:00:00+00:00</updated>
    <author>
      <name>Alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Nachdem wir unsere ersten JavaScript Workshops erfolgreich durchgeführt haben, machen wir jetzt mit dem nächsten Thema weiter: wir bieten einen Workshop &lt;a href=&quot;https://upstre.am/training/ruby-testing.html&quot;&gt;Test Driven Development in Ruby&lt;/a&gt; an.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Du bist erfahrene(r) ProgrammiererIn und bringst ein Feature nach dem nächsten an den Start. Nur irgendwann schleichen sich Fehler ein. Refactoring geht nicht mehr, und neue Anforderungen bringen Probleme.
  Was dir fehlt sind Tests. Wir zeigen dir wie’s geht.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;Wann: 30. September 2011, 10-18 Uhr&lt;/li&gt;
&lt;li&gt;Wer: Trainer: Thilo, Alex, max. 10 Teilnehmer&lt;/li&gt;
&lt;li&gt;Wo: bei &lt;a href=&quot;https://co-up.de&quot;&gt;co.up&lt;/a&gt;/Upstream in Berlin Kreuzberg&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href=&quot;https://upstre.am/training/ruby-testing.html&quot;&gt;Mehr Infos und zur Anmeldung&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2011/07/sublime-text-2-with-rvm-on-osx/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2011/07/sublime-text-2-with-rvm-on-osx/"/>
    <title>Sublime Text 2 with RVM on OSX</title>
    <updated>2011-07-30T00:00:00+00:00</updated>
    <author>
      <name>Alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;While everyone is still waiting for Textmate 2 from time to time an interesting alternative pops up. One of those alternatives is &lt;a href=&quot;http://www.sublimetext.com/blog/articles/sublime-text-2-public-alpha&quot;&gt;version 2 of Sublime Text&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;One of the key features of a text editor for programming is being able to run my tests/specs from within the editor. Sublime offers a build command that runs a ruby file and shows the output within the editor. Not as pretty as TextMate with its HTML output but it’s something to start with.&lt;/p&gt;

&lt;p&gt;The problem is that Sublime simply runs &lt;code&gt;ruby&lt;/code&gt;, which in most cases will invoke the ruby interpreter that ships with OSX. Instead I want it to use my project specific &lt;code&gt;.rvmrc&lt;/code&gt; file and run the Ruby version and Gemset I’ve specified for RVM. It took me a while to figure this out and I think there must be an easier way but this works:&lt;/p&gt;

&lt;p&gt;Create a file ~/bin/rvm_ruby that looks like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;#!/usr/bin/env ruby

file = File.expand_path(ARGV[0] || (STDERR.puts(&amp;#39;you must specify a ruby file&amp;#39;); exit(-1)))
dir = File.dirname file
while dir.size &amp;gt; 1
  if File.exist?(dir + &amp;#39;/.rvmrc&amp;#39;)
    exec(&amp;quot;source \&amp;quot;$HOME/.rvm/scripts/rvm\&amp;quot;; cd #{dir}; ruby #{file}&amp;quot;)
  else
    dir = dir.sub(/\/[^\/]*$/, &amp;#39;&amp;#39;)
  end
end

puts &amp;quot;Could not find any .rvmrc above #{file}&amp;quot;
exit -1
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Make it executable by running &lt;code&gt;chmod +x rvm_ruby&lt;/code&gt;. Now edit the file &lt;code&gt;~/Library/Application Support/Sublime Text 2/Packages/Ruby/Ruby.sublime-build&lt;/code&gt; and change it to look like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;{
    &quot;cmd&quot;: [&quot;/Users/$YOUR_USERNAME$/bin/rvm_ruby&quot;, &quot;$file&quot;],
    &quot;file_regex&quot;: &quot;^(...*?):([0-9]*):?([0-9]*)&quot;,
    &quot;selector&quot;: &quot;source.ruby&quot;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That’s it. Now when you edit a Ruby file and press Cmd-B it runs the file with the correct Ruby version.&lt;/p&gt;

&lt;p&gt;By the way, you can do the same with the &lt;a href=&quot;https://github.com/SublimeText/RSpec&quot;&gt;RSpec package&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2011/05/javascript-workshop-for-designers-2/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2011/05/javascript-workshop-for-designers-2/"/>
    <title>JavaScript Workshop for Designers 2</title>
    <updated>2011-05-19T00:00:00+00:00</updated>
    <author>
      <name>Alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;&lt;img src=&quot;/uploads/2010/12/tumblr_lct24j4TMC1qz7h5bo1_500.png&quot; alt=&quot;Workshop&quot; /&gt;&lt;/p&gt;

&lt;p&gt;We are doing another iteration of our &lt;a href=&quot;http://jstraining.de&quot;&gt;JavaScript for Designers Workshop&lt;/a&gt; (in German). Our designer Kristina and I (Alex) will be teaching the basics of programming in the browser for a full day. You don’t need to have any knowledge about programming. You don’t even have to be a designer :)&lt;/p&gt;

&lt;p&gt;The workshop is scheduled for July 15, early bird tickets are available for €300 until June 10.&lt;/p&gt;

&lt;p&gt;For more information visit &lt;a href=&quot;http://jstraining.de&quot;&gt;jstraining.de&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2011/03/testing-the-anytime-date-picker-with-cucumberselenium/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2011/03/testing-the-anytime-date-picker-with-cucumberselenium/"/>
    <title>Testing the AnyTime date picker with Cucumber/Selenium</title>
    <updated>2011-03-20T00:00:00+00:00</updated>
    <author>
      <name>Alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;On &lt;a href=&quot;https://www.cobot.me/&quot;&gt;Cobot&lt;/a&gt; we are making extensive use of the &lt;a href=&quot;http://www.ama3.com/anytime/&quot;&gt;AnyTime&lt;/a&gt; date picker. When it came to integration testing with Cucumber/Capybara, until recently I got away with using the &lt;code&gt;Rack::Test&lt;/code&gt; driver and doing&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;And I fill in &quot;2010-01-01 12:45:00&quot; for &quot;Date&quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Yesterday I finally needed to enter a date in a Scenarion that was using Javascript (with the &lt;code&gt;Selenium&lt;/code&gt; driver. With JavaScript enabled the above step doesn’t work anymore because when Selenium tries to type into the text field Anytime pops up and resets the text field’s contents. After some fiddling around I decided that I would make selenium click the actual buttons on the date picker. Not only did this seem to be the least hackish way, it would also mean I would be testing &lt;em&gt;the real thing&lt;/em&gt;, i.e. clicking on buttons like a user would. This becomes especially important once you start with internationalization and different time formats (e.g. 24h vs. 12h system), where you want to make sure AnyTime generates the proper date string.&lt;/p&gt;

&lt;p&gt;Long story short, it took me almost a day to figure out how to do this. I played around with a lot of variations, but in the end this is what you have to do:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;When /I select the time &amp;quot;([^&amp;quot;]+)&amp;quot; from &amp;quot;([^&amp;quot;]+)&amp;quot;/ do |time_string, label|
  time = Time.parse(time_string)
  And %Q{I fill in &amp;quot;&amp;quot; for &amp;quot;#{label}&amp;quot;}
  find_field(label).click

  click_on_selectors &amp;quot;.AnyTime-btn:visible:contains(#{time.year})&amp;quot;,
    &amp;quot;.AnyTime-mon#{time.month}-btn:visible&amp;quot;,
    &amp;quot;.AnyTime-dom-btn:contains(#{time.day}):visible:first&amp;quot;,
    &amp;quot;.AnyTime-hr#{time.hour}-btn:visible&amp;quot;,
    &amp;quot;.AnyTime-x-btn:visible&amp;quot;
end

def click_on_selectors(*selectors)

  def recurse(*selectors)
    if selectors.any?
      wait_for_css_selector_fn(selectors.first,
        &amp;quot;$(&amp;#39;#{escape_javascript selectors.first}&amp;#39;).click(); #{recurse(*selectors[1..-1])}&amp;quot;)
    else
      &amp;#39;window.__capybara_wait = false;&amp;#39;
    end
  end

  page.evaluate_script &amp;quot;window.__capybara_wait = true&amp;quot;
  page.evaluate_script recurse(*selectors)
  wait_until 10 do
    page.evaluate_script &amp;quot;!window.__capybara_wait&amp;quot;
  end
end


include ActionView::Helpers::JavaScriptHelper
def wait_for_css_selector_fn(selector, after)
  &amp;lt;&amp;lt;-JS
    (function() {
      var time = new Date().getTime();
      var runDelayed = function() {
        if(!$(&amp;#39;#{escape_javascript selector}&amp;#39;).length) {
          if(time &amp;lt; new Date().getTime() - 5000) {
            throw(&amp;#39;waited too long for #{escape_javascript selector}&amp;#39;)
          } else {
            window.setTimeout(runDelayed, 100);
          }
        } else {
          #{after};
        };
      }
      window.setTimeout(runDelayed, 100);
    })();
  JS
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In you Cucumber feature you can then call:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;When I fill in the time &quot;2010-01-01 12:00&quot; for &quot;Date&quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;What the above code essentially does is open the date picker and click on all the required buttons. Just as important (and that was the tricky part) it asynchronously waits for the necessary events (open/close date picker, date shows up in text field). Enjoy.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2011/01/2011-01-euruko-2011-call-for-papers/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2011/01/2011-01-euruko-2011-call-for-papers/"/>
    <title>Euruko 2011 call for papers</title>
    <updated>2011-01-14T00:00:00+00:00</updated>
    <author>
      <name>thilo</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Its very exciting to organize a conference, especially if it is the well known &lt;a href=&quot;http://euruko.org/&quot;&gt;EuRuKo&lt;/a&gt;. The preparations for this year &lt;a href=&quot;http://euruko2011.org/&quot;&gt;EuRuKo in Berlin&lt;/a&gt; are in full effect.  We just published the &lt;a href=&quot;http://euruko2011.org/2011/01/13/call-for-papers.html&quot;&gt;call for papers&lt;/a&gt; and I hope we get a good deal of interesting talk proposals from you until the deadline at the 22th February. So stretch you fingers and send in a proposal&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;…[if] you have researched something about Ruby, developed a gem, found a unique usage for Ruby or you had a life changing experience with Ruby. &lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;For details see the &lt;a href=&quot;http://euruko2011.org/2011/01/13/call-for-papers.html&quot;&gt;official post&lt;/a&gt;. We will choose wisely from the submissions so that you’ll never fell the urge to leave the track on this single track conference :).&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2010/12/javasscript-under-scrutiny-at-platforms-2011/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2010/12/javasscript-under-scrutiny-at-platforms-2011/"/>
    <title>JavaScript under scrutiny at Plat_forms 2011</title>
    <updated>2010-12-22T00:00:00+00:00</updated>
    <author>
      <name>thilo</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;blockquote&gt;
  &lt;p&gt;… we consider their participation a glimpse of the future …&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With these words our team &lt;a href=&quot;https://upstre.am/team&quot;&gt;(Alex, Frank and Me)&lt;/a&gt; got accepted with JavaScript into the web development platform comparison “&lt;a href=&quot;http://www.plat-forms.org/platforms-announcement&quot;&gt;Plat_forms 2011&lt;/a&gt;“. Big words :) &lt;/p&gt;

&lt;p&gt;Plat_forms is a contest in which teams of three programmers compete to implement the same requirements for a web-based system within two days, using different technology platforms. It will be held in Nürnberg from 18th to 19th January.&lt;/p&gt;

&lt;p&gt;The purpose of the Plat_forms is to provide new insights into the real pros, cons and emergent properties of each platform by analyzing various aspects like usability, structure, performance, scalability etc. &lt;/p&gt;

&lt;p&gt;Sadly we were the only applicant for Javascript, so our results for JavaScript platform will be treated noncompetitively in the evaluation. The other participating platforms are our beloved Ruby, PHP, Perl and good old Java. &lt;/p&gt;

&lt;p&gt;Although we use Ruby as our main language we are getting more and more comfortable with JavaScript. Just recently JavaScript got a huge boost with faster &lt;a href=&quot;http://en.wikipedia.org/wiki/V8_%28JavaScript_engine%29&quot;&gt;VMs&lt;/a&gt;, &lt;a href=&quot;http://nodejs.org/#about&quot;&gt;server side execution&lt;/a&gt;, &lt;a href=&quot;http://expressjs.com&quot;&gt;powerful libraries&lt;/a&gt; and more possibilities on the client side commonly regarded as &lt;a href=&quot;http://en.wikipedia.org/wiki/Html5#New_APIs&quot;&gt;HTML5&lt;/a&gt;. So our usage of JavaScript increased over time and now ranges from rich client interfaces over special server side tasks to small JavaScript only apps. &lt;/p&gt;

&lt;p&gt;With our participation with JavaScript at Plat_forms 2011 we want to push our skills and boundaries further. We are excited and anticipate the insights that our participation with JavaScript will reveal.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2010/10/diplom-thesis-a-distributed-application-with-couchdb-and-javascript/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2010/10/diplom-thesis-a-distributed-application-with-couchdb-and-javascript/"/>
    <title>Diplom thesis: a distributed application with CouchDB and Javascript</title>
    <updated>2010-10-27T00:00:00+00:00</updated>
    <author>
      <name>Lena</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Today I finally &lt;a href=&quot;http://lenaherrmann.net/2010/10/27/ta-da-here-is-my-thesis&quot;&gt;released the thesis I wrote at Upstream&lt;/a&gt; during the best part of the last year. The title is &quot;Implementation of a distributed application using the document-oriented database CouchDB” An outliner as a replicable distributed system&quot;. I investigated the capabilities of CouchDB in general and CouchApps in particular to see if they're a good choice for implementing a distributed system. Before that, several other approaches to implementing such a system were examined. The main part is a description of the concept and realisation of the outliner (see below). Where it made sense, snippets of the source code have been used to illustrate certain issues. Also included is a presentation of the used technologies, which include Sammy.js as routing framework, JQuery for the user interface, Cucumber/Culerity and JSpec for testing purposes, CouchDB Lounge for scaling and Amazon EC2 for deployment.&lt;/p&gt;

&lt;p&gt;In the course of the research I implemented an outliner that allows collaborative editing of documents. An outliner is an editor you can use to organize text into a tree structure. The idea for this has been heavily inspired by Upstream's SaaS product &lt;a href=&quot;http://doingtext.com/&quot;&gt;doingText&lt;/a&gt;. The application I wrote is a CouchApp, which means it is Javascript code which is served by a local CouchDB instance. It therefore runs locally in the browser and is also usable when users have unreliable internet access or none at all. Using CouchDB's built-in replication and conflict detection features, the documents can be exchanged and synchronised whenever there is an internet connection available. Quite a part of the development effort went into the handling and resolution of conflicts that may occur during replication.
&lt;/p&gt;

&lt;p&gt;You can view or download English and German versions of the thesis &lt;a href=&quot;http://lenaherrmann.net/2010/10/27/ta-da-here-is-my-thesis&quot;&gt;on my blog&lt;/a&gt;. There you can also find links to the &lt;a href=&quot;http://lena.couchone.com:5984/doingnotes/_design/doingnotes/index.html#/outlines&quot;&gt;deployed application&lt;/a&gt; and to the &lt;a href=&quot;https://github.com/lenalena/doingnotes&quot;&gt;source code on github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Along the way I learned tons of things, mainly about CouchDB and concepts of modern web applications. It was also great to be able to use Javascript for more than just UI enhancement, this gave me the chance to properly grok Javascript as a first class language. &lt;a href=&quot;http://lenaherrmann.net/2010/08/05/thesis-is-done-thanks-for-your-support&quot;&gt;Thanks a lot to Upstream&lt;/a&gt; for sponsoring my work and the professional support along the way!&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2010/08/upstream-summer-party/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2010/08/upstream-summer-party/"/>
    <title>Upstream Summer Party</title>
    <updated>2010-08-16T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Sorry for the short notice but we have been busy with being on vacation :)&lt;/p&gt;

&lt;p&gt;Tomorrow (August 17th 20:00) is the date for our annual summer party. As last year’s location ceased to exist we are moving to the &lt;a href=&quot;http://www.kikiblofeld.de/&quot;&gt;Kiki Blofeld&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Be there and let us buy you a drink or two.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2010/07/transactions-in-couchdb/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2010/07/transactions-in-couchdb/"/>
    <title>Transactions in CouchDB</title>
    <updated>2010-07-30T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;So we’ve been told that all these fancy new NoSQL stores don’t support transactions because that wouldn’t scale, and we’d just have to live with that. So yes, technically, CouchDB doesn’t support transactions, yet it still does. In a way.&lt;/p&gt;

&lt;p&gt;What CouchDB doesn’t support is transactions that span multiple read/write operations, i.e. write document a, then write document b, if something goes wrong, roll back both writes. What it does support is single document “transactions”, i.e. a document is either written completely or not. So if our application requires a transaction, all we have to do is make sure that transaction happens in a single document.&lt;/p&gt;

&lt;p&gt;Here’s our use case: at &lt;a href=&quot;https://www.cobot.me/&quot;&gt;Cobot, our coworking space management service&lt;/a&gt;, we have a feature where a coworking space can charge a coworker for a one time service, e.g. usage of a meeting room. The way it works is that the manager of a space goes to cobot and enters an amount and description, e.g. “$10” and “meeting room”. At the end of the month, a cron job sends out invoices for all the one-time charges.&lt;/p&gt;

&lt;p&gt;Our problem lies with this cron job. The job has to find all the charges for a coworker that haven’t been invoiced yet, create an invoice and mark the charges as invoiced. Without transactions, if something went wrong between creating the invoice and marking the charge as invoiced, the charge could end up being invoiced twice, because it hasn’t been marked as invoiced the first time.&lt;/p&gt;

&lt;p&gt;Clearly we need to throw CouchDB away now and go back to a proper™ (a.k.a. relational) database, right?
Well, no. As I said earlier we can try to move the transaction into a single document.&lt;/p&gt;

&lt;p&gt;In our relational past, we would have had an &lt;em&gt;invoices&lt;/em&gt; table and a &lt;em&gt;charges&lt;/em&gt; table. The &lt;em&gt;invoices&lt;/em&gt; table would carry all the invoices data (have fun with that), and the charges would have a field for &lt;em&gt;amount&lt;/em&gt; and &lt;em&gt;description&lt;/em&gt;, as well as a boolean field &lt;em&gt;invoiced&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;In the world of NoSQL (documents, no transactions), instead we have 2 documents: an &lt;em&gt;invoice&lt;/em&gt; document that contains all the invoice data and a &lt;em&gt;charge&lt;/em&gt; document with the amount and description again. Oh, and the documents also have _id_s.&lt;/p&gt;

&lt;p&gt;Now when we create the invoice, instead of marking the charge as invoiced, we add the charge’s id to an array &lt;em&gt;invoiced_charges&lt;/em&gt; in the invoice.&lt;/p&gt;

&lt;p&gt;That was pretty easy. Now the tricky part is, next month, to determine which charges have been invoiced already. The first approach could be to make two requests:&lt;/p&gt;

&lt;p&gt;Fetch all the charges by using a view that just emits every document that is a charge&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;function(doc) {
  if(doc.type == 'charge') {
    emit(doc.id, null);
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then fetch all the charge ids from the invoices:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;function(doc) {
  if(doc.invoiced_charges) {
    doc.invoiced_charges.forEach(function(id) {
      emit(id, null);
    });
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then on the client side we can throw all the charges whose ids are in the list of invoiced charges.&lt;/p&gt;

&lt;p&gt;But we can do better: We can use a list function to do the filtering within CouchDB. First we have to combine the above views into one:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;function(doc) {
  if(doc.type == 'charge') {
    emit('charge', null);
  }
  if(doc.invoiced_charges) {
    doc.invoiced_ids.forEach(function(id) {
      emit('_invoiced', null)
    });
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now we add the following list function:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;function() {
  // some helpers
  Array.prototype.index = function(val) {
    for(var i = 0, l = this.length; i &amp;lt; l; i++) {
      if(this[i] == val) return i;
    }
    return null;
  }

  Array.prototype.include = function(val) {
    return this.index(val) !== null;
  }


  // interesting stuff
  var used_ids = [];

  send_json_results(function(row, sender) {
    if(row['key'] == '_invoiced') {
      used_ids.push(row['id']);
    } else {
      if(!used_ids.include(row['id'])) {
        sender.send_row(row);
      };
    };
  });

  // more helpers

  // this just makes sending json from a list function easier
  function send_json_results(callback) {
    send('{&quot;rows&quot;: [');
    var first_row = true, sender = {};
    sender.send_row = function(json) {
      if(!first_row) {
        send(',');
      }
      first_row = false;
      send(JSON.stringify(json));
    };

    while(row = getRow()) {
      callback(row, sender);
    }
    send(']}');
  };
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;(Disclaimer: this is not exactly the same code as used in cobot and I haven’t tested it again.)&lt;/p&gt;

&lt;p&gt;Apart from all the helpers, what this essentially does it put all the invoiced charges ids into a list and check every charge against that list, only sending it down to the client if it hasn’t been invoiced already.&lt;/p&gt;

&lt;p&gt;As the view is sorted by the keys (‘_invoiced’, ‘charge’) we can be sure that all the invoiced charge ids have been collected before the list function checks the first charge.&lt;/p&gt;

&lt;p&gt;There you have it, transactions in CouchDB.&lt;/p&gt;

&lt;p&gt;P.S. If you are writing your stuff in Ruby (and possibly Rails), &lt;a href=&quot;https://github.com/langalex/couch_potato&quot;&gt;Couch Potato&lt;/a&gt; now has support for creating and querying list functions. See the readme.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2010/06/experimenting-with-nodejs-mite/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2010/06/experimenting-with-nodejs-mite/"/>
    <title>Experimenting with node.js/mite</title>
    <updated>2010-06-03T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Recently on one of my research fridays I decided to work on a problem that’s been bugging us for a long time. We are big fans of the timetracking service &lt;a href=&quot;http://mite.yo.lk&quot;&gt;mite&lt;/a&gt; and use it for all our projects. The problem is that mite is designed around accounts. You can have many users per account, and then you can run reports across all the users that worked on specific projects. On most projects we’re working with independents who have their own mite accounts. Our problem is that we can’t run reports across multiple mite accounts.&lt;/p&gt;

&lt;p&gt;Mite does have a nice &lt;a href=&quot;http://mite.yo.lk/en/api/&quot;&gt;JSON API&lt;/a&gt; though, so there must be a way to fix this. The result of my work is &lt;a href=&quot;http://mite-e.upstre.am/&quot;&gt;mite.enterprise&lt;/a&gt;, which allows you to enter multiple mite accounts and then report on those in one go. You can find the source on &lt;a href=&quot;https://github.com/langalex/mite_enterprise&quot;&gt;github&lt;/a&gt; and it’s running at &lt;a href=&quot;http://mite-e.upstre.am&quot;&gt;mite-e.upstre.am&lt;/a&gt; so you can use it straight away.&lt;/p&gt;

&lt;p&gt;While the app itself is relatively simple it posed some challenges I want to share here:&lt;/p&gt;

&lt;h3 id=&quot;making-multple-api-requests-and-still-be-fast&quot;&gt;Making multple API requests and still be fast&lt;/h3&gt;

&lt;p&gt;The way the app works is that every time it needs data from mite it makes all the neccessary queries to the mite api and immediately delivers it to the browser. It doesn’t have its own persistence or cache layer for holding data. In order to keep things fast these requests to the mite API need to run in parallel. While I could have written a standard Rails app and spawn a new thread for every request I decided to try something new: &lt;a href=&quot;http://nodejs.org/&quot;&gt;node.js&lt;/a&gt; - node is the new cool kid on the block - a server side Javascript framework where everything is handled asynchronously, similar to Ruby’s &lt;a href=&quot;http://rubyeventmachine.com/&quot;&gt;eventmachine&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In order to run the API requests in parallel I wrote a Javascript function that fires off the requests and collects all the data in an asynchronous fashion:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;function DataCollector(no_of_requests, callback) {
  var datas = [];
  return {
    collector_callback: function() {
      return collect_request_data(function(data) {
        datas.push(data);
        if(datas.length == no_of_requests) {
          callback(datas);
        };
      });
    }
  };
};

var mite_client = {
  time_entries: function(params, callback) {
    // go to the mite api and pass the data to the callback
  }
};

var project_ids = [1, 2, 3, 4, 5],
  data_collector = DataCollector(projects.length, function(datas) {
  // do something with the collected data, e.g. send it to the browser
});

project_ids.forEach(function(project_id) {
  mite_client.time_entries(project_id, data_collector.collector_callback());
});
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I think it’s best to read this from bottom to top. At the bottom, for every project id that was given to the app (I simplified the code for readability) I ask the mite client for all the time entries for that project. I need to pass the mite client a callback, which in turn gets passed the actual data. Since I don’t want to act on the data immediately but want to wait until all the requests have returned, I’m asking the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DataCollector&lt;/code&gt; instance for a callback. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DataCollector&lt;/code&gt; uses a closure so it can collect all the returned data. The data is collected through functions that are created by its &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;collector_callback&lt;/code&gt; function that I pass to the mite client where they are used as callbacks. Because the callback functions all have access to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;datas&lt;/code&gt; array they can push their data on it until enough data is collected - only then the final callback to process the data is called.&lt;/p&gt;

&lt;p&gt;While this might seem a little complicated and confusing at first I think it actually is pretty cool. By using all the callbacks the server app never waits for any I/O operations to complete, hence it is lightning fast and can probably handle thousands of concurrent clients.&lt;/p&gt;

&lt;h3 id=&quot;storing-account-data-securely&quot;&gt;Storing account data securely&lt;/h3&gt;

&lt;p&gt;In order to talk to the mite api we need the accounts’ API keys. I didn’t want to have to worry too much about security and how to store this data on the server so I chose a different approach: I’m not storing any data on the server at all. Instead, I’m using the browsers’ &lt;a href=&quot;http://en.wikipedia.org/wiki/Web_Storage&quot;&gt;local storage&lt;/a&gt;. This is a small key value store that most modern browsers support. The data is stored on the hard disk of the client, hence there’s no danger that anyone will steal all the API keys from the server, because they are distributed all across the web.&lt;/p&gt;

&lt;p&gt;Another advantage of that approach is that I don’t need a signup/login process for the app. When a user goes to the &lt;a href=&quot;http://mite-e.upstre.am&quot;&gt;mite.enterprise site&lt;/a&gt; they can start using it immediately. No email address, no password. The app just looks into the &lt;em&gt;local storage&lt;/em&gt; and loads the according data.&lt;/p&gt;

&lt;h3 id=&quot;nice-and-fast-gui&quot;&gt;Nice and fast GUI&lt;/h3&gt;

&lt;p&gt;I wanted to keep things small and simple, so I decided to keep all the HTML/CSS out of the server. My node.js server only serves JSON and static files. For handling user interactions and rendering templates I’m using the excellent &lt;a href=&quot;http://code.quirkey.com/sammy/&quot;&gt;Sammy&lt;/a&gt; framework in combination with &lt;a href=&quot;http://blog.couch.io/post/622014913/mustache-js&quot;&gt;mustache.js&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sammy is like &lt;a href=&quot;http://www.sinatrarb.com/&quot;&gt;Sinatra&lt;/a&gt; but it’s implemented in JavaScript and runs in the browser. You can map URLs to actions, but instead of loading a new page for every request you do some AJAX requests (or not even that) and then replace whatever parts of the page you want using jQuery.&lt;/p&gt;

&lt;h3 id=&quot;conclusions&quot;&gt;Conclusions&lt;/h3&gt;

&lt;p&gt;Node.js is an awesome library. First it lets me implement servers that are fast as hell, secondly I can implement them in the same language I use for the frontend. With local storage I don’t have to implement a signup/login process. Instead, users can use the app immediately and their data is stored on their computer. With Sammy I can quickly put a rich and responsive GUI on top of my servers which only have to deliver JSON. I guess it’s the future :)&lt;/p&gt;

&lt;p&gt;Oh, and mite just became much more useful for us, too.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2010/04/capybara-mix-and-match-acceptance-testing/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2010/04/capybara-mix-and-match-acceptance-testing/"/>
    <title>Capybara - Mix and match acceptance testing </title>
    <updated>2010-04-26T00:00:00+00:00</updated>
    <author>
      <name>thilo</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;http://farm4.static.flickr.com/3298/3657577149_630b87edee.jpg&quot; title=&quot;Mix and Match&quot; class=&quot;alignleft&quot; width=&quot;300&quot; /&gt; The more layers you add when testing your application the slower these tests become.&lt;/p&gt;

&lt;p&gt;A unit test takes a fraction of a second to run. A simple integration test maybe up to a second. When JavaScript and a real browsers gets involved probably longer. But unnecessary long test runs hurt productivity. Up until a couple of weeks ago I had to make trade-offs either in speed, test-coverage or maintainability of my test suite when it came to real full stack integration/acceptance testing. Here is how I perceive these problems.&lt;/p&gt;

&lt;p&gt;A standard &lt;a href=&quot;http://cukes.info/&quot;&gt;Cucumber&lt;/a&gt; (a framework to write natural readable acceptance tests) setup uses &lt;a href=&quot;https://github.com/brynary/webrat&quot;&gt;Webrat&lt;/a&gt; which interacts with your app through a simple Rails integration session, so no JavaScript. The best what Webrat could do in this scenario, when it comes to JavaScript, is to recognize some common patterns in your html and send according requests - but still,  most of the JavaScript code is not covered.&lt;/p&gt;

&lt;p&gt;Webrat can also be used to drive &lt;a href=&quot;http://seleniumhq.org/projects/remote-control/&quot;&gt;Selenium&lt;/a&gt;, which interacts with your web app through a real browser. This will cover Javascript, CSS and possible browser issues. But compared to Webrat Selenium is snail mail slow.&lt;/p&gt;

&lt;p&gt;With &lt;a href=&quot;https://github.com/langalex/culerity&quot;&gt;Culerity&lt;/a&gt; or &lt;a href=&quot;https://github.com/svenfuchs/steam/&quot;&gt;Steam&lt;/a&gt; there are alternatives to Webrat which use &lt;a href=&quot;http://htmlunit.sourceforge.net/&quot;&gt;HtmlUnit&lt;/a&gt;: HtmlUnit is a “GUI-Less browser for Java programs”. It models HTML documents and provides an API that allows you to load pages, fill out forms, click links, etc… just like you do in your “regular” browser.  They are significantly faster than scripting a normal browser with Selenium at the cost that you don’t see how design works or if any browser issues cause trouble and it is still slower than Webrat without a special backend.&lt;/p&gt;

&lt;p&gt;In order to run acceptance tests which require full JavaScript support separately from those who don’t to get shorter test runs I use two separate environments as they are generated out of the box either by Cucumber or Culerity. This leads to duplication of the setup code and step definitions, which means that there is more to maintain and more that could possible break.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/jnicklas/capybara&quot;&gt;Capybara&lt;/a&gt; addresses the shortcomings of a two environments solution and still allows you to be flexible in what backend you use for integration testing on a per scenario basis in Cucumber. It does that by using Cucumber’s tags feature.&lt;/p&gt;

&lt;p&gt;Capybara “is inspired by and aims to replace Webrat as a DSL for interacting with a web application. It is agnostic about the driver running your tests and currently comes bundled with rack-test, Culerity and Selenium support built in”. In the future we certainly will see other backends. I saw that a &lt;a href=&quot;https://github.com/smparkes/capybara-envjs&quot;&gt;backend for env-js&lt;/a&gt; (a pure &lt;a href=&quot;http://ejohn.org/blog/bringing-the-browser-to-the-server/&quot;&gt;JavaScript browser environment&lt;/a&gt;) is a already in the making.&lt;/p&gt;

&lt;p&gt;Below is an example that runs one scenario with Selenium and the other with Culerity.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;  @selenium
  Scenario: Searching a blog post using autocomplete
    Given a blog post titled &amp;amp;quot;Autocomplete made easy&amp;amp;quot; containing &amp;amp;quot;Some example&amp;amp;quot;
    When I go to the search page
      And I fill in &amp;amp;quot;Search&amp;amp;quot; with &amp;amp;quot;Auto&amp;amp;quot;
      And I follow &amp;amp;quot;Autocomplete made easy&amp;amp;quot;
    Then I should see &amp;amp;quot;Some example&amp;amp;quot;

  @culerity
  Scenario: delete found comments
    Given a comment by author &amp;amp;quot;Thilo&amp;amp;quot;
    Given a comment by author &amp;amp;quot;Tilo&amp;amp;quot;
  When I search for &amp;amp;quot;ilo&amp;amp;quot;
    And I check the element with xpath &amp;amp;quot;//p[contains(@class,'delete')]/input&amp;amp;quot;
    And I press &amp;amp;quot;Submit&amp;amp;quot;
  Then I should see &amp;amp;quot;Are you really sure?&amp;amp;quot;
  When I press &amp;amp;quot;Yes&amp;amp;quot;
  Then I should see &amp;amp;quot;Comments were delete&amp;amp;quot;
  And I should see &amp;amp;quot;0 Comments&amp;amp;quot;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;You might have noticed the XPath expression in the second scenario. From time to time you will have to deal with XPath expressions when writing custom steps as XPath is used to locate elements within the DOM. The default steps can be scoped with CSS selectors or XPath, Capybara takes care of the conversion to XPath then.&lt;/p&gt;

&lt;p&gt;Using Capybara with Cucumber is straightforward:
First get the gem:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;gem install capybara&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;On OSX you may have to install libffi, you can install it via MacPorts with:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sudo port install libffi&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Then go to your rails project and generate the cucumber files as followed:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;script/generate cucumber --capybara&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;And you are ready to go to mix and match your acceptance with tags. For further informations like using capybara without cucumber, asynchronous JavaScript etc. checkout Capybara’s &lt;a href=&quot;https://github.com/jnicklas/capybara/blob/master/README.rdoc&quot;&gt;README&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Capybara definitely eases maintaining acceptance test and will give impulses to improve other tools, e.g. &lt;a href=&quot;https://github.com/thatcher/env-js&quot;&gt;env-js&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Are you stuck in a maintenance nightmare or simply want to bring your testing skills to the next level? &lt;a href=&quot;https://upstre.am/contact-us/&quot;&gt;Get in touch with us&lt;/a&gt;.&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2010/04/product-launch-cobot-coworking-space-management-service/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2010/04/product-launch-cobot-coworking-space-management-service/"/>
    <title>Product Launch: cobot - coworking space management service</title>
    <updated>2010-04-01T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Back in december I attended the “Berlin Coworking Day”, a meetup of the German coworking scene. In the talks I saw a recurring problem was the lack of proper software to help with all the tasks that running a coworking space requires. Since we had been running &lt;a href=&quot;https://co-up.de&quot;&gt;our own coworking space&lt;/a&gt; for a while the problems were not new to me.&lt;/p&gt;

&lt;p&gt;So, after a few hours I decided to leave the meetup and start to scratch my own itch. Since &lt;a href=&quot;https://upstre.am/2009/12/02/time-out/&quot;&gt;upstream was taking the month off&lt;/a&gt; anyway I had enough time to play around.&lt;/p&gt;

&lt;p&gt;After roughly 4 weeks of development we released the first version of our &lt;a href=&quot;https://www.cobot.me/&quot;&gt;coworking space management software Cobot&lt;/a&gt; - it obviously didn’t have a lot of features, but enough to help our own needs.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.cobot.me/&quot;&gt;&lt;img src=&quot;https://upstre.am/wp-content/uploads/2010/04/cobot.png&quot; alt=&quot;&quot; title=&quot;Cobot&quot; width=&quot;562&quot; height=&quot;524&quot; class=&quot;alignnone size-full wp-image-810&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The basic principle is this: a coworking space owner signs up her space and gets her own cobot site under its own subdomain. There she adds payment plans, payment methods, invoicing details etc. Anyone who wants to start coworking at this space signs up and chooses a plan and payment method.&lt;/p&gt;

&lt;p&gt;From then on cobot helps the owner of the space in various ways. It manages payment tracking and invoicing (can be fully automated with Paypal), it helps to keep track of day to day tasks and problems (“the printer ran out of paper”) and provides a platform for communication between coworking space and coworkers.&lt;/p&gt;

&lt;p&gt;In addition it allows a space owner to track key metrics of their space. From turnover to occupancy this gives coworking spaces the numbers they need to optimize their plans and the number of coworkers they can fit in.&lt;/p&gt;

&lt;p&gt;Even for our small coworking space (which is now full) cobot has made things a lot easier already (we actually know who hasn’t paid yet now). Our next step is to use &lt;a href=&quot;https://www.cobot.me/guides/radius&quot;&gt;cobot’s wifi login/presence tracking system&lt;/a&gt;  so we know how full we really are, and maybe fit in a few more people.&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;http://www.centernetworks.com/cobot-provides-managed-coworking-services?utm_source=feedburner&amp;amp;utm_medium=feed&amp;amp;utm_campaign=Feed%3A+Centernetworks-+%28CenterNetworks+-%29&quot;&gt;first&lt;/a&gt; &lt;a href=&quot;http://www.dangerouslyawesome.com/2010/02/how-cobot-gets-coworking-management-its-made-of-people/ http://www.killerstartups.com/Web-App-Tools/cobot-me-a-web-tool-for-managing-coworking-spaces &quot;&gt;responses&lt;/a&gt; were quite positive and we started talking to a number of coworking spaces, mostly in Germany and the United States. With the feedback we’re getting and constantly improving the site we’re confident to become the first choice for many of the coworking spaces that are being started all over the world these days.&lt;/p&gt;

&lt;p&gt;If you are interested in how cobot works you can read more details and see screenshots in the &lt;a href=&quot;https://www.cobot.me/guides&quot;&gt;guides section&lt;/a&gt; of the site.&lt;/p&gt;

&lt;p&gt;If you do have a coworking space we hope you &lt;a href=&quot;https://www.cobot.me/&quot;&gt;hop over to Cobot and sign up&lt;/a&gt; - you can try it out for free with up to 5 coworkers. After that it’s only 1 EUR/1.50 USD per coworker and month.&lt;/p&gt;

&lt;p&gt;If you want to follow along the development, there’s a twitter account (&lt;a href=&quot;https://twitter.com/cobot_me&quot;&gt;@cobot_me&lt;/a&gt;) and the &lt;a href=&quot;https://blog.cobot.me/&quot;&gt;Cobot blog&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2010/02/from-journeyman-to-client/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2010/02/from-journeyman-to-client/"/>
    <title>From Journeyman to Client</title>
    <updated>2010-02-13T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Agile project management makes the client an integral part of the development process. This article explains what your responsibilities and powers as a client are, and helps you to understand project management the way we see it.&lt;/p&gt;

&lt;p&gt;In the traditional (waterfall) model of software development someone writes a specification of the whole software and then the programmers fail to implement it within the given time/budget. To be honest I don&amp;#8217;t know a lot about this way of doing software, but I&amp;#8217;ve seen it fail.&lt;/p&gt;

&lt;p&gt;But I do know a bit about how we write software. In contrast, agile development works in small steps. There is no upfront specification, as we acknowledge that this specification would be incomplete and quickly outdated by changing requirements. Instead we work in short iterations (at upstream typically ~2 weeks) where we only plan ahead for a short amount of time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;By aggressively prioritizing what is most important we are able to release software that fulfills the needs of the business with less clutter in a shorter amount of time.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For me this sentence is really the essence of agile project management as it shows its most important point: prioritizing what is important for your business.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What is software the fulfills the needs of the business?&lt;/em&gt; It&amp;#8217;s a software that solves your specific problem. If you want to sell something online it allows your customers to click a buy button. If you need to send invoices it will put a bunch of numbers on a sheet.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What is less clutter?&lt;/em&gt; Less clutter means if you want to sell something online it neither has a tag cloud nor a facebook like wall for every product where users can post their video reviews. If you need to send invoices it will not let you upload pictures for it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What is less time?&lt;/em&gt; Less time means we deliver your product faster because we spend less time on implementing features because there are fewer features. It does not mean that we can program faster because we are doing agile (seriously we can&amp;#8217;t).&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What is prioritizing?&lt;/em&gt; You do the things first that are most important.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;So what is important?&lt;/em&gt; And here is where it gets difficult.&lt;/p&gt;

&lt;p&gt;What is important is the client&amp;#8217;s decision. The problem is that a lot of things can be (thought of as being) important. For a web site, the start page has to have a maximum conversion rate, SEO is important, scalability is important (isn&amp;#8217;t it?), usability is important, choice of technology is important, the database schema is important&amp;#8230; you get the idea. &lt;/p&gt;

&lt;p&gt;But do you know what&amp;#8217;s really important? That your business problem is solved. And do you want to know how many kinds of business problems there are? Well, there&amp;#8217;s exactly one: make (more) money.&lt;/p&gt;

&lt;p&gt;I think it actually is that simple, but from what I&amp;#8217;ve seen it can become really hard to focus on that one goal, when you think you have to consider all that other stuff as well, but let me say this again: as a client it&amp;#8217;s not your responsibility to think about the database and scalability (you won&amp;#8217;t have to scale in the beginning). Your one and only problem is: how do I make money with this. Everything else comes after it.&lt;/p&gt;

&lt;p&gt;A good way to focus on solving that problem might be to ask yourself this question: How do I make (more) money?&lt;/p&gt;

&lt;h2&gt;Case Study&lt;/h2&gt;

&lt;p&gt;Let&amp;#8217;s stick to the example of an online shop and let&amp;#8217;s assume you want sell e-books. Of course you look at the competition first, and there is amazon.com. They sell e-books and from what it seems they are making a whole lot of money from it, so that must to be a good starting point. From their site you collect a bunch of requirements you also want in your shop:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Categories: Books must be in categories so people can find them.&lt;/li&gt;
&lt;li&gt;Tags: With tags people can click on tag clouds and then see all the books to that tag.&lt;/li&gt;
&lt;li&gt;Related books: &amp;#8220;People who have bought this might also like&amp;#8230;&amp;#8221; - I love that feature&lt;/li&gt;
&lt;li&gt;User reviews/ratings: when users review the books we can make a top list and then everyone will buy the top books.&lt;/li&gt;
&lt;li&gt;Licensing: You have talked to the publishers already and they have these complicated licenses where you may only sell a certain book in a certain country but only at full moon when it&amp;#8217;s raining - so the shop needs to take all this into account.&lt;/li&gt;
&lt;li&gt;Internationalization: We need at least English, Spanish and Mandarin to reach more markets&lt;/li&gt;
&lt;li&gt;Statistics: You want to know at what time and weather conditions people buy books from where, on which browser and operating system and which IP address they used to do it. From that data you will be able to optimize the hell out of the start page.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, that looks like a reasonable list of requirements doesn&amp;#8217;t it? With this shop you could steal a nice market share from amazon right? Well, let&amp;#8217;s take that list to the test: does it help me to make money?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;categories: no&lt;/li&gt;
&lt;li&gt;tags: no&lt;/li&gt;
&lt;li&gt;related books: no&lt;/li&gt;
&lt;li&gt;reviews/ratings: no&lt;/li&gt;
&lt;li&gt;licensing: no&lt;/li&gt;
&lt;li&gt;internationalization: no&lt;/li&gt;
&lt;li&gt;statistics: no&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Surprised? I hope at least a little bit. So what would make you some money?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In order to make money &lt;br /&gt;
as the owner of the shop &lt;br /&gt;
I want people to buy a book&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With all the fancy features above you forgot (sorry, I forgot) the only important feature of the site: a button that says &amp;#8220;Buy this e-book&amp;#8221;. By the way the format of that feature request comes from the &lt;a href=&quot;http://behaviour-driven.org/&quot;&gt;BDD&lt;/a&gt; (behavior driven development) people. This is the generic template:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In order to &amp;lt;business value&amp;gt; &lt;br /&gt;
as &amp;lt;role&amp;gt; &lt;br /&gt;
I want &amp;lt;feature&amp;gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is a brilliant template that you can use to take all your feature ideas to the test. Remember that the only valid business value here is to make money. And that the &lt;a href=&quot;http://lizkeogh.com/2010/02/02/theyre-not-user-stories/&quot;&gt;only role that is important here is you as the stakeholder&lt;/a&gt; of the project.&lt;/p&gt;

&lt;p&gt;Let&amp;#8217;s look at your features:&lt;/p&gt;

&lt;h3&gt;Categories&lt;/h3&gt;

&lt;p&gt;In order to make money &lt;br /&gt;
as the owner of the shop &lt;br /&gt;
I want categories&lt;/p&gt;

&lt;p&gt;How is a category going to make money? A category is a means to, well, put your books into categories. You might at some point decide that you can sell more books by putting them into categories because that big list of 1000 books on you start page crashes people&amp;#8217;s browser and they can&amp;#8217;t buy your books anymore, but for now you haven&amp;#8217;t even sold a single book yet, so categories are definitely not going to make you any money right now.&lt;/p&gt;

&lt;h3&gt;Tags&lt;/h3&gt;

&lt;p&gt;A few years ago someone declared that if you want to be &amp;#8220;Web 2.0&amp;#8221; you need tags. That&amp;#8217;s why a lot of websites have tags nowadays. Like categories you can&amp;#8217;t make any money out of tags.&lt;/p&gt;

&lt;h3&gt;Related books&lt;/h3&gt;

&lt;p&gt;While this is a popular feature at amazon, your yet to be launched shop wouldn&amp;#8217;t even have enough data at the beginning to make any sensible claims about what books are related. Again, at the beginning this won&amp;#8217;t matter at all. You can always add it later, when the money making assertion becomes true.&lt;/p&gt;

&lt;h3&gt;Reviews/Ratings&lt;/h3&gt;

&lt;p&gt;While reviews can be a good way to promote books and build trust for your customers, when you launch your site there won&amp;#8217;t be any reviews because nobody has bought a single book yet. So unless you charge people for being able to write reviews this is not going to pay your rent.&lt;/p&gt;

&lt;h3&gt;Licensing&lt;/h3&gt;

&lt;p&gt;Sure, we can build you a system that grabs the weather from weather.com, can calculate the moon phases over the sahara and figure out people&amp;#8217;s countries by their IP address, but it&amp;#8217;s going to cost you. How about this: You start and market your shop in only one country. This can be your home country or the country with the simplest licensing model. After you have sold enough books there we can start working on the rest (and you can pay us from the money you have made already).&lt;/p&gt;

&lt;h3&gt;Internationalization (I18n)&lt;/h3&gt;

&lt;p&gt;Again, we can build you any system you want, but be warned, some people read and write from right to left or top to bottom. WIth I18n you are opening a whole new can of worms. How about we do I18n later, after you have proven that your business model works in one country (or language region, which might cover a lot of countries (thinking English/Spanish/Mandarin)).&lt;/p&gt;

&lt;h3&gt;Statistics&lt;/h3&gt;

&lt;p&gt;Sure it&amp;#8217;s important to measure the success (or failure) of your business. But before you have a business there isn&amp;#8217;t really much to measure is there? And how about this: for now every time someone buys a book you open up your spreadsheet app on your computer and put a date and a price in it. I know it may seem archaic but it works. If you want to go crazy you can even use a real book made of paper, but since you&amp;#8217;re selling e-books you probably don&amp;#8217;t want that.&lt;/p&gt;

&lt;p&gt;All these examples boil down to this: test your features agains the BDD template: can I make more money &lt;em&gt;now&lt;/em&gt; when we implement this. A lot of them are really not that important now and you can use something simpler instead.&lt;/p&gt;

&lt;h2&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Deciding what is important is the one big way that you can control the project. Programming takes time and we don&amp;#8217;t usually have a lot of ways to make that faster within a project. But choosing wisely the features that you really really need to start selling your products can make the difference between a 4 week and a 6 months project. We do the best we can to help you with those decisions but ultimately they are yours.&lt;/p&gt;

&lt;p&gt;If we spend 6 months working on your project and it&amp;#8217;s still not done then that&amp;#8217;s not a programming problem, it&amp;#8217;s a management problem, because we have been working on too many features, of which many probably weren&amp;#8217;t as important as you thought (no offense). On the other hand if we make it in a few weeks, we'll congratulate you and bring some drinks to your early launch party.&lt;/p&gt;

&lt;p&gt;(On a side note we recently released &lt;a href=&quot;https://www.cobot.me/&quot;&gt;cobot.me&lt;/a&gt; after only 4 person weeks of development it doesn't have a lot of features, but that&amp;#8217;s a different post.)&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2010/01/git-meets-tracker-a-possible-workflow/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2010/01/git-meets-tracker-a-possible-workflow/"/>
    <title>Git meets Tracker, a possible Workflow</title>
    <updated>2010-01-17T00:00:00+00:00</updated>
    <author>
      <name>thilo</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;&lt;a href=&quot;https://upstre.am/wp-content/uploads/2010/01/Screen-shot-2010-01-17-at-10.40.39-.png&quot;&gt;&lt;img src=&quot;https://upstre.am/wp-content/uploads/2010/01/Screen-shot-2010-01-17-at-10.40.39-.png&quot; alt=&quot;&quot; title=&quot;Some Git branches&quot; class=&quot;alignleft size-thumbnail wp-image-770&quot; /&gt;&lt;/a&gt;When working with our clients we use &lt;a href=&quot;http://pivotaltracker.com&quot;&gt;pivotal tracker&lt;/a&gt; to manage requirements and priorities. Which works very well, as it keeps overhead low and offers a very focused communication channel for feature related questions.&lt;/p&gt;

&lt;p&gt;The last step for a successful delivery of a feature requires our customer to accept that feature or hand it back to us if something isn’t satisfying. To enable our customers to do that we provide them with a staging environment, which is a non public version of our customers’ product with the latest code integrated so that he can try out new features before they go live. We deploy to the staging system directly from the master branch usually, from time to time we introduce feature branches if we think something takes longer or might need refinement after more customer feedback. By the time of an actual live deployment we normally have ironed out any remaining issues which shown up during the client’s review on the staging system.&lt;/p&gt;

&lt;p&gt;This simple system works pretty well and effective if you are in control of the staging environment, have only one person as the customer to talk to and your developers’ communication works like in a hive mind (which it does, mostly :)). This system reached its limit when we worked with &lt;a href=&quot;http://paperc.de&quot;&gt;PaperC&lt;/a&gt; lately, one of our valued clients.&lt;/p&gt;

&lt;p&gt;We coached their programmer in BDD and pair programming some time ago and still working with them very closely to get improvements and new features out of the door fast. The limits with our simple workflow became visible when I paired with &lt;a href=&quot;https://twitter.com/phuesler&quot;&gt;Patrick&lt;/a&gt; together for PaperC and they also had a pair on site with us at &lt;a href=&quot;https://co-up.de/&quot;&gt;co.up&lt;/a&gt;. At first delivered tickets began to stack up waiting for acceptance, also because the staging system became unstable as issues weren’t fixed fast enough.&lt;/p&gt;

&lt;p&gt;Things got more complicated as we put features on staging which weren’t supposed to make it into the next live deployment as they where part of a bigger, not yet thought to an end, interface change. These features just should be shown in action to see how things can go on from what we did so far. This incident cost us some extra time to put these feature related commits out of the master branch into a feature branch. So we switched to create a feature branch for everything that was more than “add a new input field to that”. This left us with a lot of branches we had to keep track of when deploying to the staging system, but at least we could undo merge commits easily when a feature wasn’t suppose to be deployed live. Then we started a discussion on how we could do this in a better way. Before I describe the workflow which I think is best suited, as we already employed it successfully in the past, I’d like to mention two suggestions I’m not comfortable with an why.&lt;/p&gt;

&lt;p&gt;One idea was the introduction of pair branches, where every pair commit their work to. And these branches would be merged into master for acceptance. Apart from not really resolving the issue, it just mitigates it onto a pair branch, and it contradicts the principle of collective code ownership.&lt;/p&gt;

&lt;p&gt;Another proposal was to create a special production branch and selectively cherry-pick commits which are supposed to go live. This points into the right direction, as it gives you more control of what goes live, but it might leave you with a lot of commits to pick from, if you have a developer like me who commits rather often. And it doesn’t give you a single point to roll back from if a feature that has multiple commits get pulled off the release.&lt;/p&gt;

&lt;p&gt;So now for my preferred and practically proven solution. First off, it requires an extra server which I refer to as experimental. It is a lightweight (not so close to production setup) staging system with a database schema that should be easily migrate-able up and down like on a CI server. It should be fed with some test data automatically. It doesn’t have to be as much data as on staging, just enough to try out most features.&lt;/p&gt;

&lt;p&gt;Now to the git workflow part. Smaller changes and bug fixes go directly to master as always. Bigger features should be done in separate branches and merged to master once things are done (Don’t forget to delete the remotes if the feature gets accepted). The master branch should be deployed to the experimental server at least once a day, likely more often so that features can be quickly accepted. Features that aren’t necessarily ready for production yet can be deployed to experimental for acceptance testing simply by changing the branch from which the experimental environment should be deployed from the master branch to the feature branch and rebuild the database if required. Before an imminent release the master should be pulled to staging so that any remaining issues can be fixed. Such fixes should happen on the staging branch and can later be merged back to master. If everything is fine, merge staging to production and deploy to the live system if you are ready. This way you always have a clean production branch, it’s easier to pull off features from a release and get faster feedback for completed features without worrying to mess up the next production release.&lt;/p&gt;

&lt;p&gt;What is your approach to managing commits and releases? Do you find this post helpful? Feel free to leave some feedback in the comments.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2010/01/upstream-spreading-to-scandinavia/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2010/01/upstream-spreading-to-scandinavia/"/>
    <title>Upstream spreading to Scandinavia</title>
    <updated>2010-01-12T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;As of last week upstream is now present in 2 more countries: Finland and Sweden. We see a strong increase in the web development market in the scandinavian area so we want to be there before the rest of the world notices. Ok ok, so actually that’s not the reason. Both &lt;a href=&quot;https://twitter.com/bionadeholunder&quot;&gt;Frank&lt;/a&gt; and &lt;a href=&quot;https://twitter.com/langalex&quot;&gt;I&lt;/a&gt; moved for personal reasons.&lt;/p&gt;

&lt;p&gt;Frank is now situated in Turku, Finland and I’m in Lund, Sweden. We will both be staying there for about half a year and continue to work for upstream remotely. As our company pair programs all the time so are we“ only remotely now, using iChat’s screen sharing/audio chat and good old Textmate. It actually works smoother than it did when we tested it in the office’s wireless network. Scandinavian communication infrastructure is pretty good.&lt;/p&gt;

&lt;p&gt;While we are here in the north we will be attending and possibly speaking at a few conferences in the area like &lt;a href=&quot;http://nordicruby.org/&quot;&gt;Nordic Ruby&lt;/a&gt; and the &lt;a href=&quot;http://www.swdc-central.com/&quot;&gt;Scandinavian Web Developer Conference (SWDC)&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you are interested or know anyone who would want to hire us for a web project either in Sweden or Finland please drop us a note. While remotely working for the Berlin office is ok it would be nice to work on our own. We create high quality, fully tested webapps in a few week’s time, we help with code quality and scalability problems, we can do training/coaching on various programming/agile topics, remotely or on site.&lt;/p&gt;

&lt;p&gt;Oh and if you don’t have a project for us but live in the area say hi so we can invite you for a beer or two. :)&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://maps.google.com/maps/ms?ie=UTF8&amp;amp;source=embed&amp;amp;msa=0&amp;amp;msid=111510584897679640706.00047cf59067ffdc2fcde&amp;amp;ll=55.702854,13.192912&amp;amp;spn=0.845041,1.777039&quot;&gt;Upstream Locations&lt;/a&gt; - please zoom out until you see Australia.&lt;/p&gt;

&lt;iframe width=&quot;525&quot; height=&quot;350&quot; frameborder=&quot;0&quot; scrolling=&quot;no&quot; marginheight=&quot;0&quot; marginwidth=&quot;0&quot; src=&quot;http://maps.google.com/maps/ms?ie=UTF8&amp;amp;source=embed&amp;amp;msa=0&amp;amp;msid=111510584897679640706.00047cf59067ffdc2fcde&amp;amp;ll=55.702854,13.192912&amp;amp;spn=0.845041,1.777039&amp;amp;output=embed&quot;&gt;&lt;/iframe&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2009/12/time-out/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2009/12/time-out/"/>
    <title>Time-out</title>
    <updated>2009-12-02T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Upstream is taking the december off. Except from support for running projects we haven’t taken on any new projects for this month. The reason is that we wanted some time to think about the future of our company, but also to have time for other things.&lt;/p&gt;

&lt;p&gt;We have already done this for a weekend in January when we &lt;a href=&quot;https://upstre.am/2009/01/22/upstream-went-to-maine/&quot;&gt;went to Maine&lt;/a&gt;. It was a fun weekend but we spent a lot of time walking around at the beach and in the sauna, which left too little time for developing any significant ideas. So this time we are doing it for a whole month.&lt;/p&gt;

&lt;p&gt;For the thinking part we will be doing a few meeting throughout the month to talk about different aspects, like the kinds of customers we want to attract in the future, the kind of work we want to be doing, how much and where we will want to work and also how much money we want to be making with all this. We are not going to do any fancy brainstorming-like things. Instead everyone spends their time doing whatever he or she thinks will produce the best ideas - be it sleeping in every day, long breakfasts or running around in the park. We will then gather occasionally to collect and exchange ideas.&lt;/p&gt;

&lt;p&gt;For the “doing other things” part: first of all we want to give our &amp;lt;a href=”“https://co-up.de/about.html&amp;gt;new office and coworking space&amp;lt;/a&amp;gt; some love. The walls are all white and empty yet and there are still a few ikea boxes stacking up. Another part is to attend some of the many events Berlin has to offer, but that we mostly have to skip because of work.&lt;/p&gt;

&lt;p&gt;I already spent yesterday at the coworking day, where Germany’s coworking community gathered to discuss the issues of setting up and running coworking spaces. That was my first day off and I already got a few new ideas from it that could help upstream in the future. Next up is &lt;a href=&quot;http://tedxkreuzberg.org&quot;&gt;TedX Kreuzberg&lt;/a&gt;, where the smart people of Kreuzberg will come and tell us about their brilliant ideas. I’m very much looking forward to that.&lt;/p&gt;

&lt;p&gt;I will report back on our progress here from time to time. So far I can only say: “Please try this at home”. Not having to worry about projects and day to day problems all the time is very refreshing and I am confident 2010 will be a great year for us.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2009/11/upstream-on-the-move/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2009/11/upstream-on-the-move/"/>
    <title>Upstream on the Move</title>
    <updated>2009-11-15T00:00:00+00:00</updated>
    <author>
      <name>thilo</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;&lt;img src=&quot;http://upstream-berlin.com/wp-content/uploads/2009/11/IMG_0340-300x202.jpg&quot; alt=&quot;Upstream on the Move&quot; title=&quot;Upstream on the Move&quot; width=&quot;300&quot; height=&quot;202&quot; class=&quot;alignleft size-medium wp-image-715&quot; /&gt;We &lt;a href=&quot;http://img9.yfrog.com/i/kyf.mp4/&quot;&gt;just moved into our new office&lt;/a&gt; at &lt;a href=&quot;http://maps.google.com/maps?q=Adalbertstrasse+7,+10999+Berlin&amp;amp;oe=utf-8&amp;amp;client=firefox-a&amp;amp;ie=UTF8&amp;amp;hq=&amp;amp;hnear=Adalbertstra%C3%9Fe+7,+Kreuzberg+10999+Berlin,+Germany&amp;amp;ei=QVQAS8GqI9GFnAfCi-CNCw&amp;amp;ved=0CAgQ8gEwAA&amp;amp;ll=52.499536,13.419467&amp;amp;spn=0.003919,0.011362&amp;amp;z=17&quot;&gt;Adalbertstrasse 7, 10999 Berlin&lt;/a&gt;, the old one just got too small. Too small for Upstream and too small to offer a good co-working experience. During the last years, also with the help of our co-working space, we were able to grow &lt;a href=&quot;http://upstream-berlin.com/network&quot;&gt;our network&lt;/a&gt; of skilled freelancing professionals which allow us to do more and bigger projects and offer our customers the best people for the job.&lt;/p&gt;

&lt;p&gt;We had a good time with our old office. For example we co-worked together with Jan Lehnardt (&lt;a href=&quot;https://twitter.com/janl&quot;&gt;@janl&lt;/a&gt;) of CouchDB fame, taught Ruby and TDD to then still new developers like Lena Herrmann (&lt;a href=&quot;https://twitter.com/kilaulena&quot;&gt;@kilaulena&lt;/a&gt;), found skilled developers like Patrick Hüsler (&lt;a href=&quot;https://twitter.com/phuesler&quot;&gt;@phuesler&lt;/a&gt;), found great designers like Kristina Schneider (&lt;a href=&quot;https://twitter.com/kriesse&quot;&gt;@kriesse&lt;/a&gt;), won good PHP Developer like Robin Mehner (&lt;a href=&quot;https://twitter.com/rmehner&quot;&gt;@rmehner&lt;/a&gt;) over to Ruby, ran &lt;a href=&quot;http://upstream-berlin.com/?s=devhouse&quot;&gt;DevHouses&lt;/a&gt; and &lt;a href=&quot;http://upstream-berlin.com/2009/01/15/upcoming-event-screencast-week-cockpit/&quot;&gt;Cockpit Nights&lt;/a&gt; to learn and share, coded with international recognized personalities like Pat Allan (&lt;a href=&quot;https://twitter.com/pat&quot;&gt;@pat&lt;/a&gt;), had lots of well known guests in our office and some good parties too. But the old upstream office just got to its limits. I’m happy that all our co-workers moved with us.&lt;/p&gt;

&lt;p&gt;The new office will allow us to keep a productive and inspiring work environment, which I think is essential to deliver &lt;a href=&quot;http://upstream-berlin.com/projects/&quot;&gt;great products&lt;/a&gt;, and evolve all the great things we did so far. For example Alex will expand our efforts in providing an affordable space for independent workers that combines a relaxed and social atmosphere with the infrastructure and productivity of a well equipped work place with &lt;a href=&quot;https://co-up.de&quot;&gt;co.up&lt;/a&gt; (&lt;a href=&quot;https://twitter.com/co_up&quot;&gt;@co_up&lt;/a&gt;). This way I think we will improve our network even further and get peoples and ideas together for the best outcome possible.&lt;/p&gt;

&lt;p&gt;Another great thing about the new office is that its interior and its central location next to Kottbusser Tor underground station allow us to keep hosting the &lt;a href=&quot;http://www.rug-b.de&quot;&gt;Ruby User Group Berlin&lt;/a&gt; meet-up as part of our involvement in the local &lt;a href=&quot;http://upstream-berlin.com/community&quot;&gt;developer scene&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So if you are in need of a desk (see &lt;a href=&quot;https://co-up.de&quot;&gt;co.up for details&lt;/a&gt;), want to get in touch with the tech scene or are looking for skilled people for your project, visit us in our new office.&lt;/p&gt;

&lt;p&gt;I look forward to a great time with more great people and hope that our recently started &lt;a href=&quot;http://twitpic.com/p03jx&quot;&gt;community photo&lt;/a&gt; wall will grow fast with photos of well known and yet not so well known faces.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2009/11/awesome-presentations-with-boom-amazing/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2009/11/awesome-presentations-with-boom-amazing/"/>
    <title>Awesome presentations with boom amazing</title>
    <updated>2009-11-13T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;div class=&quot;wp-caption alignright&quot;&gt;&lt;img src=&quot;http://upstream-berlin.com/wp-content/uploads/2009/11/Screen-shot-2009-11-13-at-14.56.03--300x221.png&quot; alt=&quot;Screen shot 2009-11-13 at 14.56.03&quot; title=&quot;Screen shot 2009-11-13 at 14.56.03&quot; width=&quot;300&quot; height=&quot;221&quot; class=&quot;alignnone size-medium wp-image-698&quot; /&gt;&lt;br /&gt; Boom amazing in action at JSConf.EU&lt;/div&gt;

&lt;p&gt;Last weekend at &lt;a href=&quot;http://jsconf.eu&quot;&gt;JSConf.EU&lt;/a&gt; I gave another &lt;a href=&quot;http://jsconf.eu/2009/speaker/alexander_lang_writing_apps_on.html#entry-3395&quot;&gt;talk on CouchDB and CouchApps&lt;/a&gt;. This gave me an excellent excuse to hack on &lt;a href=&quot;https://github.com/langalex/boom_amazing&quot;&gt;boom amazing&lt;/a&gt; instead of preparing my talk. Boom amazing is my personal presentation tool. Instead of slides my presentation is laid out on a single large surface and I can pan around, zoom in and out and rotate my viewpoint to show specific contents.&lt;/p&gt;

&lt;p&gt;In the end I managed to add a bunch of new features and still get my talk done. The following post explains how boom amazing works and how you can create your own fancy presentations with it.&lt;/p&gt;

&lt;h3 id=&quot;what_is_behind_it&quot;&gt;What is behind it?&lt;/h3&gt;

&lt;p&gt;Boom amazing consists of a few relatively simple building blocks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One or more SVG files that contain all the texts and graphics&lt;/li&gt;
&lt;li&gt;a CouchApp that includes some HTML and CSS, and JavaScript&lt;/li&gt;
&lt;li&gt;some client side JavaScript to handle manipulating SVG and moving around (using JQuery and the &lt;a href=&quot;http://keith-wood.name/svg.html&quot;&gt;jQuery SVG plugin&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;CouchDB: stores all the data: the presentation itself (as document attachments), the CouchApp and all the camera positions and data for replaying presentations&lt;/li&gt;
&lt;li&gt;A browser that displays the SVG file, I use &lt;a href=&quot;http://barbariangroup.com/software/plainview&quot;&gt;PlainView&lt;/a&gt; which has a fullscreen mode.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;generating_svgs_on_osx8230&quot;&gt;Generating SVGs on OSX&amp;#8230;&lt;/h3&gt;

&lt;p&gt;&amp;#8230; pretty much sucks. I&amp;#8217;ve tried a few tools already but still haven&amp;#8217;t found anything that is simple and stable. I first tried Adobe Illustrator which can export to SVG, but first of all it&amp;#8217;s slow, it sometimes crashes and I&amp;#8217;m not willing to pay that much money just for drawing a few circles and write some text.&lt;/p&gt;

&lt;p&gt;My current setup consists of &lt;a href=&quot;http://www.tweakersoft.com/vectordesigner/&quot;&gt;Vector Designer&lt;/a&gt; for drawing and &lt;a href=&quot;http://www.scribus.net/&quot;&gt;Scribus&lt;/a&gt; for converting to SVG. While I do like working in Vector Designer very much it doesn&amp;#8217;t have an SVG exporter, it limits the maximum zoom and it has problems with the kerning when I set the font size below a certain level. I might be able to work around the zoom and kerning problems by simply using larger page sizes (this time I used 2x2 meters). I then export my drawing to the EPS format which works okay, except that the day before the conference Vector Designer suddenly created file sizes of 115MB instead of a few hundred KB like before. Which brings me to Scribus. It&amp;#8217;s an open source vector drawing app with a truly ugly user interface, but it can import EPS and export SVG. At least sometimes. For me it worked in ~40% of the cases. Everything else was crashes, unknown import errors and missing data.&lt;/p&gt;

&lt;p&gt;There&amp;#8217;s a command line tool called pdf2svg (which you can install using &lt;a href=&quot;http://macports.org&quot;&gt;MacPorts&lt;/a&gt;) which I also tried, but the resulting SVGs are much larger than what Scribus creates and the browser refused to display them.&lt;/p&gt;

&lt;p&gt;There is hope though, a one man company called &lt;a href=&quot;http://www.bohemiancoding.com/&quot;&gt;Bohemian Coding&lt;/a&gt; is working on a vector tool called &lt;a href=&quot;http://pieteromvlee.net/blog/?p=96&quot;&gt;Sketch&lt;/a&gt; and their previous apps look really slick.&lt;/p&gt;

&lt;p&gt;If you&amp;#8217;re not on OSX or can tolerate working with X11 apps there&amp;#8217;s also &lt;a href=&quot;http://www.inkscape.org/&quot;&gt;Inkscape&lt;/a&gt; which has built-in SVG support.&lt;/p&gt;

&lt;h3 id=&quot;using_it&quot;&gt;Using it&lt;/h3&gt;

&lt;p&gt;In order to use boom amazing all you have to do is &lt;a href=&quot;https://github.com/langalex/boom_amazing&quot;&gt;download it&lt;/a&gt; and push it into CouchDB.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;git clone git://github.com/langalex/boom_amazing.git
cd boom_amazing
curl -X PUT http://localhost:5984/boom_amazing
couchapp push boom_amazing
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;After that go to futon and upload your SVG file into any document in the &lt;em&gt;boom_amazing&lt;/em&gt; database. Then you go to &lt;em&gt;http://localhost:5984/boom_amazing/_design/boom_amazing/index.html&lt;/em&gt;. When you move your mouse to the top a toolbar appears where you can select your presentation from the dropdown. Once the file has loaded you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;pan around by click &amp;amp; dragging the mouse&lt;/li&gt;
&lt;li&gt;zoom by pressing &amp;amp; holding [alt] and moving the mouse up and down&lt;/li&gt;
&lt;li&gt;rotate by holding down [ctrl] and moving the mouse left and right&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whenever you like a position you can save that by clicking the &lt;em&gt;save&lt;/em&gt; button in the toolbar. After you have recorded all positions you can then jump back and forth between them with the &lt;em&gt;Previous&lt;/em&gt; and &lt;em&gt;Next&lt;/em&gt; buttons or by pressing [b] and [space] - which is what I do when presenting.&lt;/p&gt;

&lt;h3 id=&quot;hacking&quot;&gt;Hacking&lt;/h3&gt;

&lt;p&gt;If you are interested in extending the app here are the basics of how it works:&lt;/p&gt;

&lt;p&gt;In &lt;em&gt;_attachments/javascripts/app.js&lt;/em&gt; you find a small &lt;a href=&quot;http://code.quirkey.com/sammy/&quot;&gt;sammy&lt;/a&gt; application which controls all the actions like loading and saving data. Sammy maps the hash part of the URL to different actions, so when a link points to e.g. &lt;em&gt;#/slides/23&lt;/em&gt; sammy will automatically call the code to load slide (slide refers to a saved position) 23.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;screen.js&lt;/em&gt; includes all the logic that handles mouse and keyboard events to move around. It also does all the computations for things like smooth transitions between positions and rotating around the center of the screen instead of just (0, 0).&lt;/p&gt;

&lt;p&gt;The way this all works is that the jQuery SVG plugin loads the file and turns everything into DOM nodes. The application then takes all the nodes and puts them into a single group node. A group node can have an attribute &lt;em&gt;transforms&lt;/em&gt; which expects a rotate, scale and translate parameter - the rest is up to the browser.&lt;/p&gt;

&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;The feedback I have been getting for the talks where I have used boom amazing has been great so I am going to continue to use and extend it. Right now performance with larger SVGs is an issue but I don’t have any ideas wether I can do anything about it“ except for buying faster laptops. There are also still a bunch of basic features missing, like editing or removing saved positions, or reordering.&lt;/p&gt;

&lt;p&gt;I would love to see other people to pick up boom amazing and use it for their presentations. It certainly is more work than putting together a bunch of slides but then again I think it is worth the effort, and the results easily make any fancy Keynote presentation look like 1990. :D&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2009/10/unit-testing-couchdb-views-with-couch-potato/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2009/10/unit-testing-couchdb-views-with-couch-potato/"/>
    <title>Unit Testing CouchDB Views with Couch Potato</title>
    <updated>2009-10-30T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;I just released &lt;a href=&quot;https://github.com/langalex/couch_potato/tree/v0.2.14&quot;&gt;Couch Potato 0.2.14&lt;/a&gt; and amongst other things it has a new feature i think is pretty neat: you can unit test your (JavaScript) views using RSpec and Ruby.&lt;/p&gt;

&lt;p&gt;You can declare a view in Couch Potato like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Comment&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;property&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:post_id&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;view&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:by_post_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:post_id&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This will generate a pair of map/reduce function and push them to Couch Potato. The map function looks something like this:&lt;/p&gt;

&lt;p&gt;[javascript]
function(doc) {
  if(doc.ruby_class == ‘Comment’) {
    emit(doc.post_id, 1);
  }
}
[/javascript]&lt;/p&gt;

&lt;p&gt;And here’s a unit test for that function:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;describe&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Comment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'by_post_id'&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;it&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;quot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;should&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;the&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;post_id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;quot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Comment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;by_post_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;should&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ruby_class&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'Comment'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:post_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;As you can see all you have to do is pass a Ruby Hash that looks like your CouchDB document and the expected results. You can also pass in multiple results if you expect your map function to emit multiple key/value pairs.&lt;/p&gt;

&lt;p&gt;Testing a reduce function works the same way:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;describe&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Comment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'by_post_id'&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;it&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;quot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;should&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reduce&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;the&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;number&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;comments&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;quot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Comment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;by_post_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;should&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reduce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([],&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;For testing re-reducing you simply call &lt;code&gt;.should rereduce(...).to(...)&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;How it works&lt;/h3&gt;

&lt;p&gt;So how come you can test JavaScript functions in pure Ruby? Well, by stealing other people’s tricks. I recently contributed a few patches to &lt;a href=&quot;https://github.com/janl/mustache.js&quot;&gt;mustache.js&lt;/a&gt; which is a new templating library ported to JavaScript by &lt;a href=&quot;https://twitter.com/janl&quot;&gt;@janl&lt;/a&gt;. It has a test runner currently implemented in Ruby which generates JavaScript code on the fly, runs it using &lt;a href=&quot;http://www.mozilla.org/js/spidermonkey/&quot;&gt;Spidermonkey&lt;/a&gt; and reads back the results. I have added a few steps to this process:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;A custom RSpec matcher collects the map function, a Ruby Hash representing the input and the expected output&lt;/li&gt;
  &lt;li&gt;The input is converted to JavaScript using the JSON gem&lt;/li&gt;
  &lt;li&gt;JavaScript code is generated that runs the document through the map function and prints the resulting JSON.&lt;/li&gt;
  &lt;li&gt;The Ruby code runs spidermonkey, collects the output and parses it back to Ruby using again the JSON gem&lt;/li&gt;
  &lt;li&gt;The results are compared to the expected values.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can see how it works by looking at &lt;a href=&quot;https://github.com/langalex/couch_potato/blob/master/spec/unit/rspec_matchers_spec.rb&quot;&gt;the code for the RSpec matchers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I think this addition lowers the barrier to test your views quite a bit. Although I’m usually not a big fan of “one language to rule them all” and love writing JavaScript, being able to write all the necessary tests in Ruby when working on a Ruby project makes things way easier.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2009/10/testing-couchapps-with-cucumber-and-culerity/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2009/10/testing-couchapps-with-cucumber-and-culerity/"/>
    <title>Testing Couchapps with Cucumber and Culerity</title>
    <updated>2009-10-25T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;On last week’s RailsCamp UK I started hacking on a new &lt;a href=&quot;https://github.com/couchapp/couchapp&quot;&gt;CouchApp&lt;/a&gt; called HejHej. Its purpose is to help me learn Swedish, but what’s more important here: I wrote this app BDD style using &lt;a href=&quot;http://cukes.info/&quot;&gt;Cucumber&lt;/a&gt;, the famous BDD tool and &lt;a href=&quot;https://github.com/langalex/culerity&quot;&gt;Culerity&lt;/a&gt;, my humble addition that allows me to test any webapp (including client side JavaScript) with it.&lt;/p&gt;

&lt;p&gt;The whole thing is &lt;a href=&quot;https://github.com/langalex/hejhej&quot;&gt;available on Github&lt;/a&gt; so you can check out the features and steps there. In the following post I will show the necessary steps to test your own CouchApps with Cucumber.&lt;/p&gt;

&lt;p&gt;First of all you will need to install some software:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Java - most computers have it installed already (OSX does). If not go to &lt;a href=&quot;http://java.sun.com&quot;&gt;java.sun.com&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;JRuby - Culerity is written in Ruby but wraps a Java library called HTMLUnit, hence we need both. You can get JRuby at &lt;a href=&quot;http://jruby.org&quot;&gt;jruby.org&lt;/a&gt;. Just download the tar and extract it to any directory.&lt;/li&gt;
  &lt;li&gt;Culerity - comes as a RubyGem that you will have to install into JRuby. To install run &lt;code&gt;JRUBY_HOME/bin/jruby -S gem install culerity --source=http://gemcutter.org&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Cucumber - &lt;code&gt;jruby -S gem install cucumber&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;RestClient - helps to clean up the test database: &lt;code&gt;jruby -S gem install rest-client jruby-openssl&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Next change to your CouchApp’s directory and create the following directory structure:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
  CouchApp root
  |- features
     |- support
        |- env.rb
        |- paths.rb
     |- step_definitions
        |- common_culerity_steps.rb
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Copy and paste the following into your env.rb:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
  require 'rubygems'
  require 'culerity'

  require 'cucumber/formatter/unicode'

  require 'restclient'

  Before do
    RestClient.delete &quot;#{host}/#{database}&quot; rescue nil
    RestClient.put &quot;#{host}/#{database}&quot;, &quot;&quot;
    system &quot;couchapp push&quot;
  end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Warning: the before hook you see there will delete and recreate your CouchDB database before every run, so make sure you are using a separate database for testing than for your actual “production” database.&lt;/p&gt;

&lt;p&gt;This is what your common_culerity_steps.rb should look like:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
  require 'culerity'

  Symbol.class_eval do
    def to_proc
      Proc.new{|object| object.send(self)}
    end
  end unless :symbol.respond_to?(:to_proc)

  Before do
    $server ||= Culerity::run_server
    $browser = Culerity::RemoteBrowserProxy.new $server, {:browser =&amp;gt; :firefox, :javascript_exceptions =&amp;gt; true, :resynchronize =&amp;gt; true, :status_code_exceptions =&amp;gt; true}
    $browser.log_level = :warning
  end

  def host
    'http://localhost:5984'
  end

  def database
    'hejhej'
  end

  at_exit do
    $browser.exit if $browser
    $server.close if $server
  end

  When /I press &quot;(.*)&quot;/ do |button|
    button = [$browser.button(:text, button), $browser.button(:id, button)].find(&amp;amp;:exist?)
    button.click
    When 'I wait for the AJAX call to finish'
  end

  When /I click &quot;(.*)&quot;/ do |link|
    When &quot;I follow \&quot;#{link}\&quot;&quot;
  end

  When /I follow &quot;(.*)&quot;/ do |link|
    _link = [[:text, /^#{Regexp.escape(link)}$/], [:id, link], [:title, link]].map{|args| $browser.link(*args)}.find{|__link| __link.exist?}
    raise &quot;link \&quot;#{link}\&quot; not found&quot; unless _link
    _link.click
    When 'I wait for the AJAX call to finish'
  end

  When /I follow \/(.*)\// do |link|
    $browser.link(:text, /#{link}/).click
    When 'I wait for the AJAX call to finish'
  end

  When /I fill in &quot;(.*)&quot; with &quot;(.*)&quot;/ do |field, value|
    find_by_label_or_id(:text_field, field).set value
  end

  When /I attach &quot;(.*)&quot; to &quot;(.*)&quot;/ do |value, field|
    $browser.file_field(:id, find_label(field).for).set(value)
  end

  When /I check &quot;(.*)&quot;/ do |field|
    find_by_label_or_id(:check_box, field).set true
  end

  def find_by_label_or_id(element, field)
    begin
      $browser.send(element, :id, find_label(/#{field}/).for)
    rescue #Celerity::Exception::UnknownObjectException
      $browser.send element, :id, field
    end
  end

  When /^I uncheck &quot;(.*)&quot;$/ do |field|
    $browser.check_box(:id, find_label(field).for).set(false)
  end

  When /^I select &quot;([^&quot;]+)&quot; from &quot;([^&quot;]+)&quot;$/ do |value, field|
    find_by_label_or_id(:select_list, field).select value
  end

  When /^I select &quot;([^&quot;]+)&quot;$/ do |value|
    $browser.option(:text =&amp;gt; value).select
  end

  When /I choose &quot;(.*)&quot;/ do |field|
    $browser.radio(:id, find_label(field).for).set(true)
  end

  When /I go to the (.+)/ do |path|
    $browser.goto host + path_to(path)
  end

  When /I wait for the AJAX call to finish/ do
    sleep 0.4
  end

  When /^I visit &quot;([^&quot;]+)&quot;$/ do |url|
    $browser.goto host + url
  end

  Then /^I should see &quot;(.*)&quot;$/ do |text|
    Then &quot;I should see /#{Regexp.escape(text)}/&quot;
  end

  Then /^I should see \/(.*)\/$/ do |text|
    # if we simply check for the browser.html content
    # we don't find content that has been added dynamically,
    # e.g. after an ajax call
    div = $browser.div(:text, /#{text}/)
    begin
      div.html
    rescue
      #puts $browser.html
      raise(&quot;div with '#{text}' not found&quot;)
    end
  end


  Then /I should see the text &quot;(.*)&quot;/ do |text|
    $browser.html.should include(text)
  end

  Then /I should not see the text &quot;(.*)&quot;/ do |text|
    $browser.html.should_not include(text)
  end

  Then /I should not see &quot;(.*)&quot;/ do |text|
    div = $browser.div(:text, /#{text}/).html rescue nil
    div.should be_nil
  end

  Then /I should see no link &quot;([^&quot;]+)&quot;/ do |text|
    $browser.link(:text =&amp;gt; text).should_not exist
  end

  Then /I should not find the page &quot;([^&quot;]+)&quot;/ do |url|
    no_exception = false
    begin
      $browser.goto host + url
      no_exception = true
    rescue =&amp;gt; e
      e.message.should =~ /404/
    end
    no_exception.should be_false
  end

  Then /^&quot;([^\&quot;]*)&quot; should be chosen$/ do |field|
    find_by_label_or_id(:radio, field).should be_checked
  end

  Then /^&quot;([^\&quot;]*)&quot; should be checked$/ do |field|
    find_by_label_or_id(:check_box, field).should be_checked
  end

  Then /^&quot;([^\&quot;]*)&quot; should not be checked$/ do |field|
    find_by_label_or_id(:check_box, field).should_not be_checked
  end

  Then /^&quot;([^\&quot;]*)&quot; should be selected$/ do |selection|
    $browser.option(:text =&amp;gt; selection).should be_selected
  end

  When 'show response' do
    p $browser.url
    open_response_in_browser
  end


  def find_label(text)
    $browser.label :text, text
  end

  def open_response_in_browser
    tmp_file = '/tmp/culerity_results.html'
    FileUtils.rm_f tmp_file
    File.open(tmp_file, 'w') do |f|
      f &amp;lt; &amp;lt; $browser.div(:id, 'content').html
    end
    `open #{tmp_file}`
  end
&lt;/code&gt;&amp;lt;/code&amp;gt;&lt;/pre&gt;

&lt;p&gt;Make sure to change the database name (and host if necessary).&lt;/p&gt;

&lt;p&gt;Finally the &lt;code&gt;paths.rb&lt;/code&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
  module NavigationHelpers
    def path_to(page_name)
      case page_name
      when /start page/
        &quot;/#{database}/_design/#{database}/index.html&quot;
      else
        raise &quot;Can't find mapping from \&quot;#{page_name}\&quot; to a path.&quot;
      end
    end
  end

  World(NavigationHelpers)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now that you are done with the prerequisites you can write your first feature. Create a file &lt;code&gt;start_page.feature&lt;/code&gt; in the &lt;code&gt;features&lt;/code&gt; directory and paste the following:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
  Feature: start page
    In order to feel welcome
    As a user
    I want to be welcomed on the start page

    Scenario: go to the start page
      When I go to the start page
      Then I should see &quot;Welcome&quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now you can run your first feature:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;jruby -S cucumber features/start_page.feature&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Congratulations, you can now test drive your application. Explore the step definitions in the &lt;code&gt;common_culerity_steps.rb&lt;/code&gt; in order to learn how to traverse links (When I follow “link”), fill out forms etc.&lt;/p&gt;

&lt;p&gt;In order to make the feature pass you should add the text “Welcome” to the index.html of your CouchApp.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2009/10/devhouseberlin-2/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2009/10/devhouseberlin-2/"/>
    <title>DevHouseBerlin^2</title>
    <updated>2009-10-06T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Last weekend the second DevHouseBerlin took place. A public event and the perfect excuse for you to not be available for your non-geek-,  girl- and boyfriends during the weekend.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://upstream-berlin.com/wp-content/uploads/2009/10/IMG_6817-300x199.jpg&quot; alt=&quot;DevHouse^2&quot; title=&quot;DevHouse^2&quot; width=&quot;300&quot; height=&quot;199&quot; class=&quot;aligncenter size-medium wp-image-653&quot; /&gt;&lt;/p&gt;

&lt;p&gt;DevHouseBerlin stands in the tradition of the &lt;a href=&quot;http://superhappydevhouse.org/&quot;&gt;SuperHappyDevHouse&lt;/a&gt;. For one Weekend we opened up the office space from &lt;a href=&quot;http://finnlabs.de&quot;&gt;finnlabs&lt;/a&gt;, &lt;a href=&quot;http://rocket-rentals.de&quot;&gt;rocket rentals&lt;/a&gt; and us again to host everybody who is curious about technology in general and programming in particular, offering the opportunity to work on, show off or discuss whatever you like together. We provided music, drinks - especially &lt;a rel=&quot;lightbox&quot; href=&quot;http://upstream-berlin.com/wp-content/uploads/2009/10/IMG_6832.jpg&quot;&gt;Club Mate&lt;/a&gt; -, food - &lt;a href=&quot;http://spreeschnittchen.de&quot;&gt;super delicious deluxe sandwiches&lt;/a&gt; - and other basic infrastructure, although our supposedly capable new WiFi router failed miserably on serving 30+ devices.&lt;/p&gt;

&lt;p&gt;The 25 available seats were gone weeks before the actual event so we didn’t blog beforehand so that you wouldn’t be disappointed. But as we will move to a bigger office soon we will scale the next DevHouseBerlin too. So here are some highlights and results you missed.&lt;/p&gt;

&lt;p&gt;We had an international guest and hero with us, &lt;a href=&quot;https://twitter.com/pat&quot;&gt;Pat&lt;/a&gt;. During the last weeks he got the full blown Berlin tech culture while pairing with us, giving &lt;a href=&quot;http://www.slideshare.net/freelancing_god/sphinx-beyond-the-basics&quot;&gt;his sphinx talk&lt;/a&gt; at &lt;a href=&quot;http://www.rug-b.de&quot;&gt;rug-b&lt;/a&gt; (Ruby User Group Berlin) and joining the dev house, where he obviously had fun although he was constantly &lt;a rel=&quot;lightbox&quot; href=&quot;http://upstream-berlin.com/wp-content/uploads/2009/10/IMG_6865.jpg&quot;&gt;besieged by fans&lt;/a&gt; ;). He even managed to get some new features for thinking sphinx done (&lt;a href=&quot;http://gist.github.com/200609&quot;&gt;1&lt;/a&gt;, &lt;a href=&quot;https://github.com/freelancing-god/thinking-sphinx-raspell&quot;&gt;2&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;We also planned a &lt;a href=&quot;http://wiki.upstream-berlin.com/index.php/RealTimeBattle&quot;&gt;realtime battle&lt;/a&gt; challenge, a programming game, in which robots controlled by programs are fighting each other. But despite collaborative effort we couldn’t get it running for OS X so &lt;a href=&quot;https://twitter.com/langalex&quot;&gt;alex&lt;/a&gt;, &lt;a href=&quot;https://twitter.com/til&quot;&gt;till&lt;/a&gt;, &lt;a href=&quot;http://twiter.com/overbryd&quot;&gt;lukas&lt;/a&gt; and &lt;a href=&quot;https://twitter.com/pat&quot;&gt;pat&lt;/a&gt; took instead the challenge to re-implement it with ruby and javascript. A slightly too big endeavor for these two days with so many other things going on. See their progress and fork it on &lt;a href=&quot;https://github.com/langalex/realtimebattle_rb&quot;&gt;github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A major source of distraction was the &lt;a rel=&quot;lightbox&quot; href=&quot;http://upstream-berlin.com/wp-content/uploads/2009/10/IMG_6853.jpg&quot;&gt;Segway&lt;/a&gt; which &lt;a href=&quot;https://twitter.com/jodok&quot;&gt;@jodok&lt;/a&gt; parked in the office. Although you look totally dorky it is a lot of fun, even just riding down the corridor.&lt;/p&gt;

&lt;p&gt;Dispite the calamity on the corridor &lt;a href=&quot;https://twitter.com/klimpong&quot;&gt;Till&lt;/a&gt; and Christian proved that you can write web apps fast and well with PHP, they rewrote planet php, a php blog aggregator, in a couple of hours on the first evening.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://twitter.com/nedos&quot;&gt;Dmitry&lt;/a&gt; and &lt;a href=&quot;https://twitter.com/otype&quot;&gt;Hans-Gunther&lt;/a&gt; brought a 19” server rack to play around with the Xen Hypervisor, well a 19” rack is certainly the right stage for Xen. Hans-Gunther also saved our evening by providing back-up Internet through his G1 while our router was recovering.&lt;/p&gt;

&lt;p&gt;Something else which had to do with big scales was &lt;a href=&quot;http://lucene.apache.org/mahout/&quot;&gt;Mahout&lt;/a&gt;, a bunch of machine learning libraries built on &lt;a href=&quot;http://hadoop.apache.org/&quot;&gt;Hadoop&lt;/a&gt;, an open-source implementation of frameworks for reliable, scalable, distributed computing and data storage. Some really mind bending stuff if you ask me. Too bad I missed the presentation by &lt;a href=&quot;https://twitter.com/mainec&quot;&gt;MaineC&lt;/a&gt;. If you are still studying you might want to attend her &lt;a href=&quot;https://bird.cs.tu-berlin.de:4430/anmeldung/anmeldung/welcome&quot;&gt;Mahout course at the TU-Berlin&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You and I also missed the discussion about the best editor – emacs or vim – and - even worse - &lt;a href=&quot;http://couchdb.apache.org/&quot;&gt;CouchDB&lt;/a&gt; in 30 minutes. An Introduction to CouchDB, the schema-free document-oriented Database, given by &lt;a href=&quot;https://twitter.com/janl&quot;&gt;Jan&lt;/a&gt;, the european mister CouchDB himself. Of course he was around all the time during the DevHouse and gave help to get on track with CouchDB and related stuff.&lt;/p&gt;

&lt;p&gt;I could rave on and on how cool the second DevHouseBerlin was, but just take a look at the &lt;a href=&quot;http://devhouseberlin.de&quot;&gt;wiki&lt;/a&gt; page where you can find more projects, people and photos. So that even if you were not with us this time you might take something out of it.&lt;/p&gt;

&lt;p&gt;See you next time at DevHouseBerlin.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2009/09/ruby-metaprogramming-dynamically-inherited-methods/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2009/09/ruby-metaprogramming-dynamically-inherited-methods/"/>
    <title>Ruby Metaprogramming: Dynamically Inherited Methods&trade;</title>
    <updated>2009-09-19T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Yesterday I &lt;a href=&quot;https://github.com/langalex/couch_potato/commit/30e90b05abb461e1c4474fb12de2c6a4a6b4a88b&quot;&gt;added a new feature&lt;/a&gt; to &lt;a href=&quot;https://github.com/langalex/couch_potato&quot;&gt;Couch Potato&lt;/a&gt;, my persistence layer for &lt;a href=&quot;http://couchdb.apache.org&quot;&gt;CouchDB&lt;/a&gt; that I call &lt;em&gt;Dynamically Inherited Methods&amp;trade;&lt;/em&gt; and found the implementation I came up with interesting enough to share. Here&amp;#8217;s the problem:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class User

  def self.string_attr_reader(name)
    define_method name do
      instance_variable_get(&quot;@#{name}&quot;).to_s
    end
  end

  string_attr_reader :nickname
  attr_writer :nickname
end

user = User.new
user.nickname = 123
user.nickname # =&amp;gt; '123'
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This code adds a macro to the class user called &lt;em&gt;string_attr_reader&lt;/em&gt;. When you call this macro on the class and pass it a name it generates a method that returns the instance of the same name converted to a string. This all works perfectly until you want to override the generated method to add some behavior:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class User
  def nickname
    super.gsub('6', '7')
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The enhanced method now replaces all occurrences of 6 with a 7 - except it doesn&amp;#8217;t. When you run the above code you will get an exception where Ruby complains there is no super to call. That&amp;#8217;s because we defined the &lt;em&gt;nickname&lt;/em&gt; method in the &lt;em&gt;User&lt;/em&gt; class so we didn&amp;#8217;t inherit it, hence no super.&lt;/p&gt;

&lt;p&gt;The one way out of this is to use &lt;em&gt;alias_method&lt;/em&gt;, or &lt;em&gt;alias_method_chain&lt;/em&gt; when you are using Rails:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class User
  def nickname_with_six_replaced
    nickname_without_six_replaced.gsub('6', '7')
  end
  alias_method_chain :nickname, :six_replaced
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;First of all this isn&amp;#8217;t very beautiful anymore, and secondly using &lt;em&gt;alias_method_chain&lt;/em&gt; when you could use standard object oriented metaphors (like inheritance) doesn&amp;#8217;t make a lot of sense - except that you can call yourself a metaprogramming programmer, yay.&lt;/p&gt;

&lt;p&gt;Anyway, there is another way - actually involving much funkier meta programming - to solve the problem:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class User
  def self.string_attr_reader(name)
    accessor_module.module_eval do
      define_method name do
        instance_variable_get(&quot;@#{name}&quot;).to_s
      end
    end
  end

  private

  def self.accessor_module
    unless const_defined?('AccessorMethods')
      accessor_module = const_set('AccessorMethods', Module.new)
      include accessor_module
    end
    const_get('AccessorMethods')
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The updated code now dynamically creates a Module called &lt;em&gt;User::AccessorMethods&lt;/em&gt; and includes it into the &lt;em&gt;User&lt;/em&gt; class. All methods generated by &lt;em&gt;string_attr_reader&lt;/em&gt; are now added to that new module instead of the class. The result is that when overriding those methods you can now call super because they&amp;#8217;re are inherited from the new module.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class User
  attr_writer :nickname
  string_attr_reader :nickname

  def nickname
    super.gsub('6', '7')
  end
end

user = User.new
user.nickname = 678
user.nickname # =&amp;gt; '778'
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;While this code involves some fairly crazy metaprogramming which would be too much for a simple example like the above, I think libraries like CouchPotato can still benefit, as the application code can become cleaner by not having to do any metaprogramming but resort to standard object oriented ways of programming.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2009/08/upstream-goes-san-francisco/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2009/08/upstream-goes-san-francisco/"/>
    <title>Upstream goes San Francisco!</title>
    <updated>2009-08-31T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Today is our last day in the Berlin office before this year’s &lt;em&gt;remote office&lt;/em&gt;. After the first iteration last year, where we stayed in beautiful Oslo for a week, we have extended the time and distance for this year: 3 weeks of San Francisco.&lt;/p&gt;

&lt;p&gt;We’ll be working there (mostly) as usual during the days and then explore the city in the nights. For the first week we &lt;a href=&quot;http://www.airbnb.com/rooms/8567&quot;&gt;rented a beautiful apartment&lt;/a&gt; from where we can work and start exploring. We are still waiting for some other great place to come to us for the rest of our stay and we’ll also try out all the cool and hip coworking spaces. Apart from that we want to meet geeks and normal people, go to an apple store for the first time, maybe attend some user group events, meet with some CouchDB folks, drive across the bay … whatever comes up.&lt;/p&gt;

&lt;p&gt;If you want us to buy you a beer, have some spare desks for a few days in your office where we could work, any hints for places/events we have to visit or know someone who might rent out an apartment please let us know in the comments. You can also follow and contact &lt;a href=&quot;https://twitter.com/upstream_agile&quot;&gt;us&lt;/a&gt; &lt;a href=&quot;https://twitter.com/langalex&quot;&gt;on&lt;/a&gt; &lt;a href=&quot;https://twitter.com/freaklikeme&quot;&gt;twitter&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2009/07/kickstart-rspec-with-spork/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2009/07/kickstart-rspec-with-spork/"/>
    <title>Kickstart Rspec with Spork</title>
    <updated>2009-07-27T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;For doing Red/Green TDD it is essential to have fast running tests. But no matter how fast your tests are, there are always these seconds when you and your pair are sitting in front of the screen waiting that the test run actually starts. That is the time when Rails, your code, gems and plugins are loaded. In a rather small project, which I use as an example here, this cumulates into a start up time of around 5s before EACH test run. This becomes even longer when your project grows.&lt;/p&gt;

&lt;blockquote&gt;
.....

Finished in 0.127571 seconds

5 examples, 0 failures

real    0m6.456s
user    0m4.682s
sys     0m1.681s
&lt;/blockquote&gt;

&lt;p&gt;Although 5s, as in my example, aren’t such a long time, it sums up to a significant amount when doing Red/Green TDD. So everything that helps you to cut down test startup times, increases your productivity. AutoSpec or &lt;a href=&quot;https://github.com/pelle/rspactor/tree/master&quot;&gt;Rspactor&lt;/a&gt; approach this on the user site by running your test as soon as you change something. But they won’t cut down the technical implied start up time. Spec-server tried to approach the load time issue first by loading the Rails stack including the libraries of your environment upfront in an extra process. It uses Drb to get your test code to the pre-loaded testing process. Although the idea behind spec-server is the right one, it never worked for me in day to day usage. I came in situations in which tests failed or code wasn’t reloaded properly at all. But recently I discovered &lt;a href=&quot;http://wiki.github.com/timcharper/spork&quot;&gt;spork&lt;/a&gt; thanx to Rany (&lt;a href=&quot;https://twitter.com/purzelrakete&quot;&gt;@purzelrakete&lt;/a&gt;). Spork takes the idea from spec-server and learned from its mistakes. E.g. it has its own class loading mechanism that takes care of not preloading your models at startup, instead of relying on rails auto loading. Beside working more reliable, this is also an option for other frameworks but Rails. It just works! Running the same test code than before with spork (&lt;code&gt;spec paht_to_spec --drb&lt;/code&gt;), cuts the startup time into half.&lt;/p&gt;

&lt;blockquote&gt;
.....

Finished in 0.066628 seconds

5 examples, 0 failures

real    0m3.588s
user    0m0.469s
sys     0m0.108s

&lt;/blockquote&gt;

&lt;p&gt;When doing 200 test startups a day, a number which is not unrealistic, consider doing it Red/Green, this a 10 min time win :) You probably won’t need more than 10 min to set up spork.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Install spork&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;gem install spork&lt;/code&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Bootstrap your Rails app&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;
cd /path/to/project/root
spork --bootstrap
&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This command adds some instructions and code to spec/spec_helper.rb. Basically two blocks, one to tell which code to preload and one to tell which to reload on every test run. Code outside these blocks is run at both times.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Modify spec/spec_helper.rb&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Modify spec/spec.opts&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Add &lt;code&gt;--drb&lt;/code&gt; to &lt;code&gt;spec/spec.opts&lt;/code&gt; so that rake spec uses drb to communicate with spork. Set &lt;code&gt;TM_RSPEC_OPTS --drb&lt;/code&gt; in TextMate so that the rspec TextMate bundle uses drb as well.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Fire up spork for rspec&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;spork rspec&lt;/code&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Kickstart your RSpec Tests&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you find out that some of your code wasn’t reloaded properly, &lt;code&gt;spork -d&lt;/code&gt; shows you which classes are loaded and where from. There shouldn’t be any of your app/classes in it. If that’s the case, the reason for this can be either a eager plugin (e.g. older versions of thinking_sphinx) or you used your code in initializers or the environment. Put such code in spork blocks like these in your spec_helper for the testing environment, this will prevent wrong preloading.&lt;/p&gt;

&lt;p&gt;Here is an example how the spec_helper could look like:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'rubygems'&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'spork'&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;quot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;RAILS_ENV&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;quot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;quot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;quot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;Spork&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;prefork&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;expand_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dirname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;__FILE__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;quot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/../&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;quot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;)&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'spec'&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'spec/rails'&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'machinist/active_record'&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;Spork&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each_run&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'spec/blueprints'&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Oh and btw, you can use spork for Cucumber as well, just modify cucumbers env.rb (same as you did with your spec_helper) and then launch spork with &lt;code&gt;spork cucumber&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;And one last thing, as we are talking about faster test cycles, take a look at &lt;a href=&quot;https://github.com/grosser/parallel_specs/tree/master&quot;&gt;parallel _specs&lt;/a&gt;, to run your specs on multiple cores.&lt;/p&gt;

&lt;p&gt;Happy TDDing!&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2009/06/upstream-agile-project-checklist/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2009/06/upstream-agile-project-checklist/"/>
    <title>Upstream Agile Project Checklist (updated)</title>
    <updated>2009-06-04T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;&lt;strong&gt;&lt;em&gt;updated:&lt;/em&gt;&lt;/strong&gt; added sven’s point from the comments&lt;/p&gt;

&lt;p&gt;Explaining agile to customers is hard, sometimes more, sometimes less so. In order to improve communication and make it clear what to expect and what not to I came up with the following checklist of what agile projects are about:&lt;/p&gt;

&lt;p&gt;You DO get a working, maintainable, production ready, 100% quality piece of software in a fixed amount of time.&lt;/p&gt;

&lt;p&gt;You do get a first working, maintainable, production ready, 100% quality version of that software after a very short time (usually 1-2 weeks).&lt;/p&gt;

&lt;p&gt;You DO get to launch your software in time.&lt;/p&gt;

&lt;p&gt;We DO pair program all the time to ensure that quality and it doesn’t slow us down.&lt;/p&gt;

&lt;p&gt;We DO write automated tests to ensure that quality and it doesn’t slow us down.&lt;/p&gt;

&lt;p&gt;You DON’T necessarily get all the features you wanted after an iteration.&lt;/p&gt;

&lt;p&gt;That’s why it’s important that you DO prioritize what’s most important for you.&lt;/p&gt;

&lt;p&gt;We DO estimate how long each feature will take us to implement. But it’s only an estimate.&lt;/p&gt;

&lt;p&gt;We DON’T accept a functional specification you bring in as a basis for our work.&lt;/p&gt;

&lt;p&gt;We DON’T believe this would work. We DON’T believe it is possible to completely specify a software upfront with tolerable effort.&lt;/p&gt;

&lt;p&gt;We DO use your functional spec as a basis for a discussion where we will write down stories for each feature together with you. Those form the basis of our work.&lt;/p&gt;

&lt;p&gt;We DO embrace changing requirements by working in short iterations.&lt;/p&gt;

&lt;p&gt;We DO deliver successful software projects when working like this.&lt;/p&gt;

&lt;hr /&gt;
&lt;p&gt;Signature Customer&lt;/p&gt;

&lt;p&gt;Anything I forgot? The comments are open.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2009/05/conference-triathlon-euruko-ruby-on-os-x-railswaycon/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2009/05/conference-triathlon-euruko-ruby-on-os-x-railswaycon/"/>
    <title>Conference Triathlon - Euruko, Ruby on OS X, RailsWayCon</title>
    <updated>2009-05-28T00:00:00+00:00</updated>
    <author>
      <name>thilo</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Wow, this was the month of conferences. First we visited Barcelona for the &lt;a href=&quot;http://www.euruko2009.org/&quot;&gt;Euruko&lt;/a&gt;: a great conference taking place every year in a different city. We attended it the 3rd time. The talks ranged from practical like “Cooking with &lt;a href=&quot;http://wiki.opscode.com/display/chef/Home&quot;&gt;Chef&lt;/a&gt;”, over entertaining like “Fun with Ruby (and without R***s), program your own games with &lt;a href=&quot;http://www.libgosu.org/&quot;&gt;Gosu&lt;/a&gt;” to just geeky like the lightning talk about “&lt;a href=&quot;https://github.com/dira/vimmish/tree/master&quot;&gt;Vimmish&lt;/a&gt; and how much fun gramma parsers can be”. I really liked the 2 days 1 track format and the people I met there. And you can be sure that we will be in &lt;a href=&quot;http://www.euruko2010.org/&quot;&gt;Krakow next year&lt;/a&gt; too.&lt;/p&gt;

&lt;p&gt;5 days later I visited Amsterdam for Ruby on OS X, which I already &lt;a href=&quot;http://upstream-berlin.com/2009/05/18/notes-from-the-ruby-on-osx-conference/&quot;&gt;blogged about&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And a little more than a week later, until yesterday, four of us attended the &lt;a href=&quot;http://it-republik.de/konferenzen/railswaycon/&quot;&gt;RailsWayCon&lt;/a&gt; here in Berlin, which tries to fill the gab that the RailsConf Europe left. The first day was reserved for whole day tutorial sessions.&lt;/p&gt;

&lt;p&gt;The second day offered a lot of advanced topics to choose from in 3 tracks. I chose to hear more about &lt;a href=&quot;http://www.paperplanes.de/2009/5/27/railswaycon_slides.html&quot;&gt;Asynchronous Processing&lt;/a&gt; from Mathias Meyer and the nitty gritty details about Events from Lourens NaudÃ©. The keynote about the Present and Future of Programming Languages by Ola Bini was also very interesting.&lt;/p&gt;

&lt;p&gt;Upstream also took an active part at the conference: Alex gave his talk about &lt;a href=&quot;http://langalex.couch.io/boom_amazing/_design/boom_amazing/index.html?svg=../../presentation/intro.svg#/slides/1&quot;&gt;CouchDB Frameworks for Ruby and CouchApp&lt;/a&gt; (using his new presentation tool boom_amazing) and I introduced &lt;a href=&quot;http://www.slideshare.net/freaklikeme/macruby-hotcocoa&quot;&gt;MacRuby&lt;/a&gt;, the Ruby that plays nice with Objective-C.&lt;/p&gt;

&lt;p&gt;On the third day Yehuda Katz revealed some more details about Rails 3. The talk by Michael Koziarski about Rails Performance was good for a reality check. In the afternoon everybody was tired after 3 days of conference and the talks lost quality. Still a very good conference with potential.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2009/05/notes-from-the-ruby-on-osx-conference/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2009/05/notes-from-the-ruby-on-osx-conference/"/>
    <title>Notes  from the  Ruby on OS X conference</title>
    <updated>2009-05-18T00:00:00+00:00</updated>
    <author>
      <name>thilo</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;&lt;em&gt;Edit: I updated some infos, to address things Eloy pointed out in the comment.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I had the opportunity to be at the &lt;a href=&quot;http://rubyonosx.com/&quot;&gt;Ruby on OS X&lt;/a&gt; conference in Amsterdam. As the name suggests, it was a conference focused on the state of Ruby on the OS X Platform. The talks generally gave a good background about the current status and where Ruby on OS X is going. All talks will be available in video format, &lt;a href=&quot;https://twitter.com/fngtps/status/1746100179&quot;&gt;according to fingertips&lt;/a&gt;. In the meantime, here are my conference notes for a quick overview. Though they vary very much in detail, depending on my personal interest and my ability to focus after getting to Amsterdam during the night before the conference.&lt;/p&gt;

&lt;p&gt;The first talk was about &lt;a href=&quot;http://limechat.net/&quot;&gt;LimeChat&lt;/a&gt; an IRC chat program by Satoshi Nakagawa written in RubyCocoa. Some LimeChat highlights are the compact interface, its unobtrusive operation, the always-accessible server list, the build in online paste service support and the customizable themes. Satoshi pointed out some edges between RubyCocoa and Objective-C. He mentioned necessary conversion between NSString and RubyString, because they aren’t comparable, the required NSMutableString for Unicode Characters and that the creation of Ruby threads causes interruption of the application loop. Further, he reported that the usage of ruby socket lib or net/http causes hang-ups, thus he used the Objective-C equivalent libs. On the other hand he pointed out that performance was good enough, memory usage and leaks weren’t an issue either, even during longer operation. Determining factor for memory usage was the complexity of the DOM tree in the WebView backed chat window. So no major problem here. LimeChat is currently being ported to MacRuby.&lt;/p&gt;

&lt;p&gt;The second talk was held by Rich Kilmer who presented the state of HotCocoa and &lt;a href=&quot;http://www.macruby.org/&quot;&gt;MacRuby&lt;/a&gt;. I &lt;a href=&quot;http://rubyconf2008.confreaks.com/os-x-application-development-with-hotcocoa.html&quot;&gt;watched&lt;/a&gt; and &lt;a href=&quot;http://www.slideshare.net/mattetti/macruby-hotcocoa-presentation-by-rich-kilmer&quot;&gt;read&lt;/a&gt; similar talks by him online already, so I only wrote down some interesting bits. In the beginning he gave some insight into the relation between Ruby and Apple. For example ruby gems were changed for Apple in a way that is allow to install certain gems in an extra repository where they are protected from user access. Apple will ship the next OS X version with Ruby 1.8.7. He also stated very clearly, that &lt;a href=&quot;http://www.rubycocoa.com/an-introduction-to-rubycocoa&quot;&gt;RubyCocoa&lt;/a&gt; will still be supported by Apple in the future. Which makes not much sense to me, because since the appearance of MacRuby the &lt;a href=&quot;http://sourceforge.net/project/stats/detail.php?group_id=44114&amp;amp;ugn=rubycocoa&amp;amp;type=svn&amp;amp;mode=year&amp;amp;year=2008&quot;&gt;development is effectively come to an halt&lt;/a&gt;. But, as Eloy stated in the comment, Apple promised to support it into the future, because developer have put lot of effort into applications based on RubyCocoa.&lt;/p&gt;

&lt;p&gt;Then Rich introduced MacRuby. Laurent stated during this part of the presentation, that although memory allocation is a little slower in MacRuby, Objects don’t need more Memory than their pure Objective-C counterpart. After outlining the improvements of MacRuby, Rich came to his Cocoa-mapping layer &lt;a href=&quot;http://www.macruby.org/trac/wiki/HotCocoa&quot;&gt;HotCocoa&lt;/a&gt;. HotCocoa tries to provide sensible defaults for most Cocoa GUI Objects. It also provide short names for important, mostly rather long named Objective-C Constants. All the mapping files take care of themselves to include their corresponding framework. He than explains HotCocoas delegation feature, which allows you to provide delegate methods by defining its logic in a block. A documentation for all these mappings, which can be viewed only in &lt;code&gt;lib/ruby/1.8/hotcocoa/mappings&lt;/code&gt; until now, will be available soon.&lt;/p&gt;

&lt;p&gt;Further, he explained the HotCocoa command line tool. Which, like the Rails script command, generates some infrastructure to build and deploy MacRuby apps from the command line. &lt;del datetime=&quot;2009-05-18T20:13:36+00:00&quot;&gt;This infrastructure will be extended soon by providing a more MVC like directory structure. Another addition will be the support of a virtual file system which allow to ship self contained applications and hide the source code.&lt;/del&gt; At the end of his talk he mentioned the possibility to bring MacRuby code to other platforms with the use of a ahead of time compilation.&lt;/p&gt;

&lt;p&gt;Without further ado the talk about &lt;a href=&quot;http://rucola.rubyforge.org&quot;&gt;Rucola&lt;/a&gt; by Eloy Duran followed. Rucola is a CLI focused MVC framework for RubyCocoa. It provides rake tasks for compiling, testing, bundling and update feed setup. Like known from Rails, it supports multiple environments. For the ease of development it offers generators for various purposes e.g. for test stubs. It also provides libs to help with testing. Rucola will also be available in MacRuby.&lt;/p&gt;

&lt;p&gt;After the lunch-break &lt;a href=&quot;http://www.johnmacshea.org/&quot;&gt;John Shea&lt;/a&gt; talked about his experience with MacRuby and game programming. He just needed 2 hours to implement Tetris in 2D with NSView. Doing this in OpenGL was a little harder. He showed different other games made by him which use OpenGL and were implemented mainly with MacRuby (99%). OpenGL can be used through CoreGraphics Interface with MacRuby. That way, he achived to display 100K polygons including textures with 58.1 Frames. Also some nice particle effects implemented with MacRuby where shown. As limiting factor when using MacRuby he pinpointed the garbage collection and the method dispatch performance. Latter should be fixed with MacRuby 0.5 as Laurent stated later.&lt;/p&gt;

&lt;p&gt;The following talk by Manfred Stienstra was about testing Cocoa Apps with the Rucola framework. As a testing framework it uses test spec together with mocha for mocking.&lt;/p&gt;

&lt;p&gt;One major point of the talk was how to test controllers. Rucola provides methods to generate fake outlets to test the controller against. But this didn’t work with Objective-C controller. Thus as another mean a small compiler in Rucola can be used to compile and return a controller from a nib file. Working fake outlets for Objective-C Controllers will be supported as expected on MacRuby though.&lt;/p&gt;

&lt;p&gt;Sometimes an existing application loop is required. Manfred suggested to setup and run this loop in a separate thread in the background while testing. Also the issues of UI testing came up. A practical approach was to load the nib file and inspect the content.&lt;/p&gt;

&lt;p&gt;A less technical, but still not less interesting talk about shipping OS X desktop applications with an interpreted language was given by Koen Bok from &lt;a href=&quot;http://www.madebysofa.com/&quot;&gt;Sofa&lt;/a&gt;. At first he talked about shipping the source with the product. In his experience it is better to face the legal and commercial risk by protecting the code by law than invest time and money in obfuscation. So you will be legal prepared, if problems arise, which didn’t happen so far to Sofa. The second part was targeted specifically at people who came from web development to the Mac. You should be prepared to be still a second grade citizen on the OS X platform. As Ruby people we should use our advantages of mature frameworks and tools we know from our work with e.g. rails. Third, the user experience is a major factor on OS X with a higher baseline than web applications. Thus more work is required e.g. for Drag and Drop behavior or keyboard shortcuts. Also snappiness is an important factor for the user experience at OS X. So optimizing speed/snappiness is an optimization of the user experience. The iPhone is a good example to learn from. As for the general interface design it is a good idea to have a designer in the team, or as Koen stated it. Get a Designer!&lt;/p&gt;

&lt;p&gt;The last official talk was about &lt;a href=&quot;http://www.libgosu.org/&quot;&gt;Gosu&lt;/a&gt;, an easy-to-use game development library and &lt;a href=&quot;https://github.com/FooBarWidget/rubystein/tree/master&quot;&gt;Rubenstein&lt;/a&gt;, a satirical re-implementation of Wolfenstein in Ruby with the help of Gosu. Julian Raschke, the creator of Gosu and Ninh Bui from phusion and the author of Rubenstein, held the talk. First Ninh explained some theory about &lt;a href=&quot;http://en.wikipedia.org/wiki/Raycasting&quot;&gt;Raycasting&lt;/a&gt; for rendering a 3D game. Then he looked at some optimizations, like splitting the space in discreet elements of pixels to speed up the Raycasting. At last he explained how he used Gosu for texturing in Rubenstein.&lt;/p&gt;

&lt;p&gt;Then Julian got into detail about Gosu. The library consists only of 9 classes and has a really easy to use API. It is around for 3 years already, and the main focus stayed on the easy usability. It is available for Mac, Linux and Windows (&lt;code&gt;gem install gosu&lt;/code&gt;). Julian showed some games created with Gosu and talked how it helps to interact with other frameworks, e.g. open-gl for 2D/3D effects or &lt;a href=&quot;http://wiki.slembcke.net/main/published/Chipmunk&quot;&gt;Chipmunk&lt;/a&gt; for physics simulation. At last both demoed Rubenstein, hilarious. That marked the official end of ruby on OS X.&lt;/p&gt;

&lt;p&gt;At the Hackfest after lunch Laurent Sansonetti, the brain behind MacRuby, showed some internals about the currently experimental 0.5 branch, which is based on &lt;a href=&quot;http://llvm.org/&quot;&gt;LLVM&lt;/a&gt;. He talked about its current state, which mile stones they reached already to make MacRuby 0.5 installable, and showed some dark corners of ruby code, which make the job so hard.&lt;/p&gt;

&lt;p&gt;I also got a glimpse on a new iPhone game from Julian made with Gosu, which looked pretty good. But before you get too excited about the thought of ruby on the iPhone, it was made with C++ version of Gosu. The Game will be officially released soon.&lt;/p&gt;

&lt;p&gt;Later I joined Cristi, Eloy, Julian, Laurent and others for a beer and a night out in Amsterdam till I caught my train back to Berlin early in the morning.&lt;/p&gt;

&lt;p&gt;Ruby on OS X was totally worth the effort to get there spontaneously. This rather long day I learned a lot about Cocoa and MacRuby, met very nice people, as on every ruby conference so far, and got an impression of Amsterdam that made me want to visit the city again, but than with more time.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2009/05/new-couch-potato/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2009/05/new-couch-potato/"/>
    <title>New Couch Potato: simple, testable, opinionated.</title>
    <updated>2009-05-17T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;After my &lt;a href=&quot;http://www.slideshare.net/langalex/ruby-sittin-on-the-couch&quot;&gt;talk about Ruby CouchDB frameworks at Scotland on Rails&lt;/a&gt; where I dismissed a few of of the libraries available (including my own &lt;a href=&quot;https://github.com/langalex/couch_potato/blob/030d5d7118330ef08559faade8492361a0765b27/README.md&quot;&gt;Couhch Potato&lt;/a&gt;) as not fitting the CouchDB way of doing things, I have been hacking away the past few weeks working on a complete overhaul of Couch Potato.&lt;/p&gt;

&lt;p&gt;As a first result I have just released version 0.2 of the framework. Its new goals are simplicity, embracing the CouchDB semantics and testability. In order to achieve this I had to introduce some major changes:&lt;/p&gt;

&lt;p&gt;I disconnected models from the database - there are no more save/get/find methods in the models. Instead you can hand the models to a database object that will save/load documents for you - this allows for unit tests that are completely disconnected from the database&lt;/p&gt;

&lt;p&gt;I have dropped associations and thrown away all the ActiveRecord like view creation/querying, replacing it with a new, more CouchDB like system. That new system is lighter, simpler and easier to extend.&lt;/p&gt;

&lt;p&gt;The following paragraphs will show you how to work with the new Couch Potato.&lt;/p&gt;

&lt;h3 id=&quot;saving_loading_models&quot;&gt;Saving / loading models&lt;/h3&gt;

&lt;p&gt;As I said I have decoupled the models from the database, a model doesn&amp;#8217;t have permanent access to the database anymore. Instead you instantiate a database object yourself and tell it to load or save a model object. This change isn&amp;#8217;t so much about CouchDB as it is about testability. Having the database separated means you can now have true unit test of your models  without talking to the database. Here is how you save/load models:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Book&lt;/span&gt;
  &lt;span class=&quot;kp&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;CouchPotato&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Persistence&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;property&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:title&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;CouchPotato&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;database_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'my_db'&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# in Rails this is done for you
&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;db&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;CouchPotato&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;database&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;book&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:title&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'The Passionate Programmer'&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# good book
&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;save!&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;book&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# saves the book or raises an exception
&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;load&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;_id&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# the original book&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The database is responsible for running the model&amp;#8217;s validations and lifecycle callbacks, saving the document to the database and afterwards setting the _id and _rev on the model. This way a lot of the persistence related logic is removed from the models making them more lightweight and most importantly easier to test (see below).&lt;/p&gt;

&lt;h3 id=&quot;new_views&quot;&gt;New Views&lt;/h3&gt;

&lt;p&gt;Overhauling of the views had two main goals:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;provide a simple and extensible way for saving/querying views that works the way CouchDB works&lt;/li&gt;
&lt;li&gt;since models don&amp;#8217;t have access to the database anymore, find a new way to query them&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is how you create and query a simple view:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Book&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;view&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:by_title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:title&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;property&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:title&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

 &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;CouchPotato&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;database&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;view&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;by_title&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# no parametters
&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;view&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;by_title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'The Passionate Programmer'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:descending&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# just use any of the parameters CouchDB accepts&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The way views work is now essentially reversed (inversion of control, rings a bell?). Instead of the view method calling the database the new view method creates a specification for a CouchDB view. The view is passed to the actual database in order to query CouchDB. Again this makes testing easier (see next section) but it also gives Couch Potato a clean interface in order to make creating views easier through abstractions. There is now a hierarchy of view specification classes that you can use to more easily create and query views. The above example used the CouchPotato::View::ModelViewSpec which is the default. This spec creates a view whose map function emits one (or an array of) attribute of the model and returns full documents.&lt;/p&gt;

&lt;p&gt;Other view specs let you create more customized views, for example the RawViewSpec, which lets you define your own map/reduce functions and return the raw CouchDB results hash.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Book&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;view&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:title_length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:raw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:map&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;function(doc) emit(doc.title.length, null)}&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;property&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:title&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;view&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;title_length&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# returns something like {:rows =&amp;amp;gt; [{:key =&amp;amp;gt; 25}]}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;For more examples see the &lt;a href=&quot;http://langalex.github.com/couch_potato/rdoc/classes/CouchPotato/View.html&quot;&gt;Documentation in the CouchPotato::View::*ViewSpec classes&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;testing&quot;&gt;Testing&lt;/h3&gt;

&lt;p&gt;As I have mentioned repeatedly decoupling the database from the models makes testing easier. With the new Couch Potato you can unit test your models without hitting the database once. First of all that makes your test lightning fast, and secondly it allows you to more easily test for example your lifecycle callbacks because you can call them directly passing them a stub or mock database.&lt;/p&gt;

&lt;p&gt;Here&amp;#8217;s an example: (with RSpec)&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Book&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;property&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:title&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;property&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:slug&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;describe&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'create'&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;it&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'should generate a slug'&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;book&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:title&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'The Passionate Programmer'&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;run_callbacks&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:before_create&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;slug&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;should&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'the-passionate-programmer'&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;If you don&amp;#8217;t like calling the run_callbacks method you can also use the actual database object but without a real connection to CouchDB. Couch Potato is based on the excellent CouchRest - a more low level CouchDB framework. The CouchPotato::Database constructor expects an instance of a CouchRest database which we can replace with a stub:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;describe&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'create'&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;it&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'should generate a slug'&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;book&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:title&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'The Passionate Programmer'&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;CouchPotato&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Database&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stub&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'couchrest database'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:save_doc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{})&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;save&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;book&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;slug&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;should&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'the-passionate-programmer'&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;For more details and examples please see the README, the RDocs and watch this blog. If you want to know all about Couch Potato I encourage you to read through its source code and specs - it&amp;#8217;s not that much code actually. Note that all this is still work in progress and time will show how well all of this works. I would be happy to hear your feedback.&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2009/04/installing-yum-on-centos-53/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2009/04/installing-yum-on-centos-53/"/>
    <title>Installing yum on CentOS 5.3</title>
    <updated>2009-04-30T00:00:00+00:00</updated>
    <author>
      <name>thilo</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;We recently deployed an app on a VM with CentOS 5.3. To our delight, the packet manager yum wasn’t pre-installed. We spent some time to get it running. The Howto on &lt;a href=&quot;http://eric.lubow.org/2008/misc/adding-yum-to-centos-5/&quot;&gt;http://eric.lubow.org/2008/misc/adding-yum-to-centos-5/&lt;/a&gt; was a great help, but it was for CentOS 5.2. So this is an update to Eric’s blogpost.&lt;/p&gt;

&lt;p&gt;You need to have rpm and wget already installed. Create a temporary directory, paste the following code in a file in that directory, make it executable and run it:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;
mkdir temp &amp;amp;&amp;amp; cd temp
vim yumdownload
&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;
#!/bin/bash
&lt;/code&gt;
&lt;code&gt;
for file in \
elfutils-0.137-3.el5.i386.rpm \
elfutils-libs-0.137-3.el5.i386.rpm \
elfutils-libelf-0.137-3.el5.i386.rpm \
expat-1.95.8-8.2.1.i386.rpm \
gmp-4.1.4-10.el5.i386.rpm \
libxml2-2.6.26-2.1.2.7.i386.rpm \
libxml2-python-2.6.26-2.1.2.7.i386.rpm \
m2crypto-0.16-6.el5.3.i386.rpm \
python-2.4.3-24.el5.i386.rpm \
python-elementtree-1.2.6-5.i386.rpm \
python-iniparse-0.2.3-4.el5.noarch.rpm \
python-sqlite-1.1.7-1.2.1.i386.rpm \
python-urlgrabber-3.1.0-5.el5.noarch.rpm \
readline-5.1-1.1.i386.rpm \
rpm-4.4.2.3-9.el5.i386.rpm \
rpm-libs-4.4.2.3-9.el5.i386.rpm \
rpm-python-4.4.2.3-9.el5.i386.rpm \
sqlite-3.3.6-2.i386.rpm \
yum-3.2.19-18.el5.centos.noarch.rpm \
yum-fastestmirror-1.1.16-13.el5.centos.noarch.rpm \
yum-metadata-parser-1.1.2-2.el5.i386.rpm
do wget http://mirror.centos.org/centos-5/5.3/os/i386/CentOS/$file;
done
&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;
chmod a+x yumdownload
./yumdownload
&lt;/code&gt;
This will get you all rpms you might need before be able to install yum.&lt;/p&gt;

&lt;p&gt;Now try installing them all by typing:
&lt;code&gt;
rpm -Uvh *.rpm
&lt;/code&gt;
This might not work, at least not for us. So try to install yum directly with &lt;code&gt; rpm -Uvh yum-3.2.19-18.el5.centos.noarch.rpm&lt;/code&gt; and let the errors guide you. When you run into a circular dependencies, install the rpm by ignoring the dependencies. eg.
&lt;code&gt;
rpm -Uvh --nodeps yum-fastestmirror*
&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This should finally allow you to install yum. Good Luck. ;)&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2009/03/the-case-of-activerecord-vs-couchdb/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2009/03/the-case-of-activerecord-vs-couchdb/"/>
    <title>The case of ActiveRecord vs. CouchDB</title>
    <updated>2009-03-31T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;After my &lt;em&gt;Ruby sittin’ on the Couch&lt;/em&gt; talk about Ruby Frameworks and CouchDB at &lt;a href=&quot;http://scotlandonrails.com/&quot;&gt;Scotland on Rails&lt;/a&gt; last week a bit of a debate started. In the talk I did compare the available Ruby Frameworks and discussed how well they fit the CouchDB way of doing things. In my conclusion I recommended using either CouchRest or RelaxDB for development and at the same time urged people not to use one of the ActiveRecord like libraries like CouchFoo.&lt;/p&gt;

&lt;p&gt;As it seems to me the intention of my talk hasn’t reached all of its audience yet I’ll try to make my point again using this blog post.&lt;/p&gt;

&lt;p&gt;The frameworks I looked at could be divided into two classes: the ones using CouchDB semantics (e.g. CouchRest/RelaxDB) and the ones trying to provide an ActiveRecord like interface for the applications (e.g. CouchFoo). The reasons I recommended not to use ActiveRecord semantics are:&lt;/p&gt;

&lt;h3&gt;CouchDB is not about relations&lt;/h3&gt;

&lt;p&gt;In ActiveRecord we have to model our domain models so they fit into a relational schema. That means flat tables and relationships between two tables, through a third and fourth table etc. To get results from the database we usually join a few tables and get back the resulting rows, nicely converted into Ruby objects for us. That is (sort of) fine for a relational database because all it has are those tables but it doesn’t work at all with CouchDB.&lt;/p&gt;

&lt;h3&gt;To use CouchDB to its full potential you need to understand and use its views&lt;/h3&gt;

&lt;p&gt;CouchDB doesn’t have a concept of tables at all. And the way you pull your data from CouchDB is fundamentally different. Instead of joining data from different tables via an SQL query you procedurally build up an index of data by providing the map and reduce functions which you then query.&lt;/p&gt;

&lt;p&gt;In order to fully use CouchDB you have to write custom map/reduce functions and abstracting that into an API that was designed for generating SQL queries doesn’t allow you that.&lt;/p&gt;

&lt;p&gt;I could go into more details but that really is my main point. I know from my experience that changing the wiring in your head after too many years of ActiveRecord is hard so I don’t expect anyone to immediately agree with me but I do believe that the only viable way of using CouchDB is through the interface that was designed for it and not by an abstractions that just happened to be there first.&lt;/p&gt;

&lt;p&gt;I’m looking forward to a lively discussion, either in the comments or on other blogs.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2009/03/the-upstream-rails-application-template/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2009/03/the-upstream-rails-application-template/"/>
    <title>The upstream Rails Application Template</title>
    <updated>2009-03-06T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Ruby on Rails 2.3 is almost there and brings a bunch of pretty cool new features. One of them is called templates and it allows you to customize the bootstrapping of new applications in order to automate the initial setup of your new app and hence get up to speed faster.&lt;/p&gt;

&lt;p&gt;I find the naming a bit confusing: since we already have view templates in Rails I will refer to this new feature as application templates from now on.&lt;/p&gt;

&lt;p&gt;So what does an application template do? Whenever you start a new rails application you are already using the Rails default template by issuing the &lt;code&gt;rails&lt;/code&gt; command. It creates the usual app, config etc. folders and generates a bunch of scripts and configuration files for you. Application templates simply extend this process by allowing you to add your own setup steps. This is done by writing your own template file (or grabbing someone else’s).&lt;/p&gt;

&lt;p&gt;Pratik Naik has already written &lt;a href=&quot;http://m.onkey.org/2008/12/4/rails-templates&quot;&gt;an excellent tutorial&lt;/a&gt; on this so I’m not going to repeat what’s already written. Basically Rails offers you a few convenience methods for adding for example routes, plugins, gems or files to your new application.&lt;/p&gt;

&lt;p&gt;So this blog post is more about showing off my very own template that I wrote today. (For the impatient: &lt;a href=&quot;http://gist.github.com/75038&quot;&gt;here it is&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Of course I’m not the first and only one who has written such a template so before I started I found &lt;a href=&quot;https://github.com/jeremymcanally/rails-templates/tree/master&quot;&gt;this collection of templates&lt;/a&gt; which I immediately started to &lt;del&gt;steal&lt;/del&gt; borrow from. As you probably know every programmer has its own style, uses his own unique set of tools in his (or her) own way. Which is why I found all of the templates I saw didn’t really fit my very own needs. The ones I found were pretty basic (to me) so when I rolled my own I took what I needed from what was there and added a whole bunch of my own stuff:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;setup of configuration files for Rspec, Cucumber and &lt;a&gt;Culerity&lt;/a&gt; (Culerity is my plugin for driving Celerity - think Selenium but withour having to use a real browser -  with Cucumber)&lt;/li&gt;
  &lt;li&gt;a working user registration/login process using authlogic, including all controllers and views&lt;/li&gt;
  &lt;li&gt;an XHTML application layout with jQuery (+ a few plugins) and blueprint CSS set up&lt;/li&gt;
  &lt;li&gt;a simple capistrano deployment script&lt;/li&gt;
  &lt;li&gt;German localization for all the built-in helpers&lt;/li&gt;
  &lt;li&gt;a Thinking Sphinx configuration file&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I pretty much need all of the above for 99% of my Rails projects and it costs me at least a day every time to set this all up. Well, not anymore :) Now I can run &lt;code&gt;rails my_new_app -m http://gist.github.com/75038&lt;/code&gt; and immediately after that start working on the distinct features.&lt;/p&gt;

&lt;p&gt;I’m already planning to add a few extra. First of all the user authentication needs cucumber features so I don’t break anything when extending that. I also want to add configuration and deployment for a staging server. And maybe make the whole script a bit more configurable, i.e. install thinking sphinx or not etc.&lt;/p&gt;

&lt;p&gt;The script is already pretty large so I’m not sure how much more I would want to put into it. Since you can apply templates to exisitng apps it will probably make sense to split the whole thing up at some point, so I could put the authentication part into its own template.&lt;/p&gt;

&lt;p&gt;If you want to get started with application templates now I suggest you simply start with mine and/or the others and grab whatever you need. Just make sure you re-publish what you added so we can all build upon everyone’s work. Thanks.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2009/02/testing-pdfs-with-cucumber-and-rails/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2009/02/testing-pdfs-with-cucumber-and-rails/"/>
    <title>Testing PDFs with Cucumber and Rails</title>
    <updated>2009-02-14T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;On a recent Rails project we have been working a lot with PDF documents. The application generates PDF invoices and account statements, places markers and comments into existing documents and also merges multiple single page PDFs into one. We do all this with a few PDF tools:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;the &lt;a href=&quot;http://ruby-pdf.rubyforge.org/pdf-writer/&quot;&gt;PDF::Writer&lt;/a&gt; library for Ruby to generate PDFs&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.pdfhacks.com/pdftk/&quot;&gt;pdftk&lt;/a&gt; - to merge multiple PDFs into one or overlay them&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.foolabs.com/xpdf/&quot;&gt;pdftotext&lt;/a&gt; part of the xpdf library, to extract texts from PDFs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On OSX all of these can be installed via MacPorts. Debian has packages as well.&lt;/p&gt;

&lt;p&gt;While PDF::Writer is a Ruby library pdftk is a command line tool. We simple call it using &lt;code&gt;Kernel.system&lt;/code&gt; and check the return code via the &lt;code&gt;$?&lt;/code&gt; variable.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;system&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;pdftk &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;source_pdf&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; ... output &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;target_pdf&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;PdfError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;pdftk returned error code &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;vg&quot;&gt;$?&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;With all this PDF processing the need for testing the contents of the generated documents arose. We have factored out all the PDF processing into a bunch of extra classes which we simply unit test with RSpec: make sure the parameters are passed to the command line correctly, that the right exception is thrown for each return code etc.&lt;/p&gt;

&lt;p&gt;In addition to unit testing we also write customer driven acceptance tests with &lt;a href=&quot;http://cukes.info&quot;&gt;Cucumber&lt;/a&gt;, where we assert on a high level the outcome of certain actions. With HTML pages we can simply use the built-in steps that in turn use Webrat to parse the HTML like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;Given a purchase over 200 EUR
And an invoice
When I go to the start page
And I follow &quot;Invoice&quot;
Then I should see &quot;200 EUR&quot;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now in our case the invoice link links to a PDF but we still want to know what’s inside the document. The solution we came up with looks like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;Given a purchase over 200 EUR
And an invoice
When I go to the start page
And I follow the PDF link &quot;Invoice&quot;
Then I should see &quot;200 EUR&quot;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;What this does in the background is follow the link as usual, write the response into a temporary file, convert that to text using &lt;em&gt;pdftotext&lt;/em&gt; and write the result back into the response. This way we can make assertions about the contents of the PDF almost as if it were an HTML page (except for tags of course). Here is the implementation:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;no&quot;&gt;When&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'I follow the PDF link &quot;$label&quot;'&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;click_link&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;temp_pdf&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Tempfile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'pdf'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;temp_pdf&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;body&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;temp_pdf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;close&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;temp_txt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Tempfile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'txt'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;temp_txt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;close&lt;/span&gt;
  &lt;span class=&quot;sb&quot;&gt;`pdftotext -q &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;temp_pdf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;temp_txt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;read&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;temp_txt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;path&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2009/02/bug-fighting-with-test-driven-development/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2009/02/bug-fighting-with-test-driven-development/"/>
    <title>Bug fighting with Test Driven Development Follow-Up</title>
    <updated>2009-02-08T00:00:00+00:00</updated>
    <author>
      <name>thilo</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;On Thursday I gave a talk about Bug fighting with &lt;a href=&quot;http://en.wikipedia.org/wiki/Test-driven_development&quot;&gt;TDD&lt;/a&gt; and how it will help you to write better software at the &lt;a href=&quot;http://www.rug-b.com&quot;&gt;Ruby User Group in Berlin&lt;/a&gt;. Sorry, I won’t put the slides online, although people asked about it. I think they are a pretty useless collection of keywords and pictures without the talking. Instead I like to provide a collection of links to dive into the whole testing matters.&lt;/p&gt;

&lt;p&gt;First a collection of tools you can choose from for various testing scenarios. They all have their pros and cons, it just comes down to which will do the job best for you.&lt;/p&gt;

&lt;h3&gt;Tools for &lt;a href=&quot;http://en.wikipedia.org/wiki/Unit_testing&quot;&gt;Unit Testing&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;a href=&quot;http://rspec.info/&quot;&gt;Rspec&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;Test/Unit (part of Rails)&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;http://thoughtbot.com/projects/shoulda&quot;&gt;shoulda&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Tools for &lt;a href=&quot;http://en.wikipedia.org/wiki/Mock_object&quot;&gt;Mocking&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;a href=&quot;https://github.com/jimweirich/flexmock/tree/master&quot;&gt;Flexmock&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;https://github.com/floehopper/mocha/tree/master&quot;&gt;Mocha&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;https://github.com/btakita/rr/tree/master&quot;&gt;RR&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Tools for &lt;a href=&quot;http://www.extremeprogramming.org/rules/functionaltests.html&quot;&gt;Acceptance Testing&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;a href=&quot;http://gitrdoc.com/brynary/webrat/tree/master/&quot;&gt;Webrat&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;http://cukes.info/&quot;&gt;Cucumber&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;http://htmlunit.sourceforge.net/&quot;&gt;HtmlUnit&lt;/a&gt; (with JRuby)&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;http://seleniumhq.org/&quot;&gt;Selenium&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Tools to generate test objects&lt;/h3&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;a href=&quot;https://github.com/notahat/machinist/tree/master&quot;&gt;Machinist&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;https://github.com/thoughtbot/factory_girl/tree/master&quot;&gt;FactoryGirl&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;https://github.com/flogic/object_daddy/tree/master&quot;&gt;ObjectDaddy&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Misc tools&lt;/h3&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;a href=&quot;http://www.zenspider.com/ZSS/Products/ZenTest/&quot;&gt;Autotest&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;http://opensource.thinkrelevance.com/wiki/tarantula&quot;&gt;Tarantula&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;http://upstream-berlin.com/2009/01/28/culerity-full-stack-rails-testing-with-cucumber-and-celerity/&quot;&gt;Culerity&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To save you the initial searching where to start, I collected some links where you find basic information about testing in general or with certain tools.&lt;/p&gt;

&lt;h3&gt;Getting started&lt;/h3&gt;

&lt;ul&gt;
	&lt;li&gt;&lt;a href=&quot;http://guides.rubyonrails.org/testing_rails_applications.html&quot;&gt;A Guide to Testing Rails Applications&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;http://blog.davidchelimsky.net/articles/2007/05/14/an-introduction-to-rspec-part-i&quot;&gt;An introduction to RSpec&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;http://upstream-berlin.com/2008/08/09/functional-testing-awesomeness-with-webrat/&quot;&gt;Functional Testing awesomeness with Webrat&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;http://www.patmaddox.com/blog/2009/1/15/how-i-test-controllers-2009-remix&quot;&gt;How I Test Controllers, 2009 Remix&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;http://thoughtbot.com/projects/shoulda/tutorial&quot;&gt;Shoulda tutorial&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;http://upstream-berlin.com/2007/09/06/mocha-prasentation/&quot;&gt;Mock objects explained and intro to Mocha&lt;/a&gt; (de)&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;For everyone who wants to get more into the subject, I drilled down my feed reader to collect some interesting blog posts on testing matters.&lt;/p&gt;

&lt;h3&gt;Opinions and In-sign&lt;/h3&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;a href=&quot;http://hasmanythrough.com/gtfdo/&quot;&gt;The Great Test Framework Dance-off&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot; http://feedproxy.google.com/~r/jayfields/mjKQ/~3/v1ig5SYT2Ko/thoughts-on-developer-testing.html&quot;&gt;Thoughts on Developer Testing&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;http://www.patmaddox.com/blog/2008/12/21/spikes-test-after-and-learning-how-to-test&quot;&gt;Spikes, test-after, and learning how to test&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;http://blog.davidchelimsky.net/2008/12/11/a-case-against-a-case-against-mocking-and-stubbing&quot;&gt;A case against a case against mocking and stubbing&lt;/a&gt;&lt;/li&gt;
        &lt;li&gt;&lt;a href=&quot;http://feeds.feedburner.com/~r/GiantRobotsSmashingIntoOtherGiantRobots/~3/445951359/a-critical-look-at-the-current-state-of-ruby-testing&quot;&gt;A critical look at the current state of Ruby testing&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;http://jamesgolick.com/2009/1/16/one-line-tests-without-the-smells&quot;&gt;One Line Tests Without the Smells&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;http://martinfowler.com/articles/mocksArentStubs.html&quot;&gt;Mocks Aren't Stubs&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;http://feeds.feedburner.com/~r/LitanyAgainstFear/~3/458790019/&quot;&gt;Testing as Communication: Real-World Techniques Notes&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;http://www.patmaddox.com/blog/2008/7/23/when-duplication-in-tests-informs-design&quot;&gt;When duplication in tests informs design&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;http://rubyconf2008.confreaks.com/writing-code-that-doesnt-suck.html&quot;&gt;Writing Code That Doesn't Suck&lt;/a&gt; (vid)&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;So much for my two cents to make the software world better.&lt;/p&gt;

&lt;p&gt;Does your company need help with software testing? We can help: check out our new product &lt;a href=&quot;http://upstream-berlin.com/scene-investigation&quot;&gt;Scene Investigation&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2009/02/hacking-couchdb-learning-erlang-and-testing-in-javascript/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2009/02/hacking-couchdb-learning-erlang-and-testing-in-javascript/"/>
    <title>Hacking CouchDB, learning Erlang and testing in Javascript</title>
    <updated>2009-02-01T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Last week was my first &lt;a href=&quot;http://erlang.org&quot;&gt;Erlang&lt;/a&gt; gig. I paired up with &lt;a href=&quot;http://jan.prima.de/&quot;&gt;Jan Lehnardt&lt;/a&gt; (core committer of CouchDB). Our goal was to implement a &lt;a href=&quot;https://github.com/janl/couchdb/tree/old-stats-new&quot;&gt;new statistics module&lt;/a&gt; for CouchDB within one week. Jan knew more about the CouchDB code and Erlang, I contributed my knowledge about testing and pair programming.&lt;/p&gt;

&lt;h3&gt;Integration Testing in JavaScript&lt;/h3&gt;

&lt;p&gt;Since the testing infrastructure in CouchDB left some room for improvement we decided to introduce some new concepts. The existing tests were written in JavaScript penetrating the database via its HTTP/JSON interface. The tests were really long and coarse grained which lead to a problem: When one of the tests broke it wasn’t very easy to figure out which one. We didn’t want to replace or add too much to the existing, self-made test framework so we just added a bit of sugar: BDD style specs with meaningful names:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;response_count_tests&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;should show the number of 404 responses&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Given
&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;restartServer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// When
&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;CouchDB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;GET&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;/some_nonexistant_url&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Then
&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;TEquals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;CouchDB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;stats_request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;httpd&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;not_found&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;all_tests&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;response_count_tests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;...];&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;all_tests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;test_group&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;all_tests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;test_group&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;test_group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;](&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The TEquals function is just our extension of an existing function T which is responsible for reporting errors when the assertion doesn’t match. By passing the name of the &lt;del&gt;test&lt;/del&gt; spec we can now display that name along with the error message:&lt;/p&gt;

&lt;p&gt;Error in ‘should show the number of 404 responses’: ‘CouchDB.stats_request(‘httpd’, ‘not_found’)’ should be ‘1’ but was ‘0’.&lt;/p&gt;

&lt;h3&gt;Unit Testing in Erlang&lt;/h3&gt;

&lt;p&gt;In addition to those integration tests we wanted to add some unit testing on a lower level. Turns out the only unit testing framework for Erlang seems to be &lt;a href=&quot;http://svn.process-one.net/contribs/trunk/eunit/doc/overview-summary.html&quot;&gt;EUnit&lt;/a&gt;, which is a very barebones (at least when you come from something like &lt;a href=&quot;http://rspec.info&quot;&gt;RSpec&lt;/a&gt;) xUnit type of tool. You define a test by appending “_test” to your function name and then you have a bunch of assert statements at your disposal. Well that’s at least something and it worked. Here is an example:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;should_return_zero_if_nothing_has_been_counted_yet_test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;test_setup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fun&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;no&quot;&gt;Result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;MODULE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;httpd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;average_request_count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}),&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assertEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;0.00&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;We added the test_setup for starting and tearing down processes needed for our tests. Then we pass a fun with the actual test code. We got through the week pretty ok with this but I really missed a few things:&lt;/p&gt;

&lt;li&gt;The test output was not &lt;a href=&quot;http://jamesshore.com/Blog/Red-Green-Refactor.html&quot;&gt;red/green&lt;/a&gt; - ok I could live with this, at least for a while, if the output was at least formatted in a way that would allow me to spot the problems more easily. The error output of erlang seems to be a big mess in general.&lt;/li&gt;
&lt;li&gt;No mocking/stubbing framework. I don't even know if this is possible at all, at least I'm now aware of any way to change the behavior of Erlang code after compilation. (would that contradict Erlang's share nothing philosophy?)&lt;/li&gt;
&lt;li&gt;contexts for tests - I want to be able to group my tests, not sure how this would work in Erlang syntax as there are no nested namespaces or things you can use to group methods (except for modules, but I want something smaller and you can't nest them). You would probably end up using lists of funs or something.&lt;/li&gt;

&lt;h3&gt;Erlang&lt;/h3&gt;

&lt;p&gt;I’ve &amp;lt;a href=”“http://upstream-berlin.com/2007/07/24/programming-erlang-functional-programming/&amp;gt;read the book, I’ve &lt;a href=&quot;http://upstream-berlin.com/2009/01/15/upcoming-event-screencast-week-cockpit/&quot;&gt;watched numerous screencasts&lt;/a&gt; but this was my first real life project in Erlang. Many people complain about its syntax and while this isn’t the most beautiful language I’ve ever seen I have to say it’s not really a big problem. I got used to it on the first day.&lt;/p&gt;

&lt;p&gt;What is really powerful in Erlang is pattern matching. Instead of simply assigning a value to a variable you can always match against the structure of that data to extract parts of it:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;no&quot;&gt;Data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Hans&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Wurst&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;born&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1980&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;23&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;FirstName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;born&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Year&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Day&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This example lets you pull out numerous details from the tuple in a single statement. You can do the same for case statements or when overloading functions. So this pattern matching is a powerful concept seen everywhere in Erlang code. It makes the code you write pretty dense which is cool on the one hand but sometimes also makes it hard to read so you have to be careful how much you want to cram into a single line of code.&lt;/p&gt;

&lt;p&gt;CouchDB is built using make - which felt like 1990. I’ve never used make much myself so I’m not really qualified to talk about it but we did spend a good amount of time in our MakeFile because of some whitespace problem, and we still haven’t managed to integrate our new tests into the build process. I’d love to see something like &lt;a href=&quot;http://martinfowler.com/articles/rake.html&quot;&gt;Rake being used&lt;/a&gt; instead.&lt;/p&gt;

&lt;p&gt;All in all this have been very pleasant first steps though. Pairing up for this turned out to be a very good idea, I have learned a lot about Erlang and am eager for more now.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2009/01/culerity-full-stack-rails-testing-with-cucumber-and-celerity/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2009/01/culerity-full-stack-rails-testing-with-cucumber-and-celerity/"/>
    <title>Culerity = Full Stack Rails Testing with Cucumber and Celerity</title>
    <updated>2009-01-28T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Since the day we started upstream testing was our obsession. When we began all we had was the Rails built-in unit, functional and integration tests. When we learned about RSpec the world became a brighter place. When the RSpec team released their &lt;a hef=&quot;http://upstream-berlin.com/2007/09/24/railsconf-europe-2007-roundup-1-rspec/&quot;&gt;StoryRunner&lt;/a&gt; we were thrilled. We started writing stories in English and were able to run them. Whoa. It soon turned out the first version had some serious issues with staying DRY. More versions were released, we rewrote our stories, step by step StoryRunner got better and we got better, too.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://cukes.info&quot;&gt;&lt;img src=&quot;http://upstream-berlin.com/wp-content/uploads/2009/01/cucumber.png&quot; alt=&quot;&quot; title=&quot;cucumber&quot; width=&quot;250&quot; class=&quot;alignright size-full wp-image-527&quot; /&gt;&lt;/a&gt; Then &lt;a href=&quot;http://upstream-berlin.com/2008/08/26/cucumber-next-generation-rspec-story-runner/&quot;&gt;Cucumber came out&lt;/a&gt; and it was a huge leap ahead again. With the integration of &lt;a hef=&quot;http://upstream-berlin.com/2008/08/09/functional-testing-awesomeness-with-webrat/&quot;&gt;Webrat&lt;/a&gt; we could write much shorter stories, but more importantly we could test that our links and forms were working as well. Everything was working perfectly and the world was a happy place - as long as we didn’t use any JavaScript/AJAX…&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Welcome to Culerity.&lt;/strong&gt; &lt;a href=&quot;https://github.com/langalex/culerity&quot;&gt;Culerity&lt;/a&gt; is a small gem that integrates Cucumber with &lt;a href=&quot;http://celerity.rubyforge.org/&quot;&gt;Celerity&lt;/a&gt;, a wrapper around &lt;a href=&quot;http://htmlunit.sourceforge.net/&quot;&gt;HtmlUnit&lt;/a&gt; which in turn is a Java library that parses HTML and runs the embedded JavaScript code. And unlike &lt;a href=&quot;http://wtr.rubyforge.org/&quot;&gt;Watir&lt;/a&gt; or &lt;a href=&quot;http://seleniumhq.org/&quot;&gt;Selenium&lt;/a&gt; this all happens in headless mode: without hijacking your browser.&lt;/p&gt;

&lt;p&gt;The problem with Celerity has been that it only works in a JRuby environment, which means you  either had to run your application in JRuby as well (which might not even work with certain libraries and plugins) or somehow work your way around it by running your tests in JRuby and your application in whatever Ruby you wanted to use and somehow glue it together. Culerity now fills this gap. After installing it you run your Cucumber features as usual. Celerity now spawns a Java process in the background, sends all the Celerity calls to this process and evaluates the results back in the original Ruby environment” everything works (almost) as if you were running just in your single Ruby process. For an easier start Culerity comes with the same set of Cucumber step definitions that are provided by a default Cucumber/Webrat installation. This means you can reuse your step definitions written for Webrat.&lt;/p&gt;

&lt;p&gt;To get started you can either clone the &lt;a href=&quot;https://github.com/langalex/culerity&quot;&gt;GitHub repository&lt;/a&gt; or simply install the gem (langalex-culerity). Then follow the instructions in the &lt;a href=&quot;https://github.com/langalex/culerity/raw/2c2e48d65dcd5eaca81816f9976a773e1454ba1f/README.textile&quot;&gt;README&lt;/a&gt;. If you find something is not working for you or could be improved please feel free to fork the source code and show it some love. The codebase is really small right now and fully spec’d.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Does your company need help with software testing? We can help: check out our new product &lt;a href=&quot;/scene-investigation&quot;&gt;Scene Investigation&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2009/01/upstream-went-to-maine/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2009/01/upstream-went-to-maine/"/>
    <title>Upstream went to Maine</title>
    <updated>2009-01-22T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;We &lt;a href=&quot;http://upstream-berlin.com/2009/01/07/upstream-goes-to-maine/&quot;&gt;went to Maine&lt;/a&gt;” well, close. To evaluate the past year and make plans for 2009, the Upstream crew recently spent a weekend together. We stayed in a holiday home right on the North Sea coast where we enjoyed the frosty weather and a cosy fireplace. Everyone brought about three cameras on average. This resulted in heaps of pictures with “naturey crap” (the amateur is speaking). See by yourself on &lt;a href=&quot;http://flickr.com/photos/tags/upstreamgoestomaine&quot;&gt;Flickr&lt;/a&gt;, &lt;a href=&quot;http://miss-sophie.tumblr.com/archive/2009/1&quot;&gt;Tumblr&lt;/a&gt; and &lt;a href=&quot;http://kommt-mit.de/albums/103&quot;&gt;KommtMit&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://flickr.com/photos/ipom/3199266346/&quot;&gt;&lt;img src=&quot;http://farm4.static.flickr.com/3100/3199266346_e86166dea3.jpg?v=0&quot; alt=&quot;group photo&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our CEOs Alex and Thilo gave a presentation about Upstream’s past year. Then, our aim was to outline what Upstream should do in 2009 and how each of us wants to get involved: How much we want to work and for how much, which technologies we want to learn, does it make more sense to attend conferences or to get involved in various open source projects? We collected our ideas and discussed some of them further.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://flickr.com/photos/langalex/3213193738/&quot;&gt;&lt;img src=&quot;http://farm4.static.flickr.com/3454/3213193738_cdf737e410.jpg?v=0&quot; alt=&quot;break&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next to talking business, we wanted to get to know each other better, so everyone had also prepared a presentation about a non-technical topic of interest:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Alex introduced us to &lt;a href=&quot;http://www.slideshare.net/langalex/sailing-presentation-937825&quot;&gt;Sailing&lt;/a&gt;, &quot;the most expensive way of travelling inconveniently&quot;. &lt;/li&gt;
&lt;li&gt;Matthias talked about &lt;a href=&quot;http://www.slideshare.net/mattmatt/upstream-goes-to-maine-presentation&quot;&gt;Photography&lt;/a&gt;, what else! &lt;/li&gt;
&lt;li&gt;Katrin gave a talk about how to recycle old consumer items by turning them into furniture, showing examples from &lt;a href=&quot;http://ecoble.com/2008/05/28/ten-clever-furniture-designs-from-recycled-materials/&quot;&gt;ecoble&lt;/a&gt;, &lt;a href=&quot;http://weburbanist.com/2008/03/26/20-eye-catching-pieces-of-recycled-urban-furniture-geeky-and-ecological-reuse-of-ordinary-objects/&quot;&gt;WebUrbanist&lt;/a&gt; and &lt;a href=&quot;http://webdesignhamburg.net/lockengeloet/catalog/index.php&quot;&gt;Lockengeloet, Hamburg&lt;/a&gt;. &lt;/li&gt;
&lt;li&gt;Thilo shared his &lt;a href=&quot;http://en.wikipedia.org/wiki/Role-playing_game&quot;&gt;Pen and Paper Roleplaying Game&lt;/a&gt; experiences by reading from his hero's diary. &lt;/li&gt;
&lt;li&gt;Frank talked about &lt;a href=&quot;http://en.wikipedia.org/wiki/Screamo&quot;&gt;Screamo&lt;/a&gt;, his favorite kind of Hardcore/Punk Music, with many sound snippets! &lt;/li&gt;
&lt;li&gt;I presented &lt;a href=&quot;http://action-samba-berlin.so36.net&quot;&gt;Rhythms of Resistance&lt;/a&gt;, a network of political samba activists.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href=&quot;http://flickr.com/photos/langalex/3213195160/&quot;&gt;&lt;img src=&quot;http://farm4.static.flickr.com/3124/3213195160_b3698c1a62.jpg?v=0&quot; alt=&quot;taking pictures&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After all those fun sessions and walking around at the beach taking pictures of every grain of sand it wasn’t so easy to get back to work but we managed”
As a first result we are today relaunching the upstream website presenting our new software development/consulting products: &lt;a href=&quot;http://upstream-berlin.com/lift-off&quot;&gt;Lift Off&lt;/a&gt;, &lt;a href=&quot;http://upstream-berlin.com/scene-investigation&quot;&gt;Scene Investigation&lt;/a&gt; and &lt;a href=&quot;http://upstream-berlin.com/turbo-boost&quot;&gt;Turbo Boost&lt;/a&gt;. More on this in another blog post.&lt;/p&gt;

&lt;p&gt;For further reading about our weekend &lt;a href=&quot;http://holgarific.net/?p=131&quot;&gt;see what Mathias wrote&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2009/01/seven-things/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2009/01/seven-things/"/>
    <title>Seven Things</title>
    <updated>2009-01-22T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Hey a new meme, gotta be part of it. We got tagged by &lt;a href=&quot;http://jan.prima.de/~jan/plok/archives/165-Seven-Things.html&quot;&gt;Jan&lt;/a&gt; and &lt;a href=&quot;http://till.klampaeckel.de/blog/archives/8-Seven-Things-Tagged-by-Chuck-Burgess.html&quot;&gt;Till&lt;/a&gt; about “seven things”&lt;/p&gt;

&lt;p&gt;These are the rules:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Link your original tagger(s), and list these rules on your blog.&lt;/li&gt;
&lt;li&gt;Share seven facts about yourself in the post” some random, some weird.&lt;/li&gt;
&lt;li&gt;Tag seven people at the end of your post by leaving their names and the links to their blogs.&lt;/li&gt;
&lt;li&gt;Let them know they have been tagged by leaving a comment on their blogs and/or Twitter.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So here we go:&lt;/p&gt;

&lt;p&gt;[caption id=”attachment_512” align=”alignright” width=”290” caption=”Our old Logo”]&lt;img src=&quot;http://upstream-berlin.com/wp-content/uploads/2009/01/old_logo.png&quot; alt=&quot;Our old Logo&quot; width=&quot;290&quot; height=&quot;106&quot; class=&quot;size-full wp-image-512&quot; /&gt;[/caption]&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The name &lt;em&gt;upstream&lt;/em&gt; was suggested by friend. After having spent months in search for a company name we just accepted it out of desperation. Wasn't such a bad choice was it?&lt;/li&gt;
&lt;li&gt;When seeing our old logo most people told us it looked like a tennis ball (it was supposed to be a river) - so good we did an overhaul last year.&lt;/li&gt;
&lt;li&gt;We never ever hire anyone permanently - everyone who works for us is a freelancer. We don't want anyone to be dependent on us and we don't want to be responsible for anyone.&lt;/li&gt;
&lt;li&gt;we pair program all the fucking time (ppatft) .. mostly&lt;/li&gt;
&lt;li&gt;If my &lt;a href=&quot;http://culturedcode.com/things/&quot;&gt;GTD tool&lt;/a&gt; wouldn't remind me every week our office plants would have died long ago&lt;/li&gt;
&lt;li&gt;We have &lt;a href=&quot;https://twitter.com/freelancing-god&quot;&gt;hired programmers&lt;/a&gt; and scored gigs over twitter.&lt;/li&gt;
&lt;li&gt;We can write PHP but don't tell anyone.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I’m tagging &lt;a href=&quot;http://paperplanes.de&quot;&gt;Mathias&lt;/a&gt;, &lt;a href=&quot;http://www.nach-vorne.de/&quot;&gt;Gregor&lt;/a&gt;, &lt;a href=&quot;http://grundprinzip.de/&quot;&gt;Martin&lt;/a&gt;, &lt;a href=&quot;http://shortcut.no/blog&quot;&gt;Marius&lt;/a&gt;, &lt;a href=&quot;http://freelancing-gods.com/&quot;&gt;Pat&lt;/a&gt;, &lt;a href=&quot;http://blog.kriesse.traeumt.net/&quot;&gt;Kristina&lt;/a&gt; and &lt;a href=&quot;http://www.plomlompom.de/&quot;&gt;Christian&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2009/01/upcoming-event-screencast-week-cockpit/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2009/01/upcoming-event-screencast-week-cockpit/"/>
    <title>Upcoming Event: Screencast Week @ Cockpit (updated)</title>
    <updated>2009-01-15T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;We’ll be hosting another of our Cockpit Nights. This time instead of listening to talks we’ll be watching Screencasts. Wednesday Jan 21st is Erlang, Thursday 22nd is Objective-C day. More details in our &lt;a href=&quot;http://wiki.upstream-berlin.com/index.php/ScreencastWeek&quot;&gt;public wiki&lt;/a&gt;.&lt;/p&gt;

&lt;div style=&quot;float: right; margin-left: 20px&quot;&gt;&lt;a href=&quot;http://pragmatic.tv&quot;&gt;&lt;img src=&quot;http://wiki.upstream-berlin.com/images/1/1c/Pragmatictv.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;update: Screencasts courtesy of The Pragmatic Programmers. We thank them very much for providing them. Check out their whole series of screencasts at &lt;a href=&quot;http://pragmatic.tv&quot;&gt;http://pragmatic.tv&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Want to stay up to date on events? Follow &lt;a href=&quot;https://twitter.com/upstream_agile&quot;&gt;@upstream_agile&lt;/a&gt; on Twitter or subscribe to the &lt;a href=&quot;http://www.google.com/calendar/ical/qlat42csulq2krlclc3rk3e8io%40group.calendar.google.com/public/basic.ics&quot;&gt;iCal Feed&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2009/01/upstream-goes-to-maine/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2009/01/upstream-goes-to-maine/"/>
    <title>upstream goes to Maine</title>
    <updated>2009-01-07T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;&lt;a href=&quot;http://www.flickr.com/photos/maxblack/2301777537/&quot;&gt;&lt;img src=&quot;http://farm3.static.flickr.com/2204/2301777537_66eb19515e.jpg?v=0&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;

Well, sort of. We are &lt;a href=&quot;http://kommt-mit.de/journeys/179&quot;&gt;going&lt;/a&gt; to the &lt;a href=&quot;http://www.atraveo.de/objekte/178687.php&quot;&gt;north sea&lt;/a&gt; over the next weekend. Following &lt;a href=&quot;http://www.37signals.com/svn/posts/1272-37signals-goes-to-maine&quot;&gt;our heroes at 37signals&lt;/a&gt; we will be spending two days at the countryside discussing the future of our little company.

Of the 6 people going everyone will be doing 2 15 minute talks: one about upstream and his/her involvement, one about a random topic of interest. The goal of this trip is to find out what upstream should stand for and do in 2009 (and to take as many pictures as possible, cook and eat lots of yum food and relax at the fireplace).

I'll blog about the results after the weekend, stay tuned :)&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2009/01/speaking-at-scotland-on-rails/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2009/01/speaking-at-scotland-on-rails/"/>
    <title>Speaking at Scotland on Rails</title>
    <updated>2009-01-07T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;div style=&quot;float:right; margin-left: 20px&quot;&gt;&lt;img src=&quot;http://upstream-berlin.com/wp-content/uploads/2009/01/scotland_on_rails.png&quot; alt=&quot;&quot; title=&quot;scotland_on_rails&quot; width=&quot;154&quot; height=&quot;196&quot; class=&quot;alignnone size-full wp-image-402&quot; /&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;http://scotlandonrails.com/&quot;&gt;Scotland on Rails&lt;/a&gt; will take place On March 26-28 2009 in Edinburgh. The organizers have just published the &lt;a href=&quot;http://scotlandonrails.com/schedule&quot;&gt;list of speakers&lt;/a&gt;. Pretty nice lineup including Jim Weirich, Yehuda Katz, Pat Maddox, Chad Fowler and more. And the best thing: I will be speaking about using CouchDB with Ruby/Rails, starring live coding and &lt;a href=&quot;https://github.com/langalex/couch_potato&quot;&gt;couch potato&lt;/a&gt; - my own little persistence layer for CouchDB.&lt;/p&gt;

&lt;p&gt;&lt;br style=&quot;clear:right&quot; /&gt;&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2008/12/keep-your-exceptions-inbox-clean/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2008/12/keep-your-exceptions-inbox-clean/"/>
    <title>Keep your exceptions inbox clean</title>
    <updated>2008-12-17T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;div style=&quot;float:right; margin-left: 10px; margin-bottom: 10px&quot;&gt;&lt;a href=&quot;http://flickr.com/photos/e3000/649897994/&quot;&gt;&lt;img src=&quot;http://farm2.static.flickr.com/1274/649897994_4c90bc2a03.jpg?v=0&quot; width=&quot;300&quot; /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;When &lt;a href=&quot;http://hoptoadapp.com&quot;&gt;Hoptoad&lt;/a&gt; came out we were really excited. No more hundreds or even thousands of emails in case of some server failure or &lt;em&gt;gasp&lt;/em&gt; even a bug deployed to the production server. Instead a nice looking website with a clean list. Nice.&lt;/p&gt;

&lt;p&gt;Within days we had switched all projects from exception_notifier to Hoptoad - and then that was it. Occasionally Hoptoad would send an email with an exception but most of the time it would be some crawler requesting an invalid URL so we read and forgot about it. The problem was that with Hoptoad to forget about those errors had become be really easy - too easy. So last week a client told me the signup form on our project was showing an error page and nobody had been able to sign up for days.&lt;/p&gt;

&lt;p&gt;Wait. We are monitoring the app and get notified about every exception. Of course we did but over the months we had accumulated more than 50 exceptions in Hoptoad and nobody was really paying attention to that big pile of false alarms caused by some crawler anymore. So yes, we did miss that positive alarm.&lt;/p&gt;

&lt;p&gt;As a consequence we spent the same day going through all our exceptions in all projects and killed them all.&lt;/p&gt;

&lt;p&gt;A big part of the exception came from crawlers requesting non-existent actions and stuff like that. With Rails 2.2 this is relatively easy to fix. In our routes.rb we have declared most of our routes as restful resources:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;  &lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;resources&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:posts&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now the &lt;code&gt;PostsController&lt;/code&gt; does not implement the &lt;code&gt;index&lt;/code&gt; action but we do get requests on &lt;em&gt;/posts&lt;/em&gt; from time to time which result in an &lt;em&gt;Unknown Action&lt;/em&gt; exception. The solution is to remove the &lt;code&gt;index&lt;/code&gt; route:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;  &lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;resources&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:posts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:except&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:index&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;In other cases we had to improve our guards on parameters a little. Passing an empty string for the &lt;code&gt;page&lt;/code&gt; parameter to a &lt;code&gt;paginate&lt;/code&gt; call seems to be a favorite pastime for the google crawler.&lt;/p&gt;

&lt;p&gt;So, lessons learned: keep your exception inbox clean. Even if it’s just a bunch of bots.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2008/12/devhouseberlin-impressions/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2008/12/devhouseberlin-impressions/"/>
    <title>DevHouseBerlin FTW!</title>
    <updated>2008-12-10T00:00:00+00:00</updated>
    <author>
      <name>thilo</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;http://farm4.static.flickr.com/3202/3087018332_acf79469c3_o.jpg&quot; title=&quot;devhouse&quot; class=&quot;alignleft&quot; width=&quot;250&quot; height=&quot;175&quot; /&gt;The DevHouseBerlin weekend is over, but don’t worry if you missed this one. This first one was such a success that we will do more of them.&lt;/p&gt;

&lt;p&gt;Thanks to all of those who came, helped, organized and shared their knowledge to make this such fun. I’m not lying when saying that everything turned out to be better than expected.&lt;/p&gt;

&lt;p&gt;The upfront organization was minimal. Special thanks to Jan and Alex! We even got some unexpected PR beforehand thanks to an &lt;a href=&quot;http://t3n.yeebase.com/aktuell/news/newspost/devhouseberlin-developer-wochenende-in-berlin/2247/&quot;&gt;online article&lt;/a&gt; about the DevHouse. In response we bumped up the available slots to 20. Which were all filled up soon.&lt;/p&gt;

&lt;p&gt;The location &lt;a href=&quot;http://boxhagener119.de&quot;&gt;Box 119&lt;/a&gt; which houses the offices of upstream, &lt;a href=&quot;http://www.finn.de/&quot;&gt;finnlabs&lt;/a&gt; and &lt;a href=&quot;http://www.rocket-rentals.de/&quot;&gt;rocket rentals&lt;/a&gt; was an ideal location for such an event with its medium large office rooms and a bigger foyer. Box 119 had a solid infrastructure, the WiFi and Internet didn’t let us down once. We even had a decent coffee machine, major factor!&lt;/p&gt;

&lt;p&gt;Saturday was packed. I guessed more than 30 people at once filled the DevHouse. Everybody was very open and eager to share. The presentation slots for the evening were filled up already by the early afternoon. I even lost my planned slot because of getting out of bed a bit later than planned.&lt;/p&gt;

&lt;p&gt;Right from the beginning major hacking was going on. In the afternoon the talks started. In parallel there was tons of time to chat, eat and hack at will within a very relaxing atmosphere.&lt;/p&gt;

&lt;p&gt;PHP raised in my opinion again, after I learned through Falko’s Meta Programming PHP talk that it also has a method_missing equivalent. Nico gave some helpful user experience insights by analyzing start pages on the projector. It’s amazing how many - in retrospective obvious things - you can do wrong on your start page. With Fabian we had a non technical discussion about Economy, after his talk about Fixing Money, which unfortunately left out the solution. After that talk I heard some creepy stories from The Enterprise at dinner.&lt;/p&gt;

&lt;p&gt;At 2am at least a dozen people were still in the house. I left at 3:30am still full awake thanks to the optimal &lt;a href=&quot;http://twinkle.tapulous.com/index.php?hash=f413f8e4b518add5b0f537ac584a6569c19fa1d1&quot;&gt;Club Mate supply&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Sunday wasn’t that busy. People dropped in later, I had plenty of time to get a presentation slot this time. ;) Some realtime programming battle was going on in the afternoon. You can find Gregor’s &lt;a href=&quot;https://github.com/schmidt/realtimebattle.rb/tree/master&quot;&gt;battle tank A.I. on Github&lt;/a&gt;. Filip showed some quick code kung fu by building a poll app with &lt;a href=&quot;http://www.djangoproject.com/&quot;&gt;Django&lt;/a&gt; in his talk. This little python Framework is worth a look, if you need to get up small apps fast.
My first talk on Bug Fighting with &lt;a href=&quot;http://en.wikipedia.org/wiki/Test-driven_development&quot;&gt;TDD&lt;/a&gt; went well. People seemed to be very interested in the subject. I had some time to play with JRuby and was able to get &lt;a href=&quot;http://htmlunit.sourceforge.net/&quot;&gt;HtmlUnit&lt;/a&gt; to run with &lt;a href=&quot;https://github.com/brynary/webrat/tree&quot;&gt;Webrat&lt;/a&gt;, although my solution is still sort of hackish.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://incubator.apache.org/couchdb/&quot;&gt;CouchDB&lt;/a&gt; was all over the place of course with Jan around. He helped interested folks to get it up an running and helped out with problems related to CouchDB. Alex used the time to enhance his CouchDB persistence layer &lt;a href=&quot;https://github.com/langalex/couch_potato/tree/master&quot;&gt;couch_potato&lt;/a&gt;. It will make the transition for rails people much easier.&lt;/p&gt;

&lt;p&gt;People who stayed late this sunday - which excludes me - witnessed as the very first the rise of &lt;a href=&quot;http://www.thecloudplayer.com/&quot;&gt;cloudplayer&lt;/a&gt; the most beautiful online music player in the world wide interweb, that Erik &amp;amp; Henrik gave the last touches at the DevHouse.&lt;/p&gt;

&lt;p&gt;These where just some of the talks, projects and peoples that I get to know that weekend. There was so much more as you can see from the &lt;a href=&quot;http://www.flickr.com/groups/991102@N22/pool/&quot;&gt;photos&lt;/a&gt; and the &lt;a href=&quot;http://wiki.upstream-berlin.com/index.php/DevHouseBerlin&quot;&gt;wiki&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Wow, it was just great. And next time will be even better. We will tell you more soon on this ;)&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2008/11/introducing-devhouseberlin/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2008/11/introducing-devhouseberlin/"/>
    <title>Introducing DevHouseBerlin</title>
    <updated>2008-11-13T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;We are very proud to announce DevHouseBerlin.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://wiki.upstream-berlin.com/index.php/DevHouseBerlin&quot;&gt;DevHouseBerlin&lt;/a&gt; is a fun-packed weekend of hacking and sharing knowledge. We open the &lt;a href=&quot;http://boxhagener119.de/&quot;&gt;Box119&lt;/a&gt; office for a weekend and invite hackers of all sorts to join us working on projects, sharing code and ideas and just hanging out among fellow geeks. &lt;/p&gt;

&lt;p&gt;DevHouseBerlin runs on December 6th &amp;amp; 7th, 2008 and it is a free event. &lt;a href=&quot;http://wiki.upstream-berlin.com/index.php/DevHouseBerlin&quot;&gt;You can sign up on the wiki&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We are highly influenced by the &lt;a href=&quot;http://superhappydevhouse.org/&quot;&gt;SuperHappyDevHouse&lt;/a&gt;: &lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;SuperHappyDevHouse is a non-exclusive event intended for creative and curious people interested in technology. We&amp;#8217;re about knowledge sharing, technology exploration, and ad-hoc collaboration. Come to have fun, build things, learn things, and meet new people. It&amp;#8217;s called hacker culture, and we&amp;#8217;re here to encourage it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href=&quot;http://wiki.upstream-berlin.com/index.php/DevHouseBerlin&quot;&gt;Find out more about DevHouseBerlin&lt;/a&gt;. &lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2008/10/cocoa-for-rubyists-with-macruby/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2008/10/cocoa-for-rubyists-with-macruby/"/>
    <title>Cocoa for Ruby(ists) with MacRuby</title>
    <updated>2008-10-28T00:00:00+00:00</updated>
    <author>
      <name>thilo</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Apple’s Application Framework Cocoa gained popularity recently, also due to the iPhone. Like many others we started to learn Objective-C to play around with the iPhone a couple of months ago. The basic syntax is fairly easy to learn, also because lots of sources exist to &lt;a href=&quot;http://cocoadevcentral.com/d/learn_objectivec/&quot;&gt;get you up and running&lt;/a&gt;. But in my opinion the real challenge is to understand how the interface builder works&lt;/p&gt;

&lt;h3&gt;The past&lt;/h3&gt;
&lt;p&gt;But as a Rubyist the C like syntax of Objective-C was quite a pain. So I didn’t enjoy working with Objective-C. The existing Ruby bridge &lt;a href=&quot;http://rubycocoa.sourceforge.net/HomePage&quot;&gt;RubyCocoa&lt;/a&gt;  has its own &lt;a href=&quot;http://en.wikipedia.org/wiki/RubyCocoa#Disadvantages&quot;&gt;drawbacks&lt;/a&gt;. Especially moving parameter names into the method name to mimic Objective-C’s keyed arguments doesn’t appear future proof to me. This all kept me from getting deeper into the subject.&lt;/p&gt;

&lt;h3&gt;The future&lt;/h3&gt;
&lt;p&gt;In March this year&lt;a href=&quot;http://www.macruby.org&quot; target=&quot;_BLANK&quot;&gt; MacRuby&lt;/a&gt; made its appearance as a successor for RubyCocoa. MacRuby is a port of Ruby 1.9 on top of the Objective-C runtime and garbage collector, which aims to &lt;a href=&quot;http://www.macruby.org/trac/wiki/WhyMacRuby&quot; target=&quot;_BLANK&quot;&gt; fix all of the RubyCocoa drawbacks&lt;/a&gt; and bring the full power of Ruby to the Cocoa framework. With the &lt;a href=&quot;http://www.macruby.org/post/macruby-03&quot; target=&quot;_BLANK&quot;&gt;0.3 release&lt;/a&gt; a couple of weeks ago, MacRuby now supports the Interface Builder, thus making it a full fledged member of the OS X Development tool chain. Since then MacRuby gained serious &lt;a href=&quot;http://www.rubyinside.com/macruby-03-released-now-with-interface-builder-support-1195.html&quot;&gt;popularity&lt;/a&gt;. An official Apple Developer Connection Tutorial for “&lt;a href=&quot;http://developer.apple.com/mac/articles/scriptingautomation/cocoaappswithmacruby.html&quot;&gt;Developing Cocoa Applications Using MacRuby&lt;/a&gt;” was published recently. More &lt;a href=&quot;http://blog.springenwerk.com/2008/10/macruby-and-core-data-tutorial.html&quot;&gt;good tutorials&lt;/a&gt; can be found on the web, so I just link to them instead of writing another.&lt;/p&gt;

&lt;p&gt;During the weekend I went through the first chapters of “&lt;a href=&quot;http://www.amazon.com/Cocoa-Programming-Mac-OS-3rd/dp/0321503619/ref=sr_1_1?ie=UTF8&amp;amp;s=books&amp;amp;qid=1225145630&amp;amp;sr=8-1&quot; target=&quot;_BLANK&quot;&gt;Cocoa Programming for Mac OSX&lt;/a&gt;” with MacRuby instead of Objective-C to get familiar with the Cocoa library and Interface Builder. It went super smooth so far, I really had fun. Here is just a short example of the very first app out of the book to see how the code compares to objective-c.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Foo&lt;/span&gt;
	&lt;span class=&quot;nb&quot;&gt;attr_writer&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:text_field&lt;/span&gt;

	&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;seed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;vi&quot;&gt;@text_field&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;StringValue&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Generator doesn't need to be seeded ;)&quot;&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

	&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;generate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;vi&quot;&gt;@text_field&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;StringValue&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;^ Ruby vs. Objective-C v&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;&lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Foo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NSObject&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;IBOutlet&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NSTextField&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;textField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IBAction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gerneate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IBAction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;seed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;impelemtation&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Foo&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IBAction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;generate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;generated&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;textField&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;setIntValue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;generated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IBAction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;seed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;srandom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;textField&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;setStringValue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Generator seeded&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;As you can see you don’t need a header file and much less special characters. It even gets better, because you don’t need to call &lt;code&gt;[[NSMutableArray alloc] init]&lt;/code&gt; but &lt;code&gt;Array.new&lt;/code&gt; or just &lt;code&gt;[]&lt;/code&gt; to initialize an empty Array. Isn’t Ruby beautiful? And with MacRuby you can have it for your Cocoa Apps too. So &lt;a href=&quot;http://www.macruby.org/trac/wiki/InstallingMacRuby&quot;&gt;jump on board&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2008/10/couch-potato-unleashed-a-couchdb-persistence-layer-in-ruby/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2008/10/couch-potato-unleashed-a-couchdb-persistence-layer-in-ruby/"/>
    <title>Couch Potato unleashed - a couchdb persistence layer in ruby (updated)</title>
    <updated>2008-10-27T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; the gem is now available, see the installation instructions below.&lt;/p&gt;

&lt;p&gt;After several weeks of incubating on my computer it’s finally time to get real: I have just open sourced Couch Potato under the MIT license. You can &lt;a href=&quot;https://github.com/langalex/couch_potato&quot;&gt;get Couch Potato on github&lt;/a&gt; now. For an introduction to CouchDB and ruby please read my previous blog post &lt;a href=&quot;http://upstream-berlin.com/2008/09/25/a-couchdb-primer-for-an-activerecord-mindset/&quot;&gt;A CouchDB primer for an ActiveRecord mindset&lt;/a&gt;. The following is a very short introduction into using Couch Potato. If you want to know more you can start with the &lt;a href=&quot;https://github.com/langalex/couch_potato/tree/master/README.textile&quot;&gt;README&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The goal of Couch Potato is to create a migration path for users of ActiveRecord and other object relational mappers to port their applications to CouchDB. It therefore offers a basic set of the functionality provided by most ORMs and adds functionality unique to CouchDB on top.&lt;/p&gt;

&lt;h3&gt;Installation&lt;/h3&gt;

&lt;p&gt;Couch Potato is available as a gem from http://gems.github.com, so you can just do&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;err&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:/&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gems&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;github&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;com&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# if you haven't alread
&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;langalex&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;couch_potato&lt;/span&gt;

&lt;span class=&quot;err&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'rubygems'&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'couch_potato'&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'couch_potato'&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;CouchPotato&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;database_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'name of the db'&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Alternatively you can  download the sources from github. If you are using rails just copy the files into vendor/plugins, create a RAILS_ROOT/config/couchdb.yml file (see the README for the format) and you are ready to go. For other applications you will have to require the lib/couch_potato.rb file and then set the database name by calling &lt;code&gt;CouchPotato::Config.database_name = 'name of the db'&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;As Couch Potato is still very young you can expect its feature set to grow quite a bit in the near future. What you can download now is the very core together with a few features giving you a glimpse of what is about to come:&lt;/p&gt;

&lt;h3&gt;Persistence&lt;/h3&gt;

&lt;p&gt;Create a new class and make its instances persistable by including the Persistence module. As there is no schema in a CouchDB you have to declare the properties you want to persist:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;User&lt;/span&gt;
  &lt;span class=&quot;kp&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;CouchPotato&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Persistence&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;property&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now you can save your objects:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'joe'&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;save&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# or save!&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Properties:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; 'joe'
&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:first&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'joe'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'joey'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:last&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'doe'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:middle&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'J'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# you can set any ruby object that responds_to :to_json
&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;_id&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; &quot;02097f33a0046123f1ebc0ebb6937269&quot;
&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;created_at&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; Fri Oct 24 19:05:54 +0200 2008&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;You can of course also retrieve your instance:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;no&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;02097f33a0046123f1ebc0ebb6937269&quot;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; &amp;lt; #User 0x3075&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3&gt;Associations&lt;/h3&gt;

&lt;p&gt;As of now has_many and belongs_to are supported. By default the associated objects are stored in separate documents linked via foreign keys just like in relational databases.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;User&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;has_many&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:addresses&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:dependent&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:destroy&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Address&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;belongs_to&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:user&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;property&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:street&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;addresses&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;build&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:street&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'potato way'&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;addresses&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; &amp;lt; #Address 0x987&amp;gt;
&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;addresses&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create!&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# raises an exception as street is blank
&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;addresses&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; true&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;When saving an object all associated objects are automatically saved as well. All these save operations are sent to CouchDB in one operation which means the whole process is atomic across all objects saved, plus only one database roundtrip is required making it much faster.&lt;/p&gt;

&lt;p&gt;As CouchDB can not only store flat structures you also store associations inline:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;User&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;has_many&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:addresses&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:stored&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:inline&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This will store the addresses of the user as an array within your CouchDB document.&lt;/p&gt;

&lt;h3&gt;Callbacks&lt;/h3&gt;

&lt;p&gt;Couch Potato supports the usual lifecycle callbacks known from ActiveRecord:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;User&lt;/span&gt;
  &lt;span class=&quot;kp&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;CouchPotato&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Persistence&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;before_create&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:do_something_before_create&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;after_update&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:do_something_else&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3&gt;Versioning&lt;/h3&gt;

&lt;p&gt;Couch Potato supports versioning your objects, very similar to the popular acts_as_versioned plugin for ActiveRecord. To use it include the module:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Document&lt;/span&gt;
  &lt;span class=&quot;kp&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;CouchPotato&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Persistence&lt;/span&gt;
  &lt;span class=&quot;kp&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;CouchPotato&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Versioning&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;After that your object will have a version that gets incremented on each save.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;doc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;doc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; 1
&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;doc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;save&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;doc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; 2&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;You can access the older versions via the versions method.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;doc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;versions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; 1&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;When passing a version number the version method will only return that version:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;doc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;versions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; 1&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3&gt;Ordered Lists&lt;/h3&gt;

&lt;p&gt;Couch Potato supports ordered lists for has_many relationships (with the :stored =&amp;gt; :separately option only), very similar to the popular acts_as_list plugin for ActiveRecord. To use it include the module:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PersistenArray&lt;/span&gt;
  &lt;span class=&quot;kp&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;CouchPotato&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Persistence&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;has_many&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:items&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Item&lt;/span&gt;
  &lt;span class=&quot;kp&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;CouchPotato&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Ordering&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;belongs_to&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:persistent_array&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;set_ordering_scope&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:persistent_array_id&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;PersistenArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;item1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create!&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;item1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;position&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; 1
&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;item2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create!&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;item2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;position&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; 2&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;You can move items up and down simply by changing the position:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;item2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;position&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;item2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;save!&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;item1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;position&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; 2&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;Couch Potato is very young and it’s open source, so if you find any bugs (you most definitely will) please go to the &lt;a href=&quot;http://upstream.lighthouseapp.com/projects/18819-couch-potato&quot;&gt;bug tracker&lt;/a&gt; and file a ticket. If you want to contribute please create a fork on github. If you have ideas for improvements please email me or comment on this post.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2008/10/barcamp-berlin-3-looking-back/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2008/10/barcamp-berlin-3-looking-back/"/>
    <title>Barcamp Berlin 3: looking back</title>
    <updated>2008-10-20T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Last weekend was &lt;a href=&quot;barcampberlin3.org/&quot;&gt;Barcamp Berlin 3&lt;/a&gt; and I was there, too. This year’s location was the Deutsche Telekom Haupstadtrepräsentanz - an awesome place.&lt;/p&gt;

&lt;p&gt;Anyway, the purpose of this post is to summarize the sessions i attended and found worthy to write about. So here we go. Slides are mostly posted in the &lt;a href=&quot;http://barcampberlin3.mixxt.org/networks/wiki/index.Slides&quot;&gt;barcamp wiki&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;How to promote your Web 2.0 Application&lt;/h3&gt;
&lt;p&gt;This first session was held by Michael Sliwinski of &lt;a href=&quot;http://nozbe.com&quot;&gt;nozbe.com&lt;/a&gt; - a very energetic talk presenting a couple of tips and tricks to turn the visitors of your web tool into caring users and finally paying customers. Michael has built nozbe for himself and only later started turning into and app for others. His idea(l)s are heavily influenced by the &lt;a href=&quot;http://37signals.com&quot;&gt;37signals&lt;/a&gt; guys - looks like we operate on the same level as he does :)&lt;/p&gt;

&lt;p&gt;Back to his talk, some of his points were: show people how to use our site, show them the benefits hey will have from using your product (not your features, their benefits), tell them why to use this app and not the competition, testimonials are important even though nobody reads them, email still works - send newsletters and special offers - much higher impact than blog posts, people still don’t use or even understand RSS. And most importantly: love your app and show that love to others.&lt;/p&gt;

&lt;h3&gt;doingtext&lt;/h3&gt;
&lt;p&gt;We skipped the next session in order to &lt;del&gt;prepare&lt;/del&gt; go through &lt;a href=&quot;http://doingtext.com/discussions/df43bcb635aeae21da15649e25beccc7&quot;&gt;our own presentation&lt;/a&gt; titled “getting things written” one last time (ok seriously we had started working on it the day before). We ended up with around 70 slides for a 30 minute talk. I had added a &lt;a href=&quot;http://blog.doingtext.com/2008/10/this-weeks-newbies-presentations-and-text-formatting-with-textile/&quot;&gt;presentation feature&lt;/a&gt; to &lt;a href=&quot;http://doingtext.com&quot;&gt;doingtext&lt;/a&gt; the week before so we did the entire show straight from the website. We had decided to make this an entertainment show so we started explaining what paper was and how to use it and then applied the principles people use to work on texts with paper (show text to others, comment, highlight, talk about it, view history, merge changes) to the various online tools available (sending ms word documents by email, google docs, writewith, adobe buzzword), then text editos with collaboration features (gobby, subethaedit) and finally presenting doingtext and its communication focused approch to text collaboration.&lt;/p&gt;

&lt;p&gt;It looks like the talk was good - we had around 60 people attending and staying until the end of a longer discussion following our presentation.&lt;/p&gt;

&lt;p&gt;Today I repeated the same talk at the &lt;a&gt;Webmontag Berlin&lt;/a&gt;. Having only 8 minutes of time I ended up rushing through the 70 slides and almost rapping the words in super high speed - earning quite &lt;a href=&quot;https://twitter.com/matejunkie/statuses/967810924&quot;&gt;a&lt;/a&gt; &lt;a href=&quot;https://twitter.com/skddc/statuses/967947111&quot;&gt;bit&lt;/a&gt; &lt;a href=&quot;https://twitter.com/klimpong/statuses/967851164&quot;&gt;of&lt;/a&gt; &lt;a href=&quot;https://twitter.com/Bumi/statuses/967812687&quot;&gt;praise&lt;/a&gt; - thanks everyone.&lt;/p&gt;

&lt;h3&gt;books for freaks&lt;/h3&gt;
&lt;p&gt;This wasn’t really a presentation but more a everyone talk about their favorite books thing. &lt;a href=&quot;http://www.amazon.de/Here-Comes-Everybody-Organizing-Organizations/dp/1594201536/ref=sr_1_2?ie=UTF8&amp;amp;s=books-intl-de&amp;amp;qid=1224541023&amp;amp;sr=8-2&quot;&gt;Here comes everybody&lt;/a&gt; (great book about how people gather on the internet, how communities work, how the economy shifts from limited production capabilities to limitless production and much more), but also fictional books. Charles Stross was mentioned as the author of a couple of weird (in the positive sense) sci-fi books (The Atrocity Archives) - will buy one of those soon.&lt;/p&gt;

&lt;h3&gt;jquery tips &amp;amp; tricks&lt;/h3&gt;
&lt;p&gt;The presentation itself was actually a bit lame (sorry guys) but it reminded e that I really wanted to try out jQuery. On the next project - promised. &lt;code&gt;$('.item').show().siblings().hide()&lt;/code&gt; - pretty slick eh?&lt;/p&gt;

&lt;h2&gt;our favourite tv series&lt;/h2&gt;
&lt;p&gt;Very good session for leaning back and watching funny scenes from a couple of tv series. They even mentioned &lt;a href=&quot;http://de.youtube.com/watch?v=Pya1wq91dv4&quot;&gt;Top Gear&lt;/a&gt; - the funniest and most overproduced car magazine ever.&lt;/p&gt;

&lt;h2&gt;death to the ipod&lt;/h2&gt;
&lt;p&gt;This one was mostly about a pretty cool gadget called the &lt;a href=&quot;http://www.pacemaker.net/&quot;&gt;pacemaker&lt;/a&gt; - basically a mobile dj tool that fits in your hand. Comes with 2 decks, crossfader, equalizer, effects, beatcounter, prelistening via a separate headphone out etc. - pretty cool, I even got to test it out for a couple of minutes.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2008/10/resource-route-methods-for-custom-templates/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2008/10/resource-route-methods-for-custom-templates/"/>
    <title>How to get resource route methodes for custom templates in rails</title>
    <updated>2008-10-08T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;If you write a custom template handler, e.g. for pdf templates you might want to use the rails resource route methods e.g. new_users_path. Here is the way how to get this working for a imaginary PDF template handler.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PdfRender&lt;/span&gt;

    &lt;span class=&quot;nb&quot;&gt;attr_reader&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:controller&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;#delegate the url_for call to the controller
&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;delegate&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:url_for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:to&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:controller&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;action_view&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@action_view&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;action_view&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@controller&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;action_view&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;controller&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# more handler code
&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#install routes for the handler
&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;ActionController&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Routing&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Routes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;named_routes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;PdfRenderer&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2008/10/product-launch-doingtextcom/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2008/10/product-launch-doingtextcom/"/>
    <title>Product Launch: doingtext.com</title>
    <updated>2008-10-01T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;We have just launched the closed beta of a new product: &lt;a href=&quot;http://doingtext.com&quot;&gt;doingtext.com&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;Doingtext is a text collaboration tool. After entering your invite key you will be able to start a new discussion about a text that is already there or to be written. You can send the URL of this discussion to anyone you want (they don’t need an account or invite) and they can comment on every line of your text and suggest changes.&lt;/blockquote&gt;

&lt;p&gt;If you are working with texts please head over and &lt;a href=&quot;http://doingtext.com/invites&quot;&gt;get a beta invite&lt;/a&gt; and help us turn doingtext into a great product. We rely on your feedback.&lt;/p&gt;

&lt;p&gt;We are &lt;a href=&quot;https://twitter.com/doingtext&quot;&gt;posting regular updates on twitter&lt;/a&gt; and the &lt;a href=&quot;http://blog.doingtext.com&quot;&gt;doingtext blog&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2008/09/a-couchdb-primer-for-an-activerecord-mindset/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2008/09/a-couchdb-primer-for-an-activerecord-mindset/"/>
    <title>A CouchDB primer for an ActiveRecord mindset</title>
    <updated>2008-09-25T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;div style=&quot;float:right; margin-left: 10px; margin-bottom: 10px&quot;&gt;
  &lt;img src=&quot;http://upstream-berlin.com/wp-content/uploads/2008/09/cabernet-couch-300x178.jpg&quot; alt=&quot;picture from http://uk.gizmodo.com/2006/07/27/kick_back_on_the_cabernet_couc.html&quot; title=&quot;Couch&quot; width=&quot;300&quot; height=&quot;178&quot; /&gt;
  &lt;div style=&quot;font-size: 80%&quot;&gt;That couch was our intro picture at the workshop. &lt;br /&gt;(picture from &lt;a href=&quot;http://uk.gizmodo.com/2006/07/27/kick_back_on_the_cabernet_couc.html&quot;&gt;gizmodo.com&lt;/a&gt;)&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Monday was the first event at our new &lt;a href=&quot;htp://hallenprojekt.de/upstream&quot;&gt;upstream office&lt;/a&gt;, starring &lt;a href=&quot;https://twitter.com/janl&quot;&gt;@janl&lt;/a&gt; and &lt;a href=&quot;https://twitter.com/langalex&quot;&gt;me&lt;/a&gt; presenting an introduction to &lt;a href=&quot;http://couchdb.org&quot;&gt;CouchDB&lt;/a&gt; - including a new hands on examples part - and afterwards an overview of what can be done with CouchDB in Ruby so far. The talks were followed by a discussion that gave (hopefully not only) me a couple of new insights I want to share here - after summarizing the evening for the people who couldn’t make it. There will also be a video recording with synchronized slides of both talks be available in the next days (thanks &lt;a href=&quot;https://twitter.com/klimpong&quot;&gt;@klimpong&lt;/a&gt;).&lt;/p&gt;

&lt;h3&gt;What is CouchDB&lt;/h3&gt;

&lt;p&gt;CouchDB ist the new cool kid on the block. It’s a document oriented database that has replication built in, can scale massively and uses an HTTP REST interface to query it. Documents are stored as JSON constructs and can be queried with views that are built using &lt;a href=&quot;http://en.wikipedia.org/wiki/Mapreduce&quot;&gt;Map-Reduce&lt;/a&gt; (a smaller company called google has had a bit of success with that recently). Oh and it’s written in &lt;a href=&quot;http://upstream-berlin.com/2007/07/24/programming-erlang-functional-programming/&quot;&gt;Erlang&lt;/a&gt;. Jan has given a number of talks on numerous events already, so there are already a couple of &amp;lt;a href=”“http://mwrc2008.confreaks.com/10lehnardt.html&amp;gt;videos&amp;lt;/a&amp;gt; and &lt;a href=&quot;http://www.slideshare.net/bbcwebdev/introduction-into-couchdb-jan-lehnardt-presentation&quot;&gt;slides&lt;/a&gt; available - not from the hands on part though :) For that you should watch &lt;a href=&quot;http://jan.prima.de/~jan/plok/&quot;&gt;his blog&lt;/a&gt; I guess.&lt;/p&gt;

&lt;h3&gt;Help me get rid of SQL&lt;/h3&gt;

&lt;p&gt;So as promised in the title of this post I am now going to concentrate on the need of people like me: people who have been using some sort of SQL based database for ages and things like ActiveRecord for not so many ages but enough to wash their brain to think in relations and joins and columns and all that. Btw here are &lt;a href=&quot;http://upstream-berlin.com/wp-content/uploads/2008/09/couchdb_ruby.pdf&quot;&gt;my slides&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;My talk starts with a couple of random thoughts (that are more clear to me now after talking them through with some smart people). With relational databases we used to have tables, columns, rows, joins and SQL queries. We were mapping complex objects onto flat tables and since that doesn’t work so well used multiple tables and helper tables to make it all happen. With CouchDB pretty much all that is gone. Instead of rows in tables with a fixed schema we now have schema-less documents that can store pretty much any kind of data structure. Instead of SQL with its known power and problems we now have those map-reduce views that work completely differently. On the one hand that means more freedom on how to do stuff, on the other it means more questions on how actually do it. To make a long story short: &lt;strong&gt;the essential problem here is to get rid of our relational mindset. That applies for designing databases as well as for designing tools and libraries for working with CouchDB&lt;/strong&gt;. (apparently a book called document design will help with this, can’t find it on amazon though) Which leads me to the next topic of my talk.&lt;/p&gt;

&lt;h3&gt;CouchDB in the Ruby world&lt;/h3&gt;
&lt;p&gt;I have picked three frameworks that are currently available as open source: &lt;a href=&quot;https://github.com/jchris/couchrest/&quot;&gt;CouchRest&lt;/a&gt;, &lt;a href=&quot;https://github.com/arunthampi/activecouch&quot;&gt;ActiveCouch&lt;/a&gt; and &lt;a href=&quot;https://github.com/paulcarey/relaxdb/tree&quot;&gt;RelaxDB&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;CouchREST is a thin wrapper that provides a simple API for getting, putting, posting and deleting JSON documents to and from the CouchDB HTTP interface. I don’t actually have much more to say about it. It works, it doesn’t do a lot and that’s all great. Use it. No objections from my side.&lt;/p&gt;

&lt;p&gt;ActiveCouch is - as the name suggests - inspired by ActiveRecord. It provides an ActiveCouch::Base class you can inherit from. After that you have to define the properties you want to store (you can’t obviously get those from the database schema anymore as with ActiveRecord) and then you can start creating, saving and finding objects. They have also integrated migrations that allow you to create (very simple) views using a ruby syntax. Without going too much into detail I think these guys haven’t come so very far in moving from the relational to the document mindset. CouchDB just doesn’t fit very well with migrations and records. Plus there is hardly any documentation to find about it at all. Sorry, not going to use it as it is now.&lt;/p&gt;

&lt;p&gt;RelaxDB at first looks very similar to ActiveCouch. Inherit from base class, define properties, create, save, find by id. But: they don’t have migrations (one step futher away from SQL, one step closer to CouchDB). Instead they automatically generate a view when you call a find method for the first time. Sounds like a good idea to me. RelaxDB also supports basic associtations, so you can declare a has_many relationship between two classes and they get stored in separate documents. Definitely useful but still the same as ActiveRecord.&lt;/p&gt;

&lt;h3&gt;CouchPotatoe&lt;/h3&gt;

&lt;p&gt;CouchPotatoe now is my own take on a Ruby persistence layer for CouchDB. Since I only have been coding on anything CouchDB related for 3 days I am not claiming for it to be anything close to useful or better than any other framework. For now its main purpose is to help me learn Couch (as we long time CouchDBers say ;) ) and its principles. But that might change in the future, who knows…&lt;/p&gt;

&lt;p&gt;So anyway, before letting you into the huge set of features let me stress this again: the problem to solve here is not to clone ActiveRecord to work with Couch (we don’t have records anymore, we have documents) but to come up with new concepts that help writing apps on top of documents.&lt;/p&gt;

&lt;p&gt;So the first and currently only features (the CouchPotatoe::Persistence Module) doesn’t actually do much else than the other frameworks (but a bit :) ). It converts a Ruby object into JSON and stores it into CouchDB and loads and converts it back into a Ruby object.
The difference between a record and a document is that a record consists of a flat list of attributes while a document can have a deeper structure, e.g. a tree, which can have lists or subtrees and all that. Potatoe (as we long time CouchPotatoe devs say) helps you …. (damnit, i’m just &lt;a href=&quot;http://en.wikipedia.org/wiki/Potatoe#Spelling&quot;&gt;reading on wikipedia that “Potatoe” is actually considered incorrectly spelled and it should be “Potato”&lt;/a&gt; … oh well) … to build these more complex objects by providing a couple of helpers. The first is called &lt;code&gt;property&lt;/code&gt; and lets you simply define an attribute of any value and type that then gets stored as is into Couch. The second is called &lt;code&gt;has_many&lt;/code&gt;. Although that might sound familiar it’s not the same as the ActiveRecord version. With &lt;code&gt;has_many&lt;/code&gt; you can chose to either store a list of sub objects inline into you document or as separate documents. In both cases everything is stored and retrieved automagically for you. So you can either do sort of join multiple documents like you would in a relational database or you can create more complex documents. I’m planning to add more of those helpers as time and ideas progress (trees, inline versioned attributes… if you have any ideas please tell me).&lt;/p&gt;

&lt;p&gt;The second feature that deals with retrieving stuff. It’s only a concept for now so nothing is implemented yet. During the workshop the idea came up that instead of providing some magic finder methods like in ActiveRecord to call and generate views, those should be handled as first class objects. I think I have only scratched the surface of what you can do with Couch views but I have heard people who know what they are talking about use the word &lt;em&gt;crazy&lt;/em&gt; to describe it. So basically that sounds just like an idea very well worth trying and I’ll implement something along the lines of mapping a Ruby class to a view and provide helpers to generate, update and query that view. Whatever it may look like.&lt;/p&gt;

&lt;p&gt;As soon as those basics are implemented I will put the project up on my &lt;a href=&quot;https://github.com/langalex&quot;&gt;GitHub account&lt;/a&gt; and that hope to be able to raise a bit of interest. So be patient a couple of weeks maybe. So long, I’ll be posting updates here when I have more to tell.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2008/09/let-the-text-fields-grow/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2008/09/let-the-text-fields-grow/"/>
    <title>Let the text fields grow</title>
    <updated>2008-09-18T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;&lt;a href=&quot;http://farm3.static.flickr.com/2127/2496729637_138b7290fd.jpg?v=0&quot;&gt;&lt;img alt=&quot;&quot; src=&quot;http://farm3.static.flickr.com/2127/2496729637_138b7290fd.jpg?v=0&quot; title=&quot;Fields of Green&quot; class=&quot;alignleft&quot; width=&quot;250&quot; height=&quot;166&quot; /&gt;&lt;/a&gt;I find it rather hard to choose the right size for text boxes. They are either to small so that you feel like caged in while writing into them or they are so big that you feel lost in that great white emptiness. These times are past since someone came up with auto expanding text areas (e.g. &lt;a href=&quot;http://facebook.com&quot;&gt;facebook&lt;/a&gt; uses them).&lt;/p&gt;

&lt;p&gt;For one of our &lt;a href=&quot;http://kommt-mit.de&quot;&gt;rails apps&lt;/a&gt; we use John R. Wulff’s ‘&lt;a href=&quot;https://github.com/joergbattermann/text_area_with_auto_resize/tree&quot;&gt;text_area_with_auto_resize&lt;/a&gt;’ plugin. The plugin will give you auto growing text boxes for all text_area calls. I wrote a little &lt;a href=&quot;http://upstream-berlin.com/wp-content/uploads/2008/09/inplace_text_area_with_auto_resize.js&quot;&gt;extension&lt;/a&gt; for prototype’s (1.6.x) in-place editing text area that makes these text areas auto growing as well. So we can have auto growing text areas all over the place. ;)&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2008/09/couchdb-workshop/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2008/09/couchdb-workshop/"/>
    <title>CouchDB-Workshop</title>
    <updated>2008-09-17T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Am 22.9. 2008 gibt es einen Workshop zu &lt;a href=&quot;http://couchdb.org/&quot;&gt;CouchDB&lt;/a&gt; mit &lt;a href=&quot;https://twitter.com/janl&quot;&gt;Jan Lehnardt&lt;/a&gt; (CouchDB maintainer) und &lt;a href=&quot;https://twitter.com/langalex&quot;&gt;mir&lt;/a&gt;. Nach einer kurzen Einführung wird der Fokus vor allem auf hands on experience liegen, d.h. es wird Beispiele und Code geben. Nach Jans CouchDB-Teil werde ich einen Überblick über die derzeitigen Ruby-Bibliotheken geben und wie damit der schnelle Einstieg zu schaffen ist.&lt;/p&gt;

&lt;p&gt;“Leider” sind bereits &lt;a href=&quot;http://wiki.upstream-berlin.com&quot;&gt;alle Plätze ausgebucht&lt;/a&gt;, da wir nur begrenzt Stühle zur Verfügung haben. Wer trotzdem kommen will trägt sich am besten in die Warteliste ein und begnügt sich dann eventuell mit einem Platz auf dem Boden. Sicherlich wird das nicht der letzte Workshop bei uns sein. Mal sehen was uns demnächst noch so alles einfällt…&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2008/09/rails-camp-denmark-back-to-nature-the-ruby-way/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2008/09/rails-camp-denmark-back-to-nature-the-ruby-way/"/>
    <title>Rails Camp Denmark - Back to nature the ruby way.</title>
    <updated>2008-09-07T00:00:00+00:00</updated>
    <author>
      <name>thilo</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;http://railscamp08.org/images/yield.png&quot; title=&quot;Rails Camp 08 in Denmark&quot; class=&quot;alignleft&quot; width=&quot;247&quot; height=&quot;166&quot; /&gt;&lt;/p&gt;

&lt;p&gt;If you ever meet a bunch of people sitting with their laptops in front of a tent in the middle of nowhere you might have stumbled across a rails camp. &lt;a href=&quot;http://railscamp08.org&quot;&gt;The rails camp&lt;/a&gt; is a new &lt;a href=&quot;http://en.wikipedia.org/wiki/Unconference&quot;&gt;unconference&lt;/a&gt; where ruby hackers meet to exchange ideas and have fun in an open and inspiring environment. And what could be more open and inspiring when being torn out of your everyday internet/office/city routine by camping. Being cut off from the internet will bring up new approaches and insights to overcome programming challenges as well as bring the members of the ruby community more in touch with each other. Of course it’s also a hell of fun when you’re off from your daily routine with a lot to laugh, drink and play.&lt;/p&gt;

&lt;p&gt;That’s why we will attend the &lt;a href=&quot;http://railscamp08.org/#dk_october_2008&quot;&gt;rails camp in Denmark&lt;/a&gt; to meet our fellow rubyists (eg. from &lt;a href=&quot;http://irb.no/-/bulletin/show/91505_rubyists-visiting-oslo&quot;&gt;Oslo&lt;/a&gt; or the last &lt;a href=&quot;http://www.facebook.com/group.php?gid=17995395190&quot;&gt;Euruko&lt;/a&gt;) and might code some crazy ideas. The Rails Camp Denmark will likely take place at the &lt;del&gt;&lt;a href=&quot;http://www.doodle.ch/participation.html?pollId=7te8uh2r73fn5ubc&quot;&gt;end of October&lt;/a&gt; - the final date isn’t set yet -&lt;/del&gt; 24th October just outside &lt;a href=&quot;http://maps.google.de/maps?f=q&amp;amp;hl=de&amp;amp;geocode=&amp;amp;q=Svendborg,+Denmark&amp;amp;ie=UTF8&amp;amp;ll=55.541065,9.827271&amp;amp;spn=1.892865,4.515381&amp;amp;z=8&quot;&gt;Svendborg on the Danish island of Funen&lt;/a&gt;. If you want to join the rails camp you should invest 75 Euro and be prepared for camping over a weekend. More details and discussions can be found in the &lt;a href=&quot;http://groups.google.com/group/railscamp-dk&quot;&gt;google group&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Hold your thumbs that the temperatures at the End of October don’t drop below zero so we won’t have to keep our &lt;a href=&quot;http://arstechnica.com/journals/apple.ars/2006/5/1/3804&quot;&gt;mac books running to heat our tent&lt;/a&gt;. ;)&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2008/08/cucumber-next-generation-rspec-story-runner/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2008/08/cucumber-next-generation-rspec-story-runner/"/>
    <title>cucumber - next generation rspec story runner (updated)</title>
    <updated>2008-08-26T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;yesterday while skimming through &lt;a href=&quot;https://twitter.com/langalex&quot;&gt;my Twitter timeline&lt;/a&gt; I &lt;a href=&quot;https://twitter.com/aslak_hellesoy/statuses/898654198&quot;&gt;stumbled&lt;/a&gt; across aslak hellesoy’s &lt;a href=&quot;https://github.com/aslakhellesoy/cucumber/wikis&quot;&gt;cucumber&lt;/a&gt;. cucumber is a rewrite of the &lt;a href=&quot;http://rspec.info/&quot;&gt;rspec story runner&lt;/a&gt;, allowing you to write &lt;a href=&quot;http://en.wikipedia.org/wiki/User_stories&quot;&gt;user stories&lt;/a&gt; in plain text and make them executable by adding a bunch of ruby magic.&lt;/p&gt;

&lt;p&gt;the features of both - story runner and cucumber - are basically the same. cucumber just gets rid of all these little glitches that disturb your work flow when working with story runner and adds that little bit of polish that makes the difference. but see for yourself:&lt;/p&gt;

&lt;p&gt;first of all it replaces those 5 lines of infrastructure code you needed for every story with a set of conventions. (require helper, require and run story text file, load additional steps files). stories are now called features and have a corresponding .feature file extension. the steps are located in *steps.rb files as before but don’t require any more &lt;code&gt;steps_for&lt;/code&gt; or &lt;code&gt;with_steps&lt;/code&gt; calls.&lt;/p&gt;

&lt;p&gt;also improved is running stories: there’s a script called cucumber that can run a feature directly by calling &lt;code&gt;cucumber my_feature.feature&lt;/code&gt; and there’s a rake task (&lt;code&gt;rake features&lt;/code&gt;) to run all of them - say hello to my build server.&lt;/p&gt;

&lt;p&gt;the wording has also changed. instead of &quot;as a &amp;lt;role&amp;gt; I want to be able to &amp;lt;feature&amp;gt; so that &amp;lt;outcome&amp;gt;&quot; we now have &quot;in order to &amp;lt;outcome&amp;gt; a &amp;lt;role&amp;gt; should be able to &amp;lt;feature&amp;gt;. this might seem like a minor twist but I think it’s an important one. now you (your customer) have to think about which outcome you want even before you talk about features. if you can’t come up with a sensible outcome don’t even finish that sentence. hope that will end the &quot;oh we still need a 'so that' clause for that feature&quot; discussions.&lt;/p&gt;

&lt;p&gt;adding before and after hooks has now become easier. instead of &lt;a href=&quot;http://upstream-berlin.com/2008/07/14/rspec-story-runner-auf-deutsch/&quot;&gt;adding listeners&lt;/a&gt; you now just create a &lt;code&gt;Before do .. end&lt;/code&gt; or &lt;code&gt;After do .. end&lt;/code&gt; block.&lt;/p&gt;

&lt;p&gt;another addition is the inclusion of a couple of &lt;a href=&quot;http://upstream-berlin.com/2008/08/09/functional-testing-awesomeness-with-webrat/&quot;&gt;webrat&lt;/a&gt; helpers. some people might argue that this something an application neutral framework like rspec (after all you can test anything with rspec, not just web apps) shouldn’t care about, but honestly, i think at least 80% of the people using rspec use it for web apps, so helping these people write better specs should be worth it. and webrat is definitely a big step in the right direction if you want to be &lt;del&gt;testing&lt;/del&gt; specing webapps. if you don’t need it just delete the &lt;em&gt;common_webrat.rb&lt;/em&gt; file. if you do use rspec for web testing take a look at that file. with this you can start test driving your web app without writing any ruby code by clicking links and submitting forms.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;  &lt;span class=&quot;no&quot;&gt;When&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;go&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/&quot;&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;And&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;follow&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;New Entry&quot;&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;And&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fill&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;my first blog entry&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Body&quot;&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;And&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;press&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Submit&quot;&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;Then&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;should&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;see&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Blog entry was saved&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Simple as that. Your (first?) cucumber feature spec. If you want more source code take a closer look at the &lt;a href=&quot;https://github.com/aslakhellesoy/cucumber/tree/master/examples&quot;&gt;examples on github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Last but not least the output has been enhanced. By default all successful steps are now in green, pending steps in yellow and failures in red - and the backtrace can be found next to that failed step instead of at the end as it used to be.&lt;/p&gt;

&lt;p&gt;&lt;del&gt;What’s still missing is a decent TextMate bundle:&lt;/del&gt;&lt;/p&gt;

&lt;p&gt;&lt;del&gt;Unfortunately &lt;a href=&quot;http://doingtext.com&quot;&gt;I’m really busy these days&lt;/a&gt; so I probably won’t be the one making this feature shine green. Hopefully someone else will soon.&lt;/del&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt; There is a TextMate bundle now an it’s working. It highlights your feature’s syntax and lets you run a single scenario from within TextMate. Great. &lt;a href=&quot;https://github.com/bmabey/cucumber-tmbundle/tree/master&quot;&gt;Get it from GitHub&lt;/a&gt; while it’s hot. (thanks Paul)&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2008/08/functional-testing-awesomeness-with-webrat/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2008/08/functional-testing-awesomeness-with-webrat/"/>
    <title>Functional Testing awesomeness with Webrat</title>
    <updated>2008-08-09T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;I recently started using &lt;a href=&quot;http://www.brynary.com/uploads/webrat/rdoc/index.html&quot;&gt;Webrat&lt;/a&gt; in one of our project’s test suites and fell in love with it immediately. Webrat is a functional testing library for Ruby on Rails which you can use in your rails integration tests (or better: the RSpec Story Runner) to let your tests walk through your application. The big difference between Webrat and a “standard” Rails Integration Test is that with Webrat you click on actual links and submit actual forms, instead of just sending requests to you application.&lt;/p&gt;

&lt;p&gt;This closes an important gap to testing in a real browser (like with Selenium) and now gives us much more confidence in our tests - we now not only know that our models and controllers integrate, but we also know that they work with our views. That had been an unpleasant problem for a while. All our tests were passing but we still had way too many problems with invalid links and forms submiting to the wrong action. No more.&lt;/p&gt;

&lt;p&gt;Webrat provides a sort of DSL (everything is a DSL nowaday isn’t it? :) ) for interacting with all the HTML elements you would find in a HTML document like links, checkboxes, radio buttons, text fields, selects and submit buttons. A test case might look like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;visits&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'/'&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;clicks_link&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'Sign up'&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;fills_in&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'username'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:with&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'joedoe'&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;selects&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'Male'&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;checks&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'Terms of Service'&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;clicks_button&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Webrat now fetches the start page of the application, parses it and follows the first link labelled with “Sign Up”, gets the corresponding page, parses the form, fills in the values and submits it. Whenever a response comes back with anything else than a 200 (or 302, in which case that redirection is followed automatically) or an element on the page can’t be found, Webrat complains with an error and you know there’s probably something wrong in your view.&lt;/p&gt;

&lt;p&gt;In Order to get this working with Story Runner I have wrapped most calls into When calls, for example:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;no&quot;&gt;When&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'I click on $link'&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;link&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;clicks_link&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;link&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;When&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'I submit the form'&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;clicks_button&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;When&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'I select $option'&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;option&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;selects&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;option&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;And now my Story looks like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;no&quot;&gt;Scenario&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;successful&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;signup&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;When&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;go&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;the&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;page&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;And&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;click&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;on&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Sign&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;up&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;And&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Male&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;And&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;check&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;the&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Terms&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Service&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;And&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;submit&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;the&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;form&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;Then&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;the&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;page&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;should&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;show&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Thank&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;you&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;signing&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;up&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;I’m not sure yet if it makes sense to release those wrappers as a plugin, we’ll see. After all it’s just a few lines of trivial code.&lt;/p&gt;

&lt;p&gt;By the way, &lt;a href=&quot;https://github.com/brynary/webrat&quot;&gt;Webrat is hosted on awesome github&lt;/a&gt;, so after using it for 30 minutes (seriously) &lt;a href=&quot;https://github.com/langalex/webrat&quot;&gt;I forked my own version&lt;/a&gt; and added support for the Rails date helpers (&lt;code&gt;selects_date Date.today, :from =&amp;gt; 'signup_date'&lt;/code&gt; instead of &lt;code&gt;selects 'December', :from =&amp;gt; 'signup_date_2i'; selects '2008', :from =&amp;gt; 'signup_date_1i'; selects '01', :from =&amp;gt; 'signup_date_3i'&lt;/code&gt;) and Emails (e.g. clicking the activation link in a signup email). (Bryan Helmkamp, if you read this: Why haven’t you pulled this (and in fact any other fork) into the main line yet?)&lt;/p&gt;

&lt;p&gt;All in all the experience has been fantastic. Webrat is on all my Rails projects now. The only - conceptual - problem so far is that I can’t use it to test AJAX. But that’s the topic of another post. And there’s &lt;a href=&quot;https://github.com/johnnyt/webrat&quot;&gt;a fork&lt;/a&gt; on github that experiments with &lt;a href=&quot;http://celerity.rubyforge.org/&quot;&gt;celerity&lt;/a&gt;, which can do AJAX and everything.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2008/07/warum-ich-selbstandig-geworden-bin/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2008/07/warum-ich-selbstandig-geworden-bin/"/>
    <title>Warum ich selbständig geworden bin?</title>
    <updated>2008-07-23T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Ich kann &lt;a href=&quot;http://upstream-berlin.com/2008/03/10/4-tage-woche-und-kreditkarten-fur-alle/&quot;&gt;arbeiten wann ich will&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Ich kann arbeiten &lt;a href=&quot;http://upstream-berlin.com/2008/07/18/oslo-were-comming/&quot;&gt;wo wo ich will&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Ich kann arbeiten &lt;a href=&quot;http://upstream-berlin.com/network/&quot;&gt;mit wem ich will&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Ich kann arbeiten &lt;a href=&quot;http://kommt-mit.de&quot;&gt;woran&lt;/a&gt; ich &lt;a href=&quot;http://hallenprojekt.de&quot;&gt;will&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Ich kann mir &lt;a href=&quot;http://upstream-berlin.com/projects/&quot;&gt;meine Kunden&lt;/a&gt; aussuchen.&lt;/p&gt;

&lt;p&gt;Ich kann &lt;a href=&quot;http://rubyonrails.org/&quot;&gt;arbeiten&lt;/a&gt; &lt;a href=&quot;http://www.apple.com/macbookpro/&quot;&gt;womit&lt;/a&gt; &lt;a href=&quot;http://developer.apple.com/iphone/&quot;&gt;ich&lt;/a&gt; &lt;a href=&quot;http://macromates.com/&quot;&gt;will&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Ich kann &lt;a href=&quot;http://en.wikipedia.org/wiki/Behavior_driven_development&quot;&gt;arbeiten wie ich will&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Ich kann mir &lt;a href=&quot;http://boxhagener119.de/&quot;&gt;mein Büro&lt;/a&gt; aussuchen und einrichten wie ich will.&lt;/p&gt;

&lt;p&gt;Ich kann mir aussuchen, wieviel Geld ich verdienen will.&lt;/p&gt;

&lt;p&gt;Ich lerne jeden Tag mehr dazu, weil ich nicht nur Programmierer, sondern gleichzeitig noch Projektmanager, Geschäftsführer, Administrator, Sales-Fuzzi und sonstwas bin.&lt;/p&gt;

&lt;p&gt;Um das mal zusammenzufassen: ich habe die freiheit zu tun was ich will. Und wenn ich irgendwann mal keine Lust mehr habe kann ich mich immer noch anstellen lassen. Oder nach Hawaii gehen. &lt;em&gt;Darum&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;(Hab ich noch was vergessen? Kommentare…)&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2008/07/oslo-were-comming/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2008/07/oslo-were-comming/"/>
    <title>Oslo we're coming</title>
    <updated>2008-07-18T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;So, finally everything is prep’ed: flights booked, accommodation confirmed, host office organized and cat fed. Tomorrow, very early in the morning, off we go to Oslo to live and work there for one week ;)&lt;/p&gt;

&lt;p&gt;It’s just a short period of time but we’ll try to get the most out off it. &lt;a href=&quot;http://irb.no/-/bulletin/show/91505_rubyists-visiting-oslo&quot;&gt;We got in contact &lt;/a&gt; with the local ruby user group, starting off with a couple of beers on sunday and hopefully exchanging some great wisdom. Maybe we can also initiate a &lt;a href=&quot;http://listas.apesol.org/pipermail/chat-lrug.org/2007-November/001784.html&quot;&gt;speaker exchange&lt;/a&gt; with the &lt;a href=&quot;http://www.rug-b.de&quot;&gt;berlin ruby user group&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thanks to &lt;a href=&quot;http://shortcut.no&quot;&gt;shortcut&lt;/a&gt; who have been kind enough to let us stay at their office for the week, we are mighty excited to get a glimpse on their work culture. And in the evenings and weekends we will surely check out the vicinity of Oslo.&lt;/p&gt;

&lt;p&gt;I’m sure this will be cool work holidays ;) If anyone in Oslo wants to meet us just send an email. We’ll also be posting some photos once we’re there.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2008/07/rspec-story-runner-auf-deutsch/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2008/07/rspec-story-runner-auf-deutsch/"/>
    <title>RSpec Story Runner auf deutsch</title>
    <updated>2008-07-14T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Wir sind gerade mit einem neuen Projekt gestartet - alles richtig  “nach Lehrbuch”: kurze Iterationen, Iteration Planning zusammen mit dem Kunden, Behavior Driven Design, User Stories, automatisierte acceptance Tests - all die schönen Dinge die man in so einem modernen agilen Softwareprojekt haben will.&lt;/p&gt;

&lt;p&gt;Während der Planung der ersten Iteration haben wir zusammen mit dem Kunden User Stories geschrieben, um sie anschließend direkt in den RSpec Story Runner zu werfen, also &lt;code&gt;&lt;em&gt;As a&lt;/em&gt; User &lt;em&gt;I want&lt;/em&gt; to upload my photo &lt;em&gt;So that&lt;/em&gt; everyone can see my smiley face&lt;/code&gt;. Da unsere Kunden deutsch sprechen war das Meeting und dementsprechend auch die Stories auf deutsch, also &lt;code&gt;&lt;em&gt;Als&lt;/em&gt; Benutzer &lt;em&gt;will ich&lt;/em&gt; mein Foto hochladen können &lt;em&gt;damit&lt;/em&gt; alle mein tolles Grinsegesicht sehen können&lt;/code&gt;. Damit der Story Runner damit umgehen konnte mussten wir ihm eine neue Sprache beibringen. So geht’s:&lt;/p&gt;

&lt;p&gt;In &lt;a href=&quot;http://rspec.lighthouseapp.com/projects/5645/tickets/269-stories-in-other-languages&quot;&gt;diesem Ticket&lt;/a&gt; gibt’s einen &lt;a href=&quot;http://rspec.lighthouseapp.com/attachments/10667/story_for_ticket_269.tar.gz&quot;&gt;Patch&lt;/a&gt; für den Story Runner, womit die Schlüsselwörter in den plain text stories konfiguriert werden können. (Wer wie ich mit Kommandozeilenpatchen nicht klar kommt, TextMate hat ein schönes Diff Bundle mit dem man den Patch auf einen RSpec (z.B. 1.1.4) checkout anwenden kann).&lt;/p&gt;

&lt;p&gt;Die Konfiguration kann dann einfach in der &lt;em&gt;stories/helper.rb&lt;/em&gt; landen:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;no&quot;&gt;Spec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Runner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;configure&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;with_story_template_words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;:story&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Story&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;:scenario&lt;/span&gt;       &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'Szenario'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;:given&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'Gegeben'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;:given_scenario&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'Gegebenes Szenario'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;:when&lt;/span&gt;           &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'Wenn'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;:then&lt;/span&gt;           &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'Dann'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;:and&lt;/span&gt;            &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'Und'&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Was diesem Patch allerdings noch fehlt ist die Übersetzung der zu jeder plain text gehörenden steps. Man füge einfach noch folgendes Snippet mit ein:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Spec&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Story&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;StepGroup&lt;/span&gt;
      &lt;span class=&quot;kp&quot;&gt;alias_method&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:Wenn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:When&lt;/span&gt;
      &lt;span class=&quot;kp&quot;&gt;alias_method&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:Gegeben&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:Given&lt;/span&gt;
      &lt;span class=&quot;kp&quot;&gt;alias_method&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:Dann&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:Then&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Und schon kann man seine Stories auf deutsch schreiben (sorry das Beispiel ist irgendwie grauenhaft sinnlos, es ist schon spät):&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;no&quot;&gt;Story&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Als&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ich&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;will&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ich&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ich&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sein&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;damit&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ich&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mal&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;so&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;richtig&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ich&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sein&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;kann&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;Szenario&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ich&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bin&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ich&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bin&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ich&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Gegben&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ich&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Wenn&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ich&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ich&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bin&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Dann&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;soll&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ich&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;auch&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ich&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sein&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Und dann in den zugehörigen steps:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dirname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;__FILE__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'../helper'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;


&lt;span class=&quot;n&quot;&gt;with_steps_for&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:common&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;Gegeben&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'ich'&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@me&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;create_me&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;Wenn&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'ich ich bin'&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@me&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;be_me&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;Dann&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'soll $expected auch $result sein'&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;expected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;should&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;run&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dirname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;__FILE__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'/ich_story.txt'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;RailsStory&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Ach ja, one more thing: Um im Story Runner so eine Art setup/teardown zu haben, einfach noch ein wenig Code in den helper:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MyStoryListener&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;scenario_started&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;story&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scenario&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# hier setup code
&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;method_missing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
     &lt;span class=&quot;c1&quot;&gt;# no-op
&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;Spec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Story&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Runner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;register_listener&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;MyStoryListener&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2008/06/moving-from-rails-20x-to-rails-21/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2008/06/moving-from-rails-20x-to-rails-21/"/>
    <title>Moving from Rails 2.0.x to Rails 2.1 [Updated]</title>
    <updated>2008-06-04T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;We are in the process of moving our production code from &lt;a href=&quot;http://autoki.com&quot;&gt;autoki&lt;/a&gt; to the shiny new &lt;a href=&quot;http://weblog.rubyonrails.org/2008/6/1/rails-2-1-time-zones-dirty-caching-gem-dependencies-caching-etc&quot;&gt;rails 2.1&lt;/a&gt; and thanks to our unit tests/(r)specs we encountered some gotchas and wows we’d like to share.&lt;/p&gt;

&lt;p&gt;Here are some things that change in rails you shold be aware of.
If you are using custom callbacks for you rails observer you now have to tell rails about these callbacks first. Simply add a &lt;code&gt;define_callbacks :your_callback, :your_other_callback&lt;/code&gt; in models with custom callbacks.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;GroupMembership&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;belongs_to&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:member&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:foreign_key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:user_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:class_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'User'&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;define_callback&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:after_confirm&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# required since 2.1
&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;confirm!&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;update_attribute&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'active'&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;callback&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:after_confirm&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# custom callback
&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;GroupMembershipObserver&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Observer&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;after_confirm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;group_membership&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# will be called from confirm! in the GroupMembership
&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;code&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;here&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The call to register custom template handlers in the enviroment has changed from &lt;code&gt;ActionView::Base.register_template_handler&lt;/code&gt; to &lt;code&gt;ActionView::Template.register_template_handler&lt;/code&gt;. With a custom handler you can output pdfs for example.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;c1&quot;&gt;#register a tempalte handler in enviroment.rb or as an initializer
&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# pdf renderer
&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'pdf_render'&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;ActionView&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Template&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;register_template_handler&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'rpdf'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActionView&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;PDFRender&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Consequently the &lt;code&gt;ActionController::MissingTempalte&lt;/code&gt; Error was moved to &lt;code&gt;ActionView::MissingTemplate&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;With the &lt;a href=&quot;http://ryandaigle.com/articles/2007/12/19/what-s-new-in-edge-rails-pluggable-controller-caching&quot;&gt;new Caching&lt;/a&gt; in place the config call changed from &lt;code&gt;fragment_cache_store&lt;/code&gt; to &lt;code&gt;cache_store&lt;/code&gt; to reflect the new unified caching infrastructure.&lt;/p&gt;

&lt;p&gt;If you have included the Observable mixin in one of your ActiveRecord models, like we did before we learned to &lt;a href=&quot;http://upstream-berlin.com/blog/2007/10/27/using-and-testing-activerecordrails-observers/&quot;&gt;harness the power of &lt;code&gt;ActiveRecord::Observer&lt;/code&gt;&lt;/a&gt;, it’s time to abandon these because the &lt;code&gt;changed&lt;/code&gt; method overwrites the new &lt;code&gt;changed&lt;/code&gt; of ActivRecords dirty tracking. So if your stack trace look similar to this…&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;no&quot;&gt;NoMethodError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;undefined&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;method&lt;/span&gt; &lt;span class=&quot;sb&quot;&gt;`each' for true:TrueClass
method attributes_with_quotes in base.rb at line 2574
method update_without_lock in base.rb at line 2479
method update_without_dirty in optimistic.rb at line 70
...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;.. it’s &lt;code&gt;Observable&lt;/code&gt; overwriting the ActiveRecord &lt;code&gt;changed&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;What also comes with &lt;a href=&quot;http://ryandaigle.com/articles/2008/3/31/what-s-new-in-edge-rails-dirty-objects&quot;&gt;dirty tracking&lt;/a&gt; is, that a save call does nothing unless someting really changed. Although this is the desired behavior, it might break logic where you rely on the old behavior, e.g. when you just want to update the time stamps of an object in the database. You can tell the object with &lt;code&gt;attr_name_will_change!&lt;/code&gt; that an attribute should be written to the database.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;visit&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;#should set updated at to current time if visited
&lt;/span&gt;
     &lt;span class=&quot;n&quot;&gt;updated_at_will_change!&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;#tell the user that updated_at field will change
&lt;/span&gt;
     &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;save&lt;/span&gt;
   &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Another ActiveRecord change we found is that &lt;code&gt;:dependent =&amp;gt; true&lt;/code&gt; isn’t supported anymore although I don’t remember a deprecation warning. Just use &lt;code&gt;:dependent =&amp;gt; :nullfiy|:delete|:destroy&lt;/code&gt;.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;has_one&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:profile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:dependent&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;#this will break, use :nullfiy|:delete|:destroy instead
&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;But now let’s come to some nice stuff we discovered.&lt;/p&gt;

&lt;p&gt;ActiveRecord now complains with a &lt;code&gt;stack level to deep&lt;/code&gt; if you have circular dependency behaviors defined, eg. User has_one :profile, :dependent =&amp;gt; :destroy and Profile belogs_to :user, :dependent =&amp;gt; :destroy. Such logic can just be the result of isolated late night coding, so an error is the desired behavior in my opinion.&lt;/p&gt;

&lt;p&gt;Let’s get back to the aforementioned &lt;code&gt;changed&lt;/code&gt; method. Because it returns an array of changed field names, it is a good way to fire after save actions only if a specific field changed. For our autoki application it is a good way to fire some image processing task only if the auto picture changed.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Auto&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;after_save&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:build_new_image&lt;/span&gt;
  &lt;span class=&quot;kp&quot;&gt;private&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;build_new_image&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;regenerate_widget&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;changed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;include?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# only do this when the image was changed
&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;At the end comes the real nasty stuff. Rails 2.1 really wrecks havoc under the plugins we are using. As you might guess the most have to do with caching.&lt;/p&gt;

&lt;p&gt;While the fragement_cache_test plugins error &lt;code&gt;uninitialized constant ActionController::Caching::Fragments::MemoryStore&lt;/code&gt; can be fixed relatively easy by changing the base class for the &lt;code&gt;TestStore&lt;/code&gt;class in &lt;code&gt;frament_cache_test.rb&lt;/code&gt; to &lt;code&gt;ActiveSupport::Cache::MemoryStore&lt;/code&gt; and the calls to &lt;code&gt;reset&lt;/code&gt; have do be changed to &lt;code&gt;clear&lt;/code&gt; the &lt;a href=&quot;extended_fragment_cache&quot;&gt;timed_fragment_cache&lt;/a&gt; plugin and the &lt;a href=&quot;http://rubyforge.org/projects/zventstools/&quot;&gt;extended_fragment_cache&lt;/a&gt; are rendered useless until updated, which we don’t expect to happen, so we put together the &lt;a href=&quot;https://github.com/langalex/local_cache/tree/master/README&quot;&gt;local_cache_plugin&lt;/a&gt; as a resplacement for both.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/mislav/will_paginate/wikis&quot;&gt;Will_paginate&lt;/a&gt; requires an update from git, and it will play nice again. It resides on &lt;a href=&quot;https://github.com&quot;&gt;github&lt;/a&gt; now and can be installed with &lt;code&gt;script/install git://github.com/mislav/will_paginate.git&lt;/code&gt; thanks to the install script git support.&lt;/p&gt;

&lt;p&gt;The paginated search plugin for ferret will require an update too, because of the changes in will_paginate. But it’s still maintained so just grap the &lt;a href=&quot;http://opensoul.org/2007/8/17/acts_as_ferret-will_paginate&quot;&gt;new code from the opensoul blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So that’s all for now. (Wow this post got big)&lt;/p&gt;

&lt;p&gt;We aren’t finish yet so we will post more while we find other noteworthy changes.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2008/06/dealing-with-legacy-databases-multiple-models-per-table/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2008/06/dealing-with-legacy-databases-multiple-models-per-table/"/>
    <title>Dealing with legacy databases - multiple models per table</title>
    <updated>2008-06-04T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;On a recent Rails project I had to use an old database schema that had been designed years ago for some php app. The two main entities in the application were companies and their projects. For whatever reason the designer had created one huge table called organizations with all the companies and projects inside. A row in the database marked a company when the &lt;em&gt;is_company&lt;/em&gt; column was 1 and a project when the &lt;em&gt;is_project&lt;/em&gt; column was 1.&lt;/p&gt;

&lt;pre style=&quot;font-family:courier;font-size: 12px&quot;&gt;
|----------------------------------------------------|
| id | name                | is_company | is_project |
|----------------------------------------------------|
| 1  | google              | 1          | 0          |
| 2  | world domination    | 0          | 1          |
|----------------------------------------------------|&lt;/pre&gt;

&lt;p&gt;In my rails app I created two ActiveRecord models: Company and Project and set the table name for both to organizations:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Company&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;set_table_name&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'organizations'&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Project&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;set_table_name&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'organizations'&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now the real problem was his: when I did a &lt;code&gt;Company.find :all&lt;/code&gt; I got all the companies and all the projects, so I had to do this instead: &lt;code&gt;Company.find :all, :conditions =&amp;gt; {:is_company =&amp;gt; true}&lt;/code&gt; - throughout the project, and whenever I forgot the condition somewhere all the projects showed up in the wrong place. So what I needed was a generic solution.&lt;/p&gt;

&lt;p&gt;To make a longer story short I played with &lt;a href=&quot;http://code.google.com/p/scope-out-rails/&quot;&gt;scope_out&lt;/a&gt; defining a &lt;code&gt;projects&lt;/code&gt; scope on the Company model, so I could at least do &lt;code&gt;Company.find_all_companies&lt;/code&gt; and didn’t have to specify the conditions over and over again - but that still wasn’t DRY at all. One morning short after waking up (the time I usually have the best ideas) it dawned on me how simple the solution actually is:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Company&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;with_scope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:find&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:conditions&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:is_company&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}})&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;super&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;with_scope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:find&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:conditions&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:is_company&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}})&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;super&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;I added a scope around all find and count calls of my model and now I can just do whatever find operations I want and don’t have to think about scopes and tables ever again. Sweet object encapsulation.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2008/06/new-plugin-totally-restful-authorization/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2008/06/new-plugin-totally-restful-authorization/"/>
    <title>New plugin: totally restful authorization</title>
    <updated>2008-06-03T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;We have again released a new plugin. Continuing the series of completely original names it’s called &lt;a href=&quot;https://github.com/langalex/totally-restful-authorization&quot;&gt;totally restful authorization&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The exec summary:&lt;/strong&gt; you can declaratively add permissions to your (ActiveRecord) models for creating, viewing, updating and destroying them. A set of before filters automagically checks all incoming requests on your restful controllers for the permission and grants or denies access based on the permissions declared on the respective model.&lt;/p&gt;

&lt;h3&gt;How to install&lt;/h3&gt;
&lt;p&gt;Now with Rails 2.1 out all you have to do is &lt;code&gt;script/plugin install git://github.com/langalex/totally-restful-authorization.git&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;How to use&lt;/h3&gt;
&lt;p&gt;Include he PermissionCheck Module into the controllers you want to be checked or simply into the ApplicationController to secure your entire application.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ApplicationController&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActionController&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;
  &lt;span class=&quot;kp&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;PermissionCheck&lt;/span&gt;

  &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Second, declare permissions on your model using the built in &lt;a href=&quot;http://en.wikipedia.org/wiki/Domain_specific_language&quot;&gt;domain specific language&lt;/a&gt;.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;User&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;updatable_by&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:admin&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# updatable if updater.admin? return true
&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;updatable_by&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:except&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:admin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# special role self, allow all attributes except some to be updated
&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;updatable_by&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:newbie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:only&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# only allow some attribute to be updated
&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;viewable_by&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:anyone&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# special role, includes nil
&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;destroyable_by&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:admin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# declare multiple roles at once
&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;That’s it. From now on all requests will be checked against your model permissions and be blocked if the authorization fails. For more details see the &lt;a href=&quot;https://github.com/langalex/totally-restful-authorization/tree/master%2FREADME?raw=true&quot;&gt;README&lt;/a&gt; and the &lt;a href=&quot;https://github.com/langalex/totally-restful-authorization/tree/master/test/unit&quot;&gt;unit tests&lt;/a&gt;. (Btw. if anyone has a good idea on how to replace the controller tests with &lt;a href=&quot;http://rspec.info&quot;&gt;RSpec&lt;/a&gt; specs, i.e. get controller specs working in a plugin please tell me)&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2008/06/mysql-teil-1-den-richtigen-index-finden/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2008/06/mysql-teil-1-den-richtigen-index-finden/"/>
    <title>MySql - Teil 1: Den richtigen Index finden</title>
    <updated>2008-06-03T00:00:00+00:00</updated>
    <author>
      <name>thilo</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Die Datenbak MySql ist bei Webanwendungen sehr verbreitet, unter anderem weil sie kostenlos, relativ schnell und etabliert ist. Auch bei uns verrichtet MySQL  zuverlässig seinen Dienst. Grund genug mal unsere Erfahrungen in ein paar Blogeinträge zu gießen und damit das rumlavieren in der sehr umfangreichen &lt;a href=&quot;http://dev.mysql.com/doc/refman/5.1/de/index.html&quot;&gt;offiziellen Dokumentation&lt;/a&gt; auf echte Härtefälle zu beschränken.&lt;/p&gt;

&lt;p&gt;Zuerst schauen wir uns mal an welche Werkzeuge MySQL von Hause aus mitbringt, um beim Erstellen von sinnvollen Indizes zu helfen.&lt;/p&gt;

&lt;p&gt;Mit &lt;code&gt;EXPLAIN&lt;/code&gt; vor einem &lt;code&gt;SELECT&lt;/code&gt; in der MySQL Shell lässt sich erst einmal herausfinden, wie einzelne Abfragen ausgeführt werden. So lassen sich gut Informationen über verwendete und fehlende Indizes sammeln. Das Ergebnis von EXPLAIN wird standardmäßig als Tabelle ausgegeben. Hier kurz was die Angaben bedeuten. Die Spalte &lt;code&gt;table&lt;/code&gt; gibt den Namen der abgefragten Tabelle an. Unter &lt;code&gt;type&lt;/code&gt; steht die Art des Lesezugriffs. Steht dort &lt;code&gt;ALL&lt;/code&gt; wird ein Table Scan durchgeführt. Unter &lt;code&gt;possible_keys&lt;/code&gt; stehen die Namen der verfügbaren Indizes. Die Spalte key enthält die bei der Abfrage verwendeten Index. Es kann nur ein Index verwendet werden. Unter &lt;code&gt;extra&lt;/code&gt; stehen die Einschränkungen nach denen die Zeilen ausgewählt werden. Steht unter type ALL, aber in den Bedingungen ‘using where’ fehlt mit Sicherheit ein geeigneter Index.  Ein Grund, warum selbst ein vorhandener Index nicht verwendet wird, ist, dass die WHERE-Bedingung zu weit gefasst ist.  Unter diesen Umständen ist die Verwendung eines Indizes aufwendiger als ein Table Scan.  Ein gutes Beispiel für eine zu weit gefasst Abfrage ist, alle Frauen aus einer Nutzertabelle zu laden, da etwa die Hälfte aller Nutzer Frauen sind, ist der Aufwand für den Einsatz von Indizes viel zu groß und wird daher von MySQL ausgeschlossen. Als Richtwert gilt, wenn 20% oder weniger Zeilen durch eine Bedingung erfasst werden ist der Einsatz eines Indexes sinnvoll. Oder anders herum gesagt, ein Index sollte eine gute Selektivität besitzen. Mit &lt;code&gt;EXPLAIN tablename&lt;/code&gt; lässt sich herausfinden, welche Spalten einer Tabelle bereits mit einem Index versehen sind.&lt;/p&gt;

&lt;p&gt;Aber nicht nur die Selektivität eines Index ist entscheidend, denn jeder Index kommt zu dem Preis, dass er bei einer Änderung der Tabelle mit geändert werden muss und so &lt;code&gt;INSERTS&lt;/code&gt;, &lt;code&gt;UPDATES&lt;/code&gt; und &lt;code&gt;DELETES&lt;/code&gt; länger dauern. Also ist auch darauf zu achten wie viele Spalten der Index umfasst und wie lang die Schlüssel sind. Darum ist es gut ersteinmal zu ermitteln, welche Abfragen am meisten von einem Index profitieren würden. Um diese im Live-Betrieb zu ermitteln ist der Loggingmechanismus von MySQL ein praktisches Werkzeug. Das Logging lässt sich in der Konfigurationsdatei my.cnf (bei *nix Systemen in &lt;code&gt;/etc/mysql/&lt;/code&gt; zu finden) des Servers einrichten. Um Abfragen, die besonders lange dauern, mitzuloggen, muss dies mit &lt;code&gt;log_slow_queries  = /var/log/mysql/mysql-slow.log&lt;/code&gt; aktiviert werden. Mit dem Parameter &lt;code&gt;long_query_time = 2&lt;/code&gt; kann in Sekunden angegeben werden, ab welcher Ausführungsdauer eine Abfrage mitgelogged wird. Um Abfragen ins Log zu schreiben die keinen Index verwenden kann &lt;code&gt;log-queries-not-using-indexes&lt;/code&gt; in die Konfigurationsdatein eingetragen werden. Vorsicht, diese Einstellung kann ordentlich viel Output erzeugen.&lt;/p&gt;

&lt;p&gt;Dass waren jetzt die einfachen Bordmittel, die dabei helfen können, sinnvolle Indizes zu vergeben. Das Letzte Wort zu Indices ist damit aber noch nicht gesprochen, denn beim Locking, womit sich der nächste Post befasst, spielen sie auch eine wichtige Rolle.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2008/05/new-rails-plugin-for-making-actionmailer-asynchronous/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2008/05/new-rails-plugin-for-making-actionmailer-asynchronous/"/>
    <title>New Rails Plugin for making ActionMailer asynchronous</title>
    <updated>2008-05-19T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;We have just released a new rails plugin called &lt;em&gt;workling_mailer&lt;/em&gt;. This plugin provides a module which - when included into an ActionMailer subclass - pushes all emails that would normally be delivered synchronously into a queue for asynchronous processing. For queuing it relies on the &lt;a href=&quot;http://playtype.net/past/2008/2/6/starling_and_asynchrous_tasks_in_ruby_on_rails/&quot;&gt;workling plugin&lt;/a&gt; which can use for example twitter’s starling as a queue server.&lt;/p&gt;

&lt;p&gt;We use this on &lt;a href=&quot;http://autoki.com&quot;&gt;autoki&lt;/a&gt; which sends a lot of email (thanks to our &lt;a href=&quot;http://upstream-berlin.com/blog/2008/04/25/new-rails-plugin-social-feed/&quot;&gt;social feed plugin&lt;/a&gt; :) ).&lt;/p&gt;

&lt;p&gt;Recently we got errors from the mailserver complaining about too many concurrent connections. This did not only cause emails not being delivered and our users getting an error page, it also caused long running transactions in the database: if you send an email within an &lt;code&gt;after_create&lt;/code&gt; hook in a model this happens inside of the database transaction. That means that if you have a 60s timeout on your mail server you end up with database transactions taking 60s to finish (or being rolled back in this case) which essentially locks up your tables after a short period of time.&lt;/p&gt;

&lt;p&gt;Now that we are sending our emails from a queue one at a time, the mailserver is happy and our database transactions are again as short as they should be.&lt;/p&gt;

&lt;p&gt;You can get the sources at &lt;a href=&quot;https://github.com/langalex/workling_mailer/&quot;&gt;github&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2008/04/new-rails-plugin-social-feed/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2008/04/new-rails-plugin-social-feed/"/>
    <title>New Rails Plugin: social feed</title>
    <updated>2008-04-25T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;div style=&quot;float:right; margin-left:15px;margin-bottom: 15px&quot;&gt;&lt;img src=&quot;/uploads/2008/04/socialfeed.png&quot; alt=&quot;socialfeed.png&quot; /&gt;&lt;/div&gt;

&lt;p&gt;This is our latest and also largest plugin for &lt;a href=&quot;htp://rubyonrails.org&quot;&gt;ruby on rails&lt;/a&gt; so far. After the installation it adds a social feed as seen in the picture to your rails application. The sources have been extracted from &lt;a href=&quot;http://autoki.de&quot;&gt;autoki&lt;/a&gt; which has had a social feed for a couple of months now, so rest assured we have put some thoughts into it over time. :)&lt;/p&gt;

&lt;p&gt;Features so far: As a user I can decide what kinds of events I want to see on my social feed and also wether I want to be sent an email when an event occurs. I can also decide wether others will receive a notification on their social feed concerning my own actions.&lt;/p&gt;

&lt;p&gt;The plugin includes model extensions for the user, a controller and views for viewing the feed and editing settings as well as a generator to easily create new event types so, getting started only takes a couple of minutes. For more info check out the &lt;a href=&quot;https://github.com/langalex/social_feed/tree/master%2FREADME?raw=true&quot;&gt;README&lt;/a&gt; or &lt;a href=&quot;https://github.com/langalex/social_feed/&quot;&gt;get the sources from github&lt;/a&gt;.&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2008/04/git-commits-in-the-blog-sidebar/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2008/04/git-commits-in-the-blog-sidebar/"/>
    <title>Git commits in the blog sidebar</title>
    <updated>2008-04-25T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;As the careful reader might have noticed, we have added another RSS feed to the blog sidebar. Under the &lt;a href=&quot;http://blog.keiala.com&quot;&gt;keiala product blog&lt;/a&gt; you can now see our commits to all our open source repositories on &lt;a href=&quot;https://github.com/langalex&quot;&gt;github&lt;/a&gt;. To make this possible I have used &lt;a href=&quot;http://http://pipes.yahoo.com/&quot;&gt;yahoo pipes&lt;/a&gt; for the first time. The pipe consumes the RSS feeds of all our open source projects on github, merges these into one, prefixes the titles with the project names and also truncates them, before wordpress consumes that pipe’s output and displays it in a sidebar widget. That’s what I call a real mashup. :D If anyone is interested, here’s &lt;a href=&quot;http://pipes.yahoo.com/pipes/pipe.info?_id=9I7dtOoR3RGi1GANj0nRlg&quot;&gt;the pipe&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2008/04/hiding-attachment_fu-from-your-controllers/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2008/04/hiding-attachment_fu-from-your-controllers/"/>
    <title>Hiding attachment_fu from your controllers</title>
    <updated>2008-04-21T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Yesterday I was using &lt;a href=&quot;http://svn.techno-weenie.net/projects/plugins/attachment_fu/README&quot;&gt;attachment_fu&lt;/a&gt; to attach photos to a couple of models. Since attachment_fu requires you to create an extra model for the photo, you usually have a one to one relationship between the model and the photo.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Project&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;has_one&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:project_photo&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;But now that you have two models you also have to deal with these in your controller and views, something along the lines of this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ProjectsController&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@project&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Project&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@project&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;build_project_photo&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;create&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@project&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Project&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:project&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@project&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;project_photo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@project&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;build_project_photo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:project_photo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Sort of ugly if all you want is to add a photo to the &lt;code&gt;Project&lt;/code&gt; model. But fear not, after adding the following piece of code to your model, you can transparently assign the photo to your project model in the view:&lt;/p&gt;

&lt;p&gt;The tweaked model:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Project&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;has_one&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:project_photo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:dependent&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:destroy&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# the attachment_fu model
&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# a virtual attribute that saves a new photo in the photo model only when there's a new file uploaded
&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;photo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;photo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;project_photo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;build_project_photo&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:uploaded_data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;photo&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;photo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;blank?&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now you can use a standard view:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;%- form_for(:project) do |f| -%&amp;gt;
  &amp;lt; %= f.file_field :photo %&amp;gt;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;%- end -%&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;And a standard controller:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ProjectsController&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@project&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Project&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;create&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@project&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Project&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:project&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@project&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;save&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;(credits to &lt;a href=&quot;http://railscasts.com/episodes/73&quot;&gt;this railscast&lt;/a&gt; for the idea of using a virtual attribute, I only added a bit of sugar for attachment_fu)&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2008/04/rails-reporting-dead-simple-reports-now-supports-excel-directly/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2008/04/rails-reporting-dead-simple-reports-now-supports-excel-directly/"/>
    <title>Rails reporting: dead simple reports now supports excel directly</title>
    <updated>2008-04-12T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;&lt;a href=&quot;http://upstream-berlin.com/blog/open-source/#dead_simple_reports&quot;&gt;dead simple reports&lt;/a&gt; is a Ruby on Rails plugin that allows you to create reports from your application data within minutes. As of now it can not only generate HTML tables and CSV files but also M$ Excel spreadsheets. This is possible through the use of the &lt;a href=&quot;http://rubyforge.org/projects/spreadsheet/&quot;&gt;spreadsheet-excel&lt;/a&gt; gem. You can grab a copy from the &lt;a href=&quot;https://github.com/langalex/dead_simple_reports&quot;&gt;git repository&lt;/a&gt; or just &lt;a href=&quot;https://github.com/langalex/dead_simple_reports/tarball/master&quot;&gt;download it&lt;/a&gt; from there.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2008/04/moving-to-github/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2008/04/moving-to-github/"/>
    <title>Moving to GitHub</title>
    <updated>2008-04-11T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;div style=&quot;float:right&quot;&gt;&lt;a href=&quot;https://github.com&quot;&gt;&lt;img width=&quot;200&quot; src=&quot;/uploads/2008/04/octacat.png&quot; alt=&quot;octacat.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com&quot;&gt;GitHub&lt;/a&gt; is out of beta so now we have moved our open source projects &lt;a href=&quot;https://github.com/langalex&quot;&gt;there&lt;/a&gt;. Actually they already have been there for a while but we have now split them into their own repositories. The upstream subversion will still be online for a while but new commits will only go to git. Apart from being hyped alot these days &lt;a href=&quot;http://tomayko.com/writings/the-thing-about-git&quot;&gt;git can do some pretty cool things like committing only parts of a file or changing previous commits&lt;/a&gt; - well worth checking it out I’d say.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2008/04/monitoring-the-internals-of-your-rails-application-with-nagios/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2008/04/monitoring-the-internals-of-your-rails-application-with-nagios/"/>
    <title>Monitoring the internals of your Rails application with Nagios</title>
    <updated>2008-04-10T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;div style=&quot;float:right&quot;&gt;&lt;img src=&quot;http://nagios.sourceforge.net/images/screens/new/service-detail.png&quot; width=&quot;250&quot; /&gt;&lt;/div&gt;
&lt;p&gt;autoki has recently grown into a more and more complex application. Besides two clusters of mongrels and the mysql database we have a &lt;a href=&quot;http://www.danga.com/memcached/&quot;&gt;memcached&lt;/a&gt; server, &lt;a href=&quot;http://projects.jkraemer.net/acts_as_ferret/wiki&quot;&gt;ferret&lt;/a&gt; and &lt;a href=&quot;http://www.slideshare.net/Blaine/scaling-twitter&quot;&gt;starling&lt;/a&gt; plus &lt;a href=&quot;http://playtype.net/past/2008/2/6/starling_and_asynchrous_tasks_in_ruby_on_rails/&quot;&gt;clients for asynchronous processing&lt;/a&gt;. WIth so many services running (and sometimes not running) the need to monitor all these grew. We decided to set up &lt;a href=&quot;http://www.nagios.org/&quot;&gt;nagios&lt;/a&gt; on one of the servers - it’s ugly but it lets you monitor all sorts of stuff pretty easily via remote agents that run on each monitored server.&lt;/p&gt;

&lt;p&gt;With nagios we have access to quite a number of monitoring plugins, e.g. for monitoring TCP ports (e.g. for checking memcached is still alive), HTTP, server load, free disk space etc. Yesterday I came to a point where I wanted to monitor something nagios couldn’t: When a user uploads a bunch of photos, the task of creating copies of the photos in different sizes is put in a starling queue for asycnhronous processing. If something with that processing goes wrong and the queue gets too big I want nagios to pick this up and notify me. Time for my own nagios plugin.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://nagiosplug.sourceforge.net/developer-guidelines.html&quot;&gt;Nagios plugins are actually very simple&lt;/a&gt;. All you have to provide is something that can be executed in a shell and that returns either 0, 1 or 2 for an OK, Warning or Critical state of the monitored service. So here’s the source code for monitoring the number of photo uploads in the queue (&lt;em&gt;RAILS_ROOT/lib/check_photo_uploads.rb&lt;/em&gt;):&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;c1&quot;&gt;#!/usr/bin/env ruby
&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# load rails
&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;RAILS_ENV&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'production'&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dirname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;__FILE__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'/../config/environment'&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# print out a warning if the queue has 10, critical when 20 entries
&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;warn_count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;fatal_count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;actual_count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;PhotosUpload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;count&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;photo_uploads &quot;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;actual_count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;warn_count&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;OK&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;elsif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;actual_count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fatal_count&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;CRITICAL&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;elsif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;actual_count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;warn_count&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'WARNING'&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;actual_count&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; uploads in queue&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;exit&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# exit with the error code that is then interpreted by nagios&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;At autoki the server that runs the asynchronous processes is called jobs1 and this is also where the queue should be checked, so I added this to the /etc/nagios/nrpe.cfg file (the config file for the remote nagios agent):&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;check_photo_uploads&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/usr/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bin&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ruby&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/var/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;www&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;production&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lib&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;check_photo_uploads&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;rb&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Then I had to add the service to the nagios configuration on the monitoring server:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;define&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;host&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;jobs1&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;service_description&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;photo_uploads&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;check_command&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;check_nrpe_1arg!check_photo_uploads&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;generic&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;service&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Well, that was it, and this is how it looks - ugly but it works :)&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/2008/04/nagios_photo_uploads.png&quot; alt=&quot;nagios_photo_uploads.png&quot; width=&quot;550&quot; /&gt;&lt;/p&gt;

&lt;p&gt;(I recently signup with &lt;a href=&quot;http://www.scoutapp.com/&quot;&gt;scout&lt;/a&gt; - looks much prettier and the setup is much easier than nagios, plugins are written as ruby classes and it comes as a ruby gem - sweet concept so far, could have been my idea, more on that later)&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2008/03/4-tage-woche-und-kreditkarten-fur-alle/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2008/03/4-tage-woche-und-kreditkarten-fur-alle/"/>
    <title>4-Tage-Woche und Kreditkarten für alle</title>
    <updated>2008-03-10T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Das klingt so gut, das machen wir auch: &lt;a href=&quot;http://www.37signals.com/svn/posts/893-workplace-experiments&quot;&gt;37signals haben beschlossen, im Sommer nur 4 Tage die Woche zu arbeiten&lt;/a&gt; - nach 3 Tagen Wochenende ist man einfach entspannter, glücklicher ergo produktiver. Kreditkarten für alle und finanzielle Beteiligung an den Hobbys der Mitarbeiter - klingt auch nicht schlecht, aber so richtige Mitarbeiter haben wir ja gar nicht, da müssen wir uns was anderes überlegen.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2008/03/upstream-auf-github/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2008/03/upstream-auf-github/"/>
    <title>upstream auf github</title>
    <updated>2008-03-04T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Nach kurzer Wartezeit habe ich heute meine Einladung zum Betatest von &lt;a href=&quot;https://github.com&quot;&gt;GitHub&lt;/a&gt; bekommen und das erst gestern eröffnete &lt;a href=&quot;http://public_svn.upstream-berlin.com&quot;&gt;public upstream subversion&lt;/a&gt; ins &lt;a href=&quot;https://github.com/langalex/upstream-open-source&quot;&gt;public upstream git repository&lt;/a&gt; kopiert. Man kann unser großartiges dead simple reports plugin jetzt also auch über git ziehen.&lt;/p&gt;

&lt;p&gt;Der Einstieg in github war schonmal sehr angenehm. Um das erste Repository anzulegen bekam ich erstmal die benötigten Kommandos angezeigt und konnte die gleich in mein Terminal kopieren. Von den diversen git tutorials und &lt;a href=&quot;https://peepcode.com/products/git&quot;&gt;screencasts&lt;/a&gt; ist bei mir leider nicht viel hängen geblieben, sodass ich git wohl erst im Laufe des Praxiseinsatzes richtig kapieren werden. Ein sehr spannender Ansatz bei github ist auf jeden Fall die Möglichkeit, bestehende Repositories zu forken und dann darin zu entwickeln. Statt also irgendwelche Patches an Open Source-Projekte zu senden, forkt man einfach nur noch deren Repository, werkelt darauf herum und kann dann ggf. die Änderungen in das Main-Repository hochladen.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2008/03/upstream-goes-open-source-dead-simple-reports/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2008/03/upstream-goes-open-source-dead-simple-reports/"/>
    <title>upstream goes open source: dead simple reports</title>
    <updated>2008-03-03T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Na endlich. Nach all den Jahren des nur-Geldverdienens und Open Source-Ausnutzens haben wir es geschafft, ein paar erste Zeilen Code in die Freiheit zu entlassen: &lt;a href=&quot;http://public_svn.upstream-berlin.com/dead_simple_reports/&quot;&gt;dead simple reports&lt;/a&gt;, ein Rails-Plugin, das Reports generieren kann. Mehr dazu im neuen Bereich &lt;a href=&quot;http://upstream-berlin.com/blog/open-source/&quot;&gt;Open Source&lt;/a&gt;&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2008/02/delete-considered-harmful/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2008/02/delete-considered-harmful/"/>
    <title>Delete considered harmful</title>
    <updated>2008-02-28T00:00:00+00:00</updated>
    <author>
      <name>thilo</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;We just encountered a bug in one of our rails applications where we had accidentally used &lt;code&gt;some_mode.delete&lt;/code&gt; instead of &lt;code&gt;some_model.destroy&lt;/code&gt;. I think &lt;code&gt;delete&lt;/code&gt; should not be a public method on ActiveRecord. After all it’s just some evil means to circumvent all those rails callbacks and stuff to gain some more performance when deleting objects. It should be a protected method or something so that requires some effort (i.e. model.send(:delete)) to call it so you don’t use it accidentally when you actually want to call &lt;code&gt;destroy&lt;/code&gt;.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2008/02/textmate-snippet-power-fur-gettext/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2008/02/textmate-snippet-power-fur-gettext/"/>
    <title>TextMate Snippet Power für gettext</title>
    <updated>2008-02-17T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Es ist super wenn die Werkzeuge, die man verwendet mit einem wachsen. Deshalb bin ich von Ruby und &lt;a href=&quot;http://macromates.com/&quot;&gt;TextMate&lt;/a&gt; begeistert.&lt;/p&gt;

&lt;p&gt;Für die, die TextMate nicht kennen: TextMate ist ein TextEditor für OS X, der sich in meinen Augen durch zwei Besonderheiten als Entwicklungswerkzeug hervortut. Erstens die leistungsfähige Live-Suche, die alle Mausklicks zum Navigieren im Projekt überflüssig macht. Zweitens die wirklich mächtigen Skriptingmöglichkeiten, die ich wohl noch lange nicht erfasst habe.&lt;/p&gt;

&lt;p&gt;Jedenfalls musste ich Rahmen der Internationalisierung eines Projekts alle Strings in eine Übersetzungsmethode von &lt;a href=&quot;http://www.yotabanana.com/hiki/ruby-gettext.html&quot;&gt;gettext&lt;/a&gt; wrappen. Also ggf. Texte in den Views in Inline tags und in doppelte Anführungszeichen zu packen. Das gewünschte Ergebnis sieht etwa so aus: &lt;code&gt;&amp;lt;%= _(&quot;Zu übersetzender Text&quot;) %&amp;gt;&lt;/code&gt;. Die ganze Arbeit ist ziemlich stupide, ließ sich aber mit TextMate auf simples Markieren des entsprechenden Textes und das Drücken des Tastenkürzels &lt;strong&gt;ctrl+alt+t&lt;/strong&gt; reduzieren.&lt;/p&gt;

&lt;p&gt;Dazu hab ich im Rails Bundle ein neues &lt;a href=&quot;http://macromates.com/textmate/manual/snippets#snippets&quot;&gt;Snippet&lt;/a&gt; erstellt, das den markierten Text entsprechend einfasst. Der Code dafür sieht so aus &lt;code&gt;&amp;lt;%= _(&quot;${TM_SELECTED_TEXT}&quot;) %&amp;gt;&lt;/code&gt;. Die Variable &lt;code&gt;TM_SELECTED_TEXT&lt;/code&gt; steht einfach für den markierten Text. So weit so trivial.&lt;/p&gt;

&lt;p&gt;Da manchmal im Text bereits doppelte Anführungszeichen vorhanden waren, haben diese leider die Strings etwas zu früh beendet. Hier kommen die regulären Ausdrücke ins Spiel um diese Anführungszeichen zu escapen. Reguläre Ausdrücke lassen sich auf die Variable wie folgt anwenden &lt;code&gt;${Variable/regexp/format/option}&lt;/code&gt;. Mein Snippet Code sah dann so aus&lt;code&gt; &amp;lt;%= _(&quot;${TM_SELECTED_TEXT/&quot;/\\&quot;/g}&quot;) %&amp;gt;&lt;/code&gt;. Damit war das Anführungszeichenproblem kurz und schmerzlos erledigt.&lt;/p&gt;

&lt;p&gt;Blieb noch der Fall, dass der Sting bereits als Inline Code eingefaßt ist und nur &lt;code&gt;_()&lt;/code&gt; um den String noch nötig ist. Dazu habe ich einfach ein weiteres Snippet angelegt, das so aussah: &lt;code&gt;_(&quot;${TM_SELECTED_TEXT/&quot;/\\&quot;/g}&quot;)&lt;/code&gt;. Da ich mir aber nicht ein extra Tastenkürzel dafür merken wollte, habe ich mir die Scoping Eigenschaft von Snippets zu Nutze gemacht. Scoping in TextMate bedeutet, dass das Snippet nur in einem bestimmten Kontext angewendet wird. In welchem Kontext sich der Cursor gerade befindet, ist durch das Tastenkürzel &lt;strong&gt;shift+ctrl+p&lt;/strong&gt; zu sehen. Der Kontext kann auch aus einer Hierarchie bestehen. Also dem ersten Snippet den Kontext &lt;code&gt;text.html.ruby&lt;/code&gt; für rails html templates zugewiesen und dem zweiten den &lt;code&gt;string.quoted.single.ruby|string.quoted.double.ruby&lt;/code&gt; (Verkettungen sind auch möglich) und schon stehen beide Snippets bequem per &lt;strong&gt;ctrl+alt+t&lt;/strong&gt; zur Verfügung. Cool ;)&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2008/02/euruko-2008/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2008/02/euruko-2008/"/>
    <title>EURUKO 2008</title>
    <updated>2008-02-13T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Die Konferenzsaison fängt an. Kurz vor der &lt;a href=&quot;http://upstream-berlin.com/blog/2008/02/08/re-publica-die-zweite/&quot;&gt;re-publica&lt;/a&gt; geht’s am 28./29.3. nach Prag zur &lt;a href=&quot;http://www.euruko2008.org&quot;&gt;European Ruby Conference&lt;/a&gt;. Letztes Jahr in Wien war ziemlich lustig. Damals hatten wir in der Nacht zwischen den zwei Konferenztagen ein Twitter-basiertes Rätselspiel (twizzer) geschrieben.&lt;/p&gt;

&lt;p&gt;Falls jemand auch da hinwill, wir hätten evtl. noch Plätze im Auto frei.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2008/02/re-publica-die-zweite/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2008/02/re-publica-die-zweite/"/>
    <title>re-publica die zweite</title>
    <updated>2008-02-08T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;&lt;img src=&quot;/uploads/2008/02/d60bc49db7d10ad3a59cb0752650d50c.jpg&quot; alt=&quot;republica08&quot; /&gt;&lt;/p&gt;

&lt;p&gt;An Alle: Vom 2. 4. - 4. 4. 2008 findet in der Berliner Kalkscheune wieder die &lt;a href=&quot;http://re-publica.de&quot;&gt;re-publica&lt;/a&gt; statt. DIE Konferenz über die Blogosphäre, Politik, Kultur, Technik und alles was sonst noch so dazugehört. Hinkommen. Wir sind jedenfalls auch da.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2008/01/somewhat-safer-passwords-for-all-your-95-social-networks-and-web-x0-services/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2008/01/somewhat-safer-passwords-for-all-your-95-social-networks-and-web-x0-services/"/>
    <title>Somewhat safer passwords for all your 95 social networks and web x.0 services</title>
    <updated>2008-01-24T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;The general advice: We are all supposed to use a separate password that has at least 25 letters, numbers and special characters in it for everything we use on the net, because if we don’t, one compromised site gives an attacker access to all your other accounts using the same password. Think of a hacked &lt;a href=&quot;https://twitter.com&quot;&gt;twitter&lt;/a&gt; account resulting in all your emails on gmail made publicly available.&lt;/p&gt;

&lt;p&gt;The reality: We are all lazy bastards so most uf us have one single password they use for all their accounts (am I not right?). Some of us have 2-3 passwords for different levels of trust, so the bank account and credit card sites get one, personal email gets one and all the other sites get another. A bit better already.&lt;/p&gt;

&lt;p&gt;My suggestion: One password per site (hey I’ve heard this before). But here’s the trick (and the catch at the same time, because the passwords are similar and can hence be hacked more easily). I will use the following convention to generate a separate password that is (for me) easy to remember for each site:&lt;/p&gt;

&lt;p&gt;Take some random sentence:&lt;/p&gt;

&lt;blockquote&gt;This is my personal very secure password for [...].&lt;/blockquote&gt;

&lt;p&gt;Say you need a password for twitter, this sentence becomes:&lt;/p&gt;

&lt;blockquote&gt;This is my personal very secure password for twitter.&lt;/blockquote&gt;

&lt;p&gt;The password will be the first letter of each word in the sentence, so for this example it’s &lt;em&gt;Timpvspftw&lt;/em&gt;. I’m actually using the first two letters of the site’s name so the convention works for twitter and t*** (uhm. insert name of another website starting with t). To make things a bit more secure, you should change your scheme, e.g. use the last letters, or alternating between last and first.&lt;/p&gt;

&lt;p&gt;Using more or less random letters from a sentence to generate a secure password is nothing new. It actually has been recommended for years (decades?). My only addition to this is to use the name of the service in that sentence, so you can have separate passwords and still remember them easily. And they should be fairly secure, as long as your scheme of choosing the letters and your sentence are random enough (I’m still using something different for my bank account though).&lt;/p&gt;

&lt;p&gt;Of course the whole would be much easier with somethingh like &lt;a href=&quot;http://upstream-berlin.com/blog/2007/03/01/openid-gewinnt-an-fahrt/&quot;&gt;OpenID&lt;/a&gt; everywhere, but until then go and make up some funny sentences for your passwords.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2008/01/pregenerating-assets-in-rails-20-revisited/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2008/01/pregenerating-assets-in-rails-20-revisited/"/>
    <title>Pregenerating assets in Rails 2.0 revisited</title>
    <updated>2008-01-24T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;In a &lt;a href=&quot;http://upstream-berlin.com/blog/2007/12/21/pre-generating-cached-stylesheets-and-javascripts-with-rails-20/&quot;&gt;previous post&lt;/a&gt; I wrote about how we wanted to pregenerate our packed assets simply by firing up a mongrel on deployemnt and request a page so it would generate the assets by calling the &lt;code&gt;stylesheet_link_tag ... :cache =&amp;gt; true&lt;/code&gt; and &lt;code&gt;javascript_include_tag ... :cache =&amp;gt; true&lt;/code&gt; methods in our &lt;em&gt;application.rhtml&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;It turned out starting and stopping a mongrel during development wasn’t really a good idea. We ran into all sorts of problems with deployemnts not shutting down the mongrel or not deleting its pid file which made the next deployemnt fails and stuff like that. Plus, to be honest, this really felt like a hack from the beginning.&lt;/p&gt;

&lt;p&gt;So what we ended up doing was this: Write a small class that calls the methods to generate the all.js/all.css files and simply call that from our capistrano script. This is how it looks (&lt;em&gt;config/assets.rb&lt;/em&gt;):&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Assets&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;stylesheet_filenames&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'screen'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'screen_profile'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'game'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'screen_classes'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'screen_forms'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'screen_pages'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'redbox'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;javascript_filenames&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'prototype'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'effects'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'controls'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'upload_progress'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'prototype_extensions'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'application'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;'redbox'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'dragdrop'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The &lt;code&gt;Assets&lt;/code&gt; module simply contains our list of stylesheet and css files which we had to pull out of &lt;em&gt;application.rhtml&lt;/em&gt; so that we could generate the files without making any requests through the rails stack in order to call the methods in &lt;em&gt;application.rhtml&lt;/em&gt;. After including the &lt;code&gt;Assets&lt;/code&gt; module into &lt;code&gt;ApplicationHelper&lt;/code&gt; the calls in the layout now look like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;%= stylesheet_link_tag *(stylesheet_filenames + [{:cache =&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}])&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&amp;gt;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;%= javascript_include_tag *(javascript_filenames + [{:cache =&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}])&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;A bit ugly but it works. We have also added a class &lt;code&gt;AssetsGenerator&lt;/code&gt; to our &lt;em&gt;assets.rb&lt;/em&gt; file:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AssetsGenerator&lt;/span&gt;
  &lt;span class=&quot;kp&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Assets&lt;/span&gt;
  &lt;span class=&quot;kp&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ERB&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Util&lt;/span&gt;
  &lt;span class=&quot;kp&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActionView&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Helpers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;TagHelper&lt;/span&gt;
  &lt;span class=&quot;kp&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActionView&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Helpers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;AssetTagHelper&lt;/span&gt;


  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;generate_assets&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;stylesheet_link_tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stylesheet_filenames&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:cache&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;javascript_include_tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;javascript_filenames&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:cache&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;RAILS_ROOT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'public'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'stylesheets'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'all.css'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;css&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;read&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;open&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'w'&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;css&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;gsub&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/url\((&quot;?|'?)\/images\//&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;matches&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;url(&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;vg&quot;&gt;$1&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;compute_asset_host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_s&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/images/&quot;&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This does two things: first it calls the &lt;code&gt;stylesheet_link_tag&lt;/code&gt; and &lt;code&gt;javascript_include_tag&lt;/code&gt; methods from the &lt;code&gt;AssetTagHelper&lt;/code&gt; module to generate the all.js and all.css files. After that it reads in the css file and replaces all ocurrences of &lt;code&gt;url('images/*')&lt;/code&gt; with the url of an asset host - it’s easier than teaching our CSS guy to do it by hand, plus we don’t have any hard coded urls in our css files.&lt;/p&gt;

&lt;p&gt;We’ve been running this setup for 2 weeks now withour any problems. You could argue that it might have been easier to stick with the old asset_packager plugin but with this solution we have now one less plugin that can cause trouble and we are re-using more of the built in rails stuff than before.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2008/01/using-haproxy-with-multiple-backends-aka-content-switching/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2008/01/using-haproxy-with-multiple-backends-aka-content-switching/"/>
    <title>Using haproxy with multiple backends a.k.a. content switching</title>
    <updated>2008-01-09T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;We have been using &lt;a href=&quot;http://haproxy.1wt.eu/&quot;&gt;haproxy&lt;/a&gt; as a load balancer for &lt;a href=&quot;http://autoki.com&quot;&gt;autoki&lt;/a&gt; (rails) for a while. The setup was relatively simple. We had two servers full of mongrels and one haproxy was simply throwing requests at them using round robin.&lt;/p&gt;

&lt;p&gt;Now our requirements have gotten bigger. We have extracted the &lt;a href=&quot;http://autoki.com/autoquartett&quot;&gt;game&lt;/a&gt; into a separate application that is being deployed on its own server independently. The problem that we want to solve with this is that while we want to deploy changes to our servers whenever we want to, the game application has to keep running all the time without interruptions by a deployment and the problems that might be caused by it. Oh, and at the same time we didn’t want to change any urls. The game has been sitting in the external_games controller, so the url was autoki.com/external_games and since we have given this url to people we wanted it to stay the same.&lt;/p&gt;

&lt;p&gt;So now we have a new server called external_games.autoki.com with 10 mongrels running on it and we want all requests to /external_games go there and everything else still go to the old servers. The solution to this is to let the load balancer decide which servers to forward the requests to. Since version 1.3 haproxy has implemented exactly this - and calls it content switching. To get thsi to work you have to tweak the haproxy.cfg file a bit. This is the old &lt;code&gt;listen&lt;/code&gt; section with one pool of mongrels:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;listen&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rails&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;80&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rails&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;autoki&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;7000&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;maxconn&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;check&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rails&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;autoki&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;7001&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;maxconn&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;check&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;First we have split up our single proxy configuration into a frontend and a backend. We will actually be using two backends - one for gaming and one for the rest, and the frontend will decide which backend to use.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;backend&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rails_game&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;game&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;autoki&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;7000&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;maxconn&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;check&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;game&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;autoki&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;7001&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;maxconn&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;check&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;backend&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rails_production&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rails&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;autoki&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;7000&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;maxconn&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;check&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rails&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;autoki&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;7001&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;maxconn&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;check&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;frontend&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rails&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;80&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The rule to decide which backend to use is implemented using an acl. The following line creates a rule with the name &lt;code&gt;game&lt;/code&gt; that evaluates to tue, if the url contains the string &lt;code&gt;external_games&lt;/code&gt;. The &lt;a href=&quot;http://haproxy.1wt.eu/download/1.3/doc/haproxy-en.txt&quot;&gt;haproxy documentation&lt;/a&gt; has a whole list of keywords you can use to match against all the contents of a http packet.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;acl&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;game&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;url_sub&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;external_games&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now all we have to do is tell the frontend when to use which backend. We do this by defining a rule for the gaming backend and set a default backend for all requests not matching our acl.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;frontend&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rails&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;80&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;acl&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;game&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;url_sub&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;external_games&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;use_backend&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rails_game&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;game&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;default_backend&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rails_production&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;That’s it. Now when we go to http://autoki.com/haproxy?stats we’ll see the two backends up and running. (only after adding &lt;code&gt;stats enable&lt;/code&gt; to both backends actually)&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2008/01/letzte-worte/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2008/01/letzte-worte/"/>
    <title>Letzte Worte</title>
    <updated>2008-01-07T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;surrrrr….. RRRRRR ….. zzzzzZZZZZ …… &lt;em&gt;kreisssssssch&lt;/em&gt; zzzzZZZZZ &amp;amp;â‚¬&amp;amp;@@&amp;amp;&amp;amp;&amp;amp; &lt;em&gt;stille&lt;/em&gt; &lt;em&gt;plop&lt;/em&gt; “Neiiiiiinnnn”&lt;/p&gt;

&lt;p&gt;so klang es vorhin, als der eine Lüfter meines Laptops seine letzten Umdrehungen nahm. Jetzt ist Ruhe. R.I.P. Warten auf den 15. Januar - dann ist Macworld San Francisco und Steve beschenkt die Welt mit &lt;a href=&quot;http://www.macrumors.com/2008/01/05/thin-macbook-laptop-concept-images/&quot;&gt;neuer Hardware&lt;/a&gt;. Bis dahin bleibt mir nur, &lt;a href=&quot;http://www.amazon.de/Practical-Ruby-Projects-Eclectic-Programmer/dp/159059911X/ref=pd_bbs_sr_1?ie=UTF8&amp;amp;s=books-intl-de&amp;amp;qid=1199714980&amp;amp;sr=8-1&quot;&gt;Bücher zu lesen&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/12/anonaccess/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/12/anonaccess/"/>
    <title>24C3: AnonAccess</title>
    <updated>2007-12-28T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Gestern Abend habe ich als letztes den Vortrag zum anonymen Zugangskontrollsystem &lt;a href=&quot;http://www.das-labor.org/wiki/AnonAccess&quot;&gt;AnonAccess&lt;/a&gt; gehört. AnonAccess ist eine Eigenentwicklung die aus dem Bedürfnis entstand ein günstiges und vor allem bei Bedarf anonymes Zugangskontrollsystem für den eigenen Hackerspace zu haben.&lt;/p&gt;

&lt;p&gt;Das Ziel eines günstigen Zugangskontrollsystem konnte recht einfach durch entsprechende Hardware realisiert werden. Die Hardware besteht grob aus Chipkarten - ca. 73cent pro Stück -, Mikrocontroller - 10 Euro - und kleinere Komponenten deren Kosten auch bei 10 Euro lagen.&lt;/p&gt;

&lt;p&gt;Dem Ziel eines anonymen Systems wurde sich langsam angenähert. Als erste Idee wurden Pseudonyme auf den Karten hinterlegt und Zugangstoken von einer Datenbank vergeben. Pseudonyme sind allerdings nicht wirklich anonym. Um richtige Anonymität zu erreichen wurde die Idee so abgeändert das satt eines Pseudonym, eine zufällige ID dem der Token zugeordnet wird und diese ID auch immer mit auf der Karte geschrieben wird. Damit ist kein Rückschluss mehr möglich welche Karte welche ID hat und somit auch keine Information mehr über dessen Besitzer. Das Modell ist zwar anonym aber absolut nicht wartbar, etwa wenn jemand seine Karte verloren hat, die entsprechenden Token zurückzuziehen.&lt;/p&gt;

&lt;p&gt;Erst die Kombination der beiden Konzepte ermöglicht einen anonymen Zugang der sich auch verwalten lässt. Dazu wird eine Zweite Datenbank verwendet die  Informationen über die ID der Karten und den hinterlegten Nutzerinformationen enthält. Dies ermöglicht eine dreistufiges Identitätsmanagement. Namentlich bekannt Nutzer, ein Pseudonym und geteilte Pseudonyme. Damit die Daten nicht einfach durch den Angriff auf eines der Teilsysteme kompromittiert werden können, etwa durch auslesen der Karte, Mithören der Kommunikation oder auslesen der Datenbanken kommen entsprechende Hashing- und Verschlüsselungstechniken zum Einsatz.  Als Authentifizierungmöglichkeit für den Nutzer wurde vor kurzem die Verwendung einer PINs implementiert. Das System befindet sich immer noch im Entwicklungsstadium. Wie die Verschlüsselung im einzelnen Abläuft und wie die Datenbank aussieht kann in den &lt;a href=&quot;http://events.ccc.de/congress/2007/Fahrplan/attachments/989_anonaccess.pdf&quot;&gt;Slides&lt;/a&gt; nachgelesen werden.&lt;/p&gt;

&lt;p&gt;In der anschliessenden Diskussion wurden einige Kritik hinsichtlich der Architektur geäussert. Unteranderem wurde die Betreibssicherheit hinterfragt, da die Karte bei jeder Verwendung mit neuen Daten beschreiben wird und ein vorzeitiges herausziehen der Karte, diese unbrauchbar macht. Ein Problem dem durch den Einsatz von Einzugslesegeräten aus dem Weg gegangen werden kann. Grundlegender waren die Bedenken, dass das System nicht inhernt anonym ist, sondern auf das System vertraut werden muss.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/12/24c3-terror-und-bucher/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/12/24c3-terror-und-bucher/"/>
    <title>24c3: Terror und Bücher</title>
    <updated>2007-12-28T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;h3&gt;What is terrorism?&lt;/h3&gt;

&lt;p&gt;Meine letzte und gleichzeitig beeindruckendste Session des ersten Tages hatte den Titel &lt;a href=&quot;http://events.ccc.de/congress/2007/Fahrplan/events/2381.en.html&quot;&gt;What is Terrorism?&lt;/a&gt;. Dabei berichtete Anne Roth von ihrem Leben als Freundin eines Terrorismusverdächtigen. Ihr Freund wurde eines Tages ohne Vorwarnung von der Polizei wegen Terrorverdacht festgenommen. Seitdem und auch schon davor stehen die beiden plus Freunde und Verwandte unter ständiger Überwachung durch die Polizei. Der Vortrag war voller irgendwie schon lustiger Anekdoten (“Ich habe meine Mutter angerufen, um den die Leitung abhörenden Polizisten zu sagen, dass ich jetzt gern fernsehen würde, die Wanze aber den Empfang stört”), zum Anderen kamen aber auch Details zutage, die einen an den Fähigkeiten der deutschen Polizei zweifeln lassen. Das Gute ist, dass das Ganze inzwischen in &lt;a href=&quot;http://annalist.noblogs.org/&quot;&gt;Annes Blog&lt;/a&gt; verfügbar ist. Sehr lesenswert.&lt;/p&gt;

&lt;h3&gt;Elektronische Dokumente und die Zukunft des Lesens&lt;/h3&gt;

&lt;p&gt;Der 2. Tag begann mit einer Session auf deutsch. Steini berichtete über die neuesten Entwicklungen im Bereich der e-Books. Klassische Displays hatten bisher vor allem das Problem, zum Einen in sehr heller Umgebung (Sonnenlicht, ahhh) schlecht lesbar zu sein, zum Anderen meist nach ein paar Stunden ihr batteriebetriebenes Leben auszuhauchen, bis ihnen an einer Steckdose wieder neues eingehaucht wurde. Seit einigen Jahren versuchen nun die Hersteller, durch sogenannte eInk Displays diese Probleme zu lösen, indem diese statt selbst zu leuchten das Umgebungslicht reflektieren und bei ausbleibender Energieversorgung ihren Inhalt nicht verlieren. Die Probleme dieser Displays sind zur Zeit noch eine recht lange Umschaltzeit (teilweise 500ms) sowie eine kurze Lebensdauer (ein paar tausend Umschaltzyklen). Diese Probleme scheinen in den Labors aber langsam gelöst zu werden, sodass in 1-2 Jahren mit tatsächlich brauchbaren eReadern zu rechnen ist.&lt;/p&gt;

&lt;p&gt;Neben den technischen Neuerungen gab es Hinweise auf mögliche wirtschaftloche und gesellschaftiche Veränderungen. Dem Buchmarkt könnte mit einem Durchbruch von eReadern etwas ähnliches wie der Musikindustrie blühen. Die Verlage verlieren an Macht, da die Buchverkäufe zurückgehen, Autoren werden entlassen, Buchläden gehen pleite. Eine mögliche Entwicklung wäre, dass die nun &lt;del&gt;arbeitslosen&lt;/del&gt; wieder Zeit habenden Autoren in Eigenregie ebooks herausgeben könnten. Wie in der Musik würden die Verwertungsketten wesentlich kürzer werden (Autor -&amp;gt; Leser statt Auto -&amp;gt; Verlag -&amp;gt; Händler -&amp;gt; Leser), und damit die Preise fallen könnten. Ein weiterer interessanter Hinweis: zur Zeit muss ein Sachbuch min. 300 Seiten dick sein, damit es von einem Verlag teuer verkauft werden kann. Dies zwingt die Autoren mitunter dazu, ein Buch mit eigentlich 50 Seiten Inhalt auf die geforderte Größe aufzublähen. Durch ebooks und die entsprechend geringeren Kosten wäre es auch möglich, 50seitige “Mini-Bücher” herauszubringen. Da würde man endlich mal ein paar mehr davon schaffen.&lt;/p&gt;

&lt;p&gt;Durch die verkürzten Produktionssyklen (kein Druck, Distribution in Läden usw.) wird es weiterhin denkbar, Bücher in kleineren Teilen herauszugeben. Als Beispiel wurden Fortsetzungsromane genannt, von denen jede Woche ein weiteres Kapitel erscheinen würde, das sich ohne Probleme on demand an die Leserschaft verteilen ließe.&lt;/p&gt;

&lt;p&gt;Zum Schluß gab’s noch den obligatorischen Hinweis auf die “illustrierte Fibel für die junge Dame” aus &lt;a href=&quot;http://en.wikipedia.org/wiki/The_Diamond_Age&quot;&gt;Diamond Age&lt;/a&gt;. In diesem Roman spielt ein vernetztes, interaktives eBook eine große Rolle, das ein kleines Mädchen bei seiner Entwicklung begleitet und ihm eine Art elektronische “Kindersitter” ist.&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/12/24c3-design-patterns-for-running-a-hacker-space/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/12/24c3-design-patterns-for-running-a-hacker-space/"/>
    <title>24c3: Design patterns for running a hacker space</title>
    <updated>2007-12-27T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Von heute an findet in Berlin am Alexanderplatz der &lt;a href=&quot;http://events.ccc.de/congress/2007/&quot;&gt;24. Chaos Communication Congress (24c3)&lt;/a&gt; statt. Kurz gesagt handelt es sich bei dem ganzen um eine jährlich stattfindende Hackerkonferenz, auf der es neben der eigentlichen Technik und deren hacken um damit im Zusammenhang stehende gesellschaftlcihe und politische Themen wie z.B. die Vorratsdatenspeicherung geht.&lt;/p&gt;

&lt;p&gt;In meiner ersten Session ging es um den Eigenbau von Flugdrohnen. Im wesentlichen wurden Moellflugzeuge um Rechner, Sensoren und Kameras erweitert und das ganze mit einer Software versehen. Schon konnten wir im Sall live verfolgen, wie auf einem Acker irgendwo in Frankreich eine Drohne startete und nach unseren Anweisungen Schleifen flog und uns dabei Videobilder der Umgebung lieferte. Wirklich sehr nett das ganze - mir ist nur noch nicts eingefallen, was ich damit überwachen wollen könnte. Vielleicht die Staße vor meinem Haus, um zu entscheiden, ob ich fürs Fahrradfahren Regensachen anziehen muss oder nicht? Hm. ;)&lt;/p&gt;

&lt;p&gt;Die zweite Session hieß “Design patterns for running a hacker space”. Das Thema Orte aufbauen ist für mich vor allem im Zusammenhang mit dem &lt;a href=&quot;http://upstream-berlin.com/9to5&quot;&gt;9to5-Projekt&lt;/a&gt; (kein upstream-Projekt) interessant, bei dem es um den Aufbau von Orten geht, in denen (vor allem) Selbständige in Zukunft ihrer Abeit nachgehen können. Mehr dazu später in diesem Blog bzw. vorerst im &lt;a href=&quot;http://upstream-berlin.com/9to5&quot;&gt;9to5-Wiki&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Eine weitere Session, die für 9to5 interessant sein dürfte findet heute abend statt: 22 Uhr, AnonAccess - Ein anonymes Zugangskontrollsystem. Wie der Name verät, geht es dabei im ein System, um Mitgliedern zu einem Ort Zugang zu gewähren, ohne mechanische Schlüssel zu verteilen gleichzeitig aber den anonymen Zugang zu gewährleisten, d.h. das System weiß nicht, wer wann gekommen und gegangen ist. Ich werde leider nicht da sein können. Falls also jemand hingeht und mitschreibt, wäre ich für einen Link dankbar.&lt;/p&gt;

&lt;p&gt;Im Folgenden jedenfalls meine Notizen zu besagter Session, eine Liste der vorgestellten Patterns, die sich mit dem Aufbau und Betreiben eines Ortes für Hacker befassen. Der erste Punkt beschreibt jeweils das Problem, die nachfolgenden die vorgeschlagene Lösung. Vorbemerkung: &lt;a href=&quot;http://de.wikipedia.org/wiki/Entwurfsmuster&quot;&gt;Patterns&lt;/a&gt; sind keine Anleitung, über ihre Anwendung sollte jeweils am konkreten Fall entschieden werden. (die interessantesten Dinge hab ich mal fett gemacht)&lt;/p&gt;

&lt;h2&gt;sustainability patterns&lt;/h2&gt;
&lt;h3&gt;infrastructue pattern&lt;/h3&gt;

&lt;ul&gt;
   &lt;li&gt;first infrastructure or projects? &lt;/li&gt;
   &lt;li&gt;first infrastructure&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;infrastructure-driven - ideas come out of infrastructure, even stuff you didn't think of&lt;/strong&gt;&lt;/li&gt;
	&lt;/ul&gt;
&lt;h3&gt;grace hopper pattern&lt;/h3&gt;
&lt;ul&gt;   &lt;li&gt;is this the right time? have you thought about all the problems?&lt;/li&gt;
   &lt;li&gt;sure it is time&lt;/li&gt;
   &lt;li&gt;&lt;strong&gt;it's alwasy easier to ask for forgiveness than it is to get permision&lt;/strong&gt;&lt;/li&gt;
   &lt;li&gt;when in doubt, do it&lt;/li&gt;
   &lt;li&gt;problems will be solved (don't think about it now)&lt;/li&gt;&lt;/ul&gt;
&lt;h3&gt;community pattern&lt;/h3&gt;
&lt;ul&gt;   &lt;li&gt;how should your group communicate&lt;/li&gt;
   &lt;li&gt;3 problems: realt time communictation, discussion, documentation&lt;/li&gt;
   &lt;li&gt;mailing list, wiki , irc (&lt;em&gt;ich würde da noch Sachen wie IM (in meiner Umgebung z.Z. meist Skype), evtl. twitter, Blog hinzufügen&lt;/em&gt;)&lt;/li&gt;&lt;/ul&gt;
&lt;h3&gt;critical mass pattern&lt;/h3&gt;
&lt;ul&gt;   &lt;li&gt;how many people?&lt;/li&gt;
   &lt;li&gt;2 + 2: one partner to get kicked of, 2 more to get the work done&lt;/li&gt;
   &lt;li&gt;aim for 10 for a start&lt;/li&gt;&lt;/ul&gt;
&lt;h3&gt;strong persomalities pattern&lt;/h3&gt;
&lt;ul&gt;   &lt;li&gt;nothing gets done&lt;/li&gt;
   &lt;li&gt;need people who have authority, strong personalities, not people who use authority and get laughed at&lt;/li&gt;
   &lt;li&gt;need people with experience with building up spaces&lt;/li&gt;&lt;/ul&gt;
&lt;h2&gt;independence patterns&lt;/h2&gt;
&lt;h3&gt;landlord and neighborhood pattern&lt;/h3&gt;
&lt;ul&gt;   &lt;li&gt;weird landloard, picky neighbors&lt;/li&gt;
   &lt;li&gt;talk to neighbors, need cool neighbors, they might call the cops too often&lt;/li&gt;
   &lt;li&gt;you don't live a majority livestyle - better if your neighbors don't either&lt;/li&gt;&lt;/ul&gt;
&lt;h3&gt;roommate anti-pattern&lt;/h3&gt;
&lt;ul&gt;   &lt;li&gt;guests are fine but don't have anyone live in your space&lt;/li&gt;&lt;/ul&gt;
&lt;h3&gt;separee pattern&lt;/h3&gt;
&lt;ul&gt;   &lt;li&gt;want to chill/work in small groups, but main room is occupied, want to smoke without disturbing anyone&lt;/li&gt;
   &lt;li&gt;smaller, separate rooms instead of one big&lt;/li&gt;
   &lt;li&gt;use curtains, doors&lt;/li&gt;
   &lt;li&gt;smokers room&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;kitchen pattern&lt;/h3&gt;
&lt;ul&gt;   &lt;li&gt;you need food and caffeine at odd times&lt;/li&gt;
   &lt;li&gt;have a kitchen (cooking together - socializing)&lt;/li&gt;
   &lt;li&gt;have fridges&lt;/li&gt;
   &lt;li&gt;sell soft drink - income (most spaces 50% of income)&lt;/li&gt;
   &lt;li&gt;have a dishwasher (!)&lt;/li&gt;
   &lt;li&gt;freezer for pizza etc.&lt;/li&gt;&lt;/ul&gt;
&lt;h3&gt;coziness pattern&lt;/h3&gt;
&lt;ul&gt;   &lt;li&gt;bring in couches (also for sleeping), sofas, comfortable chairs, pillows, tables, ambient light, stereo, projector, videop game consoles&lt;/li&gt;
   &lt;li&gt;bringing in plants didn't work&lt;/li&gt;&lt;/ul&gt;
&lt;h3&gt;shower pattern&lt;/h3&gt;
&lt;ul&gt;   &lt;li&gt;people start smelling funny after hacking for too long&lt;/li&gt;
   &lt;li&gt;have a bathroom with a shower (also for people staying over night, guests from other cities)&lt;/li&gt;
   &lt;li&gt;washing machine for towels if you wanna get fancy&lt;/li&gt;&lt;/ul&gt;
&lt;h3&gt;membership fees pattern&lt;/h3&gt;
&lt;ul&gt;   &lt;li&gt;need money for rent, utilities, projects&lt;/li&gt;
   &lt;li&gt;collect fees regularly&lt;/li&gt;
   &lt;li&gt;choose appropriate amount, then make no exceptions ever&lt;/li&gt;
   &lt;li&gt;student discounts&lt;/li&gt;
   &lt;li&gt;have a totalitarian treasurer&lt;/li&gt;
   &lt;li&gt;always have 3 months of rent on your account&lt;/li&gt;&lt;/ul&gt;
&lt;h3&gt;the sponsoring anti-pattern&lt;/h3&gt;
&lt;ul&gt;   &lt;li&gt;never depend your space on external sponsors - companies go broke&lt;/li&gt;
   &lt;li&gt;space should not be located at company/university - you exclude people not in that culture ( non-students e.g.)&lt;/li&gt;
   &lt;li&gt;donations are ok&lt;/li&gt;
   &lt;li&gt;room must be independent&lt;/li&gt;&lt;/ul&gt;
&lt;h2&gt;regularity patterns&lt;/h2&gt;
&lt;h3&gt;plenum pattern&lt;/h3&gt;
&lt;ul&gt;   &lt;li&gt;internal conflicts, have democratic decision-making&lt;/li&gt;
   &lt;li&gt;have a regular meeting with all members&lt;/li&gt;
   &lt;li&gt;have an agenda and set goals&lt;/li&gt;
   &lt;li&gt;&lt;strong&gt;make people commit themselves to tasks&lt;/strong&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;h3&gt;tuesday pattern&lt;/h3&gt;
&lt;ul&gt;   &lt;li&gt;every weekday sucks for a meeting, there's alwasy someone who has an appointment&lt;/li&gt;
   &lt;li&gt;&lt;strong&gt;pick tuesday. end of discussion.&lt;/strong&gt; (for all spaces - you always know when everyone is meeting at every space)&lt;/li&gt;&lt;/ul&gt;
&lt;h3&gt;open chaos pattern&lt;/h3&gt;
&lt;ul&gt;   &lt;li&gt;you want to draw in people, have an interface to the outside world&lt;/li&gt;
   &lt;li&gt;have a monthly public and open lecture, talk or workshop for everyone&lt;/li&gt;
   &lt;li&gt;then invite interesting people to regular meetings, don't tell the weirdos :)&lt;/li&gt;&lt;/ul&gt;
&lt;h3&gt;u23 pattern&lt;/h3&gt;
&lt;ul&gt;   &lt;li&gt;your space needs fresh blood&lt;/li&gt;
   &lt;li&gt;recruit young people through a challenge, course that spans several weeks&lt;/li&gt;
   &lt;li&gt;let them solve problems in teams&lt;/li&gt;
   &lt;li&gt;tutor them, give them room&lt;/li&gt;&lt;/ul&gt;
&lt;h3&gt;sine curve pattern&lt;/h3&gt;
&lt;ul&gt;   &lt;li&gt;enthusiasm goes away after some time&lt;/li&gt;
   &lt;li&gt;enthusiasm has a sine curve with a cycle duration of 4 years&lt;/li&gt;
   &lt;li&gt;keep it running, don't give up&lt;/li&gt;&lt;/ul&gt;
&lt;h2&gt;conflict resolution patterns&lt;/h2&gt;
&lt;h3&gt;consensus pattern&lt;/h3&gt;
&lt;ul&gt;    &lt;li&gt;need a group decision make sure no one gets left behind&lt;/li&gt;
    &lt;li&gt;don't take votes - discuss until everyone agrees&lt;/li&gt;&lt;/ul&gt;
&lt;h3&gt;democracy pattern&lt;/h3&gt;
&lt;ul&gt;    &lt;li&gt;discussion doesn't lead to goal&lt;/li&gt;
    &lt;li&gt;do take votes, majority wins&lt;/li&gt;&lt;/ul&gt;
&lt;h3&gt;command pattern &lt;/h3&gt;
&lt;ul&gt;    &lt;li&gt;nobody cleans up etc.&lt;/li&gt;
    &lt;li&gt;order people, *always participate*, yell if neccessary&lt;/li&gt;&lt;/ul&gt;
&lt;h3&gt;sudo leadership pattern&lt;/h3&gt;
&lt;ul&gt;   &lt;li&gt;uou start with a nie group but you suddenly find yourself in a dictatorship run by a single person&lt;/li&gt;
   &lt;li&gt;do not have ranks&lt;/li&gt;
   &lt;li&gt;use leadership only temporarily&lt;/li&gt;&lt;/ul&gt;
&lt;h3&gt;responsibility pattern&lt;/h3&gt;
&lt;ul&gt;   &lt;li&gt;don't have time anymore for a task you volunteered for&lt;/li&gt;
   &lt;li&gt;take pride in your volunteer work&lt;/li&gt;
   &lt;li&gt;it you can't do it anymore hand it over&lt;/li&gt;&lt;/ul&gt;
&lt;h3&gt;debate culture pattern&lt;/h3&gt;
&lt;ul&gt;   &lt;li&gt;everybody's yelling at a meeting and nothing gets done&lt;/li&gt;
   &lt;li&gt;make people with actual socail skiils lead the discussion (e.g student council)&lt;/li&gt;
   &lt;li&gt;learn not to interrupt others&lt;/li&gt;&lt;/ul&gt;
&lt;h3&gt;bikeshed anti-pattern&lt;/h3&gt;
&lt;ul&gt;   &lt;li&gt;you suggest building a new bikeshed, but now everybody discusses about the color&lt;/li&gt;
   &lt;li&gt;if you suggest something everybody will discuss it&lt;/li&gt;
   &lt;li&gt;identify useless discussions and end them&lt;/li&gt;
   &lt;li&gt;www.bikeshed.com&lt;/li&gt;
   &lt;li&gt;if it's complicated people will not discuss, it it's trivial and they can contribute they will&lt;/li&gt;&lt;/ul&gt;
&lt;h3&gt;private talk pattern&lt;/h3&gt;
&lt;ul&gt;   &lt;li&gt;someone causes a problem that cannot be resolved in the group&lt;/li&gt;
   &lt;li&gt;let someone experienced talk to the person, don't expose them in front of the group&lt;/li&gt;&lt;/ul&gt;
&lt;h3&gt;old hadware pattern&lt;/h3&gt;
&lt;ul&gt;   &lt;li&gt;space filled up with junk/old hadware, can't bring in new&lt;/li&gt;
   &lt;li&gt;create a pile of old/unused hardware, let everybody take from it&lt;/li&gt;
   &lt;li&gt;anything left should be thrown away&lt;/li&gt;
   &lt;li&gt;announce it, with an escalation system (3 times)&lt;/li&gt;
 	 &lt;li&gt;&lt;a href=&quot;&quot;&gt;http://wiki.koeln.ccc.de/index.php?title=Hacker_space/Hardware-Gesetz&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;creative chaos patterns&lt;/h2&gt;
&lt;h3&gt;key pattern&lt;/h3&gt;
&lt;ul&gt;   &lt;li&gt;space shoyld be accessible all the time&lt;/li&gt;
   &lt;li&gt;hand out keys, track who owns keys, collect deposit for it&lt;/li&gt;
   &lt;li&gt;have a goof lock&lt;/li&gt;
   &lt;li&gt;or electronic locking system (creates problems)&lt;/li&gt;
   &lt;li&gt;other presentation at 24c3 about it&lt;/li&gt;&lt;/ul&gt;
&lt;h3&gt;club mate pattern&lt;/h3&gt;
&lt;ul&gt;   &lt;li&gt;need to raise funds&lt;/li&gt;
   &lt;li&gt;buy a pallette of club mate&lt;/li&gt;
   &lt;li&gt;didn't really get that - people only productive with club mate?!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Am Ende wurden noch URLs zu bestehden Hacker Spaces genannt: &lt;a href=&quot;http://das-labor.org&quot;&gt;das-labor.org&lt;/a&gt;, &lt;a href=&quot;http://c-base.org&quot;&gt;c-base.org&lt;/a&gt;, &lt;a href=&quot;http://netzladen.org&quot;&gt;netzladen.org&lt;/a&gt;.&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/12/pre-generating-cached-stylesheets-and-javascripts-with-rails-20/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/12/pre-generating-cached-stylesheets-and-javascripts-with-rails-20/"/>
    <title>Pre-generating cached Stylesheets and Javascripts with Rails 2.0</title>
    <updated>2007-12-21T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;We just switched &lt;a href=&quot;http://autoki.com&quot;&gt;autoki&lt;/a&gt; over to &lt;a href=&quot;http://weblog.rubyonrails.org/2007/12/7/rails-2-0-it-s-done&quot;&gt;Rails 2.0.2&lt;/a&gt; in the last days. While the transition we replaced the &lt;a href=&quot;http://synthesis.sbecker.net/pages/asset_packager&quot;&gt;asset_packager plugin&lt;/a&gt; with the new &lt;a href=&quot;http://blog.codefront.net/2007/12/12/concatenate-your-stylesheets-and-javascripts-in-3-seconds-rails-20-a-feature-a-day-3/&quot;&gt;Rails 2.0 :cache =&amp;gt; true&lt;/a&gt; option in &lt;code&gt;stylesheet_link_tag&lt;/code&gt;/&lt;code&gt;javascript_include_tag&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;As our assets (public/*) are served from an apache webserver that has its own checkout of the project, we can’t let Rails generate the all.css/all.js files on the fly when the first request comes in. Instead we have to pre-generate them at deployment. For asset_packager we had a capistrano task that did just that. Rails doesn’t have that. Instead the logic to generate the files is built right into the link/include methods in the AssetTagHelper(!). So now we are simply doing this in our capistrano recipe:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;generate combined css and javascript files on web&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;after&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'deploy:symlink'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:roles&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:web&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;run&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;cd &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;release_path&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &amp;amp;&amp;amp; mongrel_rails start -d -e production -p 3001 &amp;amp;&amp;amp; curl http://localhost:3001 &amp;gt; /dev/null&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;run&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;cd &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;release_path&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &amp;amp;&amp;amp; mongrel_rails stop&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Fire up a mongrel, curl the start page, bring it back down. Feels a bit overkill but it works. (at least I hope it will when I’ll first try it later today)&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/12/running-a-skipped-rails-migration/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/12/running-a-skipped-rails-migration/"/>
    <title>Running a skipped Rails Migration</title>
    <updated>2007-12-12T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Sometimes it happens that you skip a migration. Imagine you were working on a branch and are now merging back into trunk. In the meantime someone committed the migration 303 into trunk but you also had a few migrations in your branch. Now your development database is on 306 and you missed 303. Instead of migrating back to 302 and then again up to 306 you can also do the following in your rails console:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'db/migrate/303_create_comments_table'&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;CreateCommentsTable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;up&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;There goes your migration.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/12/rspec-11-with-storyrunner-coming/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/12/rspec-11-with-storyrunner-coming/"/>
    <title>RSpec 1.1 with StoryRunner coming?</title>
    <updated>2007-12-12T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;The latest update from the &lt;a href=&quot;http://rspec.rubyforge.org/&quot;&gt;rspec&lt;/a&gt; trunk changed the version.rb file indicating a 1.1 Release Candidate 1 version. Will there finally be a stable RSpec version that has &lt;a href=&quot;http://upstream-berlin.com/blog/2007/09/24/railsconf-europe-2007-roundup-1-rspec/&quot;&gt;StoryRunner&lt;/a&gt;? That’d be a great present for christmas.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/12/grafikeffekte-im-terminal/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/12/grafikeffekte-im-terminal/"/>
    <title>Grafikeffekte im Terminal</title>
    <updated>2007-12-11T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Nie mehr Langeweile auf der Shell, einfach iTunes Visuals in den Hintergrund gelegt und schon pulsiert die Kommandozeile. Es lebe die Erfindung des halbtransparenten Fensters:&lt;/p&gt;

&lt;object type=&quot;application/x-shockwave-flash&quot; width=&quot;400&quot; height=&quot;263&quot; data=&quot;http://www.vimeo.com/moogaloop.swf?clip_id=426781&amp;amp;server=www.vimeo.com&amp;amp;fullscreen=1&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=01AAEA&quot;&gt;	&lt;param name=&quot;quality&quot; value=&quot;best&quot; /&gt;	&lt;param name=&quot;allowfullscreen&quot; value=&quot;true&quot; /&gt;	&lt;param name=&quot;scale&quot; value=&quot;showAll&quot; /&gt;	&lt;param name=&quot;movie&quot; value=&quot;http://www.vimeo.com/moogaloop.swf?clip_id=426781&amp;amp;server=www.vimeo.com&amp;amp;fullscreen=1&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=01AAEA&quot; /&gt;&lt;/object&gt;
&lt;p&gt;&lt;br /&gt;.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/12/weihnachtsstockchen-20/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/12/weihnachtsstockchen-20/"/>
    <title>Weihnachtsstöckchen 2.0</title>
    <updated>2007-12-05T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;div style=&quot;float:right; margin-left: 20px;&quot;&gt;&lt;img src=&quot;/uploads/2007/12/candle-flickering2.gif&quot; alt=&quot;candle-flickering2.gif&quot; width=&quot;100&quot; /&gt;&lt;/div&gt;

&lt;p&gt;Das &lt;a href=&quot;http://knallrosatagebuch.de&quot;&gt;Knallrosa Tagebuch&lt;/a&gt; hat einen ganzen Wald von &lt;a href=&quot;http://de.wikipedia.org/wiki/St%C3%B6ckchen&quot;&gt;Stöckchen&lt;/a&gt; quer durchs Internet &lt;a href=&quot;http://anikalindtner.wordpress.com/2007/12/04/ich-machs-aber-anders-ein-weihnachtsstockchen-umgeschrieben/&quot;&gt;geworfen&lt;/a&gt;, und einer ist auch hier in diesem Blog angekommen. Da das hier aber ein Tech-Ruby-Web2.0-Startup-Blog ist, werden die Antworten wohl auch etwas anders ausfallen. Zu den Fragen:&lt;/p&gt;

&lt;h3&gt;Heißt es nun &amp;quot;zu&amp;quot; oder &amp;quot;an Weihnachten&amp;quot;?&lt;/h3&gt;
&lt;p&gt;Definitiv &lt;em&gt;to&lt;/em&gt;, denn das Web2.0 spricht Englisch und &lt;em&gt;to&lt;/em&gt; klingt auch schon wie &lt;em&gt;two point 0&lt;/em&gt;. Und es heisst auch nicht Weihnachten sondern Weihnachtsgeschäft, und in dem wird dann richtig Umsatz gemacht. Jedenfalls bei den &lt;a href=&quot;http://amazon.de&quot;&gt;großen Web-Shops&lt;/a&gt;. &lt;a href=&quot;http://autoki.com&quot;&gt;Wir&lt;/a&gt; (noch) Kleinen wundern uns derweil, was &lt;a href=&quot;http://www.autoki.com/profile/bushido&quot;&gt;unsere User&lt;/a&gt; die ganze Zeit machen.&lt;/p&gt;

&lt;h3&gt;Welcher Geschenketyp bist Du - Lastminute-StresseinkäuferIn oder GedankenspielerIn im Herbst?&lt;/h3&gt;
&lt;p&gt;Semi-Last-Minute-Online-&lt;a href=&quot;http://spreadshirt.net&quot;&gt;Mass-Customization&lt;/a&gt;-Kunde. Die Frage, ob dann auch noch pünktlich geliefert wird, verleiht dem ganzen zusätzliche Spannung gegenüber dem klassischen Offline-Weihnachtsshopping.&lt;/p&gt;

&lt;p&gt;Leider haben sich digitale Geschenke noch nicht durchgesetzt. Sonst könnte man Web Communities oder Programme verschenken. Ich erzähl ja immer wieder gern die Geschichte von dem Jungen, der seiner Freundin eine selbst programmierte, animierte Kerzenflamme geschenkt hat (nein, das war nicht ich).&lt;/p&gt;

&lt;h3&gt;Der ideale Wunschweihnachtstraum?&lt;/h3&gt;
&lt;p&gt;Ein ultraportables Macbook Pro mit 20 Zoll Display das man aber auf A5 zusammenklappen kann, mit &lt;a href=&quot;http://youtube.com/watch?v=1ftJhDBZqss&quot;&gt;Multitouch-Display&lt;/a&gt; übers ganze Gerät, dazu WLAN und UMTS, &lt;a href=&quot;http://golem.de/0712/56384.html&quot;&gt;512Gb Solid State Disc&lt;/a&gt;, 24h Aukkulaufzeit, 0.5cm dickes Gehäuse, wasserdicht und stoßfest, Wahnsinns Grafik- und CPU-Leistung und jemand, der mir dann Heiligabend ab und zu was zu essen und zu trinken vorbeibringt :)&lt;/p&gt;

&lt;p&gt;Ach ja, um erstmal auf der Geschenkschiene zu bleiben: &lt;a href=&quot;http://weblog.rubyonrails.org/2007/9/30/rails-2-0-0-preview-release&quot;&gt;Ruby on Rails 2.0&lt;/a&gt;, &lt;a href=&quot;http://www.atdot.net/yarv/&quot;&gt;Ruby 1.9&lt;/a&gt;, &lt;a href=&quot;http://rubini.us/&quot;&gt;Rubinius 1.0&lt;/a&gt;, &lt;a href=&quot;http://dev.mysql.com/downloads/mysql/6.0.html&quot;&gt;Mysql 6.0&lt;/a&gt; mit &lt;a href=&quot;http://www.golem.de/0701/49702.html&quot;&gt;Falcon Engine&lt;/a&gt; oder doch besser gleich &lt;a href=&quot;http://couchdb.org/&quot;&gt;CouchDb 1.0&lt;/a&gt;, &lt;a href=&quot;http://www.tuaw.com/2007/01/11/textmate-2s-upcoming-features/&quot;&gt;TextMate 2&lt;/a&gt;, &lt;a href=&quot;http://wiki.mozilla.org/Firefox3&quot;&gt;Firefox 3&lt;/a&gt;…&lt;/p&gt;

&lt;p&gt;Falls es das alles nicht gibt könnte ich mir auch vorstellen, auf einem Schlitten sitzend durch eine winterliche Schneelandschaft zu fahren, in der Ferne klingen die Glocken, ein paar Sternschnuppen huschen über den Himmel, im Hintergrund lauschige Musik (aber bitte nichts weihnachtliches), und meine Abendbeschäftigung wäre es, die Schneeflocken zu zählen, die vom Himmel fallen. Aber das ist natürlich alles komplett unrealisitsch, zurück zum Macbook Pro…&lt;/p&gt;

&lt;h3&gt;Die Realität?&lt;/h3&gt;

&lt;p&gt;Rails 2.0 ist sogar halbwegs realistisch, wenn man den Machern glauben kann. Das &lt;a href=&quot;http://www.macrumors.com/2007/12/05/mobile-penryns-to-launch-on-january-6th/&quot;&gt;Macbook Pro kommt dann wohl erst im Januar&lt;/a&gt;, und ich habe gar keinen Schlitten. Ganz klassisch zu Hause sitzen und Kartoffelsalat mit Wiener Würstchen mampfen dann wohl.&lt;/p&gt;

&lt;h3&gt;Bei welchem Geschenk würdest in hysterisches Lachen verfallen, weil Dir sonst nichts anderes einfällt und Du von Heulkrämpfen Kopfschmerzen bekommen würdest?&lt;/h3&gt;

&lt;div style=&quot;float:left; margin-right: 10px&quot;&gt;&lt;img src=&quot;/uploads/2007/12/computerbild-2-230.JPG&quot; alt=&quot;computerbild-2-230.JPG&quot; width=&quot;150&quot; /&gt;&lt;/div&gt;

&lt;p&gt;Das Buch &lt;a href=&quot;http://www.amazon.de/Windows-Konfiguration-Kommunikation-L%C3%B6sungen-Ultimate/dp/3827241022/ref=pd_bbs_sr_6?ie=UTF8&amp;amp;s=books&amp;amp;qid=1196888895&amp;amp;sr=8-6&quot;&gt;Windows Vista Home. Konfiguration, Kommunikation, Lösungen.&lt;/a&gt;. Oder ein &lt;a href=&quot;http://www.computerbild.de/cb/index.html&quot;&gt;Computerbild-Abo&lt;/a&gt;. Oder einen exklusiven Premiumaccount Account bei &lt;a href=&quot;http://netmoms.de&quot;&gt;Netmoms.de&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;Welchen Namen würde euer Baum tragen, wenn Du Dir über eine solche Frage Gedanken machen müsstest?&lt;/h3&gt;

&lt;p&gt;Ich würde natürlich den &lt;a href=&quot;http://www.lightsphere.com/dev/web20.html&quot;&gt;Web2.0 Namensgenerator&lt;/a&gt; fragen: &lt;em&gt;Agizu&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;Wie klingt Heilig Abend?&lt;/h3&gt;
&lt;p&gt;Auch dafür gibt es im Web natürlich eine Lösung: &lt;a href=&quot;http://soundbadge.net/&quot;&gt;soundbadge.net&lt;/a&gt; - Ein &lt;a href=&quot;http://railsrumble.com/&quot;&gt;RailsRumble&lt;/a&gt;-Projekt von u.A. &lt;a href=&quot;http://blog.krutisch.de&quot;&gt;Jan Krutisch&lt;/a&gt;. Und nächstes jahr wird uns diese Frage hoffentlich dann &lt;a href=&quot;http://www.soundcloud.com/&quot;&gt;soundcloud&lt;/a&gt; beantworten können.&lt;/p&gt;

&lt;h3&gt;Wie riecht Heilig Abend?&lt;/h3&gt;
&lt;p&gt;So frisch wie eine große, von mir aus 566 Billiarden Möglichkeiten persönlich zusammengestellte Portion &lt;a href=&quot;http://mymuesli.com&quot;&gt;MyMüsli&lt;/a&gt;. Zur Zeit auch mit Weihnachtsbassis-Müsli erhältlich. Dazu dann vielleicht noch gebrannte Mandeln und Pfefferkuchenkrümel?&lt;/p&gt;

&lt;h3&gt;Was ist Deine erste Erinnerung an Weihnachten?&lt;/h3&gt;
&lt;div style=&quot;float:right&quot;&gt;&lt;a href=&quot;http://web.archive.org/web/19981111183552/google.stanford.edu/&quot;&gt;&lt;img src=&quot;/uploads/2007/12/google_back_then.png&quot; alt=&quot;google_back_then.png&quot; width=&quot;200&quot; /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;Das war zu Zeiten als es noch kein &lt;a href=&quot;http://web.archive.org/web/19981111183552/google.stanford.edu/&quot;&gt;Google&lt;/a&gt; (1998), kein &lt;a href=&quot;http://web.archive.org/web/19990117033159/pages.ebay.com/aw/index.html&quot;&gt;Ebay&lt;/a&gt; (1997), kein &lt;a href=&quot;http://web.archive.org/web/19991013091817/http://amazon.com/&quot;&gt;Amazon&lt;/a&gt; (1999), kein Web2.0 und nicht einmal &lt;a href=&quot;http://web.archive.org/web/19961220154856/http://www.aol.com/&quot;&gt;AOL&lt;/a&gt; (1996) oder &lt;a href=&quot;http://web.archive.org/web/19990424095338/www.compuserve.com/gateway/default.asp&quot;&gt;Compuserve&lt;/a&gt; (1999), (hoffentlich lehn ich mich nicht gerade zu weit aus dem Fenster) ja nicht einmal &lt;a href=&quot;http://web.archive.org/web/20070328174840/http://www.autoki.com/&quot;&gt;autoki&lt;/a&gt; (3/2007) gab. Da kann also nichtmal das &lt;a href=&quot;http://archive.org&quot;&gt;Internet Archive&lt;/a&gt; weiterhelfen. Aber das ist auch gar nicht Webzweinullig. Jedenfalls gab es bei diesem Weihnachten einen richtigen Weihnachtsmann mit eine gelben Kiste voller Geschenke, Gedichte aufsagen, Lieder singen und all das was es als Kind halt noch gibt.&lt;/p&gt;

&lt;h3&gt;Wer war früher der Weihnachtsmann?&lt;/h3&gt;
&lt;p&gt;Ich glaub mein Opa, aber so richtig weiß ich das nicht. Vielleicht war’s ja auch &lt;a href=&quot;http://www.makebeliever.com/&quot;&gt;so einer&lt;/a&gt; - ach nee das gab’s ja da noch nicht.&lt;/p&gt;

&lt;h3&gt;Wie lange hast Du an den Weihnachtsmann geglaubt?&lt;/h3&gt;
&lt;p&gt;Ich hoffe immer noch auf ihn. Und darauf, dass er mir 2 Wochen Urlaub mitbringt.&lt;/p&gt;

&lt;h3&gt;Wie oft muss &quot;Drei Haselnüsse für Aschenbrödel&quot; sein?&lt;/h3&gt;

&lt;p&gt;Was?! Das da? Nie gesehen.&lt;/p&gt;

&lt;object width=&quot;425&quot; height=&quot;355&quot;&gt;&lt;param name=&quot;movie&quot; value=&quot;http://www.youtube.com/v/1XEgy0Abanc&amp;amp;rel=1&quot; /&gt;&amp;lt;/param&amp;gt;&lt;param name=&quot;wmode&quot; value=&quot;transparent&quot; /&gt;&amp;lt;/param&amp;gt;&lt;embed src=&quot;http://www.youtube.com/v/1XEgy0Abanc&amp;amp;rel=1&quot; type=&quot;application/x-shockwave-flash&quot; wmode=&quot;transparent&quot; width=&quot;425&quot; height=&quot;355&quot; /&gt;&amp;lt;/embed&amp;gt;&lt;/object&gt;

&lt;h3&gt;Kannst Du glauben, dass es tatsächlich Leute gibt, die diesen wunderbaren, supertollen kulturellen Kulturschatz nicht kennen? (kleiner persönlicher Seitenhieb;-) )&lt;/h3&gt;

&lt;p&gt;Nee, jetzt wo’s das auf &lt;a href=&quot;http://youtube.com&quot;&gt;youtube&lt;/a&gt; gibt.&lt;/p&gt;

&lt;p&gt;Noch Fragen? :) Mit fällt gerade niemand so richtig zum Weiterwerfen ein. Wer immer will möge sich angesprochen fühlen.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/12/random-links-smalltalky-ruby-quietbacktrace/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/12/random-links-smalltalky-ruby-quietbacktrace/"/>
    <title>Random links: smalltalky ruby &amp; quietbacktrace</title>
    <updated>2007-12-04T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Patt Maddoxx recently &lt;a href=&quot;http://evang.eli.st/blog/2007/11/22/smalltalky&quot;&gt;blogged&lt;/a&gt; about a ruby hack to avoid the dreaded nil-if, e.g. &lt;code&gt;if user.comments.first; user.comments.first.title;&lt;/code&gt;. instead you can now do: &lt;code&gt;user.comments.first.if_not_nil?{|c| c.title}&lt;/code&gt;.
&lt;a href=&quot;http://evang.eli.st/blog/2007/11/22/smalltalky&quot;&gt;http://evang.eli.st/blog/2007/11/22/smalltalky&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;dan croak has written a precious little ruby gem that filters the noise from you exception backtraces:
&lt;a href=&quot;http://giantrobots.thoughtbot.com/2007/12/3/shhh-your-test-unit-backtraces-are-too-noisy&quot;&gt;http://giantrobots.thoughtbot.com/2007/12/3/shhh-your-test-unit-backtraces-are-too-noisy&lt;/a&gt;&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/12/implementation-patterns-emerging/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/12/implementation-patterns-emerging/"/>
    <title>Implementation Patterns emerging</title>
    <updated>2007-12-03T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;I recently read Kent Beck’s new book &lt;a href=&quot;http://www.infoq.com/articles/implementations-patterns-br&quot;&gt;Implementation Patterns&lt;/a&gt; - it was a nice read and I guess it somehow strengthened my senses towards patterns a bit. Unfortunately it is written with Java in mind, and as we all know Patterns are language specific. The following can’t really be applied to Java as far as i know (having stopped writing in Java with 1.5).
So I discovered a kind of implementation pattern I had used a few times in the last weeks. Maybe everyone has already written about it but I want to stress that I discovered it myself :) Here it is:&lt;/p&gt;

&lt;h3&gt;The Selector Pattern&lt;/h3&gt;

&lt;p&gt;When to use it: When you have a complicated if-elseif-elsif-else or switch clause and want to replace it with something more elegant.&lt;/p&gt;

&lt;p&gt;When not to use it: It works well for limited complexity. I things get more complex you may want to use class inheritance instead.&lt;/p&gt;

&lt;p&gt;What it does: Put the each condition and the according logic into a hash. Then iterate through the conditions to find a match and execute its logic.&lt;/p&gt;

&lt;h4&gt;Example&lt;/h4&gt;

&lt;p&gt;Suppose you have a class &lt;code&gt;Video&lt;/code&gt; that represents a video on youtube or some other hosting provider. Its purpose is to render the neccessary HTML tags to embed it into a document. The generated HTML differs depending on the hosting provider, e.g. &lt;a href=&quot;http://youtube.com&quot;&gt;youtube.com&lt;/a&gt; or &lt;a href=&quot;http://vimeo.com&quot;&gt;vimeo.com&lt;/a&gt;. The code could be expressed like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Video&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;html&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;include?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'youtube.com'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;&amp;lt;embed&amp;gt;player.youtube.com/&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@url&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;lt;/embed&amp;gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;elsif&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;include?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'vimeo'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;&amp;lt;embed&amp;gt;vimdeo.com/player/&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@url&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/1&amp;lt;/embed&amp;gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Using the selector pattern it might look like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Video&lt;/span&gt;
  &lt;span class=&quot;vc&quot;&gt;@@renderers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;'youtube.com'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;lambda&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&amp;lt;embed&amp;gt;player.youtube.com/&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;lt;/embed&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;'vimeo'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;lambda&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&amp;lt;embed&amp;gt;vimdeo.com/player/&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/1&amp;lt;/embed&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;html&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;select_provider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;kp&quot;&gt;private&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;select_provider&lt;/span&gt;
    &lt;span class=&quot;vc&quot;&gt;@@renderers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;provider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;include?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;provider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;last&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;With only 2 providers this doesn’t make too much sense but with something like 5 your &lt;code&gt;elsifs&lt;/code&gt; start to get really ugly and the selector pattern offers a more ligtweight way to replace these than creating an entire hierarchy of subclasses for every case.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/12/euruko-2007-review/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/12/euruko-2007-review/"/>
    <title>Euruko 2007 Review</title>
    <updated>2007-12-03T00:00:00+00:00</updated>
    <author>
      <name>thilo</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;div class=&quot;content_image&quot;&gt;
&lt;a href=&quot;/uploads/2007/11/n522657501_414607_5641.jpg&quot; title=&quot;euruko 2007&quot;&gt;&lt;img src=&quot;/uploads/2007/11/n522657501_414607_5641.jpg&quot; alt=&quot;euruko 2007&quot; width=&quot;320px&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Last weekend (10. to 11. November) we were at the &lt;a href=&quot;http://euruko.com&quot;&gt;Euruko&lt;/a&gt;, the European Ruby Conference in Vienna. The conference wasn’t nearly as big as the Railsconf Europe, but the ad hoc character adapted very well to demands of the attendees and the topics were more academic. Here is a round up of the presentations I attended.&lt;/p&gt;

&lt;p&gt;The Keynote was about the future of ruby with a look back to its beginnings by &lt;a href=&quot;http://rubyhacker.com/&quot;&gt;Hal Fulton&lt;/a&gt;. About the upcoming ruby 1.9 he said, that it wouldn’t change that much, but will gain performance by having its own virtual machine. Another aspect of his talk was the shift to enterprise use.&lt;/p&gt;

&lt;p&gt;The following talk was about the Database Library &lt;a href=&quot;http://www.kuriositaet.de/ruby/dbrb/&quot;&gt;Dbrb&lt;/a&gt;, it is basically a wrapper for ruby dbi to make it easier to work with it. Interesting if you have to do data crunching closer to the relational domain.&lt;/p&gt;

&lt;p&gt;David Anderson shows a structured wiki, where you can build simple Applications with forms and so on.&lt;/p&gt;

&lt;p&gt;Ramine Darabiha, Sven C. Köhler show their Javascript based content distribution/mashup system &lt;a href=&quot;http://mysit.es&quot;&gt;MySit.es&lt;/a&gt; which is still under heavy development.&lt;/p&gt;

&lt;p&gt;After the lunch break &lt;a href=&quot;http://chneukirchen.org/blog/&quot;&gt;Christian Neukirchen&lt;/a&gt; showed &lt;a href=&quot;http://rubyforge.org/projects/rack&quot;&gt;Rack&lt;/a&gt;, a simple and modular web server interface specification. It basically wraps the status, headers, and body in a array. Following the rack specification you can add middleware between your server and the web framework, e.g. log file generation or URL rewriting.&lt;/p&gt;

&lt;p&gt;An alternative to watir, a in browser testing framework, was shown by Kingsley Hendrickse, the Ruby IE Scripting System (&lt;a href=&quot;http://www.riess-automation.com/&quot;&gt;RIESS&lt;/a&gt;) and AutoGUI.&lt;/p&gt;

&lt;p&gt;The powerfull, but hard to learn, web site scraping framework &lt;a href=&quot;http://scrubyt.org/&quot;&gt;scRUBYt&lt;/a&gt; was introduced by &lt;a href=&quot;http://www.rubyrailways.com/&quot;&gt;Peter Szinek&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The last talk of that day was about the use of &lt;a href=&quot;http://en.wikipedia.org/wiki/Domain-specific_programming_language&quot;&gt;Domain Specific Languages&lt;/a&gt; and how easy it is to build one, held by Martin Grund.&lt;/p&gt;

&lt;p&gt;In the evening we had an excellent dinner in the ‘Universitätsbräu’. My personal dish recommendation ‘Schöpfsteak’.&lt;/p&gt;

&lt;p&gt;We took the opportunity and input from the conference to try out some new stuff in a late night coding session, although I had had just 4 hours of sleep the night before. But alex will write some more about our &lt;a href=&quot;https://twitter.com&quot;&gt;twitter&lt;/a&gt; mashup &lt;a href=&quot;http://twizzer.com&quot;&gt;twizzer&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;On Sunday we missed the first talks. So i can’t tell you anything about it ;)&lt;/p&gt;

&lt;p&gt;The first I joined again was from Tim Becker, who showed &lt;a href=&quot;http://www.sun.com/bigadmin/content/dtrace/&quot;&gt;DTrace&lt;/a&gt;. &lt;a href=&quot;http://www.sun.com/bigadmin/content/dtrace/&quot;&gt;DTrace&lt;/a&gt; is an integrated tracing framework for debugging applications or the OS itself. It is integrated into Solaris and the latest Mac OS 10.5 ‘Leopard’.&lt;/p&gt;

&lt;p&gt;What Trees are good for, was explained by &lt;a href=&quot;http://po-ru.com/&quot;&gt;Paul Battley&lt;/a&gt;. For example they are good for faster best match search using the Levenshtein distance. But I have to admit, I didn’t followed that talk very well.&lt;/p&gt;

&lt;p&gt;Then Stephan Kämper talked on Quality in code, and shows some exaple of good and bad code.&lt;/p&gt;

&lt;p&gt;Hal Fulton filled the little gap before the lunch break with his proposal for an &lt;em&gt;in operator&lt;/em&gt; in Ruby.&lt;/p&gt;

&lt;p&gt;After the lunch it got even more academic with &lt;a href=&quot;http://www.cs.queensu.ca/~thurston/ragel/&quot;&gt;Ragel&lt;/a&gt;, a state machine compiler, presented by Ry Dahl&lt;/p&gt;

&lt;p&gt;Then we showed our results from the coding session.&lt;/p&gt;

&lt;p&gt;The last presentation of the &lt;a href=&quot;http://euruko.com&quot;&gt;Euruko&lt;/a&gt; was held by Sasch Schlegel about the ebXML Messaging System called &lt;a href=&quot;http://rubyforge.org/projects/hefeweizen/&quot;&gt;Hefeweizen&lt;/a&gt;. I liked the MAMAs and PAPAs analogy ;)&lt;/p&gt;

&lt;p&gt;Before we caught our flight home, we had another dinner at the ‘Universitätsbräu’.&lt;/p&gt;

&lt;p&gt;I enjoyed the &lt;a href=&quot;http://euruko.com&quot;&gt;Euruko&lt;/a&gt; very much, I will try to attend next year again. Maybe in Prague then.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/11/berlin-barcamp-2-was-konnen-social-communities-von-spielen-lernen/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/11/berlin-barcamp-2-was-konnen-social-communities-von-spielen-lernen/"/>
    <title>Berlin Barcamp 2 - Was können Social Communities von Spielen lernen?</title>
    <updated>2007-11-20T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;div class=&quot;content_image&quot;&gt;&lt;img src=&quot;/uploads/2007/11/menschaegeredichnicht.png&quot; alt=&quot;menschaegeredichnicht.png&quot; /&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;http://barcampberlin2.mixxt.de/networks/wiki/index.Reports&quot;&gt;Barcamp2 Berlin&lt;/a&gt;, meine erste Session. Holger Dieterich von &lt;a href=&quot;http://moviepilot.de&quot;&gt;moviepilot.de&lt;/a&gt; projiziert die Mechanismen, nach denen klassische Spiele funktionieren, auf social Communities im Web. Anhand von 10 Beispielen zeigt er, wie eine Community für ihre User spannender und unterhaltsamer (mehr Traffic) werden kann, indem sie von Spielen lernt.&lt;/p&gt;

&lt;p&gt;Folgene Annahmen macht Holger über Spiele:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;sie machen Spaß&lt;/li&gt;
&lt;li&gt;die Spieler werden in eine Art Flow-Zustand versetzt (vergessen die Zeit)&lt;/li&gt;
&lt;li&gt;Spiele sind ein soziales Erlebnis (z.B. Fußball)&lt;/li&gt;
&lt;li&gt;werden immer wieder gespielt &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Genau das, was man als Community Builder auch von seinen Usern sehen will :) Die Herausforderung ist nun, die Mechanismen, die Spieler ihren Spaß bringen und sie immer wieder spielen lassen, auf Web Communities zu übertragen. Ich hab das mal für &lt;a href=&quot;http://autoki.com&quot;&gt;autoki&lt;/a&gt; versucht:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Sammeln: in manchen Spielen, z.b. &lt;a href=&quot;http://de.wikipedia.org/wiki/Magic:_Die_Zusammenkunft&quot;&gt;Magic&lt;/a&gt; oder Pokemon, entsteht ein regelrechter Sammelwahn, in diesem Beispiel um die Karten, die zum Spielen benötigt werden. Man kann den Spielern also einen gewissen Sammlertrieb nachsagen, den sie im Spiel befriedigen können. Bei autoki gibt's dafür die Merkliste (auf dem Userprofil), auf der man seine Lieblingsautos sammeln kann. Ist aber noch ausbaufähig, man sollte anschließend mit diesen Autos evtl. Autoquartett spielen können o.ä. Auch das Anlegen mehrer Listen wie z.B. bei iTunes die IMixe wäre evtl. spannend.&lt;/li&gt;

&lt;li&gt;Feedback/Status: Die Spieler sollten über den Status des Spiels sowie ihren eigenen bescheid wissen. Beim Fußball gibt's z.B. gelbe Karten und einen Punktestand. Die Analogie in Social Networks sind Statusupdates z.B. über Aktivitäten der Freunde und des Users selbst, welche wir bei autoki in unseren Social Feed auf der Startseite nach dem Login abbilden. Auskunft über die eigene Aktivität gibt der Aktivitätsindex auf dem &lt;a href=&quot;http://autoki.com/profile/alex&quot;&gt;eigenen Profil&lt;/a&gt;.&lt;/li&gt;

&lt;li&gt;Hindernisse und begrenzte Resourcen: In vielen Spielen stehen mit nur begrenzte Resourcen zur Verfügung, z.B. die Anzahl der Spielkarten oder Spielsteine. Dadurch wird der Wert der entsprechenden Resource gesteigert. Wäre mal eine Überlegung wert, z.B. das Anhupen zu begrenzen. Zur Zeit hupen einige User recht wahllos ihre ganze Umgebung voll, was auch mal nerven kann. Wenn man nur 3 Leute pro Tag anhupen könnte, wäre der Wert dieser Geste gleich ein ganz anderer.&lt;/li&gt;

&lt;li&gt;Angepasster schwierigkeitsgrad: Mach es dem Neuling leicht, aber fordere den Profi immer noch heraus. In &lt;a href=&quot;http://de.wikipedia.org/wiki/Max_Payne&quot;&gt;Max Payne&lt;/a&gt; z.B. greifen die Gegner weniger hartnäckig an, wenn der Spieler zu oft daneben schießt. Im Bereich der Social Community könnte das bedeuten, Neulingen die Features erst schrittweie beizubringen, um sie nicht zu überfordern. Bei autoki gibt's inzwischen zu einigen Features Einführungstexte, die erklären, wie was funktioniert. Wenn man's kapiert hat lassen diese sich auf Nimmerwiedersehen wegklicken. Die Frage ist, ob man so weit gehen wollte, einige Features Neulingen zunächst nicht anzubieten, um sie nicht mit zu vielen Information auf einmal zu erschlagen. Eine generell bessere Strukturierung für Neulinge und Profiuser scheint mir aber sinnvoller zu sein.&lt;/li&gt;

&lt;li&gt;Customisierung: in Jedem Rollenspiel Kann ich als Spieler meinem Charakter bestimmte Fähigkeiten verleihen, ihm schicke Sachen anziehen und oft auch den Augenabstand und die Haarfarbe einstellen. Im Web macht &lt;a hef=&quot;http://myspace.com&quot;&gt;myspace&lt;/a&gt; vor, &lt;a href=&quot;http://profile.myspace.com/index.cfm?fuseaction=user.viewprofile&amp;amp;friendid=104587240&quot;&gt;was&lt;/a&gt; &lt;a href=&quot;http://profile.myspace.com/index.cfm?fuseaction=user.viewprofile&amp;amp;friendid=74865767&quot;&gt;an&lt;/a&gt; &lt;a href=&quot;http://profile.myspace.com/index.cfm?fuseaction=user.viewprofile&amp;amp;friendid=8499336&quot;&gt;customizing&lt;/a&gt; &lt;a href=&quot;http://profile.myspace.com/index.cfm?fuseaction=user.viewprofile&amp;amp;friendid=45073195&quot;&gt;alles&lt;/a&gt; &lt;a href=&quot;http://profile.myspace.com/index.cfm?fuseaction=user.viewprofile&amp;amp;friendid=2384993&quot;&gt;möglich&lt;/a&gt; &lt;a hef=&quot;http://www.myspacemaster.net/&quot;&gt;ist&lt;/a&gt; - das Ergebnis ist meist eher erschreckend ;). Die große Frage ist, was der eigenen Community wichtiger ist - personalisierte Profile bis zur Unkenntlichkeit oder Lesbarkeit und Konsistenz. autoki geht im Moment den zweiten Weg, mal sehen, ob sich da nochmal was in die andere Richtung tut.&lt;/li&gt;

&lt;li&gt;Wettbewerb: Klar, in den meisten Spielen geht es darum, größer, schneller oder reicher zu werden als die anderen - der Wettbewerb stellt wohl mit die größte Motivation dar, Spiele überhaupt immer wieder zu spielen. Nicht fehlen darf eine Highscoreliste, auf der die Champions ihre Portion Ruhm abbekommen. Autoki bedient diesen Punkt ganz einfach durch die Integration von Spielen, vor allem des Autoquartetts. Um die ersten Plätze auf der Monats- und Gesamtspiel gibt es regelmäßig Kämpfe. Weiterhin gibt es Dinge wie bestbewertete Fotos und Autos - wird &lt;stroke&gt;in Zukunft&lt;/stroke&gt; gerade noch weiter ausgebaut.&lt;/li&gt;

&lt;li&gt;Minispiele: Kleine Spiele, die nur einige Sekunden/Minuten dauern, immer wieder mal zwischendurch gespielt werden können und im Idealfall süchtig machen. Da lassen sich wohl sämtliche &lt;a href=&quot;http://www.jamba.de/&quot;&gt;Handyspiele&lt;/a&gt; einordnen, oder auch kleinere Einlagen in eigentlich komplexeren Computerspielen, wie z.B. einem &lt;a href=&quot;http://img123.imageshack.us/img123/1103/shot00005mediumzk7.jpg&quot;&gt;Spielautomaten&lt;/a&gt;, der in einem Adventure rumsteht oder ähnliches. In dem Sektor ist autoki mit dem Quartettspiel ganz weit vorne. Dauert nur 15 Runden und man kann sowohl gegen Computer als auch menschliche Gegner spielen. Trotzdem gibt's auch hier noch Verbesserungspotential, vor allem für neue User, die das Spiel nicht kennen.&lt;/li&gt;

&lt;li&gt;Glück im Spiel: Zufällig auftretende Ereignisse wie Bonuspunkte oder Gewinne motivieren, immer mal wieder zu spielen bzw. in der Community aktiv zu werden, da jederzeit die Chance auf ein solches Ereignis besteht. Mit steigender Aktivität steigt natürlich auch die Wahrscheinlichkeit eines zufälligen Gewinns. So ungefähr funktionieren bei autoki die Herausforderungen bzw. das Bewerten von Autos. Man kann sich per Zufallsbutton von einem Auto zum nächsten durchklicken und jeweils seine Stimme abgeben. Wenn man Glück hat trifft man auf ein richtig cooles Auto.&lt;/li&gt;

&lt;li&gt;Belohnung: Wer viel spielt soll auch belohnt werden. In Rollenspielen z.B. erhält der Spieler neue Ausrüstungsgegenstände nachdem er eine Aufgabe gelöst hat. Je weiter das Spiel voranschreitet, desto größer werden auch die Belohnungen. Auf autoki gibt es dazu erste Ansätze, wie z.B. den Status &quot;Mr. Autoki&quot;, der auf dem Profil des vom &lt;a href=&quot;http://www.autoki.com/groups/girls-club-die-maedels-gruppe/home&quot;&gt;Girl's Club&lt;/a&gt; &lt;a href=&quot;http://www.autoki.com/profile/lexxus&quot;&gt;Auserwählten&lt;/a&gt; prangt.&lt;/li&gt;

&lt;li&gt;Letzter Punkt: Zuschauer. Zuschauer sind neben Traffic für die Website gleichzeitig Belohnung für die Aktiven. Wenn ich ein Video hochlade und sich nach 2h 500 Leute das angeguckt haben bin ich motiviert weitere Videos hochzuladen. Wenn ich in einem Fußballstadion vor 40.000 Leuten ein Tor schieße, gibt mir das eine höhere Befriedigung als vor leeren Rängen. Dementsprechend zeigen wir auf autoki die Anzahl der Besucher auf Profilen, Fotos etc. an. Und wer die meisten hat landet ganz oben auf der entsprechenden &lt;a href=&quot;http://www.autoki.com/photos/most_viewed&quot;&gt;Top-Liste&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Die Slides gibt’s übrigens bei &lt;a href=&quot;http://www.slideshare.net/holgerd/what-can-we-learn-from-games-10-game-mechanics-that-will-make-your-web-community-more-successful/&quot;&gt;slideshare&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/10/using-and-testing-activerecordrails-observers/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/10/using-and-testing-activerecordrails-observers/"/>
    <title>Using and Testing ActiveRecord/Rails Observers</title>
    <updated>2007-10-27T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;div class=&quot;content_image&quot;&gt;&lt;img src=&quot;/uploads/2007/10/socialfeed.png&quot; alt=&quot;social feed&quot; width=&quot;320&quot; /&gt;&lt;/div&gt;
&lt;p&gt;We recently introduced a new feature in &lt;a href=&quot;http://autoki.com&quot;&gt;autoki&lt;/a&gt; called the social feed. It’s basically a yellow box displaying any events on the platform relevant to the current user, like a friend has posted a new photo, or a new interesting car was uploaded. The data model behind this is pretty straightforward, we have a FeedEvent class and all kinds of subclasses, e.g. a MessageReceivedEvent. Each event belongs to a user and an event source, in this example the user would be the user who received the message and the event source would be the message itself. For each user, we simply display all the events that belong to him or her.&lt;/p&gt;

&lt;p&gt;Now the question was this: How do we create these events? The most straightforward way would probably have been to create them in the models, so the Message model would have an after_create callback that created the event. What we didn’t like about this solution was that we would put a whole bunch of logic into the models that didn’t really belong there. Why would a Message care if there was some kind of event feed? Plus these events would be all around in our unit tests and make the bloated and probably sloooow (&lt;a href=&quot;http://upstream-berlin.com/blog/2007/08/01/using-mocha-a-review/&quot;&gt;again&lt;/a&gt;). So we wanted to use the observer pattern to remove the creation of the event from the models.&lt;/p&gt;

&lt;p&gt;For observers in a Rails app you basically have two choices waiting there for you: the &lt;a href=&quot;http://ruby-doc.org/core/classes/Observable.html&quot;&gt;ruby Observable mixin&lt;/a&gt; and the &lt;a href=&quot;http://api.rubyonrails.org/&quot;&gt;ActiveRecord::Observer&lt;/a&gt; class. We didn’t have much time (as usual) and only took a very short look at both and quickly decided to go with the ruby Observable. ARObservers seemed to only allow the usual before/after create/update/save/destroy callbacks and looked much more heavyweight than the tiny Observable module. So we did this: (sorry new example, this time with comments.)&lt;/p&gt;

&lt;h3&gt;Ruby Observable Mixin&lt;/h3&gt;

&lt;h4&gt;Integration Test&lt;/h4&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test_create_comment_creates_event&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;myuser&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;create_logged_in_user&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;create_user&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;FeedEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;delete_all&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;post&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;comments/create&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:comment&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:content&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'test'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:comment_owner_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:comment_owner_type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'User'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;assert_equal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;FeedEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h4&gt;Controller Test&lt;/h4&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test_create_attaches_observer&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new_logged_in_user&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new_challenge&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;post&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:comment&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:content&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'test'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:comment_owner_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:comment_owner_type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'Challenge'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;assert_equal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;assigns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:comment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;count_observers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h4&gt;Controller Implementation&lt;/h4&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;create&lt;/span&gt;
	&lt;span class=&quot;vi&quot;&gt;@comment&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Comment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:comment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;merge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:user_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logged_in_user_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;vi&quot;&gt;@comment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add_observer&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;CommentObserver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@comment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;save&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h4&gt;Model Test&lt;/h4&gt;
&lt;p&gt;(using mocha)&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test_create_notifies_observer&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new_user&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;observer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stub&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;comment&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Comment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:comment_owner&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:content&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'x'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new_user&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;observer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;expects&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;comment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;comment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add_observer&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;observer&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;comment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;save!&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h4&gt;Model Implementation&lt;/h4&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'observer'&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Comment&lt;/span&gt;
  &lt;span class=&quot;kp&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Observable&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;after_create&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:notify_comment_observer&lt;/span&gt;

  &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;

  &lt;span class=&quot;kp&quot;&gt;private&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;notify_comment_observer&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;changed&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;notify_observers&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h4&gt;Observer Test&lt;/h4&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test_update_creates_create_event&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;comment_observer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;CommentObserver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;comment&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new_comment&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;comment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;receiver&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new_user&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;CommentCreateEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;expects&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:create!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;comment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;receiver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:source&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;comment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;comment_observer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;update&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;comment&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h4&gt;Observer Implementation&lt;/h4&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CommentObserver&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;comment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;CommentCreateEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create!&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;comment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;receiver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:source&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;comment&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;comment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;comment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;receiver&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;comment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;receiver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;blank?&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Wow. That’s a whole lot of code for a tiny little yellow box with one sentence in it. And it’s not even complete because you have to handle deletion of the comments and some other stuff as well. (no event if user comments on his own objects).&lt;/p&gt;

&lt;p&gt;Anyway, for some reason we implemented this for 8 types of events or so: Integration test, attach observer in controller, call &lt;code&gt;changed&lt;/code&gt; and &lt;code&gt;notify_observers&lt;/code&gt; method in model, create event in observer. It was a real pain because we were implementing almost the same thing again and again. Especially attaching the observer in the controller seemed too much work. We thought about doing some meta ruby magic to be able to do the same as rails does with &lt;a href=&quot;http://api.rubyonrails.org/classes/ActionController/Caching/Sweeping.html&quot;&gt;cache sweepers&lt;/a&gt;. Instead of attaching the sweeper to the model directly you simply declare that you want this cache sweeper to be active in this action:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MyController&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ApplicationController&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;cache_sweeper&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:comments_sweeper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:only&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:create&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;It didn’t really work out because we couldn’t figure out the right magic to do the right things when the model was loaded or created without letting the model do it, which would make the whole use of observers pointless.&lt;/p&gt;

&lt;h3&gt;ActiveRecord Observers&lt;/h3&gt;

&lt;p&gt;Finally after a couple of days we took a closer look at the ARObservers. They don’t require you to add them to a model instance every time you want to use them. All you have to do in order to use them is to configure them in your environment:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;active_record&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;observers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:message_observer&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;You then derive you observer class from ActiveRecord::Observer and from then on get all your before/after create/update/save/destroy events for free, delivered right to your observer method with the corresponding name:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MessageObserver&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Observer&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;after_create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;MessageReceivedEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create!&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;receiver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:source&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now we still had the problem that we wanted custom events, e.g. an after_read event. After some digging deep down in the rails sources we found out how:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Message&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;read&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;mark_as_read!&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;strong&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;callback&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:after_read&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/strong&amp;gt;
  end
end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;That’s it. Now we implement an after_read method in the observer and we were done.&lt;/p&gt;

&lt;h4&gt;Now the testing part.&lt;/h4&gt;

&lt;p&gt;Any idea how the rails guys got the  observers attached to every model instance in the app automagically? Well, they attached the observer to the class, not the instance. Sounds like a brilliant idea doesn’t it? But wait. We said one big reason for us to use the observers was that we didn’t want our unit tests to be concerned with this. And now the observers were attached to the classes and all our tests were carrying them as well. Our first solution was to move the observer configuration from environment.rb to the development.rb/production.rb files but this only solved one problem while creating two others: We now had the observer configuration in multiple places and, more importantly, our integration tests didn’t have the observers as well, hence, were now failing.&lt;/p&gt;

&lt;p&gt;We ended up doing this: moved the observer configuration back into environment.rb, removed the observers from the model classes before running the unit tests, attached them back before running the integration tests:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# test_helper.rb
&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# remove all activerecord observers
&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;glob&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;RAILS_ROOT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'app'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'observers'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'*.rb'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;basename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'_observer.rb'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;camelize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;constantize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;delete_observers&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;(We have a separate directory for our obserers in app/obserers)&lt;/p&gt;

&lt;p&gt;For our integration tests we created a new file &lt;em&gt;integration_test_helper.rb&lt;/em&gt; which we now require instead of the &lt;em&gt;test_helper.rb&lt;/em&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dirname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;__FILE__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/test_helper&quot;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# add observers in case they have been removed by unit tests
&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;glob&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;RAILS_ROOT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'app'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'observers'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'*.rb'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;clazz&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;basename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'_observer.rb'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;camelize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;constantize&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;clazz&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;delete_observers&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;observer_clazz&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;basename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'.rb'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;camelize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;constantize&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;observer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;observer_clazz&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;instance&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;observer_clazz&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;respond_to?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:instance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;clazz&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add_observer&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;observer&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;observer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;is_a?&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Observer&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;With this setup we now have working unit and integration tests. No need for controller tests and implementation, model tests and implementation only if we have a custom event (such as after_read). What’s left are integration tests and a unit test and implementation of the observer, which is mostly trivial:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MessageObserverTest&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Test&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Unit&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;TestCase&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;setup&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@observer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;MessageObserver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;instance&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@message&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new_message&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test_create_creates_message_received_event&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;MessageReceivedEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;expects&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:create!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;receiver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:source&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@observer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;after_create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/10/mehr-als-der-flug-zum-ballermann/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/10/mehr-als-der-flug-zum-ballermann/"/>
    <title>Mehr als der Flug zum Ballermann</title>
    <updated>2007-10-17T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;div style=&quot;float:right&quot;&gt;&lt;img src=&quot;/uploads/2007/03/picture-1.png&quot; alt=&quot;kommtmit screenshot klein&quot; /&gt;&lt;/div&gt;

&lt;p&gt;Nachdem wir &lt;a href=&quot;http://kommt-mit.de&quot;&gt;kommt-mit.de&lt;/a&gt; schon Anfang des Jahres als Plattform und Community zur individuellen Reisevorbereitung gestartet haben, dringt der Trend auch langsam zu den großen durch. So titelt &lt;a href=&quot;http://golem.de&quot;&gt;golem.de&lt;/a&gt; heute: &lt;quote&gt;&lt;a href=&quot;http://www.golem.de/0710/55431.html&quot;&gt;Internet bei der Urlaubsvorbereitung immer wichtiger&lt;/a&gt;&lt;/quote&gt;. Genau. Leider beschränken sich die Aussagen dort auf die Buchung von Flügen, Hotels usw. Der eigentlich spannende Teil liegt meiner Meinung nach jedoch beim ganzen “Drumherum” der Reisevorberitung, das sich vor allem beim Verreisen mit mehreren (soll ja vorkommen) durch das Internet viel leichter abwickeln laesst.&lt;/p&gt;

&lt;p&gt;Deshalb geht es bei &lt;a href=&quot;http://kommt-mit.de&quot;&gt;kommt-mit&lt;/a&gt; auch  nicht darum, einfach nur den billigsten Flug nach Ballermann zu finden, sondern den ganzen Planunsgaufwand vorab zu meistern, und gleichzeitig die Vorfreude zu schueren.&lt;/p&gt;

&lt;p&gt;Nach der Anmeldung kann man zunächst seine Mitreisenden einladen. Der Reisegruppe steht nun ein eigener Bereich zur Verfügung, in dem sie über ein Forum ihre Reisevorbereitungen abstimmen koennen. Über die Aufgabenliste kann leicht geklärt werden, wer den Grill und wer die Kohle zum gemeinsamen Campingurlaub mitbringt. Und die beste Feuerstelle lässt sich doch schonmal vorab per Satellitenbild aussuchen.&lt;/p&gt;

&lt;p&gt;Im Anschluss können die besten Schnappschüsse des Jahresurlaubs hochgeladen und von allen bewundert werden. Es ist natürlich weiterhin erlaubt, sich auch außerhalb des Internets zum Fotogucken zu treffen ;)&lt;/p&gt;

&lt;p&gt;Auf der Startseite gibt es ein kleines &lt;a href=&quot;http://kommt-mit.de&quot; style=&quot;font-size: 150%&quot;&gt;Video&lt;/a&gt;, was die Funktionen von komm-mit noch einmal erläutert.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/10/die-armen-virtuellen-maschinen/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/10/die-armen-virtuellen-maschinen/"/>
    <title>Arme virtuellen Maschinen</title>
    <updated>2007-10-17T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;div style=&quot;float:right&quot;&gt;&lt;img src=&quot;/uploads/2007/10/img_story_1.gif&quot; alt=&quot;flying toasters&quot; /&gt;&lt;/div&gt;

&lt;p&gt;Hat sich eigentlich schonmal jemand gefragt, wozu eigentlich virtuelle Maschinen, also z.B. ein Linux in einem VMWare/Paralls, ihren Bildschirmschoner laufen lassen? Dabei haben sie doch gar keinen Bildschirm, sondern maximal ein paar hundert Pixel in einem Fenster, in dem sie ab und zu ihre bunten Grafiken dem geneigten Benutzer zeigen dürfen, falls der sie nicht einfach mit einem Klick ihr Fenster minimiert oder ganz komplett ausschaltet.&lt;/p&gt;

&lt;p&gt;Arme, dumme virtuelle Maschinen. Wenn sie doch nur die geringste Peilung hätten, worin sie da eigentlich laufen. Na hoffentlich läuft die Erde nicht auch in einer virtuellen Maschine.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/09/morgen-gleich-nach-dem-fruhstuck-mysql-schneller-machen/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/09/morgen-gleich-nach-dem-fruhstuck-mysql-schneller-machen/"/>
    <title>Morgen gleich nach dem Frühstück: mysql schneller machen</title>
    <updated>2007-09-25T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;nachricht an mich selbst: morgen frueh nochmal &lt;a href=&quot;http://joyeur.com/2007/09/25/quick-wins-with-mysql&quot;&gt;&quot;quick wins with mysql&quot;&lt;/a&gt; lesen und den eigenen mysql-server ueberpruefen.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/09/railsconf-europe-2007-roundup-3-rubyworks/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/09/railsconf-europe-2007-roundup-3-rubyworks/"/>
    <title>Railsconf Europe 2007 Roundup 3 (rubyworks)</title>
    <updated>2007-09-24T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;&lt;a href=&quot;http://rubyworks.rubyforge.org/&quot;&gt;rubyworks&lt;/a&gt; is a full rails stack put together also by &lt;a href=&quot;http://www.thoughtworks.com/&quot;&gt;thoughtworks&lt;/a&gt;. this time it’s all open source. it features &lt;a href=&quot;http://haproxy.1wt.eu/&quot;&gt;haproxy&lt;/a&gt; (supposed to be much faster and light weight than apache_mod_balancer) as load balancer, &lt;a href=&quot;http://mongrel.rubyforge.org/&quot;&gt;mongrel&lt;/a&gt; as application server, &lt;a href=&quot;http://www.tildeslash.com/monit/&quot;&gt;monit&lt;/a&gt; and &lt;a href=&quot;http://smarden.org/runit/&quot;&gt;runit&lt;/a&gt; for monitoring and controlling mongrel, mysql/postgrs/oracle ruby bindings for database connectivity and also bindings for ferret, libxml, hpricot and rmagick.&lt;/p&gt;

&lt;p&gt;everything is nicely packaged as rpms and &lt;a href=&quot;http://debian.org&quot;&gt;debian&lt;/a&gt; packages so it can easily be installed within 5 minutes (it really works). even better, the packages at the same time pretend to be ruby gems, so you can safely install other debian packages &lt;em&gt;and&lt;/em&gt; gems that depend on one of the libraries provided by rubyworks.&lt;/p&gt;

&lt;p&gt;now we had already set up a cluster of 6 servers for &lt;a href=&quot;http://www.autoki.com&quot;&gt;autoki&lt;/a&gt; with everything set up more or less perfectly so why would we need rubyworks? answer: to steal the config files for runit/monit and haproxy. our mongrel setup has always been a bit shaky, especially when it came to restarting the mongrels after a deployment. after using the rubyworks setup with runit now everything is stable. (btw runit can run and supervise any process in *nix and is ready to be the successor of the old init which is used by most linux distros to start up all the processes. one advantage is that it starts all processes at once instead of piece by piece, plus runit handles putting a process in the background and keeping it alive there, something mongrel is especially bad at).&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/09/railsconf-europe-2007-roundup-2-mingle/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/09/railsconf-europe-2007-roundup-2-mingle/"/>
    <title>Railsconf Europe 2007 Roundup 2 (mingle)</title>
    <updated>2007-09-24T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;&lt;a href=&quot;http://studios.thoughtworks.com/mingle-project-intelligence&quot;&gt;Mingle&lt;/a&gt; is the new agile project management tool from &lt;a&gt;thoughtworks studios&lt;/a&gt;. it’s not open source but it’s free for up to five users - sounds like a fair offer to me. for everyone who thinks differently - it’s written in ruby and deployed using jruby, but the class file decompiler is part of the jdk. i wonder how long they can actually make people pay for it. hello open source business models?&lt;/p&gt;

&lt;p&gt;anyway, when thilo installed mingle from one of the cds given out to all conference attendees we first were a bit disappointed. after the installation you basically get a nice looking  but totally empty screen leaving you with not much more than a big question mark over your head. we didn’t have much time then so that was it for mingle until i attended the corresponding session where things got a bit clearer: mingle is a completely customizable tool. the only thing that’s hard coded are users and cards/stories/tickets/you-name-it and that everything is a wiki page - the rest is up to you. the main features are these:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;define your own attributes for your cards: you can add new attributes to your cards and in place edit all of them - auto complete shows you a list of values that have been entered into that field before&lt;/li&gt;
  &lt;li&gt;filter cards by any combination of attributes and save the filter, after which you can add it as a tab on the top&lt;/li&gt;
  &lt;li&gt;card transitions - create a set of transitions where a card with a specific state can be transitioned to another state, e.g. from status open to status accepted and assigned to a person. this enables the project team to map their work flows into mingle&lt;/li&gt;
  &lt;li&gt;create graphs and tables from the cards - multiple graph types are supported for easy tracking of iteration progress or number of bugs etc.&lt;/li&gt;
  &lt;li&gt;project templates - so you don't have to sit in front of a blank screen as we did&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;mingle looks like it could become a really cool tool. after all, &lt;a href=&quot;http://trac.edgewall.org/&quot;&gt;trac&lt;/a&gt; is getting a bit boring after all these years(?) and we’ve actually stopped using its ticketing system, because it wasn’t easy enough to change it to our needs.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/09/railsconf-europe-2007-roundup-1-rspec/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/09/railsconf-europe-2007-roundup-1-rspec/"/>
    <title>Railsconf Europe 2007 Roundup 1 (rspec)</title>
    <updated>2007-09-24T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Last week was RailsConf 2007 in Berlin. Only a couple of days have passed and it already seems so far away. Time for some blogging before everybody forgets that it even took place and nobody’s going to read this :) So here’s the interesting part of the sessions I attended:&lt;/p&gt;

&lt;h3&gt;A Half-day of Behavior-driven Development on Rails (&lt;a href=&quot;http://rspec.rubyforge.org/&quot;&gt;rspec&lt;/a&gt;)&lt;/h3&gt;

&lt;p&gt;This was the first session on tutorial day and for me one of the most interesting. It basically gave a looong introduction to behavior driven design, how it evolved from things like TDD and who realized what while enganged in which project back in the good old times. One of the interesting parts for me was when they talked about the whole story writing/specification process. I had heard most of it before but it was a good refreshment. The central statement was to write “Software that matters”, and to achieve this, you’d have to get the specs right - as we &lt;a&gt;XP&lt;/a&gt;ers know this should be done by collecting &lt;a href=&quot;http://www.extremeprogramming.org/rules/userstories.html&quot;&gt;user stories&lt;/a&gt;. The suggested format for such a story was this:&lt;/p&gt;

&lt;blockquote&gt;
  As a [role]
  I want [feature]
  so that [outcome]
&lt;/blockquote&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;blockquote&gt;
  As an administrator
  I want to see the list of users
  so that i can choose which one to delete.
&lt;/blockquote&gt;

&lt;p&gt;Great, a template for writing story cards. Following the story, you need an acceptance test that verifies that the story has been implemented. The suggested format is this:&lt;/p&gt;

&lt;blockquote&gt;
  given [context]
  given [more context]
  when [event]
  when ...
  then [outcome]
  then ...
&lt;/blockquote&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;blockquote&gt;
  Given i am logged in as root
  Given a list of users
  When i view /users
  Then i see all the users
&lt;/blockquote&gt;

&lt;p&gt;Now in the early days you would write this on a story card or on some other maybe non-digital, maybe digital surface, but you would not do this: run the acceptance test as code. (I’m aware there are some projects who’ve tried/done it but I’ve never found one too convincing and really used it). Now you can. They actually implemented a new feature in &lt;a href=&quot;http://rspec.rubyforge.org/svn/trunk/&quot;&gt;rspec trunk&lt;/a&gt; called StoryRunner to do exactly this and here’s how it looks:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;no&quot;&gt;Story&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'As an administrator
I want to see the list of users
so that i can choose which one to delete.'&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;Scenario&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'logged in as root with 2 users in system'&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;

  &lt;span class=&quot;no&quot;&gt;Given&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'i am logged in as'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'root'&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;create_user&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;post&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/sessions/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:password&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;password&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;no&quot;&gt;Given&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'a list of users'&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
   &lt;span class=&quot;vi&quot;&gt;@users&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create_user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;create_user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;no&quot;&gt;When&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'i view'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'/users'&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;location&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;no&quot;&gt;Then&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'i see all the users'&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@users&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;should&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;have_text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;create_user&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;How cool is that, but it doesn’t even stop here. You can now reuse the code blocks you have created (!!):&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;no&quot;&gt;Scenario&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'logged in admin with no users in the system'&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;Given&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'i am logged in as'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'root'&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;Given&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'there are no users'&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;delete_all&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;When&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'i view '&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'/users'&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;Then&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'i see a message that there ar no users'&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;should&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;have_text&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'nobody there'&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Ok, technically speaking it’s actually not much more than reusing code but the way it’s done in StoryRunner, you are almost forced to create your own, application specific testing DSL! (Did I mention how fantastic this is?). For a &lt;a href=&quot;http://evang.eli.st/blog/2007/9/1/user-stories-with-rspec-s-story-runner&quot;&gt;better example which includes a “normal” rails integration test and compares it to an rspec story implementation&lt;/a&gt; see the &lt;a href=&quot;http://evang.eli.st/blog/&quot;&gt;evang.ei.st&lt;/a&gt; blog.&lt;/p&gt;

&lt;p&gt;Of course they also showed the “usual” rspec stuff (referred to as “classic rspec”) for specing models and controllers. I you haven’t already go back to the evang.eli.st and check out &lt;a href=&quot;http://evang.eli.st/blog/2007/9/15/easy-controller-tests-and-expressing-intent-through-expectations&quot;&gt;Easy Controller Tests and Expressing Intent Through Expectations&lt;/a&gt; - a really good (imho) introduction on writing controllers using &lt;del&gt;test&lt;/del&gt; spec first.&lt;/p&gt;

&lt;p&gt;Enough for now, I’m sure there will be more on rspec here soon.&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/09/mocha-prasentation/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/09/mocha-prasentation/"/>
    <title>mocha Präsentation</title>
    <updated>2007-09-06T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Hier wie versprochen die &lt;a href=&quot;/uploads/2007/09/mocha_praes.pdf&quot; title=&quot;Mocha Presentation&quot;&gt;Slides&lt;/a&gt; meiner Presentation zum Thema &lt;a href=&quot;http://mocha.rubyforge.org/&quot;&gt;mocha&lt;/a&gt; bei der &lt;a href=&quot;http://www.rug-b.com&quot;&gt;Ruby User Group Berlin&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Links:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://mocha.rubyforge.org/&quot;&gt;Mocha&lt;/a&gt;
&lt;a href=&quot;http://blog.floehopper.org/&quot;&gt;Floehopper&lt;/a&gt; (Blog des Mocha Entwicklers)
&lt;a href=&quot;http://blog.floehopper.org/articles/2007/07/22/an-introduction-to-mock-objects-in-ruby&quot;&gt;Presentation about Ruby Mocks&lt;/a&gt;
&lt;a href=&quot;http://blog.testingrails.com&quot;&gt;TestingRails Blog&lt;/a&gt; (gelöscht :()
&lt;a href=&quot;http://blog.jayfields.com/2006/01/use-stubs-and-mocks-to-convey-intent.html&quot;&gt;Use stubs and mocks to convey intent&lt;/a&gt;
&lt;a href=&quot;http://blog.jayfields.com/2007/04/ruby-mocks-and-stubs-using-mocha.html&quot;&gt;Ruby: Mocks and Stubs using Mocha&lt;/a&gt;&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/09/kleinere-thumbnails-mit-strip/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/09/kleinere-thumbnails-mit-strip/"/>
    <title>Kleinere Thumbnails mit strip</title>
    <updated>2007-09-06T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Open Source ist wirklich eine tolle Sache, jeder kann den Code für seine Zwecke anpassen. Das haben wir auch schon ein paar mal gemacht, hier kommt noch einen schönes Beispiel welches für Open Source spricht.&lt;/p&gt;

&lt;p&gt;Wir haben viele kleine thumbnails auf der Startseite von autoki, die haben wir genauso wie alle anderen Formate mit &lt;a href=&quot;http://www.kanthak.net/opensource/file_column/&quot;&gt;file_column&lt;/a&gt; generiert. &lt;a href=&quot;http://www.kanthak.net/opensource/file_column/&quot;&gt;file_column&lt;/a&gt; ist ein praktisches Plugin das eine Schnittstelle über &lt;a href=&quot;http://rmagick.rubyforge.org/&quot;&gt;RMagick&lt;/a&gt; zu &lt;a href=&quot;http://www.imagemagick.org/&quot;&gt;ImageMagick&lt;/a&gt; bildet, um einfach verschiedene Formate von Bildern für ein Model zu generieren. Vor Kurzen mussten wir feststellen, dass die &lt;a href=&quot;http://www.autoki.com&quot;&gt;autoki&lt;/a&gt; Startseite ungewöhnlich viel Bandbreite braucht. Beim Blick auf ein einzelnes Thumbnail zeigte sich, das bereits ein kleines 53x53 Pixel  Jpeg 48KB gross ist.&lt;/p&gt;

&lt;p&gt;Die erste Vermutung war, dass die Qualituatseinstellungen für die Jpeg Kompression zu hoch ist. Ein bisschen Suchen in der &lt;a href=&quot;http://www.imagemagick.org/RMagick/doc/&quot;&gt;RMagick&lt;/a&gt; Dokumentation brauchte das attribut quality hervor, mit dem sich die Qualitätseinstellungen regeln lässt. Etwas ausprobieren brachte dan auch einen Teilerfolg. Indem den schlüssel :attributes ein Hash übergeben für die einzelnen Versionen übergeben wird liess sich die qualität steuern. Aber das brachte nur ein paar Kilobyte.&lt;/p&gt;

&lt;p&gt;Also musste das Problem wo anders liegen. Nächste Ansatzpunkt war die Kompression. Auch hier half die &lt;a href=&quot;http://www.imagemagick.org/RMagick/doc/&quot;&gt;RMagick Doku &lt;/a&gt;weiter. Mit dem key :compression läst sich die Kompression festlegen. Aber auch hier kein Erfolg, wie erwartet wird automatisch die Kopression verwendet die die Dateiendung vorgibt.&lt;/p&gt;

&lt;p&gt;Also die Datei mit &lt;a href=&quot;http://macromates.com/&quot;&gt;TextMate&lt;/a&gt; geöffnet um vielleicht einen Hinweis auf das Problem zu erhalten. Dort fand sich neben dem erwarten Zeichensalat XML tags. Diese sahen nach EXIF-Informationen aus. Im EXIF-Format werden Meta-Daten über die Aufnahme wie Zeitpunkt, Kamera, Belichtungsinformation etc. gespeichert. Dazu fanden sich noch weitere XML Blöcke die offensichtlich von Photoshop stammen. Das Problem war also das die Metadaten aus der Datei nicht entfernt wurden.&lt;/p&gt;

&lt;p&gt;Jetzt wo das Problem bekannt war, konnte auch die Suchmaschiene dazu befragt werden, wie man diese Metadaten loswird. Die Antwort ist strip. Die Rmagick methode entfernt alle Nichtbilddaten. Wird bei &lt;a href=&quot;http://www.kanthak.net/opensource/file_column/&quot;&gt;file_column&lt;/a&gt; eine Bildvariante mit :thumbnail satt :version erstellt wird strip ebenfalls aufgerufen. Doch die Thumbnail Varianten lassen sich nicht mit crop noch zusätzlich beschneiden. Aber genau das machen wir um gleichmässig erschneinende thumbnails zu erstellen. Darum muss die strip Methode an geeigneter Stelle aufgerufen werden. Da die skalierung und das beschneiden in der mehtode transform_image in der FileColumn Klasse geschiet ist dort ein guter Ort strip aufzurufen. Ob nun am Anfang oder am Ende ist eigentlich egal. Da wir all unsere anderen file_column Hacks auch ans ende dieser MEthode gepack haben, kommt der strip aufruf noch dazu.Und nachdem die Bilder mittels regenerate_images neu erstellt waren, ist die Grösse der tumbnails auf etwa 4KB (ein Zehntel!) gesunken. Jetzt baut sich die Startseite viel schneller auf und der Bandbreitenbedarf ist erheblich gesunken.&lt;/p&gt;

&lt;p&gt;Also um kleine Thumbnails zu generieren kann man entweder :thumbnail statt :version in file_column verwenden, wenn man die thumbnails jedoch zuschneiden will sollte man am besten strip in der methode transform_images aufrufen um die Metadaten wie z.B. EXIF Informationen aus der Datei zu entfernen. Das ganze kann sich auch lohnen wenn viele grosse Bilder gespeichert werden müssen.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/08/mnemonic-for-duck-typing/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/08/mnemonic-for-duck-typing/"/>
    <title>Mnemonic for Duck Typing.</title>
    <updated>2007-08-26T00:00:00+00:00</updated>
    <author>
      <name>thilo</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;I’m currently reading the &lt;a href=&quot;http://www.amazon.com/Cookbook-Cookbooks-OReilly-Lucas-Carlson/dp/0596523696/&quot;&gt;Ruby Cookbook&lt;/a&gt; and found a good explanation for duck typing. You know, the concept to treat objects according to their methods rather their classes/modules.  It might be well known to you, but i like to share it anyway.&lt;/p&gt;

&lt;p&gt;Duck Typing means:&lt;/p&gt;
&lt;blockquote&gt;&quot;... if an object quaks like a duck, just go ahead and treat it like a duck&quot;&lt;/blockquote&gt;

</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/08/p3p-idiotie/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/08/p3p-idiotie/"/>
    <title>P3P - Idiotie</title>
    <updated>2007-08-20T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;P3P steht für &lt;a href=&quot;http://www.w3.org/P3P/&quot;&gt;Platform for Privacy Preferences&lt;/a&gt; und ist ein vom w3c erfundenes dingens, mit dem ich als &lt;a href=&quot;http://autoki.com&quot;&gt;Webseitenbetreiber&lt;/a&gt; eine Art maschinenlesbare Datenschutzerklaerung abgeben kann - ich kann z.B. angeben, ob ich Daten über meine Besucher speichere und wenn ja was ich mache und wem ich sie gebe usw.&lt;/p&gt;

&lt;p&gt;Das klingt alles irgendwie ganz nett, auch wenn mir so richtig noch kein guter Anwendungsfall einfallen will (ist mit eigentlich auch ziemlich egal gerade), aber zum Glück haben die Jungs aus Redmond schonmal vorgearbeitet und das ganze in ihr Browserflaggschiff namens Internet Explorer eingebaut.&lt;/p&gt;

&lt;p&gt;Und jetzt ratet mal wofür. Um den Benutzern ein sichereres und besseres surfen zu ermöglichen vielleicht? Um Websitebetreibern irgendeinen Vorteil zu bieten? Genau, haben sie nicht. Stattdessen haben sie folgendes tolles Feature eingebaut:&lt;/p&gt;

&lt;p&gt;Wenn ich auf eine Seite surfe, z.B. diese(s/n) Blog, und da zum Beispiel zum &lt;a href=&quot;http://upstream-berlin.com/blog/2007/07/20/weltpremiere-das-autoki-quartett-fur-die-eigene-website/&quot;&gt;Artikel&lt;/a&gt; über’s &lt;a href=&quot;http://autoki.com/autoquartett&quot;&gt;Autoquartett&lt;/a&gt;, dann ist besagtes Quartettspiel über einen &lt;em&gt;IFrame&lt;/em&gt; in die eigentliche Seite eingebunden, d.h. Inhalte eines fremden (des autoki)-Servers werden auf der Seite von upstream (übrigens seit heute GmbH i.G. :) ) angezeigt. Und was macht der Internet Explorer mit diesen fremden Inhalten? Er verbietet ihnen, Cookies zu speichern. Cookies sind das, in dem sich das Autoquartett u.a. dem Punktestand merkt. Ohne Cookies keine Punkte und kein Quartettspiel. Und jetzt kommt der Clou: Mit Hilfe von P3P kann man das ganze jetzt wieder abschalten, indem man nach mehrstuendiger Suche diesen &lt;a href=&quot;http://support.microsoft.com/kb/323752&quot;&gt;Artikel&lt;/a&gt;, ebenfalls in Redmond entstanden, findet. Und da steht dann geschrieben, dass die Worte &lt;code&gt;P3P: CP=&quot;CAO PSA OUR&quot;&lt;/code&gt; diesen Prachtbrowser wieder zu Vernunft bringen, also den Normalzustand ohne P3P herbeiführen. Toll. Ich bin begeistert. Mehr solche innovativen Funktionen, dann steigt der Marktanteil bestimmt auch bald wieder. (bei autoki liegt übrigens &amp;lt;a href=http://getfirefox.com”&amp;gt;Firefox&amp;lt;/a&amp;gt; vorne).&lt;/p&gt;

&lt;p&gt;Ich hab mich nicht allzu lange mit dem Kürzel beschäftigt, aber sowas wie &lt;em&gt;Wir speichern keine Daten und sind ganz lieb&lt;/em&gt; dürfte es schon sein. Von wegen ;)&lt;/p&gt;

&lt;p&gt;P.S. jaja, es gibt bestimmt auch eine total tolle Verwendung für das Ganze, bei der es egal ist, dass der Websitebetreiber über seinen Datenschutz behaupten kann, was er will. Die Kommentare sind offen :)&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/08/9to5-bald-gehts-los/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/08/9to5-bald-gehts-los/"/>
    <title>9to5 - bald geht's los</title>
    <updated>2007-08-20T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;div class=&quot;content_image&quot;&gt;&lt;a href=&quot;http://9to5.wirnennenesarbeit.de/&quot;&gt;&lt;img src=&quot;/uploads/2007/08/9to5_190x360.gif&quot; alt=&quot;9to5&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Vom 9to5-wir-nennen-es-arbeit-festival-camp gibt’s jetzt zum einen diese ansehnlichen Banner, zum anderen &lt;del&gt;den Fahrplan&lt;/del&gt; das Programm. lauter Lesungen, Hörspiele, Filme und DJs. Na wenn das nicht nett wird. Da belibt bestimmt noch genug Zeit zum plaudern und an lustigen Projekten arbeiten. Oder Zeit, sich meinen Vortrag zum Thema &lt;a href=&quot;http://9to5.wirnennenesarbeit.de/forum/topic.php?id=17&amp;amp;page&amp;amp;replies=3&quot;&gt;Startup-Gründung für Anfänger anzuhören.&lt;/a&gt; - nach derzeitigem Plan Samstag 18 Uhr im Raum &lt;em&gt;Bremen&lt;/em&gt;.&lt;/p&gt;

&lt;div style=&quot;float:right&quot;&gt;&lt;img src=&quot;/uploads/2007/08/nabaztag.thumbnail.png&quot; alt=&quot;nabaztag&quot; /&gt;&lt;/div&gt;

&lt;p&gt;Oder zusammen etwas sinnvolles oder sinnfreies mit meinem &lt;a href=&quot;http://www.nabaztag.com&quot;&gt;Nabaztag&lt;/a&gt;, diesem kleinen Plastikhasen mit WLAN (bestimmt auch ein digital bohemian), Stimme und Ohren, zu machen, der  bis dahin hoffentlich eingetroffen ist.&lt;/p&gt;

&lt;p&gt;Mehr zu alldem dann spätestens Donnerstag, oder wir sehen uns einfach da. Eintritt tagsüber übrigens frei, von 9to5 dann 60 EUR für 3 Tage.&lt;/p&gt;

&lt;p&gt;&lt;br style=&quot;clear:both&quot; /&gt;&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/08/captchas-with-rails-and-multiple-servers/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/08/captchas-with-rails-and-multiple-servers/"/>
    <title>Captchas with rails and multiple servers</title>
    <updated>2007-08-17T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;div class=&quot;content_image&quot;&gt;&lt;img src=&quot;/uploads/2007/08/picture-1.png&quot; alt=&quot;captcha&quot; width=&quot;290&quot; /&gt;&lt;/div&gt;

&lt;p&gt;On &lt;a href=&quot;http://autoki.com&quot;&gt;autoki.com&lt;/a&gt; we have this “Tell-a-friend” functionality, where people can enter the email addresses of their friends and have the link to a &lt;a href=&quot;http://www.autoki.com/albums/stuff/photos/img_1370&quot;&gt;cool photo&lt;/a&gt; or &lt;a href=&quot;http://www.autoki.com/profile/hellyeah/opel_admiral&quot;&gt;car&lt;/a&gt; sent to them. Until recently this was only accessible to logged in users because we were afraid of spam bots using it to send emails to everyone using our servers. At some point we realized that this function could be much more useful if everyone would be able to use it - member or not. The solution we chose to fight the spam bots was pretty standard: &lt;a href=&quot;http://en.wikipedia.org/wiki/Captcha&quot;&gt;Captchas&lt;/a&gt; - Completely Automated Public Turing tests to tell Computers and Humans Apart. (I just love this name)&lt;/p&gt;

&lt;p&gt;There are a couple of plugins for rails to implement captchas. Most of them display an image with distorted letters and numbers and ask the user to enter these in a form field. The alternative is to ask the user a simple question that a human can easily answer but a computer (as in spam bot) can not. The latter has two advantages: they work for blind people / text browsers and we don’t have to generate and store images - so we went with this one. validates_captcha (sorry couldn’t find a working link) is a plugin we have used before and it supports both - images and questions (so called logic captchas).&lt;/p&gt;

&lt;p&gt;Now the only problem was this: autoki runs on multiple servers and the captcha validation process involves multiple http requests, which can each go to a different server. Validates_captcha stores the captchas in a local file, so if the generation of the captcha occurs on one server but the validation on another, that other server can’t find the captcha. The solution we chose is actually pretty simple: rewrite validates_captcha to use &lt;a href=&quot;http://www.danga.com/memcached/&quot;&gt;memcache&lt;/a&gt; instead of the local file.&lt;/p&gt;

&lt;p&gt;To accomplish this we only had to change one file, &lt;em&gt;captcha_challenge.rb&lt;/em&gt;. All we had to do was replace all the occurrences of &lt;code&gt;store[:captchas]&lt;/code&gt; with memcache. The modified file now looks like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'digest/sha1'&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;FleskPlugins&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;#:nodoc:
&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# This is an abstract class. Use one of its subclasses.
&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CaptchaChallenge&lt;/span&gt;

    &lt;span class=&quot;kp&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;CaptchaConfig&lt;/span&gt;
    &lt;span class=&quot;kp&quot;&gt;extend&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;CaptchaConfig&lt;/span&gt;

    &lt;span class=&quot;no&quot;&gt;DEFAULT_TTL&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1200&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;#Lifetime in seconds. Default is 20 minutes.
&lt;/span&gt;

    &lt;span class=&quot;nb&quot;&gt;attr_reader&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:created_at&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;attr_accessor&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:ttl&lt;/span&gt;

    &lt;span class=&quot;vc&quot;&gt;@@types&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;HashWithIndifferentAccess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{})&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;#:nodoc:
&lt;/span&gt;
      &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;generate_id&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;ss&quot;&gt;:ttl&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'default_ttl'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;DEFAULT_TTL&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ttl&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ttl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@created_at&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;now&lt;/span&gt;
      &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;prune&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Implement in subclasses.
&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;correct?&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;#:nodoc:
&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;NotImplementedError&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Has this challenge expired?
&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;expired?&lt;/span&gt;
      &lt;span class=&quot;no&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;now&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;created_at&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ttl&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;other&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;#:nodoc:
&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;other&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;is_a?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;other&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;id&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;kp&quot;&gt;private&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;generate_id&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;#:nodoc:
&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;captcha_&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Digest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;SHA1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;hexdigest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_s&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;#:nodoc:
&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;write_to_store&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;#:nodoc:
&lt;/span&gt;
      &lt;span class=&quot;no&quot;&gt;CACHE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ttl&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;

      &lt;span class=&quot;c1&quot;&gt;#Get the challenge type (class) registered with +name+
&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;vc&quot;&gt;@@types&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

      &lt;span class=&quot;c1&quot;&gt;#Register a challenge type (class) with +name+
&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;register_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;klass&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;vc&quot;&gt;@@types&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;klass&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

      &lt;span class=&quot;c1&quot;&gt;# Find a challenge from the storage based on its ID.
&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;no&quot;&gt;CACHE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

      &lt;span class=&quot;c1&quot;&gt;# Delete a challenge from the storage based on its ID.
&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;delete&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;no&quot;&gt;CACHE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;delete&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

      &lt;span class=&quot;c1&quot;&gt;# Removes old instances from PStore
&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;prune&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;#prune
&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;#class &amp;lt;&amp;lt; self
&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;#module FleskPlugins&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;With this, all captchas now get stored on the central memcache server and all the application servers have access to it. And it’s even better than the original, because memcache supports &lt;em&gt;time to live&lt;/em&gt;, so old captchas get removed from the store automatically.&lt;/p&gt;

&lt;p&gt;Note: We are using the &lt;a href=&quot;http://www.deveiate.org/projects/RMemCache/&quot;&gt;MemcacheClient&lt;/a&gt; gem to access memcache from rails. The &lt;code&gt;CACHE&lt;/code&gt; constant is declared in the environment files, so in production.rb we have this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'memcache'&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;memcache_options&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;ss&quot;&gt;:compression&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
   &lt;span class=&quot;ss&quot;&gt;:debug&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
   &lt;span class=&quot;ss&quot;&gt;:namespace&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;autoki_&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;RAILS_ENV&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
   &lt;span class=&quot;ss&quot;&gt;:readonly&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
   &lt;span class=&quot;ss&quot;&gt;:urlencode&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;memcache_servers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'cache.autoki.com:11211'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;cache_params&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;memcache_servers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;memcache_options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;flatten&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;CACHE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;MemCache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cache_params&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;For development and testing we use a simple hash based stub:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TestCache&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@items&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ttl&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;object&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;[]=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;delete&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;delete&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;clear&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;clear&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;CACHE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;TestCache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now the exercise left would be to factor out the storage code so the choice of captcha storage can be configured in the validates_captcha configuration, anyone? :)&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/08/filecolumn-regenerate-images/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/08/filecolumn-regenerate-images/"/>
    <title>FileColumn: regenerate images</title>
    <updated>2007-08-12T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;If you change the images dimensions or introduce a new version for models using file column you need a way to update the existing ones.
Here is a script that will regenerate all file column images and its version. We still look for a way to generate only new versions.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# recreates all file_column images in all needed sizes
&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ImageUpdater&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;update_all&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;update_model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:photo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;update_model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:auto&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:photo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;update_model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:photo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;update_model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:photo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;kp&quot;&gt;private&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;update_model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;class_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attribute_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;eval&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;class_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;capitalize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:all&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;begin&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;dir&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;attribute_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;blank?&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;attribute_name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;=&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;upload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;save!&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;message&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# copied from file_column/test_case.rb
&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;upload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;content_type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:guess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:tempfile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;content_type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:guess&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/\.jpg$/&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;content_type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;image/jpeg&quot;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/\.png$/&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;content_type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;image/png&quot;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;content_type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;uploaded_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;content_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;basename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;uploaded_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;content_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:tempfile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# :nodoc:
&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:tempfile&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Tempfile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;basename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
      &lt;span class=&quot;no&quot;&gt;FileUtils&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;copy_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;StringIO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;IO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;StringIO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;class_eval&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;alias&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;local_path&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:tempfile&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;define_method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:local_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:stringio&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;define_method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:original_filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;define_method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:content_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;content_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/08/using-mocha-a-review/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/08/using-mocha-a-review/"/>
    <title>Using Mocha - A review</title>
    <updated>2007-08-01T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;For aprox. 3 months we have been using &lt;a href=&quot;http://mocha.rubyforge.org/&quot;&gt;mocha&lt;/a&gt; now. And we &lt;a href=&quot;http://upstream-berlin.com/blog/2007/03/30/wir-machen-unseren-tests-beine/&quot;&gt;promised&lt;/a&gt; to get back to this subject when we passed our 2000th revision.&lt;/p&gt;

&lt;p&gt;In this post I’d like to share some expirence we made while using mocha. But first a short introduction to mocha taken from &lt;a href=&quot;http://rubyforge.org/projects/mocha/&quot;&gt;the rubyforge page&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt; Mocha is a library for mocking and stubbing using a syntax like that of JMock, and SchMock. One of its main advantages is that it allows you to mock and stub methods on real (non-mock) classes and instances.&lt;/blockquote&gt;
&lt;p&gt;We started to use mocha on an existing test suite and changed our test code in place when we wrote a new test or had to update old ones. The transition was mostly painless, there were three things where we stumbled.&lt;/p&gt;

&lt;p&gt;Avoid &lt;code&gt;any_instance,&lt;/code&gt; we got some strange interactions with instances in other tests (Maybe changed with the 0.5 release, which is great btw.). You can get along very well without &lt;code&gt;any_instance&lt;/code&gt; by stubbing &lt;code&gt;.new&lt;/code&gt;. The second challenge was more the change in the way how to write tests. We were used to push all the required objects in the database, then run the code and examine the result afterwards.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test_index_shows_logged_in_user_in_highscore&lt;/span&gt;
  &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;times&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;create_user&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:game_points&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;u&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;create_logged_in_user&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:game_points&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:first_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'gamer'&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:index&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;assert_select&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'#hall_of_fame td'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;name&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This example shows an old functional test. As you can see we hit the database hard. We built some helper methods like create_user to create often used database objects, but we didn’t think about stubbing so far.&lt;/p&gt;

&lt;p&gt;With mocha in place, we could test the expected behavior, which must be expressed beforehand. We also had to build new test helpers to provide us with stubs instead of full fledged objects, or at least real objects with stubbed behavior.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test_index_shows_logged_in_user_in_highscore&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new_logged_in_user&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:game_points&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:first_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'gamer'&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;Game&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;expects&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:all_time_rank_of_user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;returns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:index&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;assert_select&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'#hall_of_fame td'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;name&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This code tests the same as the test above but this time with mocha. This test (almost) doesn’t hit the database and is more readable with the mocha syntax.&lt;/p&gt;

&lt;p&gt;The last thing we had to learn. Every unexpected call to a mock object will cause your test to fail. This is good in a way, because you lessen the risk to miss some dependency in your code. But in the beginning it was hard to find the origin for the missed call, because the type is always ‘Mock’, so that you get errors like the following:
&lt;code&gt;
#&amp;lt;Mock:0x4cf5bf8&amp;gt;.find(:first, :order =&amp;gt; 'created_at DESC'}) - expected calls: 0, actual calls: 1
Similar expectations:
find('11')
find('11')
&lt;/code&gt;
Only the call parameters and the stack trace can reveal the culprit.&lt;/p&gt;

&lt;p&gt;If you get confused about all this mocking and stubbing. You can find a good explanation between the difference of mocking and stubbing and the different flavors in &lt;a href=&quot;http://blog.floehopper.org/presentations/lrug-mock-objects-2007-07-09/&quot;&gt;these slides&lt;/a&gt; from &lt;a href=&quot;http://blog.floehopper.org/articles/2007/07/22/an-introduction-to-mock-objects-in-ruby&quot;&gt;james mead&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The reward for all the work are much faster running tests and a decoupled testsuite, because you can mock/stub dependencies.&lt;/p&gt;

&lt;p&gt;Here some real figures.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before:&lt;/strong&gt;
424 unit tests with 637 assertions run in 319 seconds.
326 functional tests with 646 assertions run in 322 seconds&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;After:&lt;/strong&gt; (still some potential)
659 unit tests  with 1177 assertions run in  165 seconds.
574 functional tests with 1285 assertions run in 171 seconds.&lt;/p&gt;

&lt;p&gt;The nubers speak for themselves 8)&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/07/web-resourcen-mit-file_column-runterladen-und-speichern/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/07/web-resourcen-mit-file_column-runterladen-und-speichern/"/>
    <title>Bilder aus dem Netz mit file_column runterladen</title>
    <updated>2007-07-29T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Hier also ein weiteres Post zum Thema &lt;a href=&quot;http://upstream-berlin.com/blog/tag/file_column&quot;&gt;file_column&lt;/a&gt; Hacks.&lt;/p&gt;

&lt;p&gt;Vor kurzem mussten wir die Anforderung umsetzen, dass Bilder, die im Netz liegen, wie eine vom Client hochgeladene Datei speichern zu können. Warum wir sowas machen wollen? In diesem Fall, um Daten von einer Anwendung über http nach autoki zu importieren. Dazu gehörten auch Bilder, die über eine URL erreichbar waren.&lt;/p&gt;

&lt;p&gt;Dafür haben wir zuerst die Zuweisung der file_column Instanz in &lt;code&gt;file_column.rb&lt;/code&gt; so angepasst, dass zwischen einer hochgeladenen Datei und einem String, der die URL enhält, unterschieden werden kann. Diese Zuweisung erfolgt in der generierten Methode für das file_coloumn-Attribut. Passenderweise heißt die Variable für den define_method-Aufruf &lt;code&gt;attr&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Die Anpassung ist im Folgenden zu sehen: Wenn ein String statt ein File-Objekt übergeben wird, rufen wir zuerst unsere Methode auf, um die Datei herunterzuladen.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;define_method&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;attr&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;=&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;is_a?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;amp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;amp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;blank?&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;send&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;attr&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;_download_file&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;state_method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;nb&quot;&gt;instance_variable_set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state_attr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:after_upload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;just_uploaded?&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:after_upload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sym&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
   &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;send&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sym&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;So weit so trivial. Nun mag man denken, dass das Problem so gut wie gelöst ist, denn eine URL lässt sich Dank uri-open wie ein File-Objekt behandeln. Ruby ist es nämlich herzlich egal, ob ein Objekt die richtige Klasse hat, solange es die gerade benötigten Schnittstellen bereitstellt. Dieses Konzept wird in Ruby &lt;a href=&quot;http://de.wikipedia.org/wiki/Duck_Typing&quot;&gt;duck typing&lt;/a&gt; genannt. &lt;a href=&quot;http://www.ruby-doc.org/stdlib/libdoc/open-uri/rdoc/&quot;&gt;open-uri&lt;/a&gt; stellt die von File-Objekten benutzten Methoden, open und read, für URLs breit.&lt;/p&gt;

&lt;p&gt;Das Problem ist aber, dass das von Mongrel erzeugte Objekt eine Instanz von CgiFile ist, die noch einige andere Methoden bereitstellt, die von file_column aufgerufen werden. Hier machen wir uns eine weitere Eigenschaft von Ruby zu nutze. Wir definieren die noch benötigten Methoden für das File-Objekt, indem wir das Bild mit &lt;code&gt;instance_eval&lt;/code&gt; zur Laufzeit zwischenspeichern . Unsere dynamisch erzeugte Download-Methode, die ein File-Objekt bereitstellt, das sich wie eine durch einen Upload erzeugt CgiFile verhält, ist nachfolgend zu sehen.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;define_method&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;attr&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;_download_file&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Tempfile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'autoki_file_column_download'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;remote_file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;URI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;write&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;remote_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;read&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;close&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'r'&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;instance_eval&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;def original_filename
    &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'/'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;
  end&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;content_type&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;image/&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;original_filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'.'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Wichtig ist es, die erzeugte temporäre Datei zu schließen und erneut als Read-Only zu öffnen, denn die Temporärdatei ist write-only beim anlegen.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/07/programming-erlang-3-funs-list-comprehensions-guards-records-ifcase/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/07/programming-erlang-3-funs-list-comprehensions-guards-records-ifcase/"/>
    <title>Programming Erlang 3: funs, list comprehensions, guards, records, if/case</title>
    <updated>2007-07-28T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;I started reading the book &lt;a href=&quot;http://www.pragmaticprogrammer.com/titles/jaerlang/&quot;&gt;Programming Erlang&lt;/a&gt; and while I read I blog along what I find most interesting/important. In the end, this will hopefully be some kind of erlang tutorial, for your pleasure and my reference. :) I also started creating an &lt;a href=&quot;http://upstream-berlin.com/blog/?page_id=120&quot;&gt;Erlang Cheat Sheet&lt;/a&gt; with all the syntax on it. Here are the &lt;a href=&quot;http://upstream-berlin.com/blog/tag/programming-erlang&quot;&gt;previous posts&lt;/a&gt; and now comes the third:&lt;/p&gt;

&lt;h3&gt;funs&lt;/h3&gt;
&lt;p&gt;Funs are anonymous functions. Funs can be assigned to variables and, more importantly, passed as arguments into functions and also be returned by functions - these would be called higher order functions.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Double&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fun&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;One of these higher order functions is &lt;code&gt;lists:map&lt;/code&gt;. It takes a list and a fun and applies that fun to every element of the list, returning again a list with the results. This way we could rewrite our list processing example from the &lt;a href=&quot;http://upstream-berlin.com/blog/2007/07/24/programming-erlang-functional-programming/&quot;&gt;previous post&lt;/a&gt; like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;List&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lists&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fun&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The implementation of map is also pretty easy:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[])&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Fun&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;H&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Fun&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;H&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Fun&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3&gt;List comprehension&lt;/h3&gt;
&lt;p&gt;List comprehensions are a special construct for processing lists. They take an expression and one or more generators and filters.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;List&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;code&gt;X * 2&lt;/code&gt; is the expression. It is applied to every element returned by the generator &lt;code&gt;X &amp;lt;- List&lt;/code&gt; which simply pulls all the elements from the list.&lt;/p&gt;

&lt;p&gt;A filter would, well, filter elements before handling them over to the expression:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;List&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Here, the filter &lt;code&gt;X &amp;gt; 1&lt;/code&gt; removed the first element from our list.&lt;/p&gt;

&lt;h3&gt;Guards&lt;/h3&gt;
&lt;p&gt;Guards are a kind of extension to pattern matching in that they are more powerful. One way to use them is for defining functions:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;greater_than_5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;greater_than_5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;greater_than_5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;true&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Guards can also be used at any place you would use an expression, where they evaluate to either true or false. By the way, true and false are nothing more than atoms, so nothing special about them. Erlang knows about the difference between true and false, though. :)&lt;/p&gt;

&lt;p&gt;You can also chain guards together. Separated by semicolons you then have a guard sequence, which ORs all the guards. If you use commas instead, Erlang ANDs all the guards.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;false&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The above doesn’t actually work. Line gives a syntax error - I don’t know why. Anyone? (Hey book, tell me.) Anyway, you can also use boolean expresion:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;true&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3&gt;Records&lt;/h3&gt;

&lt;p&gt;Records are some syntactic sugar above tuples. Now you can name the elements instead of having to remember their positions. First you have to define the record:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;book&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;price&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Here we define a record &lt;em&gt;Book&lt;/em&gt; with three fields title, price and status where status is set to &lt;code&gt;&quot;new&quot;&lt;/code&gt; by default. &lt;em&gt;(Note: record definitons can’t be typed into the shell but must be included into modules.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now we can create a record like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;MyRecord&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;#book{title = &quot;Programming Erlang&quot;, price = 19.99}.
&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#book{title = &quot;Programming Erlang&quot;, price = 19.99, status = new}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;To update do this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;MyRecord2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;MyRecord&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;#{status = old}.
&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#book{title = &quot;Programming Erlang&quot;, price = 19.99, status = old}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The above code actually creates a copy and leaves the original untouched. To read from a record we again use our precious pattern matching:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;#book{title = Title} = MyRecord.
&lt;/span&gt;
&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Title&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;s2&quot;&gt;&quot;Programming Erlang&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;.. or simpler:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;MyRecord&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;#book.title.
&lt;/span&gt;
&lt;span class=&quot;s2&quot;&gt;&quot;Programming Erlang&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3&gt;if/case&lt;/h3&gt;

&lt;p&gt;Erlang also has the usual if and case clauses:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;
     &lt;span class=&quot;no&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
       &lt;span class=&quot;s2&quot;&gt;&quot;greater&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
     &lt;span class=&quot;no&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
       &lt;span class=&quot;s2&quot;&gt;&quot; less or equal&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
   &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;There’s no explicit &lt;em&gt;else&lt;/em&gt;, instead you just keep adding guards.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;case&lt;/em&gt; clause also looks pretty familiar:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Language&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Erlang&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Language&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;of&lt;/span&gt;
     &lt;span class=&quot;s2&quot;&gt;&quot;Erlang&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;yay&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
     &lt;span class=&quot;s2&quot;&gt;&quot;C#&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;get out&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
   &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The next chapter is about exception handling, then comes “advanced sequential programming” (sounds exciting, doesn’t it? :) )&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/07/programming-erlang-functional-programming/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/07/programming-erlang-functional-programming/"/>
    <title>Programming Erlang: Functional Programming</title>
    <updated>2007-07-24T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;I started reading the book &lt;a href=&quot;http://www.pragmaticprogrammer.com/titles/jaerlang/&quot;&gt;Programming Erlang&lt;/a&gt; and while I read I blog along what I find most interesting/important. In the end, this will hopefully be some kind of erlang tutorial, for your pleasure and my reference :) Here’s the &lt;a href=&quot;http://upstream-berlin.com/blog/2007/07/22/programming-erlang-chapter-1-getting-started/&quot;&gt;first post&lt;/a&gt; and now comes the second:&lt;/p&gt;

&lt;p&gt;Yesterday I started reading the next chapter called “Sequential Programming”. It actually is about functional programming: functions, anonymous functions, higher order functions, predicates, pattern matching for calling functions a.s.o. - I had to read most of it twice to actually understand it and haven’t even finished the chapter yet. Anyway, let’s start. By the way I started creating an &lt;a href=&quot;http://upstream-berlin.com/blog/?page_id=120&quot;&gt;Erlang Cheat Sheet&lt;/a&gt; with all the syntax on it. Being used to the very clear Ruby syntax, things started to get a bit confusing in this chapter.&lt;/p&gt;

&lt;h3&gt; functions&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Note: You can not enter function definitions in the erlang shell erl. see the modules section below to learn how to run this code.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A function in erlang is a function as in any (?) language. It receives parameters and returns a value (well, there’s more, read on). The definition looks like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;area&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rectangle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Width&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This defines the function &lt;code&gt;area&lt;/code&gt; which takes a tuple containing the atom &lt;code&gt;rectangle&lt;/code&gt; (to identify the type of shape) and its with and height. It of course returns the area of the rectangle by multiplying width and height.&lt;/p&gt;

&lt;h3&gt; parameter matching&lt;/h3&gt;

&lt;p&gt;We can now define the same function again with different parameters, e.g. like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;area&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;circle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Radius&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Radius&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;3.14&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;As you might have guessed already, this now computes the area of a circle. The cool thing is, that when you now call &lt;code&gt;area&lt;/code&gt;, erlang works out which of the function to call, depending on the given parameters. If you pass a tuple with the atom &lt;em&gt;rectangle&lt;/em&gt; as its first element and two values, the first definition matches (&lt;code&gt;{rectangle, Width, Height}&lt;/code&gt;). With &lt;em&gt;circle&lt;/em&gt; and one value, the second one matches.&lt;br /&gt;
&lt;em&gt;Note: the order of the function definitions id important, erlang starts with the first and goes down the list until it finds a match. But this is only important if you call a function where multiple definitions will match your parameters.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt; List recursions&lt;/h3&gt;

&lt;p&gt;one thing you can do with this pattern matching is processing lists recursively. for example, you could define a function that takes a list of numbers and returns the negated values. you do this by defining two functions: one takes a list with at least one element in it. this function does the computation with the head (first element) and then calls itself recursively with the tail (rest) of the list. the second definition expects an empty list and simply denotes the end of the recursion. take a look:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;negate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Head&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Tail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Head&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;++&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;negate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Tail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;negate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([])&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[].&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;negate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]).&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The first definition computes the negated value of the head. It then puts the result in a list (the angle brackets around the &lt;code&gt;Head * -1&lt;/code&gt;) and concatenates the result of the computation of the tails - which is again a list of values.&lt;/p&gt;

&lt;h3&gt; Modules&lt;/h3&gt;

&lt;p&gt;Modules are as usual collections of functions. A module definition starts with &lt;code&gt;-module(my_module).&lt;/code&gt;. after that you have to export all the functions you want to use from outside of the module using &lt;code&gt;-export([my_function/1]).&lt;/code&gt; where the /1 denotes the arity of the function (the number of aruments).&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;my_module&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;export&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hello_world&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]).&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;hello_world&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;hello world&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;If you want to run any of the functions above, you have to put them into a module and save it as a &lt;em&gt;.erl&lt;/em&gt; file. You can then compile and use the module from the shell:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;my_module&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;my_module&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:hello_world&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;hello world&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;So that was the easy part of this chapter (chapter 3 in the book) but enough already for this post. Stay tuned for more.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/07/programming-erlang-chapter-1-getting-started/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/07/programming-erlang-chapter-1-getting-started/"/>
    <title>Programming Erlang - Chapter 1: Getting Started</title>
    <updated>2007-07-22T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;div class=&quot;content_image&quot; style=&quot;border: 0px&quot;&gt;&lt;img src=&quot;/uploads/2007/07/41hor7unwwl_aa240_.jpg&quot; alt=&quot;programming erlang&quot; /&gt;&lt;/div&gt;
&lt;p&gt;I just started reading the latest book from the &lt;a href=&quot;http://www.pragmaticprogrammer.com/&quot;&gt;pragmatic programmers&lt;/a&gt;: &lt;a href=&quot;http://www.pragmaticprogrammer.com/titles/jaerlang/index.html&quot;&gt;Programming Erlang&lt;/a&gt;. In this series of articles, I’m going to summarize and comment on every chapter I read. Enjoy. (The reason I’m doing this in English is that I simply feel like writing some English again and that I hope to reach a larger audience :) )&lt;/p&gt;

&lt;h3&gt;What is Erlang and why read that book?&lt;/h3&gt;

&lt;p&gt;Erlang is a programming language which has things like parallel processing and functional programming features built right into the language. Software written in Erlang is therefore supposed to utilize the power of multi core processors found in today’s and future computers much better than software written in … my precious Ruby? omg.
Anyway, Erlang seems to be a language with features completely different from what I’ve seen before, so that’s my main reason to start reading about it, and we’re supposed to &lt;a href=&quot;http://www.pragmaticprogrammer.com/loty/&quot;&gt;learn a new new language every year&lt;/a&gt; anyway. Let’s get started.&lt;/p&gt;

&lt;h3&gt;Installation&lt;/h3&gt;

&lt;p&gt;The Erlang SDK can be installed on all the big 3, i.e. OSX, Linux and Windows. For OSX, just use &lt;a href=&quot;http://www.macports.org/&quot;&gt;MacPorts&lt;/a&gt; and after a &lt;em&gt;sudo port install erlang&lt;/em&gt; we’re set to go.&lt;/p&gt;

&lt;h3&gt;Getting started&lt;/h3&gt;

&lt;p&gt;For getting started with Erlang, we can use the Erlang shell. which can be started using the &lt;em&gt;erl&lt;/em&gt; command. Now we can start entering Erlang expressions and the shell will evaluate these. &lt;em&gt;(Note: Every line ends with a period)&lt;/em&gt;&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;mi&quot;&gt;15&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This is a simple addition and pretty boring so let’s move on.&lt;/p&gt;

&lt;h3&gt;Variables&lt;/h3&gt;

&lt;p&gt;Variables are the first thing Erlang handles differently than the “normal” languages. First, all variables have to start with an uppercase latter. Second and more importantly, all variables can only be assigned a value once, so they’re actually constants, after we assign them a value.  Example: &lt;em&gt;(The % denotes a comment.)&lt;/em&gt;&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ok&lt;/span&gt;
&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;already&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The book states that having immutable variables makes our programs less error prone, because we know exactly which variable has which value - so far soo good. I feel the more important consequence from having only constants is the other advantage mentioned: it makes things much easier when it comes to parallel processing, because you can avoid sharing memory among the processes running in parallel. Makes sense, somehow.&lt;/p&gt;

&lt;h3&gt;Pattern matching&lt;/h3&gt;

&lt;p&gt;The other important feature of Erlang mentioned in chapter 1 is this: The “=” does not represent an assignment operator but is a pattern matching operator. This means that when using the “=”, Erlang doesn’t simply assign the value of the right hand side to the variable on the left hand side but it tries to match both sides of the equation.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;gets&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;assigned&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;the&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;equation&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;so&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;gets&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;assigned&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This is of course just a very basic example, the more interesting stuff comes with the different data types. Read on.&lt;/p&gt;

&lt;h3&gt;Data types&lt;/h3&gt;

&lt;p&gt;Erlang of course has the basic data types such as integers, floats and strings. (where string are actually lists of integers). In addition we get tuples, which are like C-structs:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;MyTuple&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;joe&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;doe&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This creates a tuple with 2 values and stores it into the variable &lt;em&gt;MyTuple&lt;/em&gt;. To read the second value of the tuple, we again use the pattern matching operator:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;LastName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;MyTuple&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now Erlang tries to match the two tuples and hence puts “doe” into the LastName variable. The underscore represents something like /dev/null, so “joe” is not assigned to anything here.&lt;/p&gt;

&lt;p&gt;Another basic type are lists. Lists are defined using square brackets:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;MyList&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;MyList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Another way to retrieve the first value in the list is the&lt;/td&gt;
      &lt;td&gt;operator:&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;MyList&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Rest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;MyList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Rest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;It stores the first value in First and the rest of the list in Rest. Sounds like good old recursive list processing, we’ll see.&lt;/p&gt;

&lt;p&gt;That’s it for now - I hope to read at least the next chapter over the weekend. Stay tuned for more.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/07/weltpremiere-das-autoki-quartett-fur-die-eigene-website/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/07/weltpremiere-das-autoki-quartett-fur-die-eigene-website/"/>
    <title>Weltpremiere: Das autoki-Quartett für die eigene Website</title>
    <updated>2007-07-20T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Tadaaa, weltexklusiv nur für Besucher dieses Blogs, das nagelneue autoki-online-Autoquartett - ab Montag auch für alle anderen, dieses Wochenende nur hier:&lt;/p&gt;

&lt;iframe src=&quot;http://autoki.com/external_games?partner_id=1&quot; style=&quot;padding: 0px; height: 550px; width:520px; overflow: auto; border: 0px; border-color:white;&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;

&lt;p&gt;Viel Spaß beim Spielen.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/07/ruby-cookbook-tipps-arrays-inject-und-hashes/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/07/ruby-cookbook-tipps-arrays-inject-und-hashes/"/>
    <title>Ruby Cookbook Tipps - Arrays, inject und Hashes</title>
    <updated>2007-07-20T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Thilo hat beim letzten Treffen der &lt;a href=&quot;http://www.rug-b.com&quot;&gt;Berliner Ruby User Group&lt;/a&gt; gleich mal einen Buchgutschein für O’Reilly gewonnen und hat sich das &lt;a href=&quot;http://www.oreilly.com/catalog/rubyckbk/&quot;&gt;Ruby Cookbook&lt;/a&gt; ausgesucht. Gestern ist’s angekommen und ich hab gleich mal geblättert. Hier zwei kleine Schätze, die sich auf (fast) zufällig aufgeschlagenen Seiten fanden:&lt;/p&gt;

&lt;h3&gt;Array.inject&lt;/h3&gt;
&lt;p&gt;Ich wusste schon lange, dass es diese Methode irgendwo geben musste, nur gefunden hatte ich sie noch nicht. Eine Methode, mit der man aus den Elementen eines Array einen Wert berechnen kann, z.B. die Summe eines Array voller Zahlen. Inject kann genau das, es ruft für jedes Element des Array einen übergebenen Block auf und gibt das Ergebnis an die Berechnung des nächsten Elements.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;inject&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Es geht auch etwas komplexer, z.B. aus einem Array mit Wertepaaren ein Hash aufbauen:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;inject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({})&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;el&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;el&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;el&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;last&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Wichtig hierbei ist, dass der Block wieder den Hash zurueck gibt, damit die nächste Operation wieder auf dem Hash läuft. Hash.[]= liefert jedenfalls nicht den Hash, sondern den zugewiesenen Wert.&lt;/p&gt;

&lt;h3&gt;Array.min / Array.max&lt;/h3&gt;

&lt;p&gt;Irgendwann brauchten wir mal die kleinere von 2 Zahlen. Ich hab ewig in der Doku von FixNum gesucht und nix gefunden und am Ende die eine Zeile selbst implementiert, unzwar für FixNum. Natürlich hatte ich an der falschen Stelle gesucht, denn um die kleinste von zwei oder mehr Zahlen zu finden, kann man einfach einen Array benutzen:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;min&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 1&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/07/mysql-lost-connection-error-in-grosen-rails-anwendungen/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/07/mysql-lost-connection-error-in-grosen-rails-anwendungen/"/>
    <title>MySql Lost Connection Error in großen Rails Anwendungen</title>
    <updated>2007-07-18T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Seit kurzen beunruhigt uns die folgende Fehlermeldung: &lt;code&gt;Mysql::Error: Lost connection to MySQL server during query&lt;/code&gt;. Sie tritt immer wieder sporadisch ohne erkennbare Ursache auf. Heute Nacht bin ich eher durch Zufall auf eine Erklärung und idealerweise auch auf eine Lösung des Problems gestoßen.&lt;/p&gt;

&lt;p&gt;Kurz zusammengefasst: ActiveRecord verwendet pro Model eine Datenbankverbindung. Wenn die Datenbank unter Last steht, kann es passieren, dass die Verbindung nicht schnell genug bereitgestellt wird und der &lt;code&gt;Lost-Connection&lt;/code&gt;-Fehler auftritt.&lt;/p&gt;

&lt;p&gt;Über das setzen von &lt;code&gt;ActiveRecord::Base.verification_timeout=14400&lt;/code&gt; oder einen Wert niedriger als die MySql Server &lt;code&gt;&lt;a href=&quot;http://dev.mysql.com/doc/refman/5.0/en/server-system-variables.html&quot;&gt;interactive_timeout&lt;/a&gt;&lt;/code&gt; -Einstellung in der &lt;code&gt;environment.rb&lt;/code&gt; lässt sich der Timeout heraufsetzen. Um dem Problem nachhaltig zu begegnen, hat &lt;a href=&quot;http://blog.zvents.com&quot;&gt;Tyler Kovacs&lt;/a&gt; von &lt;a href=&quot;http://www.zvents.com/&quot;&gt;zvents&lt;/a&gt; das Plugin&lt;a href=&quot;http://rubyforge.org/projects/zventstools/&quot;&gt;mysql_retry_lost_connection&lt;/a&gt; geschrieben, das versucht, die Verbindung erneut herzustellen, wenn es einen Timeout gab.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/07/ruby-user-group-berlin-prasentation-zu-file_column/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/07/ruby-user-group-berlin-prasentation-zu-file_column/"/>
    <title>Ruby User Group Berlin - Präsentation zu file_column</title>
    <updated>2007-07-03T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Ich halte am Donnerstag eine kleine Präsentation zum Thema &lt;a href=&quot;http://upstream-berlin.com/blog/2007/06/16/text-im-bild-mit-rails-file_column-und-rmagick-teil-22/&quot;&gt;file_column und Widgets/Banner&lt;/a&gt;. Wer hin will: &lt;a href=&quot;http://www.rug-b.com/&quot;&gt;www.rug-b.com&lt;/a&gt; bzw. einfach 19:30 Uhr bei /i-d media, Ohlauer Strasse 43, Berlin Kreuzberg. Man sieht sich.&lt;/p&gt;

&lt;p&gt;Die Slides gibt’s &lt;a href=&quot;/uploads/2007/07/autoki_file_column.pdf&quot; title=&quot;autoki_file_column.pdf&quot;&gt;hier&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/06/rails-konferenz-2007-ersteindruck/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/06/rails-konferenz-2007-ersteindruck/"/>
    <title>Rails-Konferenz 2007 - Ersteindruck</title>
    <updated>2007-06-23T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Gestern fand in Frankfurt (M) die &lt;a href=&quot;http://rails-konferenz.de&quot;&gt;Rails-Konferenz&lt;/a&gt; 2007 statt. Es folgt: kommentierte Fotos von der Veranstaltung. Es folgt später: Kommentare zum Inhalt der Konferenz:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/uploads/2007/06/img_8392.jpg&quot; title=&quot;img_8392.jpg&quot;&gt;&lt;img src=&quot;/uploads/2007/06/img_8392.thumbnail.jpg&quot; alt=&quot;img_8392.jpg&quot; /&gt;&lt;/a&gt;
Aus irgendeinem Grund hab es ganz viele Java-Bücher und nur ein einziges kleines Rails-Heftchen zu kaufen. Wer wohl dahinter steckt? Eine Geheimoperation der Java-Community, die Ruby kaputt machen wollen, indem sie die Entwickler wieder zurück in die alte Welt holen?&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/uploads/2007/06/img_8395.jpg&quot; title=&quot;img_8395.jpg&quot;&gt;&lt;img src=&quot;/uploads/2007/06/img_8395.thumbnail.jpg&quot; alt=&quot;img_8395.jpg&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Begeleitend zu den Büchern gab es auch ein paar Zeitschriften. Neben dem Java-Magazin, dem Eclipse-Magazin und dem PHP-Magazin auch das hier abgebildete t3n - Coverstory: PHP5 mit MySQL5 (!) Was wollen die alle hier?&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/uploads/2007/06/img_8393.jpg&quot; title=&quot;img_8393.jpg&quot;&gt;&lt;img src=&quot;/uploads/2007/06/img_8393.thumbnail.jpg&quot; alt=&quot;img_8393.jpg&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Es gab ganz viel Kaffe und lustige Snacks in Form von Gläsern, in denen eine Schicht Müsli von einer Schicht Joghurt bedeckt wurde. Leider zu wenige Löffel, damit man auch alle essen konnte.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/uploads/2007/06/img_8394.jpg&quot; title=&quot;img_8394.jpg&quot;&gt;&lt;img src=&quot;/uploads/2007/06/img_8394.thumbnail.jpg&quot; alt=&quot;img_8394.jpg&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hübsche Blumenbilder an den Wänden. Rubine hätten besser gepasst, aber das Hotel hatte vermutlich gerade keine zum Abmalen da. Hoffentlich wird das auf der RailsConf im September besser.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/uploads/2007/06/img_8396.jpg&quot; title=&quot;img_8396.jpg&quot;&gt;&lt;img src=&quot;/uploads/2007/06/img_8396.thumbnail.jpg&quot; alt=&quot;img_8396.jpg&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Es gab zwei Räume mit vielen Lampen an den Decken, viel zu kleinen Beamern (also die von den Beamern angestrahlten Flächen waren zu klein, die Beamer waren okay, so von der Größe her), Stühlen im Raum und Steckdosen unter im Boden versenkten Deckeln. Das mit den Steckdosen hätte mir ja auch mal jemand früher sagen können, immerhin saß ich zwischendurch eine halbe Stunde ratlos mit leerem (Laptop-)Akku rum.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/uploads/2007/06/img_8399.jpg&quot; title=&quot;img_8399.jpg&quot;&gt;&lt;img src=&quot;/uploads/2007/06/img_8399.thumbnail.jpg&quot; alt=&quot;img_8399.jpg&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Neben vielen Stühlen (siehe voriges Bild) gab’s auch viele kleine Flaschen mit warmer Coke und Saft und Wasser drin. Weiß man in Frankfurt noch nicht, dass man jetzt &lt;a href=&quot;http://www.bionade.com/service/BIONADE_18-1_Holunder_RGB.jpg&quot;&gt;Bionade&lt;/a&gt; (&amp;lt;= Links klicken =&amp;gt; ) und &lt;a href=&quot;http://www.vlogbot.net/show/1101/40720&quot;&gt;Club Mate&lt;/a&gt; trinkt? Vor allem zum Wachbleiben wäre ich für letzeres sehr dankbar gewesen.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/uploads/2007/06/img_8403.jpg&quot; title=&quot;img_8403.jpg&quot;&gt;&lt;img src=&quot;/uploads/2007/06/img_8403.thumbnail.jpg&quot; alt=&quot;img_8403.jpg&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Und dann gab es da noch Menschen mit lustigen Hemden, die wahrscheinlich beim Bund waren. (Hände hinterm Rücken)&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/uploads/2007/06/img_8407.jpg&quot; title=&quot;img_8407.jpg&quot;&gt;&lt;img src=&quot;/uploads/2007/06/img_8407.thumbnail.jpg&quot; alt=&quot;img_8407.jpg&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Leute kamen extra aus der Schweiz angereist, um uns lustige Slides mit Monstern drauf zu zeigen. (Vortrag: Offline arbeiten. Slingshot, AIR &amp;amp; Co. später mehr dazu)&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/uploads/2007/06/img_8413.jpg&quot; title=&quot;img_8413.jpg&quot;&gt;&lt;img src=&quot;/uploads/2007/06/img_8413.thumbnail.jpg&quot; alt=&quot;img_8413.jpg&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Echten Code gab’s auch zu sehen. Wer findet den Fehler in 10 Sekunden? Hättense mal getestet…&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/uploads/2007/06/img_8414.jpg&quot; title=&quot;img_8414.jpg&quot;&gt;&lt;img src=&quot;/uploads/2007/06/img_8414.thumbnail.jpg&quot; alt=&quot;img_8414.jpg&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Andere haben auch Fotos gemacht, dafür aber gleich richtig dicke Dinger mitgebracht. Naja, war auch echt dunkel da, nix für zittrige Hände.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/uploads/2007/06/img_8415.jpg&quot; title=&quot;img_8415.jpg&quot;&gt;&lt;img src=&quot;/uploads/2007/06/img_8415.thumbnail.jpg&quot; alt=&quot;img_8415.jpg&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Manche haben ganz viele Wörter mitgebracht, falls ihnen mal nichts zu sagen einfallen würde.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/uploads/2007/06/img_8416.jpg&quot; title=&quot;img_8416.jpg&quot;&gt;&lt;img src=&quot;/uploads/2007/06/img_8416.thumbnail.jpg&quot; alt=&quot;img_8416.jpg&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Noch andere waren da schon wesentlich weiter und haben selbst ausgedachte Wörter gezeigt.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/uploads/2007/06/img_8418.jpg&quot; title=&quot;img_8418.jpg&quot;&gt;&lt;img src=&quot;/uploads/2007/06/img_8418.thumbnail.jpg&quot; alt=&quot;img_8418.jpg&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Gefilmt hamse das Ganze auch. Mal sehen, wann das dann im Netz zu sehen sein wird. Laut Aussage des Filmers muss er erst noch seine Web-2.0-Vidoes-Slides-Synchroniserungs-Abspiel-Website fertig programmieren. Na dann. (&lt;a href=&quot;http://upstream-berlin.com/blog/2007/06/01/mountain-west-ruby-conference-videos/&quot;&gt;gibt’s doch schon&lt;/a&gt;)&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/06/rails-wird-verkauft/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/06/rails-wird-verkauft/"/>
    <title>Rails wird verkauft.</title>
    <updated>2007-06-21T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Auf Grund ihres überragenden Erfolgs haben &lt;a href=&quot;http://37signals.com&quot;&gt;37signals&lt;/a&gt; beschlossen, von jetzt an nur noch am Strand rumzuliegen. Daher verkaufen sie ihr inzwischen von Millionen benutztes Web-Framework &lt;a href=&quot;http://rubyonrails.org&quot;&gt;Ruby on Rails&lt;/a&gt;. Das ganze läuft bereits - natürlich über Ebay, jeder kann mitbieten; siehe Screenshot unten.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/uploads/2007/06/picture-1.png&quot; alt=&quot;rails ebay&quot; /&gt;&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/06/text-im-bild-mit-rails-file_column-und-rmagick-teil-22/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/06/text-im-bild-mit-rails-file_column-und-rmagick-teil-22/"/>
    <title>Text-im-Bild mit Rails, file_column und RMagick Teil 2/2</title>
    <updated>2007-06-16T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Im &lt;a href=&quot;http://upstream-berlin.com/blog/2007/05/09/bild-im-bild-und-text-im-bild-mit-rails-file_column-und-rmagick/&quot;&gt;ersten Teil&lt;/a&gt; habe ich gezeigt, wie man mit &lt;a href=&quot;http://www.kanthak.net/opensource/file_column/&quot;&gt;file_column&lt;/a&gt; in Rails automatisch andere Bilder zu einem Bild hinzufügt, um z.B. Rahmen oder Hintergründe hinzuzufügen.&lt;/p&gt;

&lt;p&gt;In diesem Teil geht es darum, auch Text in ein Bild schreiben zu können: Hierbei ergeben sich zwei Probleme. Zum einen muss der Text vom Model in die file_column-Klassen gelangen, zum anderen muss er dort via RMagick in das Bild gerendert werden. Das Ergebnis könnte dann z.B. so aussehen (autoki-&lt;a href=&quot;http://autoki.com/games&quot;&gt;Quartettkarte&lt;/a&gt;):&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.autoki.com/profile/alex&quot;&gt;&lt;img alt=&quot;BMW 1er&quot; src=&quot;http://www.autoki.com/badge/alex/bmw_1er/5.png&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fangen wir von hinten an, RMagick.&lt;/p&gt;

&lt;h3&gt;Text in RMagick rendern&lt;/h3&gt;

&lt;p&gt;Die Beschreibung des Texts im Model soll folgendermaßen aussehen:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;file_column&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:photo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:magick&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:versions&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;:badge&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;badge&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:text&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;ss&quot;&gt;:text&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'hello world'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;ss&quot;&gt;:y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;241&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;ss&quot;&gt;:color&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'#363636'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;ss&quot;&gt;:size&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Hierbei soll an die Koordinaten 10/241 der text &lt;em&gt;hello world&lt;/em&gt; in der Schriftgröße 9 und in Dunkelgrau gerendert werden.&lt;/p&gt;

&lt;p&gt;Um den Text in unser Bild rendern zu können, erweitern wir wieder die &lt;code&gt;transform_image&lt;/code&gt;-Methode in der Datei &lt;em&gt;magick_file_column.rb&lt;/em&gt; des file_column-Plugins:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;img_options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:composite&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;img_options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;img_options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;is_a?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;img_options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text_options&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;img&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;add_text&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text_options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;img&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;img&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;add_text&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;img_options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;img&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;add_text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text_options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;img&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text_options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;blank?&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;img&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;img&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;annotate&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Magick&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text_options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text_options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;pointsize&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text_options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;11&lt;/span&gt;
      &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;font_weight&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text_options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:weight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;400&lt;/span&gt;
      &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fill&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text_options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'black'&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;img&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Das war’s eigentlich auch schon - jetzt haben wir ein &lt;em&gt;hello world&lt;/em&gt; in unserem Bild. Teil 2…&lt;/p&gt;

&lt;h3&gt;Werte aus dem Model auf's Bild&lt;/h3&gt;

&lt;p&gt;Neben statischem text wollen wir auch dynamische Inhalte in unsere Bilder rendern. Wie auf der Autokarte oben zu sehen, rendern wir hier den Namen und die Leistungsdaten des Autos, die bei &lt;a href=&quot;http://autoki.com&quot;&gt;autoki&lt;/a&gt; aus der Datenbank kommen.&lt;/p&gt;

&lt;p&gt;Leider gibt es dabei ein kleines Problem: Die Definition der file_column-Bildgrößen hängen nicht an der Model-Instanz, sondern an der Model-Klasse, d.h. an der Stelle, an der wir die Texte definieren, stehen die Daten aus der Datenbank noch gar nicht zur Verfügung.  Folgendes funktioniert also &lt;em&gt;nicht&lt;/em&gt;.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;file_column&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:photo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:magick&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:versions&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;:badge&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;badge&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:text&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;ss&quot;&gt;:text&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;top_speed&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; km/h&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;ss&quot;&gt;:x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;ss&quot;&gt;:y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;241&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Stattdessen müssen wir einen kleinen Umweg gehen, wir verwenden Procs, die erst zur Laufzeit der Anwendung den Text generieren:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;file_column&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:photo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:magick&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:versions&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;:badge&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;badge&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:text&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;ss&quot;&gt;:text&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Proc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;top_speed&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; km/h&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
        &lt;span class=&quot;ss&quot;&gt;:x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;ss&quot;&gt;:y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;241&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Damit das ganze funktioniert, muss &lt;em&gt;file_column&lt;/em&gt; noch etwas erweitert werden. Zum einen muss die &lt;code&gt;add_text&lt;/code&gt;-Methode die Procs aufrufen:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;add_text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text_options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;img&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text_options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;is_a?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Proc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;blank?&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;img&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;img&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;annotate&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Magick&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text_options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text_options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;pointsize&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text_options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;11&lt;/span&gt;
      &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;font_weight&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text_options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:weight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;400&lt;/span&gt;
      &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fill&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text_options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'black'&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;img&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Zum anderen muss die Model-Instanz &lt;em&gt;file_column&lt;/em&gt; zur Verfügung gestellt werden. Dazu erweitern wir die Methoden &lt;code&gt;transform_with_magick&lt;/code&gt; und &lt;code&gt;create_magick_version_if_needed&lt;/code&gt; um eine Parameter &lt;code&gt;object&lt;/code&gt;&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;transform_with_magick&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@object&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;object&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;create_magick_version_if_needed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@object&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;object&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Zuletzt muss noch der Aufruf von &lt;code&gt;create_magick_version_if_needed&lt;/code&gt; in der Methode &lt;code&gt;url_for_image_column&lt;/code&gt; in der Datei &lt;em&gt;file_column_helper.rb&lt;/em&gt; angepasst werden:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;url_for_image_column&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;subdir&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;_state&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create_magick_version_if_needed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Das war’s. Wenn ich nichts vergessen habe, gibt’s jetzt dynamische Textinhalte in Bildern mit file_column. In einem nächsten Teil dann: Bilderkompression, Amazon S3 Backups.&lt;/p&gt;

&lt;p&gt;Wie sehen uns beim &lt;a href=&quot;http://autoki.com/games&quot;&gt;Autoquartett&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;P.S. Am 5.7. werde ich das ganze bei der &lt;a href=&quot;http://www.rug-b.com/wiki/show/HomePage&quot;&gt;Berlin Ruby User Group&lt;/a&gt; noch einmal live zeigen, kommt vorbei.&lt;/p&gt;

&lt;p&gt;P.P.S. ich schreibe das hier gerade mit der neuen Beta von &lt;a href=&quot;http://www.apple.com/safari/&quot;&gt;Safari 3&lt;/a&gt;. Vom User vergrößerbare Textareas - Gold wert mit Wordpress.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/06/mfg-rails-konferenz/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/06/mfg-rails-konferenz/"/>
    <title>MFG Rails-Konferenz</title>
    <updated>2007-06-16T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Wir fahren am Donnerstag Abend mit Auto(&lt;a href=&quot;http://autoki.com&quot;&gt;ki&lt;/a&gt;) zur &lt;a href=&quot;http://rails-konferenz.de&quot;&gt;Rails-Konferenz&lt;/a&gt; in Frankfurt/Main. Es sind noch 2-3 Plätze frei im Wagen, einfach Email an alex[at]upstream[minus]berlin.com&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/06/vertikales-webdesign/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/06/vertikales-webdesign/"/>
    <title>Vertikales Webdesign</title>
    <updated>2007-06-15T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Ich poste das hier eigentlich nur, damit ich dir URL nicht verliere … gefunden bei 37signals: &lt;a href=&quot;http://www.37signals.com/svn/posts/159-type-that-keeps-the-beat&quot;&gt;type that keeps the beat&lt;/a&gt; - wie man sich ein Raster aus Zeilenhöhe und -abstand baut und alle Seitenelemente an diesem Raster ausrichtet. Schon hat man ein stimmige(re)s Design. Dieses Bild (aus dem 37signal-Artikel) zeigt das Ganze eigentlich sehr schön:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://www.37signals.com/svn/images/Picture+3-185.png&quot; alt=&quot;font raster&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In den Kommentaren gibt’s dann auch gleich mal ein CSS-Beispiel:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-css&quot; data-lang=&quot;css&quot;&gt;&lt;span class=&quot;nt&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0.75em&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;line-height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1.25&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;h1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2em&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;line-height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1.25&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;margin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1.875em&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0.625em&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;h2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1.5em&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;line-height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1.6667&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;margin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0.8333em&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0.8334em&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;margin-bottom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1.25em&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/06/performance-benchmarking-in-rails/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/06/performance-benchmarking-in-rails/"/>
    <title>Performance Benchmarking in Rails</title>
    <updated>2007-06-10T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Na? Ist die &lt;a href=&quot;http://www.autoki.com/profile/alex&quot;&gt;Userprofil-Seite&lt;/a&gt; mal wieder zu langsam, nachdem beim letzten Release durch geschicktes anordnen und ausblenden noch einmal 3 Bilder und Informationshäppchen mehr draufgepackt werden konnten? Und jetzt will’s wieder keiner gewesen sein und niemand hat eine Ahnung, wo die 500ms denn nun genau hingehen? Für das profilen z.B. einer Rails-Seite stehen dem geneigten Entwickler mehrere Tools zur Verfügung.&lt;/p&gt;

&lt;h3&gt;logfiles&lt;/h3&gt;

&lt;p&gt;Erster Anlaufpunkt sind die logfiles in RAILS_ROOT/log - je nach logging-level stehen dort die Gesamtzeit für’s prozessieren der Seite, noch einmal unterschieden in Datenbank- und Render-Zeit (production environment) sowie zusätzlich die Zeiten, die für jeden Datenbank-Query und das rendern von Partials draufgehen.&lt;/p&gt;

&lt;h3&gt;benchmark do .. end&lt;/h3&gt;

&lt;p&gt;Wer keine Lust hat, sich im Logfile die entscheidenen Zeiten herauszusuchen, kann auf die von Rails angebotene &lt;code&gt;benachmark&lt;/code&gt;-Methode zurückgreifen. Dieser wird einfach ein Block mit dem zu profilenden Code übergeben und schon steht die laufzeit im Logfile. Funktioniert auch im Model.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;%- benachmark 'user details' do -%&amp;gt;
  &amp;lt;%= render_lots_o_stuff %&amp;gt;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;%- end -%&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3&gt;RubyProf&lt;/h3&gt;

&lt;p&gt;Wer es ganz genau wissen will, kann &lt;a href=&quot;http://rubyforge.org/projects/ruby-prof&quot;&gt;RubyProf&lt;/a&gt; verwenden. Wie es der Name vermuten lässt, handelt es sich dabei um einen “richtigen” Profiler - man bekommt also nicht nur die Gesamtlaufzeit der durchgemessenen Codes sondern das ganze aufgeschlüsselt nach Methoden, den ganzen Ruby-Stack herunter. Um RubyProf zu installieren, reicht erwartunsggemäß ein einfaches &lt;em&gt;gem install ruby-prof&lt;/em&gt;. Der eigentliche Profile-Aufruf erfolgt wieder inline und könnte so aussehen:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;% result &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;RubyProf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;profile&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;%&amp;gt;
  		  &amp;lt;%= render :partial =&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'shared/pinboard'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:object&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:locals&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:posts_comments&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@posts_comments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;%&amp;gt;&lt;/span&gt;
  		&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;% end &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;%&amp;gt;&lt;/span&gt;
  		&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;%- printer = RubyProf::GraphHtmlPrinter.new(result)
  		file = File.new('/Users/alex/Desktop/profile.html', 'w')
      printer.print(file, 0)
      file.close -%&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Als Belohnung liegt dann nach kurzer Zeit eine riesige HTML-Tabelle auf dem Desktop, mit den Laufzeiten aller Methodenaufrufe innerhalb des gewählten Blocks, und es stellt sich heraus, dass irgendjemand (wer war das nur wieder?^^) einen fiesen &lt;code&gt;after_initialize&lt;/code&gt;-Callback an den User gebaut hat, der für jeden geladenen User ein paar Milisekunden Zeit verbrät…. &lt;code&gt;after_initialize&lt;/code&gt; ist gefährlich, don’t do this at home ;)&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/06/gruenderszenede-bei-autokicom/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/06/gruenderszenede-bei-autokicom/"/>
    <title>gruenderszene.de bei autoki.com</title>
    <updated>2007-06-10T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Jan von &lt;a href=&quot;http://gruenderszene.de&quot;&gt;gruenderszene.de&lt;/a&gt; war letzte Woche bei uns und hat ein &lt;a href=&quot;http://www.gruenderszene.de/?p=153&quot;&gt;Interview&lt;/a&gt; über autoki.com gemacht.&lt;/p&gt;

&lt;script type=&quot;text/javascript&quot; src=&quot;http://de.sevenload.com/pl/Ph1Fg3J/425x350&quot;&gt;&lt;/script&gt;

&lt;p&gt;Link: &lt;a href=&quot;http://de.sevenload.com/videos/Ph1Fg3J/Autoki-kurz&quot;&gt;sevenload.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Wir haben übrigens noch viel mehr Features außer den Gruppen… :)&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/06/mountain-west-ruby-conference-videos/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/06/mountain-west-ruby-conference-videos/"/>
    <title>Mountain West Ruby Conference Videos</title>
    <updated>2007-06-01T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;&lt;a href=&quot;http://confreaks.com&quot;&gt;confreaks.com&lt;/a&gt; zeichnen Vorträge auf Konferenzen auf Video auf und stellen sie anschließend ins Netz - praktischerweise so, dass man gleichzeitig sowohl Slides als auch Vortragende sieht. Und der Flash-Videoplayer hat auch noch einen schicken Vollbildmodus. Und als erste Konferenz haben sie sich die &lt;a href=&quot;http://mtnwestruby.org/&quot;&gt;Mountain West Ruby Conference&lt;/a&gt; ausgesucht. Hab mir gerade die &lt;a href=&quot;http://mtnwestrubyconf2007.confreaks.com/session10.html&quot;&gt;Rails Code Review&lt;/a&gt; Session angesehen - sehr schöne Tipps, saubereren Code zu schreiben. Fast schon ein Klassiker zum Beispiel, statt:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;nil?&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'blah'&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;einfach:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'blah'&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/05/hi-im-a-macruby-on-rails/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/05/hi-im-a-macruby-on-rails/"/>
    <title>Hi, I'm <del>a Mac</del>Ruby on Rails</title>
    <updated>2007-05-20T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Die großartigen &lt;a href=&quot;http://www.apple.com/getamac/ads/&quot;&gt;Apple-Ads&lt;/a&gt; kennt man ja inzwischen, jetzt gibt’s auch die Rails-Variante. Toll:&lt;/p&gt;

&lt;object width=&quot;425&quot; height=&quot;350&quot;&gt;&lt;param name=&quot;movie&quot; value=&quot;http://www.youtube.com/v/PQbuyKUaKFo&quot; /&gt;&amp;lt;/param&amp;gt;&lt;param name=&quot;wmode&quot; value=&quot;transparent&quot; /&gt;&amp;lt;/param&amp;gt;&lt;embed src=&quot;http://www.youtube.com/v/PQbuyKUaKFo&quot; type=&quot;application/x-shockwave-flash&quot; wmode=&quot;transparent&quot; width=&quot;425&quot; height=&quot;350&quot; /&gt;&amp;lt;/embed&amp;gt;&lt;/object&gt;

&lt;object width=&quot;425&quot; height=&quot;350&quot;&gt;&lt;param name=&quot;movie&quot; value=&quot;http://www.youtube.com/v/n1NVfDlU6yQ&quot; /&gt;&amp;lt;/param&amp;gt;&lt;param name=&quot;wmode&quot; value=&quot;transparent&quot; /&gt;&amp;lt;/param&amp;gt;&lt;embed src=&quot;http://www.youtube.com/v/n1NVfDlU6yQ&quot; type=&quot;application/x-shockwave-flash&quot; wmode=&quot;transparent&quot; width=&quot;425&quot; height=&quot;350&quot; /&gt;&amp;lt;/embed&amp;gt;&lt;/object&gt;

&lt;object width=&quot;425&quot; height=&quot;350&quot;&gt;&lt;param name=&quot;movie&quot; value=&quot;http://www.youtube.com/v/p5EIrSM8dCA&quot; /&gt;&amp;lt;/param&amp;gt;&lt;param name=&quot;wmode&quot; value=&quot;transparent&quot; /&gt;&amp;lt;/param&amp;gt;&lt;embed src=&quot;http://www.youtube.com/v/p5EIrSM8dCA&quot; type=&quot;application/x-shockwave-flash&quot; wmode=&quot;transparent&quot; width=&quot;425&quot; height=&quot;350&quot; /&gt;&amp;lt;/embed&amp;gt;&lt;/object&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/05/textmate-tuning/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/05/textmate-tuning/"/>
    <title>TextMate tuning</title>
    <updated>2007-05-11T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Über die Monate ist &lt;a href=&quot;http://macromates.com/&quot;&gt;TextMate&lt;/a&gt;, das wir für die Entwicklung von &lt;a href=&quot;http://autoki.com&quot;&gt;autoki&lt;/a&gt; verwenden, ziemlich langsam geworden. Das betraf vor allem drei Bereiche: projektweite Suche, Dateien finden mit Apfel-T sowie von einer anderen Anwendung zu TextMate wechseln.&lt;/p&gt;

&lt;p&gt;Ich war schon kurz davor, dem TextMate-Programmierer Geld zu bieten, damit er das Ding schneller macht. Vorher noch mal kurz gegoogelt, ob es noch andere Leidtragende gibt, und siehe da: einfach mal das 600MB große Test-log in &lt;em&gt;log/test.log&lt;/em&gt; gelöscht und TextMate fliegt wieder. :)&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/05/bild-im-bild-und-text-im-bild-mit-rails-file_column-und-rmagick/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/05/bild-im-bild-und-text-im-bild-mit-rails-file_column-und-rmagick/"/>
    <title>Bild-im-Bild und Text-im-Bild mit Rails, file_column und RMagick</title>
    <updated>2007-05-09T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; &lt;a href=&quot;http://upstream-berlin.com/blog/2007/06/16/text-im-bild-mit-rails-file_column-und-rmagick-teil-22/&quot;&gt;Teil 2&lt;/a&gt; ist fertig.&lt;/p&gt;

&lt;p&gt;Gerade haben wir bei &lt;a href=&quot;http://autoki.com&quot;&gt;autoki&lt;/a&gt; &lt;a href=&quot;http://blog.autoki.com/2007/05/05/banner-baby/&quot;&gt;Badges eingeführt&lt;/a&gt; - kleine Banner, die sich auf Blogs, Foren etc. einbinden lassen, und auf denen neben dem jeweiligen Auto des Mitglieds die URL zu seinem oder ihrem Profil zu  sehen ist:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.autoki.com/profile/alex&quot;&gt;&lt;img alt=&quot;MINI Cooper S&quot; src=&quot;http://www.autoki.com/badge/alex/mini_cooper_s/4.png&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Im folgenden Posting werde ich erklären, wie wir das ganze umgesetzt haben. Ziel war es, einfach einzubindende Grafiken zu generieren. Eine Alternative wäre Flash gewesen, jedoch lässt sich Flash an vielen Stellen (vor allem als Signatur in Foren) nicht einbinden.&lt;/p&gt;

&lt;p&gt;Wir brauchten also eine Lösung, die uns für jedes Auto mehrere Banner mit jeweils einem Foto und Text darauf automatisch erstellt. Für die Skalierung der Bilder benutzen wir das &lt;a href=&quot;http://www.kanthak.net/opensource/file_column/&quot;&gt;file_column&lt;/a&gt;-Plugin für &lt;a href=&quot;http://rubyonrails.org&quot;&gt;Rails&lt;/a&gt;, das auf &lt;a href=&quot;http://rmagick.rubyforge.org/&quot;&gt;RMagick &lt;/a&gt;, den Ruby-Bindings für &lt;a href=&quot;http://www.imagemagick.org/&quot;&gt;ImageMagick&lt;/a&gt;, aufsetzt,. Damit lassen sich sehr einfach skalierte und beschnittene Versionen einer Grafik erzeugen, indem man in einem Rails-Model, nennen wir es mal &lt;code&gt;Auto&lt;/code&gt;, ein Attribut als &lt;code&gt;file_column&lt;/code&gt;-Attribute markiert:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Auto&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;file_column&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:photo&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Um die verschiedenen Bildgrößen zu erzeugen, genügt es, mittels &lt;code&gt;:version&lt;/code&gt; die gewünschten Größen anzugeben:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;file_column&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:photo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:magick&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:versions&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;ss&quot;&gt;:large&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;480x480&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;ss&quot;&gt;:thumb&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;238x&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Dieser Code erzeugt zwei Versionen: &lt;code&gt;:large&lt;/code&gt; passt unter Beibehaltung der Seitenverhältnisse in einen Rahmen von 480x480 Pixeln, &lt;code&gt;:thumb&lt;/code&gt; hat eine Breite von 238 Pixeln und eine daran angepaste Höhe.&lt;/p&gt;

&lt;p&gt;Alles ganz schön und nett, aber um damit Banner zu produzieren, kommen wir nicht umhin, &lt;code&gt;file_column&lt;/code&gt; ein wenig zu &lt;del&gt;hacken&lt;/del&gt; erweitern. Unser Problem lässt sich in 3 Schritte aufteilen:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;das Autobild herunterskalieren, so dass es auf den Banner passt,&lt;/li&gt;
  &lt;li&gt;das verkleinerte Bild in ein Banner-Template-Bild kopieren,&lt;/li&gt;
  &lt;li&gt;die URL des Autoprofils in das Bild schreiben&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Schritt eins können wir bereits lösen:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;file_column&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:photo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:magick&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:versions&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;ss&quot;&gt;:badge_1_online&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:crop&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;52:46&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:size&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;52x46!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;badge_1_online&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Mit Hilfe von &lt;code&gt;:crop&lt;/code&gt; wird das Bild zusätzlich beschnitten, damit es garantiert 52x46 Pixel groß ist.&lt;/p&gt;

&lt;p&gt;Um nun das Bild in das Banner-Template zu platzieren, verwenden wir folgende Syntax:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;file_column&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:photo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:magick&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:versions&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;ss&quot;&gt;:badge_1_online&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:crop&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;52:46&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:size&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;52x46!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;badge_1_online&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;:composite&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'badge_1.gif'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Soll heißen: Das Autobild wird an der Stelle 3/2 in das Bild &lt;em&gt;badge_1.gif&lt;/em&gt; kopiert. Wie schon erwähnt, funktioniert das so erstmal nicht. Nachdem wir die Methode &lt;code&gt;transform_image&lt;/code&gt; in der Datei &lt;em&gt;magick_file_column.rb&lt;/em&gt; des &lt;em&gt;file_column&lt;/em&gt;-Plugins folgendermaßen angepasst haben, aber schon:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;img_options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
 &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;img_options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:composite&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
					&lt;span class=&quot;n&quot;&gt;composite&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Magick&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;RAILS_ROOT&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/public/composites/&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;img_options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:composite&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
					&lt;span class=&quot;n&quot;&gt;img&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;composite&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;composite!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;img&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;img_options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:composite&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;img_options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:composite&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Magick&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;OverCompositeOp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
				&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Diese Modifikation setzt voraus, dass unser Banner-Template &lt;em&gt;badge_1.gif&lt;/em&gt; in &lt;em&gt;public/composites/&lt;/em&gt;liegt. Mittels der &lt;em&gt;RMagick&lt;/em&gt;-&lt;code&gt;composite&lt;/code&gt;-Methode wird das Auto mit zwei Zeilen Code in den Banner kopiert.&lt;/p&gt;

&lt;p&gt;Sieht doch schonmal ganz nett aus oder? Und wie das mit dem Text funktioniert, erkläre ich im nächten Post :) Das ist nämlich etwas komplizierter….&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/05/mit-grauen-schwaden-gegen-den-spam/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/05/mit-grauen-schwaden-gegen-den-spam/"/>
    <title>Mit grauen Schwaden gegen den Spam</title>
    <updated>2007-05-02T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;div class=&quot;content_image&quot;&gt;&lt;img src=&quot;/uploads/2007/05/419929809_7ab6596727.jpg&quot; alt=&quot;fog&quot; /&gt;&lt;/div&gt;
&lt;p&gt;Eigentlich hatte ich mich schon damit abgefunden: zuletzt erreichten mich taeglich ca. 150 Spam-Emails pro Tag, in denen mir die bekannten Aktienpakete, Pillen und anderen Schätze versprochen wurden. Neulich musste ich dann für einen Kunden ein paar Emailaccounts bei &lt;a href=&quot;http://webmail.us&quot;&gt;webmail.us&lt;/a&gt; (Email-Provider) einrichten. Also schnell Adressen eingetragen und noch kurz eine Testmail versandt und … kam nicht an. Also mal kurz ins Logfile des eigenen Mailserver geschaut und siehe da: &lt;em&gt;temporarily rejected RCPT … greylisted (see &lt;a href=&quot;http://en.wikipedia.org/wiki/Greylisting&quot;&gt;http://en.wikipedia.org/wiki/Greylisting&lt;/a&gt;)&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Greylisted? Black lists, white lists, klar. Aber grau? Schnell nachgesehen und herausgefunden: &lt;em&gt;method of defending electronic mail users against e-mail spam. A mail transfer agent which uses greylisting will “temporarily reject” any email from a sender it does not recognize. If the mail is legitimate, the originating server will try again to send it later, at which time the destination will accept it. If the mail is from a spammer, it will probably not be retried&lt;/em&gt; - coole sache, nur … Mailserver umkonfigurieren? Au weia. ich bin froh, dass der überhaupt funktioniert. Glücklicherweise gibt’s für &lt;a href=&quot;http://www.exim.org/&quot;&gt;Exim&lt;/a&gt; auf &lt;a href=&quot;http://debian.org&quot;&gt;Debian&lt;/a&gt; ein Paket namens greylistd. Einfach &lt;code&gt;apt-get install greylistd&lt;/code&gt; und anschließend ein &lt;code&gt;greylistd-setup-exim4 add&lt;/code&gt; und voila - greylisting a la Debian. Und das beste, ich habe in den letzten zweit Tagen tatsächlich nur eine einzige (!) Spam-Email erhalten.&lt;/p&gt;

&lt;p&gt;Wenn mir jetzt noch jemand erklärt, wie ich diese Zeitverzögerung beim senden von Emails von dynamisch verteilten IP-Adressen aus wegbekomme….&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/04/perian-divx-co-fur-quicktime/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/04/perian-divx-co-fur-quicktime/"/>
    <title>Perian: DivX &amp; Co. für Quicktime</title>
    <updated>2007-04-19T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;div class=&quot;content_image&quot;&gt;&lt;img width=&quot;300&quot; src=&quot;/uploads/2007/04/macbookfrontrow.jpg&quot; alt=&quot;macbookfrontrow.jpg&quot; /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;http://perian.org/&quot;&gt;Perian&lt;/a&gt; ist eine freie Quicktime-Komponente für OSX, die Formate wie DivX, XviD abspielen kann. Zusammen mit &lt;a href=&quot;http://www.flip4mac.com&quot;&gt;Flip4Mac&lt;/a&gt; kann Quicktime somit nun endlich die meisten Filme und Formate abspielen. Das ging zwar bisher mit &lt;a href=&quot;http://www.videolan.org/vlc/&quot;&gt;VLC&lt;/a&gt; auch, aber durch Quicktime ist das ganze doch wesentlich besser integriert: der Finder hängt sich beim Versuch, einen DivX-Film in der Vorschau anzuzeigen, nicht mehr auf und endlich kann man die ganzen Streifen auch mit Fernbedienung und &lt;a href=&quot;http://www.apple.com/imac/frontrow.html&quot;&gt;Front Row&lt;/a&gt; genießen.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/04/spas-mit-rails/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/04/spas-mit-rails/"/>
    <title>Spaß mit Rails</title>
    <updated>2007-04-13T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;&lt;span class=&quot;content_image&quot;&gt;&lt;img src=&quot;/uploads/2007/04/pair.gif&quot; alt=&quot;pair.gif&quot; /&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;Einer der großen Vorteile des &lt;a href=&quot;http://en.wikipedia.org/wiki/Pair_programming&quot;&gt;Pair Programming&lt;/a&gt; ist das Vorhandensein einer zweiten Person, mit der man zusammen über &lt;strike&gt;Blödsinn&lt;/strike&gt; Geistreiches lachen kann. Wie z.B., wenn man zum zehnten mal &lt;code&gt;pust&lt;/code&gt; statt &lt;code&gt;puts&lt;/code&gt; schreibt, um irgendwo einen Wert auf der Konsole auszugeben.&lt;/p&gt;

&lt;p&gt;Fall zwei: Mit &lt;code&gt;rake&lt;/code&gt; die Tests laufen lassen. “Na dann &lt;code&gt;rake&lt;/code&gt;ln wir uns mal wieder”, “&lt;code&gt;Rake&lt;/code&gt; dich nicht so auf”, oder “Da &lt;code&gt;rake&lt;/code&gt;t sich was.”&lt;/p&gt;

&lt;p&gt;Wir amüsieren uns jedenfalls köstlich beim programmieren :)&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/04/republica-mister-wong-und-powerpoint-karaoke-videos/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/04/republica-mister-wong-und-powerpoint-karaoke-videos/"/>
    <title>re:publica: mister wong und powerpoint-karaoke (videos!, updated)</title>
    <updated>2007-04-13T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;h3&gt;prolog&lt;/h3&gt;
&lt;p&gt;muesste es nicht langsam mal &lt;a href=&quot;http://www.apple.com/iwork/keynote/&quot;&gt;keynote&lt;/a&gt;-karaoke heissen? wer macht denn heute noch powerpoint? der &lt;a href=&quot;http://programm.re-publica.de/programm/speakers/14.de.html&quot;&gt;herr lobo&lt;/a&gt; hatte jedenfalls &lt;a href=&quot;http://anikalindtner.wordpress.com/2007/04/12/republica-die-dritte/&quot;&gt;wie sehr viele andere auch&lt;/a&gt; einen dieser &lt;a href=&quot;http://apple.de&quot;&gt;apfel-rechner&lt;/a&gt; dabei.&lt;/p&gt;

&lt;h3&gt;mister wong&lt;/h3&gt;

&lt;p&gt;ich wusste nur, dass es sich um einen sponsoren der re:publica und die deutsche variante von &lt;a href=&quot;http://del.icio.us&quot;&gt;del.icio.us&lt;/a&gt; handelt. erwartet habe ich eine werbeveranstaltung für &amp;lt;a href=http://mister-wong.de”&amp;gt;mister wong&amp;lt;/a&amp;gt;, und im endeffekt war es auch eine, aber so schlimm war das gar nicht. ich habe naemlich zum ersten mal erfahren, dass man social-bookmarking-dienste als suchmaschine benutzen kann, da ja die häufigkeit der “verbookmarkung” und das vertaggen zusammen so eine art pagerank ergeben. warum bin ich da nie drauf gekommen? seit jahren pflege ich meine bookmarks bei del.icio.us, aber auf die idee, das als suchmaschine zu benutzen bin ich aus unerfindlichen gründen nicht gekommen.&lt;/p&gt;

&lt;p&gt;schön (an mister wong) fand ich, dass man seine bookmarks aus anderen diensten wie eben del.icio.us sehr einfach und auf einen schwubs importieren kann. und auch exportieren, wenn dann das next big thing im social bookmarking kommt ;)&lt;/p&gt;

&lt;p&gt;auf meine frage, warum denn nun mister wong besser als del.icio.us ist, und ich da nun hin soll, gab’s leider keine besonders ueberzeugende antwort, ausser der, dass es da mehr deutschen content gaebe - na dann.&lt;/p&gt;

&lt;p&gt;eher amuesant fand ich die wiederholten einwuerfe aus den hinteren reihen (billigere plaetze?), dass dieser komische chinese als logo einen ernsthaften einsatz, z.b. in unternehmen, ja wohl komplet ausschliessen wuerde, und dass die zielgruppe ja wohl eher teenies seien. und ueberhaupt, haette man nicht einen deutschen oder franzosen nehmen koennen, und kommen die chinesen mit &lt;a href=&quot;http://mister-wong.cn&quot;&gt;mister-wong.cn&lt;/a&gt; ueberhaupt klar, so von wegen da so ein landsmann in der ecke? antwort: in china gibt’s gar keine wongs, hoechstens wangs. schoen.&lt;/p&gt;

&lt;p&gt;erwähnung fand noch &lt;a href=&quot;http://www.vo-pixeltown.com/&quot;&gt;visual orgasm pixel town&lt;/a&gt; - hach wie niedlich, huebsch anzusehende pixelhaeuser, die man auf zu erwerbenden grundstuecken bauen kann. gab’s zwar irgendwo schonmal aber egal. machen wir auch mit, fuer unsere &lt;a href=&quot;http://kommt-mit.de&quot;&gt;reisecommunity&lt;/a&gt;. ein strandhaus oder sowas, mal sehen. (ideen anyone?)&lt;/p&gt;

&lt;h3&gt;&lt;strike&gt;powerpoint&lt;/strike&gt; keynote karaoke&lt;/h3&gt;

&lt;p&gt;da gibt’s nicht viel zu sagen, ausser, dass die vortraege teilweise recht witzig waren und das publikum zu begeisterungsstuermen hinreissen konnten, aber seht selbst.. (ach ja, wer’s nicht weiss: beim keynote karaoke muss jemand (aus dem publikum) zu einem ihr vorher nicht bekannten thema eine prasentation (mit vorgegebenen beamer-slides) halten).&lt;/p&gt;

&lt;embed src=&quot;http://www.vimeo.com/moogaloop.swf?clip_id=169123&quot; quality=&quot;best&quot; scale=&quot;exactfit&quot; width=&quot;400&quot; height=&quot;300&quot; type=&quot;application/x-shockwave-flash&quot; /&gt;
&lt;p&gt;&amp;lt;/embed&amp;gt;&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.vimeo.com/clip:169123&quot;&gt;republica powerpoint karaoke: Stilgeschichte des Möbels&lt;/a&gt; on &lt;a href=&quot;http://www.vimeo.com/&quot;&gt;Vimeo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; hier sind die anderen zwei videos:&lt;/p&gt;

&lt;embed src=&quot;http://www.vimeo.com/moogaloop.swf?clip_id=169301&quot; quality=&quot;best&quot; scale=&quot;exactfit&quot; width=&quot;400&quot; height=&quot;300&quot; type=&quot;application/x-shockwave-flash&quot; /&gt;
&lt;p&gt;&amp;lt;/embed&amp;gt;&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.vimeo.com/clip:169301&quot;&gt;re:publica powerpoint karaoke: Ã–kosystem Pansen&lt;/a&gt; on &lt;a href=&quot;http://www.vimeo.com/&quot;&gt;Vimeo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;embed src=&quot;http://www.vimeo.com/moogaloop.swf?clip_id=169306&quot; quality=&quot;best&quot; scale=&quot;exactfit&quot; width=&quot;400&quot; height=&quot;300&quot; type=&quot;application/x-shockwave-flash&quot; /&gt;
&lt;p&gt;&amp;lt;/embed&amp;gt;&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.vimeo.com/clip:169306&quot;&gt;republica powerpoint karaoke: dorst&lt;/a&gt; on &lt;a href=&quot;http://www.vimeo.com/&quot;&gt;Vimeo&lt;/a&gt;&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/04/republica-guten-morgen/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/04/republica-guten-morgen/"/>
    <title>re:publica, Guten Morgen im Metaverse</title>
    <updated>2007-04-13T00:00:00+00:00</updated>
    <author>
      <name>thilo</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Wir sind wieder am Start. Jetzt gleich starten wir mit einen Talk zum Thema, &lt;a href=&quot;http://programm.re-publica.de/programm/events/37.de.html&quot;&gt;Metaversen&lt;/a&gt; (Kurzerklärung: Immersive 3D Welten wie z.B. &lt;a href=&quot;http://secondlife.com/&quot;&gt;Second Life&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Der Vortrag startet mit einem Screenshott des ersten grafischen Metaversum: &lt;a href=&quot;http://www.dsgames.net/qlink/habitat/pictures1.htm&quot;&gt;Habitat&lt;/a&gt;(Lucas Filmgames). Dies soll zeigen das Metaversen ihren Ursprung in Computerspielen hat. Denn werden die in metaversen verwendeten Begriffe erklärt, wie, woher der Begriff Avatar kommt. Die Geschichte der Metaversen geht wie gesagt bei den Spielen los, &lt;a href=&quot;http://de.wikipedia.org/wiki/Multi_User_Dungeon&quot;&gt;M.U.D.&lt;/a&gt; war der erste Schritt in diese Richtung, dann folgte ein weiterer Meilenstein mit TinyMUD. Dann schön anschaulich, ein noch laufendes  MUD (&lt;a href=&quot;http://www.aardwolf.com/&quot;&gt;Aardwolf&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Der nächste Schritt war Habitat. 3D zog 1995 mit Worlds Chat ein.  Es folgt eine kleine Zeitreise in &lt;a href=&quot;http://www.activeworlds.com/&quot;&gt;Active Worlds&lt;/a&gt; an einen Ort den es vor 12 Jahren gab und dann wie diese Welt sich bis heute verändert hat. (Sehr nett.)&lt;/p&gt;

&lt;p&gt;Der grosse Komerz (neben Second Life) folgt mit &lt;a href=&quot;http://www.entropiauniverse.com/&quot;&gt;Entropia Universe&lt;/a&gt;, ein Spiel mit einem Ausgefeilten Wirtschaftssystem. Ohne Kreditkarte kommt man hier nicht weit. Waffen kosten Geld und verfallen. Kosten 0,5 - 1,5 $ pro Stunde. Leider gibts Problem beim Anmelden für eine Live Demo. Das provozierte gleich den Kommentar aus dem Publikum: “Mit &lt;a href=&quot;http://openid.net/&quot;&gt;Open ID&lt;/a&gt; wäre das nicht passiert”. Es wird auf das Problem der Geldwäschemöglichkeiten bei SecondLife eingegangen, Entropia steuert dem mit einem bankähnlichem Cash Card System entgegen, um mehr Tranzparens zu schaffen.&lt;/p&gt;

&lt;p&gt;Die Diskusion verlagtert sich auf Probleme des Eigentums und wie lassen sich die online Besitztümer sichern? Und eine viel grundlegende Frage wurde eingeworfen, was hat man von virtuellen Besitz. Denn gibts ein interessanten Einblick in die Hochzeitsplanner Industrie in Second Life. Diese Industrie ist recht beliebt, da niemand den Gestalltern reinredet und sämtliche Rechte an ihren Werken halten. Die Meinung des Vortragenden ist, dass alles was von Firmen in Second Life betrieben wird &lt;a href=&quot;http://reality.org/2006/11/29/second-life-incredible-innovator-but-probably-not-sustainable/&quot;&gt;nicht nachhaltig&lt;/a&gt; ist, sondern nur zur Erprobung neuer Marketingstrategien dient. Die Kosten für die eigene Online Welt können so gespart werden.&lt;/p&gt;

&lt;p&gt;Dann machen wir den letzten Schritt und schauen in die Zukunft. Hier für steht &lt;a href=&quot;http://www.opencroquet.org/&quot;&gt;Croquet&lt;/a&gt; ein offenes und dezentralles Mehrweltensystem. Wir sehen Nutzungsbeispiele für Bildung, Unterhaltung und auch Militär. Am Ende fällt noch der Begriff Web 3.0, das Web als immersives 3D Erlebniss. Ein interessanter Blog zum Thema Metaversen gibts von &lt;a href=&quot;http://notizen.typepad.com/aus_der_provinz/&quot;&gt;Markus Breuer&lt;/a&gt;. Ein neues Metaverse mit dem Namen &lt;a href=&quot;http://www.smeet.com/&quot;&gt;Smeet&lt;/a&gt; mit Schwerpunkt auf Kommunikation wird noch aus dem Publikum eingeworfen.&lt;/p&gt;

&lt;p&gt;Jetzt noch schnell dass Posting überarbeiten, dann gehts weiter.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/04/republica-wir-sind-auch-schon-da/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/04/republica-wir-sind-auch-schon-da/"/>
    <title>re:publica, wir sind auch schon da</title>
    <updated>2007-04-11T00:00:00+00:00</updated>
    <author>
      <name>thilo</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;So, mittlerweile sind wir auch auf der &lt;a href=&quot;http://re-publica.de/&quot;&gt;re:publica&lt;/a&gt; eingetroffen. Da jetzt eine kleine Pause entstanden ist, nutze ich diese gleich einmal. Es sind doch viele bekannt Gesichter vom &lt;a href=&quot;http://barcampberlin.pbwiki.com/&quot;&gt;barcamp 06&lt;/a&gt; zu sehen. Aber mehr Frauen.&lt;/p&gt;

&lt;p&gt;Der gerade gehörte Workshop zum Thema &lt;a href=&quot;http://programm.re-publica.de/programm/events/25.de.html&quot;&gt;Interaction Design Patterns for Communities&lt;/a&gt;, war dann doch etwas unkonkreter als ich gehofft habe. Aber immerhin hat man jetzt einige Anhaltspunkt zum nachsurfen (&lt;a href=&quot;http://developer.yahoo.com/ypatterns/&quot;&gt;hier&lt;/a&gt;, &lt;a href=&quot;http://designinginterfaces.com/&quot;&gt;hier&lt;/a&gt; und &lt;a href=&quot;http://www.welie.com/patterns/&quot;&gt;hier&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Noch ein kleiner Wermutstropfen. Den Talk zu &lt;a href=&quot;http://programm.re-publica.de/programm/events/46.de.html&quot;&gt;Blogmythen&lt;/a&gt; hab ich leider verpasst. Der war schon heute Vormittag. Aber genug andere werden wohl die Nachricht in die Welt hinaus bloggen: “Blogger nicht gleich Nerds, Blogs sind relevant und Blogs sind keine Gegenöffentlichkeit”&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/04/republica-wir-sind-auch-generation-mash-up/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/04/republica-wir-sind-auch-generation-mash-up/"/>
    <title>re:publica (wir sind auch) generation mash-up*</title>
    <updated>2007-04-11T00:00:00+00:00</updated>
    <author>
      <name>thilo</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;So mal ein kleiner Live-Bog-Versuch (mit Nachbearbeitung).
Der Talk zum Thema&lt;a href=&quot;http://programm.re-publica.de/programm/events/7.de.html&quot;&gt; Mash-Ups&lt;/a&gt; mit &lt;a href=&quot;http://tim.geekheim.de/2007/04/10/tim-auf-der-republica/&quot;&gt;Tim&lt;/a&gt;: Moderation, &lt;a href=&quot;http://programm.re-publica.de/programm/speakers/11.de.html&quot;&gt;Goerg&lt;/a&gt;: Startup, &lt;a href=&quot;http://programm.re-publica.de/programm/speakers/45.de.html&quot;&gt;Bernhard&lt;/a&gt;: Free Software, &lt;a href=&quot;http://programm.re-publica.de/programm/speakers/94.de.html&quot;&gt;Joachim&lt;/a&gt; als Google.
Erstmal die Anfangsfragen: Wer kennt/nutzt Mash-Ups. Die meisten wissen hier, &lt;a href=&quot;http://de.wikipedia.org/wiki/Mashup_%28Internet%29&quot;&gt;was Mash-Ups sind&lt;/a&gt;, aber weniger als die Hälfte wollen und haben schon Mash-Ups gemacht.&lt;/p&gt;

&lt;p&gt;Georg erzählt, was Mash-Ups für ihn sind und bedeuten. Tim meint, die Blogosphähre ist ein Mash-Up (Interessante These). Google &lt;a href=&quot;http://code.google.com/&quot;&gt;entwickelt die  APIs&lt;/a&gt;, um seine  Softwareentwicklung in den Griff zu bekommen. [Dann etwas Microsoft-Vista-Bashing: Softwaretechnik von vor 20 Jahren].&lt;/p&gt;

&lt;p&gt;Tim fragt: Warum macht Google das Material frei?  Bernhard sieht Mash-Ups als Inhalte und Arbeitserleichterungen und aber auch als Bindung/Beeinflussung der Nutzer an die Inhalteanbieter. [Es folgt eine Diskussion darüber, ob &lt;a href=&quot;http://www.heise.de/newsticker/meldung/87779&quot;&gt;Kathrina-Aufnahmen von Google zensiert wurden &lt;/a&gt;(Wenn doch die Welt nur so einfach wäre, wie bei dieser Diskussion dargestellt)].&lt;/p&gt;

&lt;p&gt;Jetzt mal wieder Fakten von Georg. Es werden erstmal die Anbieter, die da sind, für zusätzliche Dienste genutzt, z.B. Buchsuche über die &lt;a href=&quot;http://aws.amazon.com/&quot;&gt;Amazon API&lt;/a&gt;. &lt;a href=&quot;http://flickr.com&quot;&gt;Flickr&lt;/a&gt; wurde durch offene APIs lebendig. Auf jeden Fall bieten APIs Mehrwert für eine Plattform. Einwurf von Tim: Nicht mehr hacken der Communities sondern einfach APIs nutzen.&lt;/p&gt;

&lt;p&gt;“Was hat die Freie-Software-Szene von Mash-Ups?” geht als Frage an Bernhard. Fazit: Kommerzielle Interessen laufen den freien Inhalten entgegen. Oder umgekehrt, z.B. genetischer Code ist frei, wird aber kommerziell besser verwertet. Google sagt dazu: Wir sind keine Pfadfinder. Nichts ist frei: Nutzer zahlt nicht, sondern durch Werbung wird bezahlt. [Interessant: So im Beispiel genant. Werbung in den Karten durch Pins.] Georg meint dazu: APIs sind auch Strategien zur Markenbildung. Bernd wird ideologisch: Er bezahlt indirekt mehr, wenn Google frei ist. [Nette Ablenkung durch die SMS-Blasen auf der Leinwand] Joachim/Google glaubt, dass Mash-Ups den Desktop ersetzen werden (Wollen wir das?).  Zur Frage von Vertrauen kommt eine Analogie zu Bank, die unser Geld verwaltet. Tim kennt Leute, die Gold kaufen und lagern (das glaube ich).&lt;/p&gt;

&lt;p&gt;[Jemand neben mir holt die Diskussion zu den Faken zurück, mit einer Frage an Georg]. Welche APIs muss man kennen und welche werden kommen? Wichtig ist ihm die &lt;a href=&quot;http://aws.amazon.com/s3&quot;&gt;S3 API&lt;/a&gt;, um IT auszulagern. Allgemein ermöglichen APIs, dass andere die Nutzungsmöglichkeiten einer Anwendung erweitern.  Es fehlt ihm noch viel Funktionalität für’s Handy. Tim gibt zu Bedenken, dass die Abhängigkeiten durch andere Services zunimmt. Dadurch stellt sich in einer kleinen Diskussion heraus, dass durch &lt;a href=&quot;http://de.wikipedia.org/wiki/Service-Level-Agreement&quot;&gt;Service-Level-Agreements&lt;/a&gt; Geld verdient werden will.&lt;/p&gt;

&lt;p&gt;[Aus dem Publikum, zurück zur Ideologie]. Banken werden durch öffentliche Behörden überwacht und kontrolliert, Google aber nicht. [Dann wieder zurück zu den Fakten.] Google möchte immer vorne dabei sein, aber de facto Standards bleiben. [Es folgte eine Interessante Formulierung:] Alte Google Web API wird eingestellt, da nicht so genutzt, wie Google sich das gedacht hat. Es folgen Details zum Umgang mit den API Keys. Lizenzgeber wollen wissen, wo das Kartenmaterial genutzt wird, darum Google Maps nicht in Intranets verwendbar. Das Statement ändert sich in: Kartenmaterial darf nicht anonym rausgegeben werden.&lt;/p&gt;

&lt;p&gt;Dann stellt Bernhard einige Fragen, um etwas vom Google-Thema wegzukommen: Wie einfach kann man denn nun real zwischen APIs wechseln? Was sind gute Mash-Ups? Wie werden die Mash-Ups finanziert? Lassen sich diese Standardisieren? Georg differenziert zwischen APIs und Formaten, die diese verwenden. Es driftet doch wieder zu Google. Google möchte transportierbaren “elektronischen Schatten” mal anbieten, (nach eigenen Regeln natürlich). Blick in die Zukunft von Google. Programmiersprache für das Web, um Daten und Mash-Ups selbst zu bewegen.&lt;/p&gt;

&lt;p&gt;Dann sind die 90 Minuten um. Letzte Worte. Georg freut sich auf &lt;a href=&quot;http://openid.net/&quot;&gt;Open ID&lt;/a&gt; etc. Bernd möchte Antworten. Joachim promoted die bereits ausgebuchten &lt;a href=&quot;http://www.google.com/events/developerday/de/details.html&quot;&gt;Google Developer Days&lt;/a&gt; (Gut das wir heute früh schon gebucht haben).&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/04/republica-interaction-design-patterns/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/04/republica-interaction-design-patterns/"/>
    <title>re:publica: Interaction Design Patterns</title>
    <updated>2007-04-11T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Nachdem Thilo sich gleich negativ zum dem Vortrag über &lt;a href=&quot;http://programm.re-publica.de/programm/events/25.de.html&quot;&gt;Interaction Design Patterns&lt;/a&gt; (Folien sowie weiteres sollen demnächst online sein) &lt;a href=&quot;http://upstream-berlin.com/blog/2007/04/11/republica-wir-sind-auch-schon-da/&quot;&gt;geäußert&lt;/a&gt; hat, will ich nochmal kurz meinen Senf dazugeben. (&lt;a href=&quot;http://en.wikipedia.org/wiki/Interaction_design&quot;&gt;Was ist eigentlich Interaction Design?&lt;/a&gt; - ) Ja, es ging sehr viel um Grundlagen und sehr wenig um praktische Beispiele oder konkrete Tips, aber da ich noch nie davon gehört (oder es wieder vergessen) habe (wer weiß das schon so genau? :)), war ich mit den gegebenen Anstößen eigentlich schon ganz zufrieden.&lt;/p&gt;

&lt;p&gt;Nochmal kurz zum Inhalt: Die Idee der Interaction Design Patterns wird, wie auch bei den &lt;a href=&quot;http://martinfowler.com/eaaCatalog/&quot;&gt;Programmier-Patterns&lt;/a&gt; auf die Architekten zurückgeführt, die sich schon Ende der 1960er damit beschäftigt haben. Der Grundegedanke ist, für immer wieder auftretende Probleme eine Lösung zu finden, die sich in der Anwendung bewährt hat, und diese zu dokumentieren. Trifft nun jemand auf so ein Problem, muss er theoretisch nur in einer Art Pattern-Library das passende Pattern finden und anwenden. Theoretisch deshalb, weil es zum einen gerade bei den Interaction Designern noch keine richtig umfangreichen Bibliotheken gibt, zum anderen, weil diese Pattern sehr abstrakt sind und erst auf die eigene Lösung angepasst werden müssen.&lt;/p&gt;

&lt;p&gt;Ein Beispiel war, dass man, um die Registrierung/Login von Usern einer Community zu ermöglichen, einen Registrierungs-Link möglichst direkt unter den Login-Button setzt; ein anderes, dass man, will man Content von Users bewerten lassen, dazu das von Amazon bekannte 5-Sterne System verwenden solle.&lt;/p&gt;

&lt;p&gt;Im Vortrag wurden 3 Links zu bestehenden Pattern-Libraries genannt. Gemerkt habe ich mir nur die &lt;a href=&quot;http://developer.yahoo.com/ypatterns/&quot;&gt;Yahoo-Library&lt;/a&gt;. Die anderen sollten dann hoffentlich bald in den Folien zu finden sein. (Thilo hat sie, sehe ich gerade.)&lt;/p&gt;

&lt;p&gt;Nach dem Vortrag hat mir &lt;a href=&quot;http://www.colinschlueter.com/&quot;&gt;Colin&lt;/a&gt; noch eine &lt;a href=&quot;http://flickr.com/photos/factoryjoe/collections/72157600001823120/&quot;&gt;Flickr-Collection&lt;/a&gt; anvertraut: dort legt ein gewisser factoryjoe jedes mal, wenn er ein Problem besonders gut gelöst sieht, einen Screenshot davon ab. So eine Art visuelle Inspirations-Library also. Sehr schick.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/04/es-wird-mit-gelauscht/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/04/es-wird-mit-gelauscht/"/>
    <title>Es wird mitgelauscht.</title>
    <updated>2007-04-11T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Gerade gabs &lt;a href=&quot;http://programm.re-publica.de/programm/events/7.de.html&quot;&gt; hier&lt;/a&gt; ein kleinen Hinweis von Tim, von wegen mitlesen von POP3-Passwörtern und Logins. Mal schauen, ob ich auch Mail von mir selbst bekomme.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/04/we-are-hiring/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/04/we-are-hiring/"/>
    <title>We are hiring</title>
    <updated>2007-04-05T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Wir suchen übrigens noch Verstärkung für’s &lt;a href=&quot;http://autoki.com&quot;&gt;autoki&lt;/a&gt;-Team:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Zwei Rails-Entwickler, Freelancer oder Angestellte, Teil- oder Vollzeit. Pflicht: Kenntnisse in Rails und verwandten Technologien (SQL, Javascript, HTML usw.), wohnen in Berlin, Lust auf TDD/XP. Kür: Erfahrungen mit TDD, XP, umfangreiche Rails-Kenntnisse, Javascript-Guru, Linux, OSX&lt;/li&gt;
  &lt;li&gt;Einen Produktmanager. Pflicht: AuskennerIn im Web&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Wir bieten: Die ultimative Erfahrung beim Aufbau einer WebZwoNull-Seite von (fast) ganz am Anfang bis zur Skalierung auf Millionen von Userm; Fame, Geld, ein tolles Team, Selbstbestimmung, ein Buero an der Sonne uvm.&lt;/p&gt;

&lt;p&gt;Bei Interesse: Mail an kontakt(at)autoki(punkt)com.&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/04/autoki-launch/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/04/autoki-launch/"/>
    <title>autoki launch</title>
    <updated>2007-04-04T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Nach einigen Monaten Entwicklungszeit ist &lt;a href=&quot;http://autoki.com&quot;&gt;Autoki&lt;/a&gt; nun offiziell online gegangen. &lt;a href=&quot;http://blog.autoki.com/?p=3&quot;&gt;dazu Ami&lt;/a&gt; im nagelneuen &lt;a href=&quot;http://blog.autoki.com&quot;&gt;Autoki-Blog&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;&quot;autoki ist der Treffpunkt für Autos mit Geschichte. Eine Plattform, bei der man interessante Autos vorstellen, anschauen, diskutieren und bewerten kann. Ein weiteres Anliegen ist es, dass wir Autobesitzer vernetzen wollen und wirklich Wert auf Autos mit Geschichte legen&quot;&lt;/blockquote&gt;

&lt;p&gt;Na denn, wir sehen uns beim Autoquartett.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/04/mingle-das-agile-projektmanagement-tool-auf-das-wir-alle-seit-jahren-gewartet-haben/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/04/mingle-das-agile-projektmanagement-tool-auf-das-wir-alle-seit-jahren-gewartet-haben/"/>
    <title>Mingle: Das agile Projektmanagement-Tool, auf das wir alle seit Jahren gewartet haben?</title>
    <updated>2007-04-01T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Ich benutze seit Jahren &lt;a href=&quot;http://trac.edgewall.com&quot;&gt;Trac&lt;/a&gt; für Projektverwaltung und Dokumentation. Bugs und Requirements zu den Tickets, Milestones anlegen, ein paar custom Reports, alles andere ins Wiki. Alles ganz schön und gut, aber das Gefühl, dass da noch mehr geht, wollte nie so richtig weichen.&lt;/p&gt;

&lt;p&gt;Ein Tool, das agile Prozesse direkt unterstützt oder gar .. quasi einfordert. Das mir all die Zahlen liefert, die ich schon immer haben wollte. Ein Tool mit eingebauter &lt;a href=&quot;http://en.wikipedia.org/wiki/Extreme_Programming_Practices#Planning_game&quot;&gt;Planning Game&lt;/a&gt;-Unterstützung. Was auch immer.&lt;/p&gt;

&lt;p&gt;Und nun kündigen ausgerechnet &lt;a href=&quot;http://thoughtworks.com&quot;&gt;Thoughtworks&lt;/a&gt;, die Altmeister des Agilen, so ein Tool an. Und es soll &lt;a href=&quot;http://studios.thoughtworks.com/mingle-project-intelligence&quot;&gt;Mingle&lt;/a&gt; heissen. Außer Werbegefasel gibt’s noch nix zu sehen, aber ich bin hochgradig gespannt.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/04/kommt-mitde-das-video/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/04/kommt-mitde-das-video/"/>
    <title>kommt-mit.de - das video</title>
    <updated>2007-04-01T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Endlich fertig, der erste Screencast zu &lt;a href=&quot;http://kommt-mit.de&quot;&gt;kommt-mit.de&lt;/a&gt; - unserer letzte Woche in public beta gegangen Reisecommunity. Das Video zeigt die ersten Schritte: Anmelden, eigene Reise anlegen, Freunde einladen, Organisatorisches und noch einiges mehr… viel Spass.&lt;/p&gt;

&lt;embed src=&quot;http://www.vimeo.com/moogaloop.swf?clip_id=163065&quot; quality=&quot;best&quot; scale=&quot;exactfit&quot; width=&quot;400&quot; height=&quot;385&quot; type=&quot;application/x-shockwave-flash&quot; /&gt;
&lt;p&gt;&amp;lt;/embed&amp;gt;&lt;/p&gt;

&lt;p&gt;(zum Video im eigenen Blog etc. einbetten: &lt;code&gt;&amp;lt;embed src=&quot;http://www.vimeo.com/moogaloop.swf?clip_id=163065&quot; quality=&quot;best&quot; scale=&quot;exactfit&quot; width=&quot;400&quot; height=&quot;385&quot; type=&quot;application/x-shockwave-flash&quot;&amp;gt;&amp;lt;/embed&amp;gt;&lt;/code&gt;)&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/03/endlich-wieder-grune-unit-tests/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/03/endlich-wieder-grune-unit-tests/"/>
    <title>Endlich wieder grüne Unit Tests (updated)</title>
    <updated>2007-03-31T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;&lt;img src=&quot;/uploads/2007/03/colored.png&quot; alt=&quot;colored.png&quot; /&gt;&lt;br /&gt;
Ich bin in den vergangen Tagen über mehrere Blogeinträge gestolpert, die sich mit dem Thema Farben in Unit Tests (also, bei deren Ausführung) beschäftigt haben. &lt;a href=&quot;http://on-ruby.blogspot.com/2006/05/red-and-green-for-ruby.html&quot;&gt;RedGreen&lt;/a&gt; ist etwas unelegant, da man statt einfach &lt;code&gt;rake&lt;/code&gt; einzutippen &lt;code&gt;rake | redgreen&lt;/code&gt; schreiben muss.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.nkryptic.com/2006/10/26/rails-colorized-rake-testing&quot;&gt;Mit diesem kleinen Patch&lt;/a&gt; und dem &lt;code style=&quot;color:green&quot;&gt;colored&lt;/code&gt; Gem geht dann doch alles vollautomatisch. Toll.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt;: gerade ist mir aufgefallen, dass die Tests, wenn man sie innerhalb von &lt;a href=&quot;http://macromates.com/&quot;&gt;TextMate&lt;/a&gt; ausführt, recht unansehnlich werden, da TextMate die entsprechenden Codes plain ausgibt, statt die Schrift farbig werden zu lassen. Bisher hatte ich oben genannten Patch in eine Datei &lt;em&gt;colorized_tests.rb&lt;/em&gt; gepackt und diese mittels &lt;code&gt;require File.expand_path(File.dirname(__FILE__) + '/colorized_tests')&lt;/code&gt;am Ende von &lt;em&gt;test_helper.rb&lt;/em&gt; eingebunden. Um die Farbcodes in TextMate abzustellen, habe ich noch ein &lt;code&gt;unless ENV[&quot;TM_PROJECT_DIRECTORY&quot;]&lt;/code&gt; dahintergehangen. Jetzt sehen die in TextMate ausgeführten Tests wieder wie gewohnt aus, und bei &lt;code&gt;rake&lt;/code&gt; gibt’s trotzdem Farben. Cool.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/03/wir-machen-unseren-tests-beine/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/03/wir-machen-unseren-tests-beine/"/>
    <title>Wir machen unseren Tests Beine</title>
    <updated>2007-03-30T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Da unsere vielen Tests mittlerweile ein wenig lange brauchen, haben wir uns am heutigen Forschungstag mal wieder mit dem Problem Testing auseinandergesetzt. Das Ergebniss diesmal schon vorne weg: unsere erfolgreichste Entdeckung ist &lt;a href=&quot;http://mocha.rubyforge.org/&quot;&gt;Mocha&lt;/a&gt;.
Mit diesem Framework lassen sich &lt;a href=&quot;http://blog.floehopper.org/articles/2006/09/11/the-difference-between-mocks-and-stubs&quot;&gt;Stubs und Mocks&lt;/a&gt; mit realen Objekten verwenden. Es folgt ein kleiner Auszug aus unserem bisherigen Testcode.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;create_activated_user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;attributes&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{})&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;email&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;generate_email_address&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:gender&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'m'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:first_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'joe'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:last_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'doe'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:email&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;ss&quot;&gt;:password&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'test'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:password_confirmation&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'test'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:city&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;berlin&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'active'&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;merge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;attributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;create_auto&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attributes&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{})&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;create_user&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Auto&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;auto_hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;merge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;attributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test_activity_returns35_for_user_with_picture_and_auto_with_picture&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;create_activated_user&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:photo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'1.gif'&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;create_auto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:photo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'2.gif'&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;assert_equal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;35&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;activity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Dauer: 795ms&lt;/p&gt;

&lt;p&gt;Nach kurzem Studium des &lt;a href=&quot;http://cheat.errtheblog.com/s/mocha/&quot;&gt;Mocha Cheat Sheets&lt;/a&gt; haben wir dann den Test wie folgt geändert.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test_activity_return35_for_user_with_picture_and_auto_with_picture&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;with_photos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stub&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:with_photos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Auto&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:photo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'test.gif'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:photo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'1.gif'&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;stubs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:autos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;returns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;with_photos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;assert_equal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;35&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;activity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Dauer: 49ms&lt;/p&gt;

&lt;p&gt;Na wenn das keine signifikate Verbesserung ist. Auf Mocha gestoßen sind wir über den &lt;a href=&quot;http://blog.testingrails.com/2006/11/19/super-simple-stubs&quot;&gt;Testing Rails Blog&lt;/a&gt;, auf dem leider nichts Neues mehr zu passieren scheint. Eine kleine Übersicht von Stub-Frameworks mit problemspezifischen Einschätzungen fanden wir auf dem &lt;a href=&quot;http://blog.jayfields.com/2006/09/ruby-stub-variations.html&quot;&gt;Blog von Jay Fields&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Wir werden Mocha in der nächsten Zeit in unseren Test integrieren, und bei der 2000ten Revision sagen wir euch wie lange unsere Test Suite läuft.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/03/1000-commit/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/03/1000-commit/"/>
    <title>1000. commit!</title>
    <updated>2007-03-30T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p class=&quot;content_image&quot;&gt;&lt;img src=&quot;/uploads/2007/03/picture-1.jpg&quot; alt=&quot;picture-1.jpg&quot; height=&quot;230&quot; width=&quot;339&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Wir haben wieder einen &lt;a href=&quot;http://upstream-berlin.com/blog/2007/03/27/launch-kommt-mitde-die-reisecommunity/&quot;&gt;Grund zu Feiern&lt;/a&gt;. Die 1000ste Revision. Hier mal ein paar Zahlen über dieses Jubiläum. Man beachte die &lt;code&gt;Code to Test&lt;/code&gt; Rate :)
&lt;br style=&quot;clear: both&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;
+----------------------+-------+-------+---------+---------+-----+-------+
| Name                 | Lines |   LOC | Classes | Methods | M/C | LOC/M |
+----------------------+-------+-------+---------+---------+-----+-------+
| Controllers          |  1588 |  1336 |      30 |     175 |   5 |     5 |
| Helpers              |  1335 |  1146 |       3 |      97 |  32 |     9 |
| Models               |  2566 |  2068 |      40 |     258 |   6 |     6 |
| Libraries            |  1693 |  1416 |      10 |      61 |   6 |    21 |
| Components           |     0 |     0 |       0 |       0 |   0 |     0 |
| Integration tests    |   179 |   157 |       2 |      16 |   8 |     7 |
| Functional tests     |  3496 |  3034 |      61 |     389 |   6 |     5 |
| Unit tests           |  8551 |  6355 |      43 |     448 |  10 |    12 |
+----------------------+-------+-------+---------+---------+-----+-------+
| Total                | 19408 | 15512 |     189 |    1444 |   7 |     8 |
+----------------------+-------+-------+---------+---------+-----+-------+
Code LOC: 5966     Test LOC: 9546     Code to Test Ratio: 1:1.6&lt;/pre&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/03/reisen-von-fruher/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/03/reisen-von-fruher/"/>
    <title>kommt-mit: Reisen von früher</title>
    <updated>2007-03-29T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Auf vielfachen Wunsch kann man ab sofort bei &lt;a href=&quot;http://kommt-mit.de&quot;&gt;kommt-mit&lt;/a&gt; das Reisedatum auch bis zu 3 Jahre in der Vergangenheit angeben - endlich können also auch die schönsten Reisen von früher mit korrektem Datum versehen werden.&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/03/rails-hat-einen-song/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/03/rails-hat-einen-song/"/>
    <title>rails hat einen song</title>
    <updated>2007-03-28T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;ganz schoen nerdig, aber naja, trotzdem nett. hier isser:&lt;/p&gt;

&lt;p&gt;[audio:http://media.libsyn.com/media/coderpath/railin_away.mp3]&lt;/p&gt;

&lt;p&gt;(via &lt;a href=&quot;http://coderpath.com/index.php?post_id=160265&quot;&gt;CoderPath&lt;/a&gt;)&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/03/launch-kommt-mitde-die-reisecommunity/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/03/launch-kommt-mitde-die-reisecommunity/"/>
    <title>Launch: kommt-mit.de - Die Reisecommunity</title>
    <updated>2007-03-27T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p class=&quot;content_image&quot;&gt;&lt;a href=&quot;http://kommt-mit.de&quot;&gt;&lt;img src=&quot;/uploads/2007/03/picture-1.png&quot; alt=&quot;kommtmit screenshot klein&quot; /&gt;&lt;/a&gt;

Endlich ist es so weit - nach nunmehr 3 Monaten Entwicklungszeit geht kommt-mit.de online. KommtMit ist die erste deutsche web 2.0-kompatible Community für Reisen und Ausflüge.

Plant vom nächsten Strandurlaub über die Fahrradtour, die Klassenfahrt, den Traumurlaub irgendwann mal, die Dienstreise, das Gruppenseminar bis hin zur  Weltumrundung alle eure Reisen.

Auf KommMit könnt ihr:
&lt;ul&gt;
	&lt;li&gt;alle Freunde einfach zu euren Reisen einladen&lt;/li&gt;
	&lt;li&gt;mit der Aufgabenliste im Voraus klären, wer denn nun den Dosenöffner nicht vergessen darf oder wer für's Picknick zuständig ist&lt;/li&gt;
	&lt;li&gt;auf der Pinnwand den Abfahrtstermin diskutieren&lt;/li&gt;
	&lt;li&gt;im Kochbuch Rezepte heraussuchen und euch eine Einkaufsliste zusammenstellen lassen&lt;/li&gt;
	&lt;li&gt;ein Reisetagebuch führen: für die zu-Hause-Gebliebenen und die Erinnerung&lt;/li&gt;
	&lt;li&gt;Fotos hochladen (und sogar angucken ;) )&lt;/li&gt;
	&lt;li&gt;das Reiseziel anhand von Satellitenbildern und Fotos erkunden&lt;/li&gt;
&lt;/ul&gt;
.. und noch so einiges mehr. Wir freuen uns über jeden Besucher und auch über Feedback, denn noch ist die Seite im Betatest. Neuigkeiten ab sofort dann immer hier im Blog.
&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/03/rails-fragment-caching-testing-and-time-based-expiry/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/03/rails-fragment-caching-testing-and-time-based-expiry/"/>
    <title>Rails Fragment Caching - Testing and time based expiry</title>
    <updated>2007-03-23T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;in the last days i started implementing caching for &lt;a href=&quot;http://autoki.com&quot;&gt;autoki.com&lt;/a&gt;. my first stop was this excellent rails caching tutorial over at &lt;a href=&quot;http://railsenvy.com&quot;&gt;railsenvy.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;basically, rails offers 3 ways of caching page content:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;page caching: an entire page gets stored on the hard disk and can then be served by apache instead of rails - very fast but almost useless for us, as every page has some dynamic element in this. we still consider it for some ajax calls.&lt;/li&gt;
	&lt;li&gt;action caching: also caches the entire page, but it's still served by rails, which means before_filters for stuff like authentication still work - still not for us, see above&lt;/li&gt;
	&lt;li&gt;fragment caching: as the name implies caches fragments of a page - yay, sounds good&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;fragment caching&lt;/h2&gt;
&lt;p&gt;the basics are really easy. first, you surround the parts of the page that you want to cache with a &lt;code&gt;&amp;lt;% cache do %&amp;gt; ... &amp;lt;% end %&amp;gt;&lt;/code&gt;. this writes the fragment to a file in /tmp/caches and from now on, rails serves this part of the page from the cache.&lt;/p&gt;

&lt;p&gt;this alone doesn’t get you any performance improvements yet, because your controller is still loading all the data from the database before rendering the view. to avoid this, you wrap your data loading code into &lt;code&gt;unless read_fragment 'name' ... end&lt;/code&gt; blocks - now you page should be running lightning fast already.&lt;/p&gt;

&lt;p&gt;the third problem to tackle is to clean the cache at the right time. this can either be done in the controllers by calling &lt;code&gt;expire_fragment 'name'&lt;/code&gt; or by using so called &lt;em&gt;sweeper&lt;/em&gt;s. they are basically observe your models and clean the right fragments on events like &lt;code&gt;after_create&lt;/code&gt; - so when you add a new user to the system, you can clean the list of users from the cache.&lt;/p&gt;

&lt;h2&gt;time based expiry&lt;/h2&gt;
&lt;p&gt;sometimes, it’s not very efficient to clean the cache every time the underlying data changes. we have some site wide statistics for example, that require a load of processing power to calculate, and it’s enough if they get recalculated, say, every hour. rails doesn’t come with time bases cache expiry, but luckily, the &lt;a href=&quot;http://livsey.org/2006/8/25/timed_fragment_cache-plugin-fragment-caching-with-time-based-expiry&quot;&gt;timed_fragment_cache plugin&lt;/a&gt; comes to the rescue. it allows you to add an expiry time to your &lt;code&gt;cache&lt;/code&gt; blocks in the erb templates and adds another method &lt;code&gt;when_fragment_expired&lt;/code&gt; to you controllers, which allows you to test if a fragment has expired before running your data loading code.&lt;/p&gt;
&lt;h2&gt;testing it&lt;/h2&gt;
&lt;p&gt;we have some fairly complex pages which results into a multitude of cache blocks and sweeper calls on many many models, so i wanted to make sure to get it all right - how else could i do this than by using &lt;a href=&quot;http://en.wikipedia.org/wiki/Test-first_development&quot;&gt;test first&lt;/a&gt;? rails doesn’t offer any support for testing its caching functionality so i had to use another plugin: the &lt;a href=&quot;http://blog.cosinux.org/pages/page-cache-test&quot;&gt;page cache test plugin&lt;/a&gt;. it basically offers two assertions: &lt;code&gt;assert_caches_fragments&lt;/code&gt; makes sure that i have inserted a &lt;code&gt;cache&lt;/code&gt; block in my template and that the fragment gets saved into the cache. &lt;code&gt;assert_expire_fragments&lt;/code&gt; makes sure that my fragment gets removed from the cache when i hit a certain action. that’s all nice and good but a few things were missing: first, i wanted to test that my cache logic in the controller was working (i.e. i wasn’t loading data when i didn’t have to) and second, i wanted to test my time based expiring fragments as well.&lt;/p&gt;
&lt;h3&gt;testing caching in the controllers&lt;/h3&gt;
&lt;p&gt;my controller test does two things: first it hits the page and checks if the action has loaded a certain &lt;code&gt;@variable&lt;/code&gt; - then it hits the same page again and checks that this time, the variable has not been loaded, i.e. the cache was hit instead. my custom assertion for that looks like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;def assert_caches_fragments(*fragments, &amp;amp;amp;block)
  yield
  fragments.each do |fragment|
    assert_not_nil(assigns(fragment[1]), &quot;Fragment '#{fragment[0]}': the variable @#{fragment[1]} doesn't get set at all&quot;)
  end
  yield
  fragments.each do |fragment|
    assert_nil(assigns(fragment[1]), &quot;Fragment '#{fragment[0]}' is not cached&quot;)
  end
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;now i can do this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;assert_caches_fragments ['/home/index/best_rated_auto', :best_rated_auto],
  ['/home/index/best_game_auto', :best_game_auto] do
    get '/'
  end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;this calls the url ‘/’ and checks that the variable &lt;code&gt;@best_rated_auto&lt;/code&gt; is filled with data. then it calls it again and makes sure rails now uses the ‘/home/index/best_rated_auto’ fragment instead. same for the second pair.&lt;/p&gt;

&lt;h3 id=&quot;testing-time-based-cache-expiry&quot;&gt;testing time based cache expiry&lt;/h3&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# time based fragment expiry cache test
# asserts that the given fragments are enclosed in when_fragment_expired calls
# fragments - 2 element arrays of [fragment_name, variable_name]
# variable_name - the name of @variable that gets set when the cache has expired
# &amp;amp;amp;block - the call that hits the action, e.g. get '/'

def assert_expires_by_time(*fragments, &amp;amp;amp;block)
  yield
  fragments.each do |fragment|
    assert_not_nil(assigns(fragment[1]), &quot;Fragment '#{fragment[0]}': the variable @#{fragment[1]} doesn't get set at all&quot;)
  end
  yield
  fragments.each do |fragment|
    assert_nil(assigns(fragment[1]), &quot;Fragment '#{fragment[0]}' is not cached at all&quot;)
    set_fragment_date fragment[0], 1.minute.ago
  end
  yield
  fragments.each do |fragment|
    assert_not_nil assigns(fragment[1]), &quot;Fragment '#{fragment[0]}' is not time cached&quot;
  end
end

def set_fragment_date(name, time)
  ActionController::Base.fragment_cache_store.write(name+'_meta', YAML.dump(time))
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;this code adds a bit more logic to the assertion in the last paragraph. after the second hit to the url it sets the fragment’s time so it is expired and hits the page a third time to check if the data comes from the database again.&lt;/p&gt;
&lt;h2&gt;plugin hacking&lt;/h2&gt;
&lt;p&gt;one last thing. in order not to fill my functional tests even more i decided to use integration tests for the cache testing. the fragment cache plugin has one limitation: in integration tests, it expects the fragment name to be a has, and that hash to contain a &lt;code&gt;:controller =&amp;gt; 'my_controller'&lt;/code&gt; pair. to work around this, i hacked the plugin the following way: in &lt;em&gt;fragment_cache_test.rb&lt;/em&gt; i changed the first line of the &lt;code&gt;check_options_has_controller&lt;/code&gt; method to this: &lt;code&gt;if option = options.detect { |option| option[:controller].nil? } &amp;amp;&amp;amp; !@controller&lt;/code&gt; - now i could keep my fragment names and still use integration tests. all i had to do was to define &lt;code&gt;@controller = MyController.new&lt;/code&gt; in my test.&lt;/p&gt;

&lt;p&gt;welcome to fully tested rails caching :) now comes all the boring stuff - implementing it everywhere.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/03/republica-anmeldung-ist-offen/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/03/republica-anmeldung-ist-offen/"/>
    <title>re:publica anmeldung ist offen</title>
    <updated>2007-03-11T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;schnell anmelden, unter https://ssl-id1.de/re-publica.de/akkreditierung/&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/03/rails-filecolumn-schneller-testen-durch-mocks/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/03/rails-filecolumn-schneller-testen-durch-mocks/"/>
    <title>Rails: FileColumn schneller testen durch Mocks</title>
    <updated>2007-03-11T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;&lt;a href=&quot;http://www.kanthak.net/opensource/file_column/&quot;&gt;FileColumn&lt;/a&gt; ist ein wirkliches zauberhaftes Plugin fuer &lt;a href=&quot;http://rubyonrails.org&quot;&gt;Ruby on Rails&lt;/a&gt;. All unsere Bilder-Uploads haben wir bisher damit umgesetzt. FileColumn bietet Helper, um Datei-Uploads zu bauen und kann die hochgeladenen Bilder natuerlich auch wieder anzeigen. Das eigentlich schoene ist aber die Integration mit &lt;a href=&quot;http://rmagick.rubyforge.org/&quot;&gt;RMagick&lt;/a&gt;, einem &lt;a href=&quot;http://ruby-lang.org&quot;&gt;Ruby&lt;/a&gt;-Binding fuer &lt;a href=&quot;http://www.imagemagick.org&quot;&gt;ImageMagick&lt;/a&gt;, wodurch sich Bilder on the fly skalieren sowie beschneiden lassen und die verschiedenen Versionen dann automatisch im Dateisystem landen. Alles, was man dazu braucht, ist eine Zeile Code im Model, z.B.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;User&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;file_column&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:photo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:magick&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:versions&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;ss&quot;&gt;:normal&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;238x&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;ss&quot;&gt;:tiny&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;26x35!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;ss&quot;&gt;:small&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:crop&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;1:1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:size&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;73x73!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;small&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Dieser Code erzeugt neben der Originalversion 3 Kopien: Eine 238 Pixel breite und entsprechend des Bildformats hohe, eine gestauchte, genau 23 x 35 Pixel grosse und eine genau 73 x 73 Pixel grosse, beschnittene Version.&lt;/p&gt;

&lt;p&gt;So weit alles schoen. Das einzige Problem war, dass unsere Unit-Tests durch FileColumn ganz schoen langsam wurden. Bei jedem Testdurchlauf, der irgendwas mit Bilder zu tun hatte, wurden jedes mal mehrere skalierte Versionen erzeugt, was natuerlich Rechenzeit kostete. Die Loesung haben wir letzte Woche dann endlich mal zusammengehackt, und sie sieht so aus: Ein Modul, was von FileColumn installierte Callbacks ausser Gefecht setzt, wird in ein &lt;a href=&quot;http://de.wikipedia.org/wiki/Mock-Objekt&quot;&gt;Mock&lt;/a&gt; des Models eingebunden, aber der Reihe nach. Hier das Modul, gelegen in &lt;em&gt;test/file_column_mock.rb&lt;/em&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;FileColumnMock&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;mock_file_column&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;attr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;define_method&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;attr&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;=&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;is_a?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;write_attribute&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;attr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;path&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;write_attribute&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;attr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;define_method&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;attr&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;_after_assign&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;define_method&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;attr&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;_after_save&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;define_method&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;attr&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;_state&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;no&quot;&gt;DummyState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:read_attribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;attr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;define_method&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;attr&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;_after_destroy&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DummyState&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@file_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file_name&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;has_magick_errors?&lt;/span&gt;
      &lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;absolute_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@file_name&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;relative_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subdir&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@file_name&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;temp_path&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@file_name&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;assign_temp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;temp_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;In unserem Beispiel wollen wir die Klasse User von ihrer FIleColumn-Last befreien. Dazu legen wir in &lt;em&gt;test/mocks/test&lt;/em&gt; eine Datei &lt;em&gt;user.rb&lt;/em&gt; an und fuellen sie mit folgendem Inhalt:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'models/user'&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dirname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;__FILE__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'/../../file_column_mock'&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;User&lt;/span&gt;
  &lt;span class=&quot;kp&quot;&gt;extend&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;FileColumnMock&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;mock_file_column&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:photo&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Der Effekt des ganzen ist, dass die Original User-Klasse aus &lt;em&gt;app/models&lt;/em&gt; nochmals geoeffnet und mehrere Methoden, die normalerweise automatisch beim Setzen des &lt;em&gt;photo&lt;/em&gt;-Attributs loslaufen, umd die skalierten Versionen zu erzeugen, durch Ueberschreiben ausser Gefecht gesetzt werden.&lt;/p&gt;

&lt;p&gt;Das war’s. Und schon fliegen die Tests wieder mit voller Geschwindigkeit.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/03/forschungsergebnisse-besser-testen/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/03/forschungsergebnisse-besser-testen/"/>
    <title>Forschungsergebnisse: Besser testen</title>
    <updated>2007-03-11T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;&lt;img src=&quot;http://farm1.static.flickr.com/146/404548732_bb818b6c35.jpg&quot; title=&quot;from flickr by frogmuseum2&quot; alt=&quot;from flickr by frogmuseum2&quot; align=&quot;left&quot; border=&quot;1&quot; height=&quot;250&quot; hspace=&quot;10&quot; vspace=&quot;10&quot; width=&quot;250&quot; /&gt;Da wir uns diese Woche etwas um die &lt;a href=&quot;http://upstream-berlin.com/blog/2007/03/11/rails-filecolumn-schneller-testen-durch-mocks/&quot;&gt;Laufzeit unsere Tests gekümmert haben&lt;/a&gt;, war das Thema unseres letzten &lt;a href=&quot;http://upstream-berlin.com/blog/2007/02/24/tag-der-forschung/&quot;&gt;Forschungstages&lt;/a&gt; “besser testen in Ruby on Rails”.  Da Testen bereits Thema bei &lt;a href=&quot;http://peepcode.com/&quot;&gt;Peepcode&lt;/a&gt; war, war der &lt;a href=&quot;http://peepcode.com/products/test-first-development&quot;&gt;Screencast&lt;/a&gt; Pflicht. Der brachte leider kaum neues. Doch ganz zum Schluss wurden dann doch noch zwei praktische Tools vorgestellt.  Mit TestTimer (&lt;code&gt;gem install test_timer&lt;/code&gt;) läst sich die Laufzeit aller einzelnen Test messen. So lassen sich echte Langläufer ausmachen. Ein weiteres sehr praktisches Tool ist &lt;a href=&quot;http://fakeweb.rubyforge.org/&quot;&gt;FakeWeb&lt;/a&gt;. Damit lässt sich Http-Kommunikation simulieren - perfekt, um die Testumgebung von externen Seiten zu entkoppeln ohne den Produktionscode zu ändern. Hier mal ein Beispiel aus der Dokumentation.&lt;/p&gt;
&lt;blockquote&gt;def test_request
FakeWeb.register_uri('http://example.com/test_me', :string =&amp;gt; &quot;Hello World!&quot;)
content = Net::HTTP.get(URI.parse('http://example.com/test_me'))
assert_equal &quot;Hello World!&quot;, content
end&lt;/blockquote&gt;
&lt;p&gt;Bei unserer Rechereche im Netz stießen wir zu guter Letzt noch auf einen kleiner Helfer, der automatisch jede Seite auf gültiges HTML prüft, jedem Link folgt, jeden Ajax Link aufruft und Formulare mit zufälligemn Daten füttert. So lassen sich Routing-Fehler, fehlende Seiten und falsche Formulare leichter aufspüren. Dieses Plugin hat den passenden Namen &lt;a href=&quot;http://blog.caboo.se/articles/2007/2/21/the-fabulous-spider-fuzz-plugin&quot;&gt;SpiderTest&lt;/a&gt;. Auch hier ist die Verwendung kinderleicht.&lt;/p&gt;
&lt;blockquote&gt; def test_spider
get '/'
assert_response :success

spider(@response.body, '/')
end&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&quot;http://blog.caboo.se/articles/2007/2/21/the-fabulous-spider-fuzz-plugin&quot;&gt;SpiderTest&lt;/a&gt; befindet sich noch in der Entwicklung.&lt;/p&gt;

&lt;p&gt;So das waren unsere Ergebnisse mal kurz zusammengefasst. Über hilfreichreiche Tipps und Kritik  freuen wir uns, genauso wie über jeden netten Kommentar :)&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/03/endlich-cruisecontrolrb-continuous-integration-a-la-ruby/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/03/endlich-cruisecontrolrb-continuous-integration-a-la-ruby/"/>
    <title>Endlich: CruiseControl.rb - continuous integration a la Ruby</title>
    <updated>2007-03-02T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;div class=&quot;content_image&quot;&gt;&lt;img src=&quot;/uploads/2007/03/ccrb.png&quot; alt=&quot;cc.rb&quot; /&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Vorwort:&lt;/strong&gt; &lt;a href=&quot;http://www.martinfowler.com/articles/continuousIntegration.html&quot;&gt;Continuous Integration&lt;/a&gt; (CI) bezeichnet das kontinuierliche, automatische integrieren aller Softwarekomponenten eines Projekts. Die geschieht ueblicherweise bei jedem Commit ins Versionskontrollsystem (bei uns Subversion). Das CI-Tool macht dabei einen frischen Checkout des Projekts und laesst die automatischen Tests laufen. Im Fehlerfall wird Alarm geschlagen, was den sofortigen Entwicklungsstillstand, bis der Fehler behoben ist, zur Folge haben sollte. Angewendet wird CI vor allem bei Freunden der agilen Softwareentwicklung, insbsondere bei den &lt;a href=&quot;http://de.wikipedia.org/wiki/Extreme_programming&quot;&gt;extreme programmern&lt;/a&gt; (also uns). Machen sollte es aber eigentlich jeder Entwickler, der von sich behauptet, professionell zu sein.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hauptteil:&lt;/strong&gt; Gestern &lt;a href=&quot;http://rossniemi.wordpress.com/2007/02/28/a-comparison-of-continuous-integration-tools-for-ruby-on-rails/&quot;&gt;gefunden&lt;/a&gt;, heute im Rahmen unseres &lt;a href=&quot;http://upstream-berlin.com/blog/2007/02/24/tag-der-forschung/&quot;&gt;Forschungstages&lt;/a&gt; installiert: &lt;a href=&quot;http://cruisecontrolrb.thoughtworks.com/documentation/&quot;&gt;CruiseControl.rb&lt;/a&gt; ist nach Java und .Net die dritte CruiseControl (CC) Implementierung, diesmal als &lt;a href=&quot;http://rubyonrails.org&quot;&gt;Ruby on Rails&lt;/a&gt;-Anwendung.&lt;/p&gt;

&lt;p&gt;Was sogleich angenehm auffaellt: Im Gegensatz zur Java-Variante benoetigt CC.rb so gut wie keine initialie Konfiguration. Nach dem Download genuegt ein einfaches &lt;code&gt;./cruise add projektname --url svn-url&lt;/code&gt; und anschliessen &lt;code&gt;./cruise start&lt;/code&gt; und schon startet ein Webserver und das builden kann losgehen.&lt;/p&gt;

&lt;p&gt;Ein kleines Problem gab es dann doch: CC.rb kommt anscheinend nicht mit einem deutschen Subversion klar (&lt;em&gt;undefined method `number’ for nil:NilClass&lt;/em&gt;). Bevor es also ans cruisen geht, noch schnell (unter *nix bash) &lt;code&gt;unset LANG &amp;amp;&amp;amp; unset LANGUAGE&lt;/code&gt; eingegeben und schon geht’s.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/03/openid-gewinnt-an-fahrt/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/03/openid-gewinnt-an-fahrt/"/>
    <title>OpenID gewinnt an Fahrt.</title>
    <updated>2007-03-01T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;&lt;a href=&quot;http://digg.com&quot;&gt;Einige&lt;/a&gt; der &lt;a href=&quot;http://yahoo.com&quot;&gt;Grossen&lt;/a&gt; arbeiten noch daran, &lt;a href=&quot;http://www.techcrunch.com/2007/02/19/make-a-claim-with-openid-on-jyte/&quot;&gt;viele&lt;/a&gt; &lt;a href=&quot;http://www.livejournal.com/&quot;&gt;viele&lt;/a&gt; &lt;a href=&quot;http://journals.aol.com/panzerjohn/abstractioneer/entries/2007/02/15/aol-and-openid-where-we-are/1406&quot;&gt;andere&lt;/a&gt; haben &lt;a href=&quot;http://openid.net/&quot;&gt;es&lt;/a&gt; schon. Trotzdem gelang &lt;a href=&quot;http://openid.net/&quot;&gt;OpenID&lt;/a&gt; der entscheidene Durchbruch erst  vor gut einer Woche, also ich mir meine &lt;a href=&quot;http://langalex.myopenid.com/&quot;&gt;persoenliche OpenID&lt;/a&gt; zugelegt habe, aber der Reihe nach:&lt;/p&gt;

&lt;h3&gt;Die Kurzfassung&lt;/h3&gt;
&lt;p&gt;OpenID ist ein dezentrales Framework zur Authentifizierung von Benutzern im Internet.&lt;/p&gt;

&lt;h3&gt;Die Langfassung&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Das Problem:&lt;/strong&gt; Als fleissiger Internetnutzer habe ich bestimmt 50 Accounts bei verschiedensten Diensten. Bei jedem dieser Dienste musste ich mich anmelden, meine Emailadresse eingeben, mir ein Passwort ausdenken und meine 30 Lieblingsfilme, Hobbys und Buecher eingeben. &lt;del&gt;Falls&lt;/del&gt; da ich faul war, haben die meisten dieser 50 Firmen das gleiche Passwort, und sobald bei einer dieser Firmen &lt;a href=&quot;http://golem.de/0702/50772.html&quot;&gt;die Datenbank ins Netz entfleucht&lt;/a&gt; (ok, angeblich waren die Passworter verschluesselt), kann der neue Besitzer sich damit bei all den anderen Firmen in meinem Namen anmelden und allen moeglichen Unfug anstellen. Im Wesentlichen habe ich also zum einen ein Komfortproblem, indem ich immer wieder die gleichen Daten eingeben muss, zum anderen ein Sicherheitsproblem, da ich verschiedensten Unbekannten meine Daten anvertraue.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ein frueherer Loesungsansatz:&lt;/strong&gt; vor einigen Jahren hatte ein &lt;a href=&quot;http://microsoft.com&quot;&gt;grosser Konzern aus Redmond&lt;/a&gt; mal ein System namens &lt;a href=&quot;http://de.wikipedia.org/wiki/Microsoft_Passport-Netzwerk&quot;&gt;Passport&lt;/a&gt; in Umlauf gebracht.
Dieses war als sogenanntes Single Sign On-System gedacht, d.h. ein Benutzer richtet sich einen Passport-Account ein, und kann sich damit fortan bei allen Seiten, die Passport unterstuetzen, mit diesem einen Account anmelden. Die Sache hatte einen kleinen Haken: man sollte ausgerechnet Microsoft seine Daten anvertrauen, dem grossen boesen Konzern, der die Weltherrshchaft an sich reissen wollte. Das hat sich inzwischen ja erledigt, da sich &lt;a href=&quot;http://google.com&quot;&gt;ein Nachfolger&lt;/a&gt; gefunden hat, aber damals…&lt;/p&gt;

&lt;div class=&quot;content_image&quot;&gt;&lt;img src=&quot;/uploads/2007/02/openid.gif&quot; alt=&quot;openid&quot; /&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Jetzte endlich:&lt;/strong&gt; &lt;a href=&quot;http://openid.org&quot;&gt;OpenID&lt;/a&gt;: wie in der Kurzfassung angekuendigt, ist OpenID dezentral, d.h. es gibt keinen zentralen Provider mehr, der meine Identitaet verwaltet, sondern es gibt viele davon, und ich kann mir einen aussuchen. In meinem Fall war das &lt;a href=&quot;http://myopenid.com&quot;&gt;MyOpenID.com&lt;/a&gt;. Nachdem ich mich dort angemeldet habe (ja, wieder Email, Passwort), bekomme ich meine eigene OpenID in Form einer URL, in meinem Fall &lt;a href=&quot;http://langalex.myopenid.com/&quot;&gt;http://langalex.myopenid.com/&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;(Kurzeinschub: gerade laeuft das &lt;a href=&quot;http://fritz.de&quot;&gt;Fritz&lt;/a&gt;-&lt;a href=&quot;http://chaosradio.ccc.de/cr121.html&quot;&gt;Chaosradio Nr. 121&lt;/a&gt; zum Thema digitale Identitaet, und just in dieser Minute schwenkt der Fokus auf OpenID, also im &lt;a href=&quot;http://chaosradio.ccc.de/chaosradio.html&quot;&gt;Podcast&lt;/a&gt; dann so bei Minute 78. Gerade wiederholen die Jungs so ziemlich genau das, was ich hier geschrieben habe. &lt;em&gt;seufz&lt;/em&gt; Zum Glueck hab ich ja noch mehr.)&lt;/p&gt;

&lt;p&gt;Also zurueck: ich habe jetzt also meine MyOpenID (oder Yahoo-OpenID oder AOL-OpenID oder von wem auch immer). Nun moechte ich zum Beispiel auf &lt;a href=&quot;http://livejournal.com&quot;&gt;LiveJournal&lt;/a&gt; mir einen Blog einrichten. Normalerweise muesste ich mich dazu zuerst bei LiveJournal anmelden, also wieder Email, Passwort usw. Stattdessen kann ich dort einfach meine OpenID-URL eingeben und mich damit anmelden. Damit sich nicht jeder, der meine OpenID kennt, damit ueberall anmelden kann, muss nun noch sichergestellt werden, dass auch wirklich ich vor dem Rechner sitze. Dazu fragt LiveJournal nun bei meinem OpenID-Provider, also MyOpenID, ob ich’s nun bin. Um das zu beweisen, muss ich auf der MyOpenID-Website mein Passwort eingeben, und ich bin drin, also bei LiveJournal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Das Ergebnis:&lt;/strong&gt; Ich habe nur noch eine Identitaet, naemlich die OpenID-URL und ein Passwort, und dieses Passowrt bekommt niemand, ausser mein OpenID-Provider zu sehen. Mit dieser Identitaet kann ich mich nun &lt;del&gt;ueberall&lt;/del&gt; schon bald bei immer mehr Diensten einloggen.&lt;/p&gt;

&lt;h3&gt;Goodies&lt;/h3&gt;

&lt;p&gt;Ein Restrisiko habe ich natuerlich immer noch, da ich immer noch meinem OpenID-Provider vertrauen muss. Um auch das zu umgehen, kann ich einfach meinen eigene OpenID-Server aufsetzen. Leider habe ich gerade den Link verloren, sorry.&lt;/p&gt;

&lt;p&gt;Nettes Gimmick: ich kann relativ einfach eine OpenID mit meiner eigenen Domain erzeugen, also sowas wie alex.upstream-berlin.com. Dazu melde ich mich zunaechst ganz normal bei einem beliebigen OpenID-Provider an. Zusaetzlich muss ich unter alex.upstream-berlin.com eine HTML-Seite bereitstellen, die ueber &lt;a href=&quot;http://www.openidenabled.com/openid/use-your-own-url-as-an-openid/&quot;&gt;ein paar Meta Tags&lt;/a&gt;  auf die eigentliche OpenID-URL verweist.&lt;/p&gt;

&lt;p&gt;Implementierung: das Entscheidene ist natuerlich, dass nun moeglichst viele Sites das ganze unterstuetzen. In absehbarer Zeit wird das in unseren Projekten der Fall sein. Eine &lt;a href=&quot;http://www.openidenabled.com/openid/libraries/ruby/openid-on-rails-ruby-openid-0-9-1-released/&quot;&gt;Ruby-Library&lt;/a&gt; scheint es ja schon zu geben.&lt;/p&gt;

&lt;p&gt;Immer wieder gerne: der Hinweis auf &lt;a href=&quot;http://identity20.com/media/OSCON2005/&quot;&gt;Dick Hardts Prasentation Identity 2.0&lt;/a&gt;, bei der das Prinzip nochmal toll erklaert wird.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/02/nochmal-zum-thema-rails-cms/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/02/nochmal-zum-thema-rails-cms/"/>
    <title>Nochmal zum Thema Rails-CMS</title>
    <updated>2007-02-25T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;div class=&quot;content_image&quot; style=&quot;border-width: 0px&quot;&gt;&lt;img src=&quot;/uploads/2007/02/rails_journal.jpg&quot; alt=&quot;rails journal&quot; /&gt;&lt;/div&gt;

&lt;p&gt;Nachdem ich vor einiger Zeit ueber diese Liste &lt;a href=&quot;http://upstream-berlin.com/blog/2007/02/12/rails-cms/&quot;&gt;Rails-basierter Content Management Systeme&lt;/a&gt; gestolpert war, habe ich mir heute zur Vorbereitung eines neuen Projekts diese Liste einmal etwas genauer angesehen.&lt;/p&gt;

&lt;p&gt;Meine Anforderungen waren eigentlich ganz einfach (dachte ich):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;eine Art Mini-Blogsystem fuer News&lt;/li&gt;
&lt;li&gt;Fotos, MP3s hochladen und auf Seiten darstellen&lt;/li&gt;
&lt;li&gt;WYSIWYG-Editor fuer alle Seiten&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bedienung auch durch Nicht-Geeks (!)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Unterstuetzung von RSS-Feeds und Kommentaren&lt;/li&gt;
&lt;li&gt;Ruby / Rails-basiert&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Die meisten der Anforderungen klingen eher nach einer Blog-Software. &lt;a href=&quot;http://wordpress.org&quot;&gt;Wordpress&lt;/a&gt; waere eigentlich wie geschaffen. Neben Blog-Features unterstuetzt es auch statische Seiten und die Administrationsoberflaeche ist einfach grosssartig, durch Plugins lassen sich MP3-Player und anderes einbauen. Leider ist das Ding in PHP geschrieben, und da ich mindestens am Layout ein paar tiefgreifende Aenderungen vornehmen werde muessen, von PHP-Code aber fuerchten muss, Augenkrebs bekommen, faellt Wordpress erstmal weg.&lt;/p&gt;

&lt;p&gt;Zunaechst also &lt;a href=&quot;http://trac.typosphere.org/&quot;&gt;Typo&lt;/a&gt; und &lt;a href=&quot;http://mephistoblog.com/&quot;&gt;Mephisto&lt;/a&gt;, zwei Rails-Blogsysteme:&lt;/p&gt;

&lt;h3&gt;Mephisto&lt;/h3&gt;
&lt;p&gt;(Uberigens “The best blogging system ever”. Das erinnert mich irgendwie &lt;a href=&quot;http://upstream-berlin.com/blog/2007/02/21/unser-user-interface-ist-gut/&quot;&gt;daran&lt;/a&gt;. Na dann.) Nach der Installation stehe ich vor einer leeren Admin-Oberflaeche, in der ich irgendwelche Sections anlegen kann. Dabei darf ich jeweils zwischen Blog-like Sections (Posts pro Seite) und Statischen Seiten (ein Post = die ganze Section) waehlen. Oben prangt auch ein Assets-Button, mit dem man Bilder hochladen kann. Tja, und dann kommt irgendwann der Editor zum Erstellen eines Blog-Eintrags. WYSIWYG is nich und wie man die Assets ohne HTML zu Coden in den Post bekommt, erschliesst sich mir leider auch nicht. Ueberhaupt ist das ganze etwas unuebersichtlich. Besagte Sections dienen offensichtlich als Kategorien fuer saemtliche Posts, wobei ein Post in beliebig vielen Sections auftauchen kann. Das macht fuer ein reines Blog Sinn, aber irgendwie fehlt mir hier im Konzept die Trennung zwischen statischem Content und Blog. Nee, Mephisto geht gar nicht.&lt;/p&gt;

&lt;h3&gt;Typo&lt;/h3&gt;
&lt;p&gt;Die Typo-Website besteht nur aus einem &lt;a href=&quot;http://rac.edgewall.org&quot;&gt;Trac&lt;/a&gt;.Na hoffentlich haben die Entwickler die Zeit, die sie an der Website gespart haben, in die Usability des eigentlichen Produkts fliessen lassen. Aber kann jemand, der nicht einmal die Praesentation seiner Software halbwegs schick gestalten kann, denn eine Software schreiben, bei der Worte wie Endnutzer, Usability und Komfort im Vordergrund stehen? Wir werden sehen …&lt;/p&gt;

&lt;p&gt;20 Minuten spaeter: Nachdem die standardmaessige Datanbankkonfiguration mit Sqlite zunaechst nur Fehler produziert hat, laeuft Typo jetzt auf Msyql. Leider bestaetigt sich meine Befuerchtung: eine furchtbar haesslche Administrationsoberflaeche begruesst mich nach dem Login mit einer bestimmt 5 Bildschirme langen Seite, in der ich irgendwelche wild zusammengewuerfelten Einstellunge vornehmen soll. Nagut, weiter zum Erstellen eines Artikels. Ein Paar verdammt grosse, leere Textfelder mit Namen wie &lt;em&gt;extended content&lt;/em&gt; (was ist das denn?!), Kategorien auch als Textarea. Ganz unten gibt’s auch was zum Datei hochladen. Aber nix, womit ich die Datei dann in die Seite einbinden koennte. Waaah. Raus hier.&lt;/p&gt;

&lt;p&gt;Tja, das waren die beiden Rails Blogsysteme. Ziemlich ernuechternd so weit. Mit denen geht schonmal gar nix. Werden die “richtigen” CMSe vorbeiziehen koennen? Lesen Sie weiter…&lt;/p&gt;

&lt;h3&gt;Radiant&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;http://radiantcms.org/&quot;&gt;Radiant&lt;/a&gt; nenn sich selbst “no fluff” und “simplified” und “flexible”. Klingt schon einmal etwas bescheidener und sympatischer als das beste Blogsystem ever, koennte aber auch “noch keine Zeit fuer mehr Features gehabt” heissen. Nach dem Login habe ich einen Baum von Seiten vor mir. Es lassen sich neue Seiten einfuegen sowie bestehende bearbeiten. Leider gibt’s auch hier kein WYSIWYG sondern nur plain text bzw. &lt;a href=&quot;http://hobix.com/textile/&quot;&gt;Textile&lt;/a&gt; (wiki-like syntax) und Co. (hatten die anderen auch alle). Seiten lassen sich Layouts zuordnen, in denen dann wiederum Snippets die einzelnen Bereiche einer Seite fuellen. Weiterhin gibt es sogenannte Behaviors, mit denen sich einer Seite Programmlogik beibringen laesst.&lt;/p&gt;

&lt;p&gt;Mit Hilfe kleiner Tags laesst sich die Logik dann in die Seite integrieren. Ein simples Blog liesse sich zum Beispiel so erzeugen: Man erstellt eine Seite, die die Liste der Eintraege anzeigen soll. Die Eintraege haengt man dann unterhalb dieser Seite als eigene Seiten ein. Ein Tag iteriert durch alle Kind-Seiten und zeigt deren Inhalt an.&lt;/p&gt;

&lt;p&gt;Ab dem naechsten Release soll es Extensions geben, mir denen sich dann neue Features hinzfuegen lassen. Der aktuelle Release Candidate hat allerdings nicht funktioniert. Klingt wirklich alles sehr schoen und einfach, gefaellt mir wirklich gut das Konzept. Aber was soll ein Normal-User damit anfangen? Leider gar nix. Also weiter.&lt;/p&gt;

&lt;h3&gt;Rubricks&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;http://rubricks.org/index_en.html&quot;&gt;Rubricks&lt;/a&gt; hat auf seiner Website Demos und Videos des Systems. In dem Video sieht man grosse bunte Icons in allen Bonbonfarben. Und wie jemand mit einem Klick ein komplettes Forum erzeugt. Und alles ist animiert und man kann die Seitenelemente mit der Maus verschieben. Ich habe Angst. Ich will keine eierlegende Wollmilchsau. Ich will was kleines, beherrschbares. Zum Glueck sehe ich nach der Installation nur einen Errror 500. Puh. Schnell wieder loeschen.&lt;/p&gt;

&lt;h3&gt;Streamlined&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;http://streamlined.relevancellc.com/&quot;&gt;Streamlined&lt;/a&gt; ist eigentlich kein CMS, wird aber im eingangs beschriebenen Artikel neben ein paar weiteren Tools als Startpunkt fuer die Entwicklung eigener Systeme empfohlen. Im Grunde ist Streamlined die &lt;a href=&quot;http://www.maybach-manufaktur.com/index.htm&quot;&gt;Maybach&lt;/a&gt;-Version des Scaffold Generators von Rails. Aber Fotos hochladen kann man damit auch nicht. Und WYSIWYG. Naechster.&lt;/p&gt;

&lt;h3&gt;Spread CMS&lt;/h3&gt;
&lt;div class=&quot;content_image&quot; style=&quot;border-width:0px&quot;&gt;&lt;img src=&quot;/uploads/2007/02/logo-2.png&quot; alt=&quot;spread-cms&quot; /&gt;&lt;/div&gt;
&lt;p&gt;Hach wie schoen. Der Besuch der &lt;a href=&quot;http://www.spread-cms.org/&quot;&gt;Spread-Website&lt;/a&gt; beschert mir sogleich ein warmes Gefuehl ums Herz. Ein tolles Web2.0-Logo mit der obligatorischen Spiegelung nach unten, eine aufgeraeumte Seite, schoene Farben und Screenshots der Admin-Oberflaeche, auf der Seiten mit eingebetteten Fotos zu sehen sind. Leider ist mein Franzoesisch etwas eingerostet, sodass sich mir die kleinen Texte neben den Bildern nur teilweise erschliessen, aber egal. Das Ding will ich sofort ausprobieren, und hier kommt der Haken: das ganze steckt noch oder ist schon vor langer Zeit in der Konzeptionsphase haengengeblieben. Neiiin.&lt;/p&gt;

&lt;p&gt;Im Projekt-eigenen Trac finde ich noch einen Artikel &lt;a href=&quot;http://www.adaptivepath.com/publications/essays/archives/000365.php&quot;&gt;Making a better CMS&lt;/a&gt;, in dem sich jemand ueber die eklatanten Maengel einiger untersuchter CMSe auslaesst. Danke, ich bin anscheinend nicht allein.&lt;/p&gt;

&lt;p&gt;Ich geh mir jetzt mal den Code von Wordpress angucken. Vorher noch ein paar Tomaten gegessen, Gemuese soll ja vorbeugen gegen (Augen)krebs. Seufz.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/02/deine-tests-wissen-die-antwort/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/02/deine-tests-wissen-die-antwort/"/>
    <title>Deine Tests wissen die Antwort.</title>
    <updated>2007-02-25T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;Nachdem wir vor kurzem auf Rails 1.2.2 umgestiegen sind, verbrachte ich letztens einige Stunden damit die Ursache für folgenden Fehler zu finden, der beim starten von mongrel auftrat:&lt;code&gt; 'remove_const': cannot remove Object::ModelTranslation (NameError)&lt;/code&gt; Die Logfiles zeigten, dass es Probleme beim Laden von Abhängigkeiten für &lt;a href=&quot;http://www.globalize-rails.org/globalize/&quot;&gt;Globalize&lt;/a&gt; gab. Allerdings war die Ursache völlig unklar. Ein Aufruf unserer &lt;a href=&quot;http://en.wikipedia.org/wiki/Unit_test&quot;&gt;Unit Tests&lt;/a&gt; mit &lt;code&gt;rake&lt;/code&gt; lieferte dann die banale Lösung: der Datenbankserver lief nicht.&lt;/p&gt;

&lt;p&gt;Das lehrt mal wieder, immer die Tests laufen lassen, wenn es Probleme gibt!&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/02/tag-der-forschung/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/02/tag-der-forschung/"/>
    <title>Tag der Forschung.</title>
    <updated>2007-02-24T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;div class=&quot;content_image&quot;&gt;&lt;img src=&quot;/uploads/2007/02/220px-nerd.jpg&quot; alt=&quot;nerd&quot; /&gt;&lt;/div&gt;
&lt;p&gt;Eine der Faszinationen der Informatik fuer micht ist, dass es eigentlich jeden Tag etwas neues zu entdecken, zu lernen und auszuprobierne gibt. Sei es eine &lt;a href=&quot;http://okonet.ru/projects/modalbox/&quot;&gt;neue Technologie&lt;/a&gt;, eine bisher unbekannte Art, etwas zu benutzen oder eine neue “&lt;a href=&quot;http://www.xprogramming.com/xpmag/testFirstGuidelines.htm&quot;&gt;best practice&lt;/a&gt;”, die einen dem unerreichbaren schwarzen Guertel naeher bringt. Dummerweise (?)  folgert daraus aber fuer alle Fachbeteiligten ein gewisser Zwang, sich moeglichst vielen dieser Neuerungen wenigsten kurz zu widmen, will man vor der Konkurrenz spielen und auch da bleiben. Nur woher die Zeit nehmen? Nach acht Stunden vor dem Bildschirm sitzt die Motivation fuer weiteres Bildschirmgeflimmer oder papierne Fachlektuere im untersten Kellergeschoss. Wochenende? Nee.&lt;/p&gt;

&lt;p&gt;Deshalb: ab sofort gibt es jeden Freitag Nachmittag den upstream Forschungs(halb-)tag. Freitag ab 14 Uhr ist - ausser in Notfaellen (es musste einen Haken geben) - das regulaere Arbeiten an Projekten untersagt. Stattdessen werden &lt;a href=&quot;http://www.amazon.de/Ajax-Rails-Scott-Raymond/dp/0596527446&quot;&gt;Buecher gewaelzt&lt;/a&gt;, &lt;a href=&quot;http://peepcode.com/&quot;&gt;Screencasts&lt;/a&gt; rezipiert oder das Netz durchforstet. Oder gebloggt. Ganz im Namen der Weiterbildung, der Forschung und des freien Experimentierens. Oder Austausch mit anderen, sollte sich das mal ergeben. Zur Unterstuetzung einer forschigen Atmosphaere werden wir des oefteren einen &lt;a href=&quot;http://karvana.de/&quot;&gt;Ortswechsel&lt;/a&gt; anstreben. Mal sehen, wie das so funktioniert. Erfahrungsberichte dann an dieser Stelle.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/02/unser-user-interface-ist-gut/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/02/unser-user-interface-ist-gut/"/>
    <title>Unser User-Interface ist gut.</title>
    <updated>2007-02-21T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;div class=&quot;content_image&quot;&gt;&lt;img src=&quot;/uploads/2007/02/bvg.png&quot; alt=&quot;bvg&quot; /&gt;&lt;/div&gt;
&lt;p&gt;Auch wenn es die Kunden vielleicht nicht sofort glauben, aber sagen können wir es auf jeden Fall schonmal - meint die &lt;a href=&quot;http://bvg.de&quot;&gt;BVG Berlin&lt;/a&gt; und vermerkt in ihren Fahrkartenautomaten nach Klick auf den Hilfe-Button: &lt;br style=&quot;clear:left&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
Die Bedienung der Automaten ist leicht.
Folgen Sie der Bedienung intuitiv.
Sie werden erfolgreich sein.
&lt;/blockquote&gt;
&lt;p&gt;Jetzt wissen wir endlich, was wir auf die Startseiten unserer Produkte schreiben sollen. Danke.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/02/testing-helpers-in-ruby-on-rails-12/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/02/testing-helpers-in-ruby-on-rails-12/"/>
    <title>Testing helpers in Ruby on Rails 1.2</title>
    <updated>2007-02-21T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;We are just migrating &lt;a href=&quot;http://autovc.com&quot;&gt;one of our projects&lt;/a&gt; from &lt;a href=&quot;http://rubyonrails.org&quot;&gt;Rails&lt;/a&gt; 1.1.6 to 1.2.2. One of the problems we encountered was, that &lt;em&gt;Recipe 44: Write tests for your helpers&lt;/em&gt; from &lt;a href=&quot;http://www.pragmaticprogrammer.com/titles/fr_rr/&quot;&gt;Rails Recipes&lt;/a&gt; (by the way &lt;a href=&quot;http://www.pragmaticprogrammer.com/titles/fr_arr/index.html&quot;&gt;Advanced Rails Recipes&lt;/a&gt; will come out in Augut 2007) is not working anymore. The line&lt;/p&gt;
&lt;blockquote&gt;@controller.send :initialize_current_url&lt;/blockquote&gt;
&lt;p&gt;resulted in a&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;can&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;clone&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;NilClass&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;After some messing around, we found out that the following setup method solves this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;setup&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@controller&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;MyController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActionController&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;TestRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;strong&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@controller&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;instance_eval&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActionController&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;UrlRewriter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{})&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/strong&amp;gt;
  @response = ActionController::TestResponse.new
end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Happy testing :)&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/02/caching-in-rails/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/02/caching-in-rails/"/>
    <title>Caching in Rails</title>
    <updated>2007-02-21T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;&lt;a href=&quot;http://peepcode.com/&quot;&gt;Peepcode&lt;/a&gt; haben wieder einen ihrer exzellenten Rails-Screencasts veröffentlicht. Thema heute: &lt;a href=&quot;http://peepcode.com/products/page-action-and-fragment-caching&quot;&gt;Caching&lt;/a&gt;. Na das schauen wir uns doch auch mal an. &lt;a href=&quot;http://autovc.com&quot;&gt;Steht ja schon auf der to-do Liste&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/02/trac-alternative/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/02/trac-alternative/"/>
    <title>Trac-Alternative</title>
    <updated>2007-02-18T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;div style=&quot;margin-right: 10px; float: left&quot;&gt;&lt;img src=&quot;/uploads/2007/02/retrospectiva.png&quot; alt=&quot;retrospectiva&quot; /&gt;&lt;/div&gt;

&lt;p&gt;Momentan setzen wir für Tickets, Dokumentation und Release-Planung auf &lt;a href=&quot;http://trac.edgewall.org/&quot;&gt;Trac&lt;/a&gt;. So richtig glücklich sind wir damit aber nicht. Denn die Entwicklung von &lt;a href=&quot;http://trac.edgewall.org/&quot;&gt;Trac&lt;/a&gt; schreitet in letzer Zeit langsam vorran, eine Unterstüzung mehrer Projekte ist in naher Zukunft nicht in Sicht, auch lief das Setup nicht immer ohne Probleme ab. Also hab ich mich mal nach einer Alternative umgeschaut. Und bin mit &lt;a href=&quot;http://retrospectiva.org/blog&quot;&gt;Retrospectiva&lt;/a&gt; fündig geworden. Es liefert die wichtigen Funktionen:
Issue tracking, Milestone/Release Management, Code Browser, Suche, Changeset und Revision Management, Wiki. Und das ganze auch für mehrer Projekte. Zum größten Teil basiert &lt;a href=&quot;http://retrospectiva.org/blog&quot;&gt;Restrospektiva&lt;/a&gt; auf &lt;a href=&quot;http://www.collaboa.org/&quot;&gt;Collaboa&lt;/a&gt;, ebenfalls ein &lt;a href=&quot;http://trac.edgewall.org/&quot;&gt;Trac &lt;/a&gt;-Konkurrent, aber ohne Wiki. Beide wurden mit userem aktuellen Lieblingframework &lt;a href=&quot;http://rubyonrails.org&quot;&gt;Rails&lt;/a&gt; entwickelt - und das ziemlich rasant. Naja &lt;a href=&quot;http://www.collaboa.org/&quot;&gt;Collaboa&lt;/a&gt; hatte zwischenzeitlich mal ein Jahr Pause eingelegt.&lt;/p&gt;

&lt;p&gt;Wir werden mal einen testweisen Umstieg wagen. Unsere Erfahrungen lest ihr dann hier.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/02/republica-in-berlin/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/02/republica-in-berlin/"/>
    <title>re:publica in berlin</title>
    <updated>2007-02-17T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;&lt;a href=&quot;http://re-publica.de&quot;&gt;&lt;img src=&quot;/uploads/2007/02/re-publica_banner_468x60.jpg&quot; alt=&quot;republica&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;vom 11.-13.4. 2007 findet in berlin die &lt;a href=&quot;http://re-publica.de/&quot;&gt;re:publica&lt;/a&gt; statt. dabei handelt es sich um eine konferenz rund um’s “web 2.0” (logo :) ). soziale netzwerke, blogs, podcasts usw. &lt;a href=&quot;http://re-publica.de/info/&quot;&gt;besser erklaeren&lt;/a&gt; koennen das aber die macher (&lt;a href=&quot;http://spreeblick.com&quot;&gt;spreeblick&lt;/a&gt; und &lt;a href=&quot;http://www.newthinking.de/&quot;&gt;newthinking&lt;/a&gt;) selbst.&lt;/p&gt;

&lt;p&gt;allem anschein nach wird das ganze wohl kostenlos sein, ich bin auf jeden fall da. um teilzunehmen, trage man sich auf der &lt;a href=&quot;http://re-publica.de/wiki/index.php/Teilnehmer#Teilnehmer.2Fin_.28Ich_werde_dabei_sein.29&quot;&gt;wiki-seite&lt;/a&gt; ein.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/02/schicke-schriften-jetzt-auch-im-web/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/02/schicke-schriften-jetzt-auch-im-web/"/>
    <title>Schicke Schriften jetzt auch im Web</title>
    <updated>2007-02-16T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;div class=&quot;content_image&quot;&gt;&lt;img src=&quot;/uploads/2007/02/t-polatino.gif&quot; alt=&quot;palatino&quot; /&gt;&lt;/div&gt;
&lt;p&gt;der aesthetische weg ist klar: abgerundete ecken, farbverlaufen, grosse buchstaben, web 2.0-standardfarbschemata.^^ was bisher fehlte, waren mehr als die vier web-standard-schriftarten. schluss damit macht &lt;a href=&quot;http://www.mikeindustries.com/sifr/&quot;&gt;sIFR&lt;/a&gt;: einmal eingebunden, steht dem stolzen website-besitzer, blogger oder community-homepage-designer ploetzlich die welt der schriften offen.&lt;/p&gt;

&lt;p&gt;technisch nett geloest: die seite enthaelt zunaechst ganz normales html. ein javascript tauscht bei vorhandenem flash-plugin die standard-schriften gegen flash-filme mit entsprechender formatierung aus. so haben auch nicht-flasher und such(maschinen) noch was von der seite.&lt;/p&gt;

&lt;p&gt;wie’s aussieht, sieht man auf der &lt;a href=&quot;http://www.mikeindustries.com/blog/files/sifr/2.0/&quot;&gt;beispielseite&lt;/a&gt;, wie’s geht, steht im &lt;a href=&quot;http://wiki.novemberborn.net/sifr/&quot;&gt;doku wiki&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;trotzdem sei an dieser stelle der vorsichtige gebrauch angemahnt - bei 200 flash-filmen pro seite macht auch der &lt;a href=&quot;http://www.apple.com/macbookpro/&quot;&gt;staerkste rechner&lt;/a&gt; schlapp ;)&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/02/10-wege-in-die-erfolglosigkeit/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/02/10-wege-in-die-erfolglosigkeit/"/>
    <title>10 Wege in die Erfolglosigkeit</title>
    <updated>2007-02-16T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;div class=&quot;content_image&quot;&gt;&lt;img src=&quot;/uploads/2007/02/g01965art11.jpg&quot; alt=&quot;hand stop&quot; /&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;http://en.wikipedia.org/wiki/Guy_Kawasaki&quot;&gt;guy kawasaki&lt;/a&gt; hat in der kiste gekramt und einmal die &lt;a href=&quot;http://blog.guykawasaki.com/2007/01/the_top_ten_stu.html&quot;&gt;zehn duemmsten wege, mit einer Webanwendung zu scheitern&lt;/a&gt;, niedergeschrieben. darunter finden sich aergernisse wie zu lange urls, usernahmen ohne @-zeichen oder sofortiger registrierungszwang, aber auch absolute blocker wie “internet explorer 5.5 only” oder nicht lesbare &lt;a href=&quot;http://en.wikipedia.org/wiki/Captcha&quot;&gt;captcha&lt;/a&gt;s. hm, und eigentlich sind es auch vierzehn punkte, und nicht zehn, aber egal. mir sind auch noch ein paar eingefallen (unsortiert):&lt;/p&gt;

&lt;h3&gt;bugs in production&lt;/h3&gt;
&lt;p&gt;nichts ist erfrischender, als eine error 500-seite bei der anmeldung oder nicht funktionierende submit-buttons. beliebt sind auch inkonsistente informationen, z.b. startseite: du hast 3 neue nachrichten, klick auf den entsprechenden link, postfach: keine nachrichten vorhanden. je nachdem, wie weit solche fehler in die zentralen funktionen einer anwendung reichen, war dieser besuch dann unter umstaenden der letzte. konkurrenz-sites erleichtern den wechsel natuerlich ungemein.&lt;/p&gt;
&lt;h3&gt;formulare&lt;/h3&gt;
&lt;p&gt;“bitte fuellen sie vor der anmeldung dieses formular mit 30 feldern aus. die als eingabe erlaubten werte unterliegen diversen beschraenkungen, die aber erst nach dem absenden bekanntgegeben werden”. geht gar nicht. ein einzelnes textfeld ist da gleich viel sympathischer.&lt;/p&gt;
&lt;h3&gt;langweiliges design&lt;/h3&gt;
&lt;p&gt;naja, kommt auf den zweck der seite an, aber das design ist nunmal aushaengeschild einer site. einer seite mit gelbem hintergrund und pinker schrift traut man einfach nix zu, sorry. mit runden ecken, farbverlaeufen und &lt;a href=&quot;http://upstream-berlin.com/blog/2007/02/16/schicke-schriften-jetzt-auch-im-web/&quot;&gt;tollen schriften&lt;/a&gt; ist man auf jeden fall auf der sicheren seite ;)&lt;/p&gt;
&lt;h3&gt;featuritis&lt;/h3&gt;
&lt;p&gt;was bei haushaltsgeraeten und kuechenmaschinen bestimmt toll ist (der waschkuehlende toaster mit eingebauter zahnbuerste), taugt im digitalen gut zur abschreckung. angenehme freiraeume auf den seiten muessen mit jedem hinzugewonnenen feature immer kleiner und gequetschter werdenden links weichen, bis es dann irgendwann &lt;a href=&quot;http://t-online.de/&quot;&gt;so aussieht&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;lange ladezeiten&lt;/h3&gt;
&lt;p&gt;mit dem erfolg kommt die serverlast. minutenlange ladezeiten und timeouts nerven ganz gewaltig, und man sieht sich schnell nach alternativen um.&lt;/p&gt;

&lt;p&gt;ach ja. ausnahmen bestaetigen wie immer die regeln. &lt;a href=&quot;http://studivz.de&quot;&gt;studivz&lt;/a&gt; hat’s schliesslich trotzdem geschafft :) und demnaechst koennt ihr uns die genannten punkte dann bei unseren &lt;a href=&quot;http://autovc.com&quot;&gt;eigenen projekten&lt;/a&gt; um die ohren hauen. und ein paar mehr punkte lassen sich bestimmt auch noch finden.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/02/zahlungsunwillige-kunden/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/02/zahlungsunwillige-kunden/"/>
    <title>Zahlungsunwillige Kunden?</title>
    <updated>2007-02-15T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;kleiner tipp fuer selbstaendige, deren kunden nicht so gerne ihre rechnungen bezahlen: 5% rabatt geben, falls eine rechnung innerhalb von 10 tagen bezahlt wird. das hat wohl schon massiv geholfen. und die zahlenden kunden fuehlen sich auch noch gut, weil sie einen rabatt bekommen haben. und von der schleichenden preiserhoehung um 5% merkt ja keiner was ;)&lt;/p&gt;

&lt;p&gt;(via &lt;a href=&quot;http://florianschmoelz.wordpress.com/&quot;&gt;flo&lt;/a&gt; (allerdings offline))&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/02/web-20-erklart/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/02/web-20-erklart/"/>
    <title>web 2.0 erklärt</title>
    <updated>2007-02-13T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;dieses wunderbar gemachte video erklaert dem interessierten nutzer einmal die unterschiede zwischen papier, web 1.0 und web 2.0. sowohl technisch als auch in bezug auf die konsequenzen, die sich daraus fuer die (netz-)welt so ergeben. habe ich erwaehnt, dass das video ganz wunderbar gemacht ist?&lt;/p&gt;

&lt;p&gt;(video nach dem klick)&lt;/p&gt;

&lt;object width=&quot;425&quot; height=&quot;350&quot;&gt;&lt;param name=&quot;movie&quot; value=&quot;http://www.youtube.com/v/6gmP4nk0EOE&quot; /&gt;&amp;lt;/param&amp;gt;&lt;param name=&quot;wmode&quot; value=&quot;transparent&quot; /&gt;&amp;lt;/param&amp;gt;&lt;embed src=&quot;http://www.youtube.com/v/6gmP4nk0EOE&quot; type=&quot;application/x-shockwave-flash&quot; wmode=&quot;transparent&quot; width=&quot;425&quot; height=&quot;350&quot; /&gt;&amp;lt;/embed&amp;gt;&lt;/object&gt;

&lt;p&gt;(via &lt;a href=&quot;http://www.techcrunch.com/2007/02/12/the-web-20-we-weave/&quot;&gt;techcrunch&lt;/a&gt;)&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/02/essen-im-friedrichshain-hot-dog-soup/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/02/essen-im-friedrichshain-hot-dog-soup/"/>
    <title>essen im friedrichshain: hot * dog * soup</title>
    <updated>2007-02-13T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;&lt;img src=&quot;/uploads/2007/02/hotdog.jpg&quot; alt=&quot;hot dog soup&quot; /&gt;&lt;/p&gt;

&lt;p&gt;ein gar wunderbarer ort zum schnellen und gesunden mittagessen zwischendurch. gelegen in der gruenberger str. (kurz hinter &lt;a href=&quot;http://maps.google.com/maps?f=q&amp;amp;hl=en&amp;amp;q=gruenberger+strasse+67,+berlin,+germany&amp;amp;sll=52.511768,13.458188&amp;amp;sspn=0.006712,0.020535&amp;amp;ie=UTF8&amp;amp;z=16&amp;amp;ll=52.511742,13.45823&amp;amp;spn=0.006712,0.020535&amp;amp;om=1&quot;&gt;ecke&lt;/a&gt; gabriel-max-str.) bietet sich dem hungrigen nebenstehendes bild.&lt;/p&gt;

&lt;p&gt;neben bestimmt 10 verschiedenen hot dogs (daenisch, brasilianisch, hot chili …) gibt es ein reichhaltiges angebot an ziemlich abgefahrenen suppen (cocos-spinat, apfel-curry, gorgonzola-lauch) - angebot staendig wechselnd. ach ja, alles auch in vegetarisch und vegan.&lt;/p&gt;

&lt;p&gt;keine ahnung, ob’s in dem laden ein wlan gibt, aber fuer laengeres verweilen ist’s dort eh zu klein. auf jeden fall haben sie ein abo der &lt;a href=&quot;http://sueddeutsche.de&quot;&gt;sueddeutschen&lt;/a&gt; und des &lt;a href=&quot;http://spiege;-online.de&quot;&gt;spiegels&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;10 von 10 punkten.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/02/rails-cms/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/02/rails-cms/"/>
    <title>rails cms</title>
    <updated>2007-02-12T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;habe gerade einen &lt;a href=&quot;http://www.rubyonrailsblog.com/articles/2006/08/28/top-20-ruby-on-rails-content-management-systems-cms&quot;&gt;artikel über content management systeme, die in rails geschrieben sind&lt;/a&gt;, gefunden. sowas kann man bestimmt immer mal gebrauchen, also poste ich das hier, um ihn spaeter wiederfinden zu koennen.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/02/hallo-welt/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/02/hallo-welt/"/>
    <title>Hallo Welt...</title>
    <updated>2007-02-12T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;… wir sind upstream aus Berlin und wir entwickeln Anwendungen für das Web ab Version 2.0. An dieser Stelle werden wir über Themen wie Start Ups, unser derzeitiges Lieblingsframework &lt;a href=&quot;http://rubyonrails.org&quot;&gt;Ruby on Rails&lt;/a&gt;, &lt;a href=&quot;http://www.apple.com/iphone/&quot;&gt;technische Spielzeuge&lt;/a&gt;, die &lt;a href=&quot;http://wirnennenesarbeit.de/&quot;&gt;digitale Boheme&lt;/a&gt;, &lt;a href=&quot;http://www.qype.com/place/20585-Pizzawerk-Berlin&quot;&gt;Mittagessen im Friedrichshain&lt;/a&gt;, Programmierkniffe, &lt;a href=&quot;http://de.wikipedia.org/wiki/Extreme_Programming&quot;&gt;agile Softwareentwicklung&lt;/a&gt;, interessantes und amüsantes aus der Netzwelt sowie natürlich unsere &lt;a href=&quot;http://autovc.com&quot;&gt;eigenen Aktivitäten&lt;/a&gt; bloggen.&lt;/p&gt;

&lt;p&gt;Unsere Hauptbeschäftigung liegt derzeit bei &lt;a href=&quot;http://autovc.com&quot;&gt;autovc.com&lt;/a&gt; - man könnte es das &lt;a href=&quot;http://studivz.net&quot;&gt;StudiVZ&lt;/a&gt; für Autofahrer nennen. Im Moment noch public beta - wir freuen uns über jeden Besucher, jedes neue Mitglied und auch Kommentare.&lt;/p&gt;

&lt;p&gt;Wir, das sind zur Zeit zwei unternehmergewordene Programmierer, sesshaft in Berlin Friedrichshain: &lt;a href=&quot;https://www.xing.com/profile/Thilo_Utke/&quot;&gt;Thilo Utke&lt;/a&gt; und &lt;a href=&quot;https://www.xing.com/profile/Alexander_Lang&quot;&gt;Alexander Lang&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Ziel dieses Blogs ist es, neben der Kommunikation unserer Projekte und Produkte, Beiträge zu Fragen und Themen der Netzgemeinschaft zu leisten sowie unser Netzwerk um interessante und fähige Menschen zu erweitern.&lt;/p&gt;

&lt;p&gt;Bei Fragen wendet euch per Email an alex[at]upstream-berlin[punkt]com bzw. thilo[at]upstream-berlin[punkt]com oder hinterlasst einen Kommentar.&lt;/p&gt;

&lt;p&gt;Alex und Thilo.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>https://upstre.am/blog/2007/02/das-internet-zerstort-den-einzelhandel/</id>
    <link type="text/html" rel="alternate" href="https://upstre.am/blog/2007/02/das-internet-zerstort-den-einzelhandel/"/>
    <title>Das Internet zerstört den Einzelhandel.</title>
    <updated>2007-02-12T00:00:00+00:00</updated>
    <author>
      <name>alex</name>
      <uri>https://upstre.am</uri>
    </author>
    <content type="html">&lt;p&gt;neulich in einer Einzelhandelsfiliale einer &lt;a href=&quot;http://www.je-computer.de/&quot;&gt;nicht ganz unbekannten Computereinzelhandelskette&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;König Kunde: Hallo
Verkäufer: … (über seinen Rechner gebeugt)
König Kunde: (steht inzwischen am Tresen, Verkäufer dreht ihm den Rücken zu) … (fängt an, mit den Fingern möglichst laut auf der Tischplatte zu tippeln)
Verkäufer: … (konzentriert auf den Rechner starrend)
König Kunde: &lt;em&gt;räusper&lt;/em&gt;
Verkäufer: …
König Kunde: Ich hätte gern ein Verlängerungskabel für einen Beamer
Verkäufer: (ohne aufzublicken) ham wa nich, Montag wieder
König Kunde: (etwas verdutzt ob der unerwarteten Antwort) Hm, schade. Ich bräuchte aber heute eins.
Verkäufer: … (nach wie vor dem Rechner zugewandt)
König Kunde: (entdeckt ein Regal mit Monitor-/Beamerkabeln und geht darauf zu)
Verkäufer: (schaut gebannt auf seinen Rechner)
Kunde: Das sind doch hier Monitor-Verlängerungskabel oder?
Verkäufer: Nee, nur Anschlusskabel.
Kunde: Naja, müste doch auch gehen oder? Ich hab so einen Adapter vom Laptop aus und da müsste man doch so ein Kabel zum Beamer…
Verkäufer: (zuvorkommend) Nee geht nich.
Kunde: (verunsichert) und so ein DVI-Kabel? Ich hab ja DVI am Rechner. Und dann der Adapter…
Verkäufer: Würd ich nich machen.
Kundenarsch: (entdeckt ein VGA-Anschlusskabel in der richtigen Länge.) Damit müsste es doch gehen oder?
Verkäufer: …
Kundenarsch: Das nehm ich.
Verkäufer: Neunzehn Fuffzich
Kundenarsch: Danke.
Verkäufer: …
Kundearsch: Tschüss.
Verkäufer: … (tippt auf seinem Rechner rum)&lt;/p&gt;

&lt;p&gt;Zu Hause angekommen, bemerkt der Kunde, dass das gekaufte Kabel eigentlich ein Verlängerungskabel ist, also genau das, was er kaufen wollte. Er freut sich wahnsinnig über den gelungenen Kauf und dankt dem Himmel, dass es den freundlichen Einzelhandel mit dem guten Service und der persönlichen Beratung noch gibt, sonst hätte er vermutlich ewig im Internet nach dem besten und güntigsten Kabel gegoogelt und es durch einen aufwändigen Mausklick bestellen müssen, woraufhin ihm das ganze am nächsten Tag nach Hause geliefert worden wäre. Glück gehabt. Danke JE Computer in der Frankfurter Allee Filiale Berlin Friedrichshain.&lt;/p&gt;
</content>
  </entry>
  
 
</feed>
