Pivotal Labs

Main menu

Skip to primary content
Skip to secondary content
  • About
  • Case Studies
  • Team
    • Executives
    • Locations
      • San Francisco (HQ)
      • Boston
      • Boulder
      • Denver
      • London
      • Los Angeles
      • New York
  • Community
    • Blogs
    • Tech Talks
    • Events
  • Careers
    • Lifestyle
    • Principles & Practices
    • Benefits
    • FAQ
    • Apply
  • Tools
  • Contact
    • Press Room
    • Press Releases
    • In The News
    • Press Kit
  • All
  • Labs
  • Standup
  • Tracker
Mark Rushakoff

Building identical gems

Mark Rushakoff
Friday, February 22, 2013

We ran into a problem where we were running `gem build` on identical input files and the built gems had different checksums; that is to say, if you run `gem build` twice in a row, the resulting `foobar.gem` files will not be identical.

A .gem file is actually a tar file (not compressed) containing two gzipped files (manifest.gz and data.tar.gz). What’s happening, as far as we can tell, is that gzipping a file embeds a timestamp somewhere in the file — here’s a gist of a Bash session that demonstrates just this idea:

Apparently gzip on its own can exclude the timestamp, but that option doesn’t seem to be exposed through tar.

So how do you build identical gems from the same input? As far as we could discover, that is not supported through any `gem` commands. To normalize a gem, you would have to untar the .gem file and then decompress the files inside; then you can do a full comparison of those contents against another .gem file that went through the same process.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Ryan Dy

SF Standup 03/14/2012: Capybara and push state

Ryan Dy
Wednesday, March 14, 2012

Help

No help

interesting

If you are using capybara-webkit gem that you should install QT 4.7+, if you need to use pushState/replaceState. Also defining those functions with mock implementations is another good solution.

The gem term-ansicolor has been removed from the heroku gem. It is still a dependency of foreman which is used by the lastest Heroku stack. The license on gem term-ansicolor is gplv2.

Guard with spork takes away a lot of the pain of using spork. Use the gem guard-spork which takes care of building you a sample guardfile. It takes care of restarting the appropriate framework you have in use be it test unit, rspec or cucumber.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Ben Smith

Do you know what your gems are doing?

Ben Smith
Saturday, October 8, 2011

A client recently expressed concern with a number of gems added to his project. A quick explanation and a little documentation cleared up what each gem was doing/why we needed it.

This satisfied the client, but it got me wondering: what’s the worst thing that could happen from a gem if it was malicious? The worst case I could imagine would be the client’s customer’s data getting stolen, the customers completely loosing faith in the site, and the client’s project failing because of it.

How likely is this to happen? I don’t really know.

How hard would it be for someone to do this?

I decided to see what it would take to harvest usernames and passwords from a typical Rails app using Devise for authentication. In less than 5 minutes, I had written an initializer which modified the behavior of the Devise controller to write out usernames and passwords to an HTML file in the public directory of the app.

The code wasn’t clever at all. I copied/pasted the create action, and added three extra lines to write out the data to the file.

      class Devise::SessionsController < ApplicationController
        prepend_before_filter :require_no_authentication, :only => [ :new, :create ]
        include Devise::Controllers::InternalHelpers

        # POST /resource/sign_in
        def create
          File.open("#{Rails.root}/public/passwords.html", 'a+') do |f|
            f.write("#{params[:user][:email]} #{params[:user][:password]}<br />")
          end
...

So the answer to my question, how hard would it be for someone to write a malicious gem that would compromise customer data: dead easy.

I packaged up the code as a gem. Anyone can easily pwn their own Devise Rails app by adding the following line to their Gemfile:

gem 'devise_hack'

Of course, who would install a gem that would pwn their own app? No one, but what about a “long con” approach?

Say I wrote a useful gem, pushed updates occasionally, and got a decent level adoption. At this point I could push a new version of the gem which contained a little hack, and wait for the usernames and passwords to roll in. Maybe like this little guy…

gem 'awesome_rails_flash_messages'

This little gem takes all of your Rails flash messages and makes them more awesome. Simple as that. Ohh, it also logs and requests containing a password to a file AND posts it to an external web service, but that’s nothing to worry about.

So how do you avoid these malicious gems? For this dead simple hack, it is dead simple to identify. All you have to do is look at the source code. If you see code that is writing credentials to a file, maybe posting to an external web service, or sending emails when it really shouldn’t be… you might want to reconsider using that gem.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter

RubyGems Warningitis Outbreak

Alex Chaffee
Thursday, May 12, 2011

Have you upgraded RubyGems lately? Is your console suddenly filled with warnings like this?

NOTE: Gem::Specification#default_executable= is deprecated with no replacement. It will be removed on or after 2011-10-01.
Gem::Specification#default_executable= called from /Users/chaffee/.rvm/gems/ruby-1.9.2-p0/specifications/thin-1.2.7.gemspec:10.

You may be showing signs of a new malady known as Warningitis! So far there is no cure, but doing the following will temporarily cure your symptoms:

gem update --system 1.7.2

Several experimental treatments are being hastily developed as well, but these have not yet been approved by the FDA. Check the “scary warnings are scary” bug thread for more details.

This has been a public health alert. Please do not panic. SARS masks and iodine pills are not recommended at this time.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter

Where, oh where has my gem server gone?

Alex Chaffee
Sunday, August 8, 2010

Uh-oh!

ERROR:  While executing gem ... (Gem::RemoteFetcher::FetchError)
    bad response Moved Permanently 301 (http://gems.rubyforge.org/latest_specs.4.8)

Whew!

gem sources -a http://rubygems.org/
gem sources -r http://gems.rubyforge.org/

Looks like they weren’t kidding when they said to switch from rubyforge to rubygems.org (née gemcutter)!

[Edited to change "http://production.s3.rubygems.org/" to "http://rubygems.org/". Note that the trailing slash is significant!]

  • 0 Shares
  • Share on Facebook
  • Share on Twitter

annotate 2.4.0 released

Alex Chaffee
Sunday, December 13, 2009

Remember the annotate_models rake task? Dave Thomas wrote it many years ago and it corrects one of the flaws in ActiveRecord: it describes the schema for a table as a comment inside the Ruby model file that it maps to. Unfortunately Dave hasn’t had time to maintain it, so a couple of years ago I cleaned up some bugs and re-published it as a pastie. Then Cuong Tran made it a gem and put it on Github, and since then, there’s been a whole lotta forkin’ goin’ on!

I recently pulled in a bunch of the forks into ctran’s master branch, and just pushed it to Gemcutter as version 2.4.0. Just run gem sources and make sure http://gemcutter.org is in your list — otherwise do gem source -a http://gemcutter.org — and sudo gem install annotate and it’ll install a binary called annotate in /usr/bin. See the README on github for more info and have fun!

One caveat: ImageMagick installs a tool called annotate too (if you’re using MacPorts it’s in /opt/local/bin/annotate). So if you see

Usage: annotate imagein.jpg imageout.jpg

then put /usr/bin ahead on the path and you’ll get ours instead.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Pivotal Labs

Introducing ActiveModelListener: Easy to use global ActiveRecord event listeners

Pivotal Labs
Sunday, September 27, 2009

I’m currently working on a large app where certain things have to happen when records are created, updated and deleted, such as:

  • Publishing to an activity feed
  • Generating emails
  • Adding entries to a changelog
  • Generating tasks and reminders

Further, the requirements state that admin users should be able to configure which of these actions happen for which objects in the system, who they go to, what the text is etc…

At first this looks like a great place for ActiveRecord Observers. However, after working with Observers there are a few things I dislike – namely that you can’t easily apply observers to all of your models, and you can’t selectively turn them on and off in tests. To remedy that problem, I created ActiveModelListener.

ActiveModelListener is a simple, global ActiveRecord event listener framework, using a middleware-esque architecture that can easily be turned on and off.

Installation

sudo gem install gemcutter
sudo gem tumble
sudo gem install active_model_listener

Usage

First, require active_model_listener above your rails initializer in environment.rb:

# environment.rb
require 'active_model_listener'
Rails::Initializer.run do |config|
  # ...
end

Next, add the listeners you’d like to apply (in order) to the ActiveModelListener in an initializer:

# config/initializers/active_model_listener.rb
ActiveModelListener.listeners << ActivityFeedListener

Then, create a listener class that defines methods for after_create, after_update and / or after_destroy, like so:

class ActivityFeedListener
  class << self
    def after_create(record)
      description = "#{record.class.name} was created"
      publish_activity_feed_items record, description
    end

    def after_update(record)
      description = "#{record.class.name} was updated"
      publish_activity_feed_items record, description
    end

    def after_destroy(record)
      description = "#{record.class.name} was deleted"
      publish_activity_feed_items record, description
    end

    def publish_activity_feed_items(record, description)
      record.activity_feed_item_subscribers.each do |subscriber|
        ActivityFeedItem.create :user => subscriber, :description => description
      end
    end

    private :publish_activity_feed_items
  end
end

Notice how the class looks almost identical to an ActiveRecord observer, so you can easily refactor between the two.

Turning off listeners in specs

When unit testing if your listeners are all firing your unit tests become integration tests. To avoid this, you can easily turn off listeners for all specs all the time:

Spec::Runner.configure do |config|
  config.before(:each) do
    ActiveModelListener.listeners.clear
  end
end

Then, when you want them back on again, you can turn them back on for a spec:

describe "Integrating with listeners" do
  before do
    ActiveModelListener.listeners << FooListener
  end
end

Specifying a subset of listeners to use

When doing data imports, migrations or certain actions that need to only use certain listeners, you can easily specify which ones you’d like to use:

ActiveModelListener.with_listeners AuditListener, ActivityListener do
  Article.create! :title => "foo"
end

After the block runs, the original listeners are restored.

If you want to run some code with no listeners at all, you can do so with:

ActiveModelListener.without_listeners do
  Article.create! :title => "foo"
end

Contributing

http://github.com/zilkey/active_model_listener

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Pivotal Labs

Introducing ActiveHash, ActiveYaml and ActiveFile – easy readonly, file-based models

Pivotal Labs
Tuesday, July 21, 2009

ActiveHash is a simple base class that allows you to use a ruby hash as a readonly datasource for an ActiveRecord-like model.

ActiveHash assumes that every hash has an :id key, which is what you would probably store in a database. This allows you to seemlessly upgrade from ActiveHash objects to full ActiveRecord objects without having to change any code in your app, or any foreign keys in your database.

It also allows you to use #belongs_to in your AR objects.

ActiveHash can also be useful to create simple test classes that run without a database – ideal for testing plugins or gems that rely on simple AR behavior, but don’t want to deal with databases or migrations for the spec suite.

ActiveHash also ships with:

  • ActiveFile: a base class that will reload data from a flat file every time the flat file is changed
  • ActiveYaml: a base class that will turn YAML into a hash and load the data into an ActiveHash object

Installation

sudo gem install zilkey-active_hash

Or go to http://github.com/zilkey/active_hash/tree/master for more information.

Usage

To use ActiveHash, you need to:

  • Inherit from ActiveHash::Base
  • Define your fields
  • Define your data

A quick example would be:

class Country < ActiveHash::Base
  field :name
  self.data = [
    {:id => 1, :name => "US"},
    {:id => 2, :name => "Canada"}
  ]
end

Defining Fields

You can define fields in 2 ways, using the :fields method, or using the :field method, which allows you to specify a default value for the field:

class Country < ActiveHash::Base
  fields  :name, :population
  field   :is_axis_of_evil, :default => false
end

Defining Data

You can define data inside your class or outside. For example, you might have a class like this:

# app/models/country.rb
class Country < ActiveHash::Base
  fields  :name, :population
end

# config/initializers/data.rb
Country.data = [
    {:id => 1, :name => "US"},
    {:id => 2, :name => "Canada"}
]

If you prefer to store your data in YAML, see below.

Class Methods

ActiveHash gives you ActiveRecord-esque methods like:

Country.all             # => returns all Country objects
Country.count           # => returns the length of the .data array
Country.first           # => returns the first country object
Country.last            # => returns the last country object
Country.find 1          # => returns the first country object with that id
Country.find [1,2]      # => returns all Country objects with ids in the array
Country.find :all       # => same as .all
Country.find :all, args # => the second argument is totally ignored, but allows it to play nicely with AR
Country.find_by_id 1    # => find the first object that matches the id

It also gives you a few dynamic finder methods. For example, if you defined :name as a field, you’d get:

Country.find_by_name "foo"      # => returns the first object matching that name
Country.find_all_by_name "foo"  # => returns an array of the objects with matching names

Instance Methods

ActiveHash objects implement enough of the ActiveRecord api to satisfy most common needs. For example:

Country#id          # => returns the numeric id or nil
Country#quoted_id   # => returns the numeric id
Country#to_param    # => returns the id as a string
Country#new_record? # => false
Country#readonly?   # => true
Country#hash        # => the hash of the id (or the hash of nil)
Country#eql?        # => compares type and id, returns false if id is nil

ActiveHash also gives you methods related to the fields you defined. For example, if you defined :name as a field, you’d get:

Country#name        # => returns the passed in name
Country#name?       # => returns true if the name is not blank

Integration with Rails

You can create .belongs_to associations from rails objects, like so:

class Country < ActiveHash::Base
  fields  :name, :population
end

class Person < ActiveRecord::Base
  belongs_to :country
end

You can also use standard rails view helpers, like #collection_select:

<%= collection_select :person, :country_id, Country.all, :id, :name %>

ActiveYaml

If you want to store your data in YAML files, just inherit from ActiveYaml and specify your path information:

class Country < ActiveYaml::Base
  field :name
end

By default, this class will look for a yml file named “countries.yml” in the same directory as the file. You can either change the directory it looks in, the filename it looks for, or both:

class Country < ActiveYaml::Base
  set_root_path "/u/data"
  set_filename "sample"
  field :name
end

The above example will look for the file “/u/data/sample.yml”.

ActiveYaml, as well as ActiveFile, check the mtime of the file you specified, and reloads the data if the mtime has changed. So you can replace the data in the files even if your app is running in production mode in rails.

ActiveFile

If you store encrypted data, or you’d like to store your flat files as CSV or XML or any other format, you can easily extend ActiveHash to parse and load your file. Just add a custom ::load_file method, and define the extension you want the file to use:

class Country < ActiveFile::Base
  set_root_path "/u/data"
  set_filename "sample"
  field :name

  class << self
    def extension
      ".super_secret"
    end

    def load_file
      MyAwesomeDecoder.load_file(full_path)
    end
  end
end

The two methods you need to implement are load_file, which needs to return an array of hashes, and .extension, which returns the file extension you are using. You have full_path available to you if you wish, or you can provide your own path.

Authors

Written by Mike Dalessio, Ben Woosley and Jeff Dean

Enjoy!

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Pivotal Labs

Introducing ActiveApi – A sane way to translate your data to xml

Pivotal Labs
Sunday, July 5, 2009

ActiveApi allows you to define a schema in Ruby, and use that schema to convert ruby objects to xml. An example looks like this:

Schema.version(:v1) do |schema|
  schema.define :article do |t|
    t.attribute :id
    t.string :title
    t.date :published_on
    t.has_many :comments
  end
end

On one of the apps I’m working on now, we have to expose our data as xml. This XML will be used as the datasource for reports, and as a way for external clients to import data into a data warehouse. The clients who will be importing the data are enterprisey – they have lots of tools that work with DTDs and XSD’s, and they’ve never heard of ActiveResource. (Many barely know what Rails is.)

On this app the data model is quite large and complex, and changes will be inevitable. We may want to generate reports using the latest and greatest xml, but some clients may take longer to update their data import code, so it’s very likely that we’ll have to maintain overlapping versions for short periods of time.

Through this process I’ve come to think that exposing your data via XML is not the job of the model. Instead, it’s the job of a separate class that is specifically designed to translate your model schema into a schema appropriate for public consumption.

A good api tool will have built-in support for:

  • XSD or DTD generation
  • Versioning
  • The ability to represent your model in a way that is not tightly coupled to the model itself – so you’re models can change at a different rate than your xml schema

ActiveApi attempts to provide this functionality.

Installation

sudo gem install zilkey-active_api

Usage

You define a schema like so:

Schema.version(:v1) do |schema|
  schema.define :article do |t|
    t.attribute :id
    t.string :title
    t.date :published_on
    t.has_many :comments
  end

  schema.define :comment do |t|
    t.belongs_to :user
    t.string :article_title, :value => proc { |element|
      element.object.article.title
    }
  end

  schema.define :user do |t|
    t.string :username, :value => :user_name
  end
end

To create xml from this schema, you could write code like this:

ActiveApi::Schema.find(:v1).build_xml(@articles, :node => :article).to_xml

Which will give you xml output that looks like this:

<?xml version="1.0"?>
<articles>
  <article id="1">
    <title>target efficient applications</title>
    <published_on>2004-08-22</published_on>
    <comments>
      <comment>
        <article_title>target efficient applications</article_title>
        <user>
          <username>foo</username>
        </user>
      </comment>
    </comments>
  </article>
  <article id="2">
    <title>recontextualize viral e-services</title>
    <published_on>2004-12-05</published_on>
    <comments>
      <comment>
        <article_title>recontextualize viral e-services</article_title>
        <user>
          <username>foo</username>
        </user>
      </comment>
    </comments>
  </article>
</articles>

Extending

ActiveApi is highly extensible. The general pattern used to extend ActiveApi is to subclass the default ActiveApi implementation, and specify that you’d like to use your subclass instead.

For example, if you are working with a database that has audit columns such as timestamps, you might want to do this:

class MyDefinitionClass < ActiveApi::Definition
  def timestamps
    date_time :created_at
    date_time :updated_at
  end
end

@schema = Schema.version(:v1, :definition_class => MyDefinitionClass) do |xsl|
  xsl.define :article do |t|
    t.timestamps
  end
end

Schema.find(:v1).build_xml([@article], :node => :article).to_xml

Which will produce the following xml:

<?xml version="1.0"?>
<articles>
  <article>
    <created_at>1945-12-21T00:00:00+00:00</created_at>
    <updated_at>1992-04-05T00:00:00+00:00</updated_at>
  </article>
</articles>

NOTE: when specifying custom definition classes, those classes must be loaded before calling Schema.version

You can also create custom classes for any element you define, like so:

@schema = Schema.version(:v1) do |xsl|
  xsl.define :article, :builder_class => "MyCustomClass"
end

class MyCustomClass < ActiveApi::ComplexType
  def build(builder)
    builder.send :foo, :bar => "baz" do |xml|
      xml.send :woot, "lol"
    end
  end
end

Schema.find(:v1).build_xml([@article], :node => :article).to_xml

Which will produce the following xml:

<?xml version="1.0"?>
<articles>
  <foo bar="baz">
    <woot>lol</woot>
  </foo>
</articles>

NOTE: since the builder classes are evaluated at runtime, you can specify a string name for the class, and the class does not have to be loaded before calling Schema.version

Features

You define your schema completely separately from your data. So you could in theory render multiple types of objects with the same schema, provided that they have the same interface. You could also render a single object in any number of ways.

You can choose to have the builder send methods on your object, or provide more complex values by using the :value => proc{} syntax. Since you can define the value of the elements separately from the names, aliasing your objects field names is built in.

The element keeps track of all of it’s ancestors, so you can access objects that were rendered as ancestors, even if those objects aren’t ancestors in your object graph.

The Schema definition just creates an array of Ruby objects, which you could use to create documentation or XSD files. One of my major goals for this gem is to create valid XSD from the schema itself. Given how extensible it is, I’m not sure how that will work yet, but I’m psyched to give it a shot.

You define your schema versions using whatever versioning scheme you want – could be a string, symbol or any other object. You can render the same objects with different schemas easily. This library is totally agnostic as to how or if you version your schemas – but if you decide to version, it makes it easy.

The schema defining DSL allows you to define any valid XSL data format – it formats dates, times and datetimes in XSD compliant formats, it URI escapes any element tagged as anyURL and has helper methods for all XSD data types in both their native form (anyURL) and a more ruby-friendly form (any_url) – so you can write:

  schema.define :article do |t|
    t.anyURL :foo
    t.any_url :foo
  end

And they will be emitted identically.

ActiveApi also allows you to define polymorphic relationships via the XSL “choice” instruction:

      xsl.define :comment do |t|
        t.belongs_to :commentable, :choice => {
          "Article" => :article,
          "User" => :user
        }
      end

This allows your API to determine which elements to render at runtime, while still giving you complete control over the schema to use for that element. This allows you to have more than one representation of your objects – one that gets rendered if the object is the root, and a different one that gets rendered if it’s being rendered as a sub-element of another object.

Integration with Rails

ActiveApi is framework agnostic. While the belongs_to / has_many syntax is Rails-like in name, it does not depend on ActiveRecord. It also does not modify or interfere with AR in any way. To use xml generated from ActiveApi in a controller, you can do this:

def index
  @articles = Article.all

  respond_to do |format|
    format.html # index.html.erb
    format.xml  do
      render :xml => ActiveApi::Schema.find(:v1).build_xml(@articles, :node => :article)
    end
  end
end

Implementation

ActiveApi uses’s Nokogori::XML::Builder to create the xml nodes. As such, the creation of the xml is fast. The rest of the code likely needs major refactoring to be performant and have a small footprint with large datasets. In particular, it creates a new object for every collection, element and value. That’s a lot of objects. I’d love to hear any comments about how to improve this aspect of the design.

Authors

While I wrote all of the code in this repo, the code was inspired by pairing on a similar project with:

  • Mike Dalessio
  • Peter Jaros
  • Ben Woosely

Development

http://github.com/zilkey/active_api/tree/master

See the issue tracker on github for the list of features and bugs I know about.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter

Erector 0.6.3 – Now Ready to Conquer The World

Alex Chaffee
Wednesday, May 6, 2009

[Updated: added instructions "If you are using a widget in rails, you now need to inherit from RailsWidget"]

Erector has been around for almost 2 years now, but we’ve always been a little reluctant to market it heavily. One reason has been that the API was a little inelegant. Another is that Rails integration wasn’t as seamless as we’d like.

With today’s release of version 0.6.3, we have hopefully fixed both of those problems. With the new RailsWidget we’ve got a clean separation between core Erector functionality and the magic we need to make it work with Rails. We’ve renamed “render” to “content” so we don’t conflict with the standard Rails render method. And we’ve changed the API for smoother lifecycle management — the constructor is about initializing the widget, and the refurbished “widget” method is about setting it up to emit its HTML.

The bad news is that you’ll have to change your existing code. The good news is there’s an update guide, which you’ll find below the fold in this blog post.

Please visit our Google Group to register comments or complaints, our project web site for full documentation and FAQs, and feel free to clone or fork our GitHub repo.

(Why 0.6.3 and not 0.6.0? Because we had to work through some glitches in the new deploy process with GitHub and Jeweler and whatnot. We’re only human…)

Erector 0.6.0 Announcement

This release is the first major API change to Erector. It will definitely break
existing code. Sorry about that, but we promise it’ll be cleaner afterwards.

Quick Update Guide

  • Rename ‘def render’ to ‘def content’ in all your widgets

  • If you are using a widget in rails, you now need to inherit from
    RailsWidget to get helper goodness

  • Change MyWidget.new(helpers, assigns, output) to just
    MyWidget.new(assigns)

  • To render a widget from inside another widget, use

    widget MyWidget, :foo=>2
    

    or

    widget MyWidget.new(:foo => 2)
    
  • If you want your variables to have attr_readers, use ‘needs’

  • If you want your widgets to be more self-documenting, use ‘needs’

Major API changes

  • “new” and “to_s” have been changed to clarify the lifecycle of a widget,
    so “new” accepts permanent state (”assigns” variables) and “to_s” accepts
    temporary, rendering state (output stream, helpers, and prettyprinting).
    This lets you do things like make collections of widgets in once place in
    your code and render them in another place.

  • Renamed “render” to “content”, which removes confusion/ambiguity with
    Rails’ “render” method and concept, and also allows “render :partial” to
    be made to work (though we’re not sure if that totally works yet).

  • To render a widget from outside code, the pattern is:

    w = DateWidget.new(:when => Time.now, :title => "Nap Time")
    puts w.to_s(:helpers => some_rails_view)
    
  • To render a widget from inside another widget:

    def content
      # first way... pass class and assigns
      widget DateWidget, :when => Time.now, :title => "Nap Time"
      # second way... pass instance
      widget DateWidget.new(:when => Time.now, :title => "Nap Time")
    end
    

    Using “widget” will improve performance over calling “raw foo.to_s” or
    whatever since it reuses the same output stream.

  • To declare variables — and raise an exception when one is not provided:

    class JohnLennon < Erector::Widget
      needs :love
    end
    
  • Formerly, every ‘assigns’ variable had an attr_reader defined for it. Now,
    only variables declared with ‘needs’ get attr_readers.

Other changes:

  • Removed Widget#to_s caching, which fixed indentation issues.

  • BUGFIX: Indentation level is now correctly propagated to nested widgets.

  • Erector’s Rails support strategy has changed. The released version of
    Erector only supports the latest stable Rails version (currently 2.3.2).
    If you need support for earlier versions of Rails, there are separate Git
    branches for each one, but we will not be in the habit of keeping these up
    to date with the latest features and patches. If someone wants to do a
    merge to a prior Rails branch, Brian will be happy to help :-)

  • 0 Shares
  • Share on Facebook
  • Share on Twitter

Topics

  • agile (783)
  • rails (117)
  • testing (90)
  • ruby (86)
  • ruby on rails (71)
  • jobs (62)
  • javascript (59)
  • techtalk (44)
  • ironblogger (42)
  • rspec (39)
  • bloggerdome (34)
  • productivity (34)
  • activerecord (30)
  • rubymine (30)
  • git (29)
  • gogaruco (29)
  • nyc (27)
  • design (24)
  • mobile (23)
  • pivotal tracker (22)
  • process (21)
  • cucumber (21)
  • jasmine (19)
  • ios (18)
  • tracker ecosystem (17)
  • webos (17)
  • objective-c (17)
  • fun (16)
  • android (16)
  • palm (16)
  • ci (16)
  • "soft" ware (16)
  • bdd (15)
  • tdd (15)
  • cedar (15)
  • rails3 (14)
  • performance (14)
  • css (14)
  • gem (13)
  • mouse-free development (12)
  • selenium (12)
  • goruco (12)
  • bundler (12)
  • api (12)
  • keyboard (11)
  • meetup (11)
  • railsconf (11)
  • nyc-standup (11)
  • capybara (10)
  • mac (10)
Subscribe to gem Feed
  1. 1
  2. 2
  3. →
  • About
  • Case Studies
  • Team
  • Community
  • Careers
  • Tools
  • Contact
  • Labs
  • Events

Contact Us

contact@pivotallabs.com
+1 415-77-PIVOT
TwitterLinkedInFacebook

Pivotal Tracker

Tracker is the award-winning agile project management tool that enables real-time collaboration around a shared, prioritized backlog.
Visit pivotaltracker.com >