Acts As Statistic Rails Gem

I finally got around to putting time into building out a gem for unified statistics gathering. The goal is to build up enough support to mimic something like the Benchmark class but log to an ActiveRecord model in a normalized format. However, unlike Benchmark, it will also support stats of counts and rates.

It was almost a year to the day before I put some effort into it beyond script/generate plugin. I do hope to keep up the development at a significantly faster rate. :-)

https://github.com/nickjones/acts_as_statistic

Filed under  //  foss   gem   rails   ruby  
Posted

Lone Star Ruby Conference 2011: Day 2 Notes

These are some of my (terse) notes from day two of the LSRC.  It would probably be helpful to read my colleague's notes as well.

Blow up Your Views

  • Instance variables in views suck
    • Real interface between C/V
      (github: voxdolo/decent_exposure)
    • Added "exposes(:foo)" in the controller and the view starts using just "foo" instead of "@foo"
  • Kill helpers
    • Example of timestamps
      • Should be writing helpers to format timestamps
      • Usually helpers are for specific objects (e.g. formats the timestamp on a particular kind of object)
    • We write helpers like print_name(user) instead of user.print_name but we want the later.
  • View Objects Implemented
    • Create an /app/decorators folder
    • Implement an attr_accessible approach to the view: Limit what it is allowed to call.
    • Github: jcasimir/draper
  • Should be splitting up responsibilities
    • Always render the same thing from the controller (define an API)
    • The view should deal with drawing the display for the client
    • Send them a JS template, assets, etc.
  • Render Engines
    • Plain HTML with jQuery Replacement
    • Mustache
    • Handlebars (SproutCore)
    • ICanHazJS

ZeroMQ

  • ZeroMQ is a messaging bus
  • Zero stands for 'zero brokers'
  • Scalability
    • Serve requests faster
    • Deals with complexity scalability
  • Uses an actor model
    • stateless
    • self-contained pieces
  • What ZeroMQ isn't
    • ZeroMQ isn't designed to write an HTTP server
      • It doesn't care what you send in messages but it cares how it sends it
      • ZeroMQ sockets only talk to other ZeroMQ sockets
    • Could write a layer that interprets HTTP and sends it into ZeroMQ (Mongrel2)
  • How to use it
    • Ruby: gem install zmq
    • Based in C and supported in every language
    • Threadsafe but can't share a socket between threads
  • Example: Request/Reply Sockets
    • Can start a client and it starts trying to communicate but will block until the server responds
    • If the server crashes in the middle and restarts but it wont automatically work.
  • Publish/Subscribe model
    • Uses option "SUBSCRIBE"
    • If the publisher goes down, the subscriber is durable
  • Push/Pull sockets
    • Multiple pull sockets can bind to a single push socket
    • Spawn more workers if the queue is backing up
  • Durable Sockets
    • Set a socket identity: give it a name
    • Setup spooling to disk for unreceived messages

JavaScript for people who didn't learn JavaScript (@jwo)

  • Prototype Inheritance
    • Only one prototype per function (but can be switched out when needed)
    • Like Ruby modules but you only get one.
    • Calling super
      • JavaScript doesn't provide a method to call super
      • var Note = Backbone.Model.extend({
          set: function(...) { } <-- override behavior and call Backbone.Model.set.call(...)
  • Using 'new'
    • Don't just use new because you think you have to
    • Use 'new' if you need the prototype of the object
    • Don't use it if your just creating a new Array, etc.
    • Object.create is useful but some browser dependencies
    • "Don't write new Array()"
      • Arrays in JS don't have limits
    • "Don't write new Function()" - just use the funciton() literal
  • Invocation
    • Functions get declared-parameters, this, and arguments
    • Creating a new object with Object.create(HelloWorld) and calling hi.speak() you get the prototype
    • Using just HelloWorld.speak doesn't provide scope so 'this' isn't bound to the object
    • JavaScript allows you to pass in the object that you want 'this' to reference (so function() in the method can actually operate on whatever object you provide)
  • JavaScript puts semicolons in where you "forgot them"
    • return
      {
        javascript: "foo"
      }; // This will fail because return gets a magic ';' on the end.

Testing JavaScript with Jasimine

  • Jasmine has rspec like syntax
  • Jasmine with jQuery
    • jasmine-jquery adds more matchers
    • Lettering.js
      • Allows you to throw spans in between chars in a particular HTML DOM element
    • SpecRunner.html
      • Added jquery and lettering js files
  • Jasmine with Rails
    • Add 'jasmine' to Gemfile
    • rake jasmine / rake jasmine:ci
  • Easier way with Evergreen "gem install evergreen"
    • Create public folder (with js in it)
    • Create spec folder with specs in it
    • evergreen run
    • Evergreen follows the name_spec.js file naming convention
    • Install coffee-script and works with Jasmine (make .coffee file)
  • Headless running
    • Install capybara-webkit
    • Switched from selenium to webkit and runs faster
  • Rosie gives you factory_girl for JS
    • github: bkeepers/rosie
  • jasminerice - jasmine + coffee script + rails 3.1

The Ballad of Goliath, EventMachine, and MongoDB

These notes are bit short since a lot of the talk was random aspects of MongoDB towards the end.
  • Goliath
    • Event Driven (Built upon EventMachine)
    • Web and app server
    • Uses fibers in Ruby 1.9
    • Matches a rackup app somewhat
      • Inherits from Goliath::API
    • Get an array of command line options
      • -u for actually specifying the user
    • By default it doesn't log at all
  • EventMachine: Watch peepcode screencode
  • MongoDB
    • Just doing Mongo::ReplSetConnection.new(host), only R/W to primary
    • Use Mongo::ReplSetConnection.new(host, :read_secondary => true) to get better read performance

Polygot Parallelism

This talk started off with a brief intro to Erlang and continued a discussion into how Rackspace was able to successfully solve a massively parallel data gathering problem.
  • Erlang
    • Immutable Data Structures
    • Single assignment => everything is immutable
  • Webmachine
    • Similar to Sinatra or Goliath
    • Basically developing a custom web server for your application's behavior.
    • http://webmachine.basho.com
    • Leaves sensible defaults for the whole HTTP life cycle and can override at any step.

Filed under  //  javascript   lsrc   rails   ruby   zeromq  
Posted

Capistrano and Bundler

Sounds great doesn't it? Carefree deployments and gem management for your app. However, I ran into a few pitfalls that made the first experience less than carefree.

bundle:install step

After integrating bundler into capistrano with

require 'bundler/capistrano'

a bundle:install step gets kicked off to install your gems locally.  Pretty cool, but beware of defaults if you're on a multiserver deployment setup.  The default step flags are --deployment --quiet which will download all of the gems from the net.  Maybe not so bad for some folks but on a beefy rails app, this took forever across four servers and 38 gems.  Nevertheless, capistrano is easily customizable and can make this easier with the following line:

set :bundle_flags, "--deployment --quiet --local"

In order for this to work properly though, make sure and package up the gems with your application!

Custom rake tasks

The app I work on has a few custom steps for compiling a custom Ruby library, building extra documentation, etc. so there are a few more after "deploy:update_code" steps to address upon deployment.  Everything was pretty simple and easy to update for bundler; the only trick is knowing you have to do this update.

Each custom step was built into a rake task for lazyness in development, so each resembled something like this:

namespace :xapian do
   desc "Rebuild Xapian index."
   task :rebuild, :roles => :xapian_index do
     run "cd #{release_path}; rake xapian:rebuild_index models=\"MyModelName\"", :env => {'RAILS_ENV' => stage}
   end
end

We were calling rake directly, but this will lead you to step failures pretty quickly if the version of rake differs (or doesn't exist) in the deployment environment.  Simply replace rake with #{rake}.  The step will automatically be executed as #{bundle_cmd} rake instead (e.g. bundle exec rake by default).

 

Filed under  //  bundler   capistrano   deployment   rails   ruby  
Posted

Versioning ActionWebService on Rails

I started a Rails project a few years ago that has grown up.  These days, there is a main consumer of a SOAP API provided through ActionWebService (with more on the way).  Most generally try and head down the RESTful path, but with server-to-server communication, it just made it easier to take a structured path.  (Custom XML/JSON output, yuck.)

Since the client system is actually the server for a desktop client, it became more necessary to independently update the two servers.  This did pose a bit of a problem with SOAP API bindings in .NET.  We wanted to establish a backwards compatible base API (to maintain the status quo) and also start working on a few API breaking updates.  This would normally be pretty easy in rails with namespaces but it unfortunately doesn't work out so well for ActionWebService.

ActionWebService actually supports a few different models of dispatching actions: direct, delegated, and layered.  Direct just maps a controller's actions to the entire described API.  Delegated enables exposing single methods as SOAP actions from another controller.  Lastly, layered is a lot like direct and delegated mixed; separate ports are created for each layered implementation, and everything is described in the same wsdl.

So...everything should be great, right? Well, it turned out that all of the session code broke.  Since the 'versioned' implementations were actually instantiated from the original controller, they're not provided the same environment as the top level. This little trick from the ruby-forum re-instated the functionality (with a minor edit):

class ServicesController < ApplicationController
    web_service_dispatching_mode :layered
    web_service(:v1) { ServicesV1Controller.new(self) }
  end

 

class ServicesV1Controller < ApplicationController
    def initialize(self)
      @controller = self
      @session = @controller.session
    end
  end

We're still working through some of the kinks in making the API fully backwards compatible, but this got us a long ways to supporting multiple API versions with SOAP.

 

Helpful post on API versioning: http://stackoverflow.com/questions/389169/best-practices-for-api-versioning

Filed under  //  actionwebservice   rails   ruby   soap   versioning   webdev  
Posted