Posted 3 months back at The Pug Automatic
More and more, I’ve been trying to contain new features in their own class.
For example, on an eBay-like auction site, users can add items to their watchlist.
A typical way of doing that in Rails may involve User.has_many :watchlistings and Item.has_many :watchlistings. But User and Item so easily become god classes with too much responsibility.
So instead, you could have a Watchlist class that is the only thing in the system that knows about Watchlisting records.
<figure class="code"><figcaption>
app/models/watchlisting.rb</figcaption>
1
2
| class Watchlisting < ActiveRecord::Base
end
|
</figure>
<figure class="code"><figcaption>
app/models/watchlist.rb</figcaption>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| class Watchlist
def self.users_watching(item)
ids = Watchlisting.where(item_id: item).pluck(:user_id)
User.where(id: ids)
end
def initialize(user)
@user = user
end
def items
ids = Watchlisting.where(user_id: @user).pluck(:item_id)
Item.where(id: ids)
end
def add(item)
Watchlisting.create!(user_id: @user, item_id: item)
item
end
end
|
</figure>
<figure class="code"><figcaption>
example.rb</figcaption>
1
2
3
4
| my_list = Watchlist.new(my_user)
my_list.add(an_item)
my_list.items # => [an_item]
Watchlist.users_watching(an_item) # => [my_user]
|
</figure>
You could of course implement these with a single query and a JOIN, if you prefer:
<figure class="code"><figcaption>
app/models/watchlist.rb</figcaption>
1
2
3
| def items
Item.joins("JOIN watchlistings ON items.id = watchlistings.item_id").where("watchlistings.user_id" => @user)
end
|
</figure>
This does mean Watchlist has to do without some Active Record niceties. A few more ids mentioned, perhaps even some raw SQL joins. But the win is that items and users know nothing about watchlists. That’s a trade-off I’m willing to make.
The alternative would be to add more public API to Item and User. That may be fine for one feature, but less so for three, or five, or ten as your app grows.
It reduces coupling. For now, there’s a table named watchlists with an Active Record class on top. These details are internal to Watchlist. External code can’t easily be coupled to them. With User.has_many :watchlists, that would be less likely.
I renamed a table in production without downtime the other week, and that was only feasible because access to that feature was well-contained.
Posted 3 months back at Ruby5
Gregg & Aimee kick off this week with Rails security issues, intro to State Machine, stabby lambda, heat maps, sucker punch, immutable tree structures, and finally we announce the Ruby Hero Awards.
Listen to this episode on Ruby5
This episode is sponsored by Top Ruby Jobs
If you're looking for a top Ruby job or for top Ruby talent, then you should check out Top Ruby Jobs. Top Ruby Jobs is a website dedicated to the best jobs available in the Ruby community.
Rails Security Releases
On Friday and Monday the Rails team put out new security releases. You'll want to upgrade.
State Machine With Rails
If you're just starting Ruby and you're not familiar with the State Machine pattern & Gem, check out this post by Jan Bernacki.
Active Record scopes vs class methods
In Rails 4, as you may know, we all need to start using the Stabby Lambda (->). However, this makes scopes even more similar to plain old class methods. Carlos Antonio wrote up a great blog post comparing the two.
Generating Heat Maps With Ruby
Thiago Jackiw just released a nifty gem called Heatmap which generates heatmaps using Ruby.
Sucker Punch
Brandon Hilkert emailed us over the weekend to let us know about Sucker Punch, a new single-process Ruby asynchronous processing library. It sits on top of Celluloid, the Actor-based concurrent object framework, allowing you to do asynchronous processing from within a single process.
Creating immutable tree data structures in Ruby
Rolf Timmermans just wrote a post on voormedia’s blog about using immutable data structures to create a search API in Ruby. Since immutable data structures can’t be changed after they’re created, they are useful in creating efficient syntax trees that share nodes.
Ruby Heroes
It’s time once again for the Ruby Hero Awards, where we need your help to find those people in the community thanklessly help others and perhaps don’t get the recognition they diserve. So if you have a minute please take a moment to nominate someone by heading over to RubyHeroes.com, typing in the github username of the person you wish to nominate, and give us a reason why they deserve to win.
Posted 3 months back at interblah.net - Home
Two years ago, as a joke and a nod to making things fast, I took a silly
domain name and served a few silly “HTTP” requests using the UK postal service
as the transport layer. It was called Postal Inter.net.

It was good fun, and I really enjoyed some of the requests that we received,
but the “server” has not been accessed for more than a year now, so I think it’s
time to put it to rest.
You were fun, postalinter.net, but your time has passed. I release you into
the quantum foam.
Gallery
Here are a few of the requests that we go. Obviously you are only seeing one
side of the communication; the responses are now lost in the ether (or in
the post boxes of the UK).

The most impressive request thoroughly embraced the nature of TCP/IP, and
arrived in a number of packets, out of order and with some data corruption (see
the missing data on the envelopes), which we had to reconstitute into the actual
request within our ‘server’. Bravo, Tom Stuart!

Tom was challenged for login details, and here was his response.

Alas, I cannot remember what was at http://experthuman.com/proof, and whatever was there is gone now. Perhaps that’s for the best.





Posted 3 months back at Railscasts
Learn how to easily add a user activity feed using the public_activity gem. Here I show both the default setup using model callbacks and a manual way to trigger activities.
Posted 3 months back at The Pug Automatic
The Capybara testing library has built-in functionality for waiting on asynchronous JavaScript (Ajax).
For example, this works even if the content is added by Ajax:
<figure class="code">
1
2
| click_link("Add to favorites")
page.should have_content("Added to favorites")
|
</figure>
Behind the scenes, Capybara will retry finding the content if it’s not yet present.
But if you write optimistic UIs that say “Added to favorites” immediately, and only then fire off the Ajax request, you may write a test more like this:
<figure class="code">
1
2
3
4
| Favorite.count.should == 0
click_link("Add to favorites")
page.should have_content("Added to favorites")
Favorite.count.should == 1
|
</figure>
And that test will fail, because Capybara will not know to wait a while before counting favorites. It only waits on UI changes.
My workaround is to make every Ajax request update something in the UI, and wait for that:
<figure class="code"><figcaption>
app/assets/javascripts/capybara_wait_for_ajax_completion.js.coffee</figcaption>
1
2
3
4
5
6
7
8
9
| $ ->
KLASS = "ajax-completed"
$(document.body)
.on "ajaxSend", ->
$(this).removeClass(KLASS)
.on "ajaxComplete", ->
$(this).addClass(KLASS)
|
</figure>
<figure class="code"><figcaption>
spec/support/helpers.rb</figcaption>
1
2
3
| def wait_for_ajax_completion
page.should have_selector("body.ajax-completed")
end
|
</figure>
<figure class="code"><figcaption>
spec/requests/favoriting_spec.rb</figcaption>
1
2
3
4
5
| Favorite.count.should == 0
click_link("Add to favorites")
page.should have_content("Added to favorites")
wait_for_ajax_completion
Favorite.count.should == 1
|
</figure>
If you have a lot of Ajax going on at once and only want to wait on one particular request, you could do the same thing but on the particular element or request, instead of capturing every request’s events like this.
Posted 3 months back at GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS - Home
The following details many of the different tools, people, and processes that run our business.
Before discussing those tools, we’d like to say we try to avoid building internal tools. It requires time and money to build and makes you reliant on yourself if and when things don’t work.
We try not to fool ourselves into thinking our problems are unique. We will use spreadsheets and manual processes first. When we do build something, it is usually after using other things for years.
Sales
We use Pipedrive for sales pipeline tracking and planning. We switched to it from Highrise, which we still use for tracking communication and people who are not in the immediate sales pipeline.
We have a Wufoo form on thoughtbot.com that potential clients fill out to contact us. It has a web hook, which posts to a Sinatra application, which then posts to Pipedrive’s API.
We use an internal scheduling app we built to manage availability and utilization. We don’t track time, but people plan out their weeks and are assigned to projects in this app. We built the schedule app after many years of using a Google docs spreadsheet to track what everyone was working on and upcoming projects.
Client contracts
We use Campfire to discuss contracts amongst ourselves while we’re drawing them up. We send the contracts to clients as PDFs. From time to time a client requests it in Microsoft Word format to make more drastic changes.
Our contract terms are pretty standard so any important differences between clients manifest themselves in the invoices, so we don’t have a seperate system for tracking contract terms.
We store contracts in Dropbox and have a series of folders for pending, current, past, and lost clients.
Client communication
We often create a “project brief” as a Google Doc for clients.
It lists the names, emails, GitHub accounts, and sometimes phone numbers of everyone on the client and thoughtbot sides.
It lays out the expected schedule of the thoughtbot team (Monday-Thursday each week) and if the client has their own office space, how many days/week we’ll work out of their office (no more than 2/week) and which days to expect us there.
It lists any regular meetings such as daily standups at 10am and a weekly retrospective Monday morning.
It links to the project’s Campfire room, project management system, GitHub repo, and other systems like Amazon Web Services, with shared credentials.
It links to the protocol and style guide that the team is agreeing to follow together.
We sometimes additionally create a spreadsheet for a client that lists all the services they are using, the monthly bill to expect, a description of its purpose, and credentials for signing in. It includes line items like GitHub, Heroku, Sendgrid, New Relic, Splunk, etc.
Invoices
We use Freshbooks for invoicing. Our goal is that all clients have recurring weekly invoices. We also track who the salesperson for a customer is in the notes field in Freshbooks.
Freshbooks provides us with our accounts receivable reports. On a weekly basis they are reported to the leadership team, and we address any concerns.
Clients can pay their invoice via check, credit card directly in Freshbooks, or wire transfer.
We use Authorize.net to accept credit card payments in Freshbooks, but we’d switch to something else if it was available.
Bookkeeping
Our outsourced bookkeeping firm, AccountingDepartment.com, reviews Freshbooks for new invoices and new payments on a daily basis and inputs them into Quickbooks. If any checks or wire transfers have come in, they are entered into Freshbooks and Quickbooks. Freshbooks sends payment received email notifications to both clients and the management team.
AccountingDepartment.com provides us with an outsourced bookkeeper, controller, and tax accountant/CPA. They provide us with a hosted Quickbooks install that we can access via Remote Desktop.
We use Earth Class Mail to receive all paper mail for our offices. This service automatically opens and scans all paper mail and sends it as a PDF email attachment, which we file in Dropbox. Earth Class Mail also automatically detects checks and deposits them into our bank account.
Finance and accounting
In Sweden we are assisted by TMF Group for company, accounting, tax, and human resources.
Last year we started working with an outsourced CFO, Charlie Popkin. Charlie has helped is put in place fairly extensive budgeting and forecasts. This is all done in Excel. On a monthly basis AccountingDepartment.com and Charlie make meaningful management summaries of the comparative financial data, forecasts, and budgets.
Management team collaboration
We use Basecamp to coordinate between Charlie and the leadership team. Examples of recent conversation topics include budgeting for 2013, when to start the process of finding an office manager for the San Francisco office, documenting our quarterly review process, confirming whether our San Francisco client contracts should be under California law, renewing our health insurance plan for another year, identifying the third advisor for our board of advisors, and deciding whether and how much to give for year-end bonuses.
We have a Campfire room for management discussion and planning. This is often discussion about potential projects, leads, availability, rotations, setting up introductions and networking for each other.
Legal
Our law firm is Gesmer Updegrove LLP. They are able to provide us with legal support for most everything we need, which is most commonly client, real estate, and company/stock matters. We also engage Costa & Riccio LLP for US immigration matters.
In Sweden we are assisted by Newcomers for immigration and relocation matters.
We use RightSignature for employee forms and contracts but we’re not currently using it for client contracts.
Content marketing
We use Tumblr for our blog. Our CSS for the blog is hosted in a custom Sinatra app deployed to Heroku.
We use Trello to manage the editorial calendar for the blog. The lists on the “Editoral Calendar” board are Next Up, Drafts, In Review, Published, Promoted.
We also use Trello to manage our technical research. The lists on the “Technical Research” board are Pending, Researching, Discussing, and Resolved. We share the current state of our research in our monthly newsletter, the ‘bot cave. The newsletter is managed by TinyLetter.
Ad buying
We invest about $500/month in Twitter’s Promoted Accounts product. This lets us find people who are interested in similar topics to us, often in Boston, San Francisco, Boulder, and Stockholm, whom we can then share technical tips and links we think are great via our Twitter account.
We spend about $250/month in Google Adwords for our workshops.
We almost never buy display ads, sometimes sponsor a good industry podcast like Dan Benjamin’s 5by5, and don’t sponsor conferences, but do sponsor pub nights, buy dinner for user groups, and pay for childcare of adults taking community-organized training programs.
Marketing metrics
We use Google Analytics and KISSmetrics heavily. Twitter’s Promoted Accounts has a good analytics dashboard that tells us how Twitter users are engaging with our tweets and which ones are resonating with them.
Telecommunication
We used to use tokbox, iChat, and freeconferencecall.com.
Now we use conference lines that are part of our VoIP system, provided by OnSip, for voice conferencing. For video conferencing we use Google Hangouts, Skype, or FaceTime/iChat.
In our conference rooms we have whiteboards, large screen TVs hooked up to Apple TVs, and Mac Minis. We used to have a webcam attached to the TV, but now we just point someone’s computer at the whiteboard or the group.
If you have any questions about any of the above or anything we may have missed, let us know.
Posted 3 months back at GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS - Home
Episode #35: I haven't lifted a pencil in years:
Ben Orenstein is joined by Dennis Najjar CPA from AccountingDepartment.com. They discuss international companies operating in the United States, the tools of his trade, how AccountingDepartment.com is set up and what their different clients look like, and why it makes sense to outsource your bookkeeping and accounting. They also explore the checks and balances you should have in bookkeeping and accounting, the accounting departments role in an organization and 1099s their purpose, and what to do if you don’t get one.
Posted 3 months back at The Pug Automatic
It’s pretty common to have multiple tests that are nearly the same.
In one app, we support bidding on both online and hammer auctions (auctions with people physically present). They’re separate controllers but with a lot of shared code and behavior.
We want to test both, but we’d rather not write two almost identical tests if we can help it.
So we’ve been using RSpec shared examples, with the template method pattern to account for the differences, and we like it.
Here’s a simplified example:
<figure class="code"><figcaption>
spec/request/online_bidding_spec.rb</figcaption>
1
2
3
4
5
6
7
8
9
10
11
12
| require "spec_helper"
require "support/shared_examples/bidding"
describe "Bidding online" do
include_examples :bidding
let(:auction) { FactoryGirl.create(:online_auction) }
def auction_path
online_auction_path(auction)
end
end
|
</figure>
<figure class="code"><figcaption>
spec/request/hammer_bidding_spec.rb</figcaption>
1
2
3
4
5
6
7
8
9
10
11
12
| require "spec_helper"
require "support/shared_examples/bidding"
describe "Bidding at hammer auction" do
include_examples :bidding
let(:auction) { FactoryGirl.create(:hammer_auction) }
def auction_path
hammer_auction_path(auction)
end
end
|
</figure>
<figure class="code"><figcaption>
spec/support/shared_examples/bidding.rb</figcaption>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
| shared_examples :bidding do
it "lets you bid when logged in" do
log_in # Implemented somewhere else.
visit auction_path
place_bid
bid_should_be_accepted
end
it "doesn't let you bid when not logged in" do
visit item_path
place_bid
bid_should_be_rejected
end
def auction_path
raise "Implement me."
end
def place_bid
fill_in "Bid", with: 123
click_button "Place bid"
end
def bid_should_be_accepted
page.should have_content("OK! :)")
end
def bid_should_be_rejected
page.should have_content("NO! :(")
end
end
|
</figure>
The only template method here is auction_path. The shared example makes sure, by raising, that it’s overridden in your concrete specs.
Posted 3 months back at GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS - Home
Chances are, some of you have run into the issue with the invalid byte sequence in UTF-8 error when dealing with user-submitted data. A Google search shows that my hunch isn’t off.
Among the search results are plenty of answers—some using the deprecated iconv library—that might lead you to a sufficient fix. However, among the slew of queries are few answers on how to reliably replicate and test the issue.
In developing the Griddler gem we ran into some cases where the data being posted back to our controller had invalid UTF-8 bytes. For Griddler, our failing case needs to simulate the body of an email having an invalid byte, and encoded as UTF-8.
What are valid and invalid bytes? This table on Wikipedia tells us bytes 192, 193, and 245-255 are off limits. In ruby’s string literal we can represent this by escaping one of those numbers:
> "hi \255"
=> "hi \xAD"
There’s our string with the invalid byte! How do we know for sure? In that IRB session we can simulate a comparable issue by sending a message to the string it won’t like - like split or gsub.
> "hi \255".split(' ')
ArgumentError: invalid byte sequence in UTF-8
from (irb):9:in `split'
from (irb):9
from /Users/joel/.rvm/rubies/ruby-1.9.3-p125/bin/irb:16:in `<main>'
Yup. It certainly does not like that.
Let’s create a very real-world, enterprise-level, business-critical test case:
invalid_byte_spec.rb
require 'rspec'
def replace_name(body, name)
body.gsub(/joel/, name)
end
describe 'replace_name' do
it 'removes my name' do
body = "hello joel"
replace_name(body, 'hank').should eq "hello hank"
end
it 'clears out invalid UTF-8 bytes' do
body = "hello joel\255"
replace_name(body, 'hank').should eq "hello hank"
end
end
The first test passes as expected, and the second will fail as expected but not with the error we want. By adding that extra byte we should see an exception raised similar to what we simulated in IRB. Instead it’s failing in the comparison with the expected value.
1) replace_name clears out invalid UTF-8 bytes
Failure/Error: replace_name(body, 'hank').should eq "hello hank"
expected: "hello hank"
got: "hello hank\xAD"
(compared using ==)
# ./invalid_byte_spec.rb:17:in `block (2 levels) in <top (required)>'
Why isn’t it failing properly? If we pry into our running test we find out that inside our file the strings being passed around are encoded as ASCII-8BIT instead of UTF-8.
[2] pry(#<RSpec::Core::ExampleGroup::Nested_1>)> body.encoding
=> #<Encoding:ASCII-8BIT>
As a result we’ll have to force that string’s encoding to UTF-8:
it 'clears out invalid UTF-8 bytes' do
body = "hello joel\255".force_encoding('UTF-8')
replace_name(body, 'hank').should_not raise_error(ArgumentError)
replace_name(body, 'hank').should eq "hello hank"
end
By running the test now we will see our desired exception
1) replace_name clears out invalid UTF-8 bytes
Failure/Error: body.gsub(/joel/, name)
ArgumentError:
invalid byte sequence in UTF-8
# ./invalid_byte_spec.rb:4:in `gsub'
# ./invalid_byte_spec.rb:4:in `replace_name'
# ./invalid_byte_spec.rb:17:in `block (2 levels) in <top (required)>'
Finished in 0.00426 seconds
2 examples, 1 failure
Now that we’re comfortably in the red part of red/green/refactor we can move on to getting this passing by updating our replace_name method.
def replace_name(body, name)
body
.encode('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '')
.gsub(/joel/, name)
end
And the test?
Finished in 0.04252 seconds
2 examples, 0 failures
For such a small piece of code we admittedly had to jump through some hoops. Through that process, however, we learned a bit about character encoding and how to put ourselves in the right position—through the red/green/refactor cycle—to fix bugs we will undoubtedly run into while writing software.
Posted 3 months back at GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS - Home
Come to BarCamp Stockholm on March 9, 2013!
Organized by thoughtbot and hosted by Spotify, BarCamp Stockholm will be the Nordic unconference of 2013.
Register at our fancy official Web site thing: http://barcampstockholm.com/.
An unconference?
People who love mobile, the Web, open source, UX, design, programming, startups, or nerdy atmospheres: this is for you. At this unconference you can lead a discussion with like-minded people, give a talk to fascinated listeners, or make your rounds along the hallway track.
The unconference is what you make of it. Want a talk about Android UIs? Sign up. An armchair linguist? Throw your name on the whiteboard. Want to hack sweet code in the hallway while soaking up the buzz of energy? You got it.
Plus, free food and drinks!
Got it?
Got it. Register today and we’ll see you there!
Posted 4 months back at Segment7
As a preview release, please file bugs for any problems you have with rdoc at
https://github.com/rdoc/rdoc/issues
RDoc 4.0 includes several new features and several breaking changes. The
changes should not affect users of rdoc or ri.
Notable feature additions are markdown support and an WEBrick servlet that can
serve HTML from an ri store. (This means that RubyGems 2.0+ no longer needs
to build HTML documentation when installing gems.)
Changes since RDoc 3.12.1:
Breaking changes
- The default output encoding for RDoc is now UTF-8. Previously RDoc used
the default external encoding which was determined from your locale.
Issue #106 by Justin Baker.
RDoc::RI::Store is now RDoc::Store so ri data generated by RDoc 4 cannot
be read by earlier versions of RDoc. RDoc::RI::Store exists as an alias
of RDoc::Store so ri data from older versions can still be read.
RDoc::RI::Store will be removed in RDoc 5.
Tests that create RDoc::CodeObjects on the fly without wiring them into
the documentation tree (did not use addclass, addmethod, etc.) must be
updated to use these methods. The documentation tree automatically
attaches them to the store instance which allows lookups to work
correctly. Additionally, a new method RDoc::Store#add_file must be used
instead of RDoc::TopLevel.new. The latter will not be attached to the
documentation tree.
- RDoc generators must accept an RDoc::Store and an RDoc::Options in
initialize. RDoc no longer passes an Array of RDoc::TopLevel objects to #generate. Use RDoc::Store#all_files instead.
- Some markup formatters (RDoc::Markup::To) now accept an RDoc::Options
instance as the first argument. Notably, the base class Formatter and
ToHtml. (This is not universal due to the difficult at accessing the
user's options instance deep inside RDoc. A future major release may
remedy this.)
- Added new markup nodes and specials that RDoc::Markup::Formatter
subclasses must handle. If you're using RDoc::Markup::FormatterTestCase
the new methods you need to add should be readily apparent.
- Removed RDoc::RI::Paths::SYSDIR and ::SITEDIR. These were hidden
constants so no breakage is expected. Use RDoc::RI::Paths::systemdir
and ::sitedir instead.
- RDoc::RI::Store#modules has been renamed to RDoc::Store#modulenames
to avoid confusion with RDoc::Store#allmodules imported from
RDoc::TopLevel.
- RDoc::RDocError has been removed. It was deprecated throughout RDoc 3.
- ri -f html is no longer supported.
- Comment definitions in C comments are now only discovered from the first
line. A colon on a subsequent line won't trigger definition extraction.
Issue #103, see also
http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/42942
- Fixed :stopdoc: for class A::B where A has not been seen. Issue #95 by
Ryan Davis
- RDoc::ClassModule#each_ancestor no longer yields itself if there is
circular ancestry
Major enhancements
ri can now show pages (README, etc.)
ri rdoc:README
Will show the README for the latest version of RDoc. You can specify
exact gem versions such as "rdoc-4.0:README" or view pages from the
standard library documentation with "ruby:README".
RDoc 3 did not save pages in ri data so you will need to regenerate
documentation from your gems to use this feature.
- Added Markdown as a supported format. The markdown format can be set on a
per-file or per-comment basis with the +:markdown:+ directive like the rd
and tomdoc formats and on a per-project basis with
rdoc --markup markdown --write-options
Removed global state from RDoc. RDoc::Store holds the documentation tree
and connects the driver to the parsers and generator. This also allows
documentation parsing and generation for multiple instances, but the rdoc
command-line tool does not support this.
Due to this change RDoc::RDoc.current and RDoc::RDoc.reset no longer
exist.
Minor enhancements
Added --page-dir option to give pretty names for a FAQ, guides, or other
documentation you write that is not stored in the project root. For
example, with the following layout:
README.txt
guides/syntax.txt
guides/conversion.txt
Running rdoc --page-dir guides will make the files in "guides" appear to
be at the top level of the project. This means they will appear to exist
at the top level in HTML output and you can access them with
ri your_gem:syntax and ri your_gem:conversion.
- Added --root for building documentation from outside the source dir.
- Added current heading and page-top links to HTML headings.
- Added a ChangeLog parser. It automatically parses files that begin
with 'ChangeLog'
- Added a table of contents to the sidebar.
- RDoc markup format merges adjacent labels in a label or note list into a
single definition list item for output.
- RDoc now tracks use of extend. Pull request #118 by Michael Granger.
- RDoc now tracks methods that use super. Pull request #116 by Erik
Hollensbe.
- Added methods ::systemdir, ::sitedir, ::homedir and ::gemdir to fetch
the components of RDoc::RI::Paths.path individually.
- Added support for rbfileconst.
- RDoc now processes files in sorted order. Issue #71 by Vít Ondruch
- RDoc now warns with --verbose when methods are duplicated. Issue #71 by
Vít Ondruch
- ri will display documentation for all methods in a class if -a is given.
Issue #57 by casper
- The RDoc coverage report will report line information for attributes,
constants and methods missing documentation. Issue #121 by Zachary Scott
- RDoc now reports a warning for files that are unreadable due to
permissions problems.
- RDoc controls documentation generation for RubyGems 2.0+
Bug fixes
- Fixed parsing of multibyte files with incomplete characters at byte 1024.
Ruby bug #6393 by nobu, patch by Nobuyoshi Nakada and Yui NARUSE.
- Fixed rdoc -E. Ruby Bug #6392 and (modified) patch by Nobuyoshi Nakada
- Added link handling to Markdown output. Bug #160 by burningTyger.
- Fixed HEREDOC output for the limited case of a heredoc followed by a line
end. When a HEREDOC is not followed by a line end RDoc is not currently
smart enough to restore the source correctly. Bug #162 by Zachary Scott.
- Fixed parsing of executables with shebang and encoding comments. Bug #161
by Marcus Stollsteimer
- RDoc now ignores methods defined on constants instead of creating a fake
module. Bug #163 by Zachary Scott.
- Fixed ChangeLog parsing for FFI gem. Bug #165 by Zachary Scott.
- RDoc now links #=== methods. Bug #164 by Zachary Scott.
- Allow [] following argument names for TomDoc. Bug #167 by Ellis Berner.
- Fixed the RDoc servlet for home and site directories. Bug #170 by Thomas
Leitner.
- Fixed references to methods in the RDoc servlet. Bug #171 by Thomas
Leitner.
- Fixed debug message when generating the darkfish root page. Pull Request #174 by Thomas Leitner.
- Fixed deletion of attribute ri data when a class was loaded then saved.
Issue #171 by Thomas Leitner.
- Fully qualified names for constants declared from the top level are now
attached to their class or module properly.
- Fixed table of contents display in HTML output for classes and modules.
- Incremental ri builds of C files now work. C variable names from previous
runs are now saved between runs.
- A word that is directly followed by a multi-word tidy link label no longer
disappears. (Like
text{link}[http://example])
- Fixed legacy template support. Pull Request #107 by Justin Baker.
- An HTML class in a verbatim section no longer triggers ruby parsing.
Issue #92 by Vijay Dev
- Improved documentation for setting the default documentation format for
your ruby project. Issue #94 by Henrik Hodne
- Fixed handling of LANG in the RDoc::Options tests. Issue #99 by Vít
Ondruch
- RDoc no longer quits when given an entry that is not a file or directory.
Issue #101 by Charles Nutter
- Fixed bug in syntax-highlighting that would corrupt regular expressions.
Ruby Bug #6488 by Benny Lyne Amorsen.
- "class Object" no longer appears in the coverage report if all its methods
are documented. This suppresses a false positive for libraries that add
toplevel methods. Pull Request #128 by Zachary Scott.
- Fixed testgenurl test name in TestRDocMarkupToHtml. Pull Request #130
by Zachary Scott.
- Comment-defined methods ahead of define_method are now discovered. Issue #133 by eclectic923
- Fixed detection of define_method documentation. Issue #138 by Marvin
Gülker.
- Fixed lexing of character syntax (
?z). Reported by Xavier
Noria.
- Add license to gem spec. Issue #144 by pivotalcommon
- Fixed comment selection for classes. Pull request #146 by pioz
- Fixed parsing of
def self.&() end. Issue #148 by Michael
Lucy
- Generated RD parser files are now included in the gem. Issue #145 by
Marvin Gülker
- Class and module aliases now create new classes to avoid duplicate names
in the class list. Issue #143 by Richard Schneeman, Rails Issue #2839
- RDoc::Markup::Parser now correctly matches indentation of lists when
multibyte characters are used in the list labels. Issue #140 by
burningTyger
- Fixed mangling of email addresses that look like labels. Issue #129 by
Tobias Koch
- Classes and modules in a C file may now be created in any order. Issue #124 by Su Zhang
- A metaprogrammed method supports the :args: directive. Issue #100
- A metaprogrammed method supports the :yields: directive.
- RDoc will now look for directives up to the end of the line. For example,
class B < A; end # :nodoc:
will now hide documentation of B. Issue #125 by Zachary Scott
- Fixed tokenization of % when it is not followed by a $-string type
- Fixed display of END in documentation examples in HTML output
- Fixed tokenization of reserved words used as new-style hash keys
- RDoc now handles class << $gvar by ignoring the body
- Fixed parsing of class A:: B.
- Worked around bug in RDoc::RubyLex where tokens won't be reinterpreted
after unget_tk.
- Fixed class << ::Foo writing documentation to /Foo.html
- Fixed class ::A referencing itself from inside its own namespace.
Changes since RDoc 4.0.0.rc.2:
- Bug fix
- Templates now use the correct encoding when generating pages. Issue #183
by Vít Ondruch
Posted 4 months back at GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS - Home
Todays’ release of Ruby Science includes two new chapters. If you’re already
reading Ruby Science, make sure to log into GitHub and download the latest
version.
In this week’s updates, we cover composition and inheritance. You’ll learn about the uses and drawbacks of Single Table Inheritance (STI), as well as how to convert an STI hierarchy to use composition through polymorphic associations.
The book is a work in progress, and currently contains around 104 pages of content. A $49 purchase gets you access to the current release of the book, all future updates, and the companion example application. In addition, purchasers have the ability to send thoughtbot their toughest Ruby, Rails, and refactoring questions.
Get your copy of Ruby Science today.
Posted 4 months back at Synthesis
I’ve been heads down working on a small, simple software app that is super simple but potentially pretty useful to individuals working within companies with more than a handful of employees. I’m finally getting close to finishing the first release.
I mentioned the idea to a friend one day last fall and he said “Yes! You should do it!” so I suspended work on the other side project I was working on and jumped right on it, since it would be super quick. I’d get it out there and be back to work on my other side project. Funny how that works.
The initial proof of concept was done in a couple days – you could get the idea of how it would work, but it was just a fake out. All that was left was to make it real. Simple, right? I kept at it for two weeks, then left for a three week trip, which brought progress to a halt. Once I got back from the trip, it was the holiday season and I needed to focus on billable work in December to make up for being gone. Finally in January I picked it back up. What initially seemed like something that could be done in a month has stretched into a few, but it’s so close.
Game Mechanics
The app is actually a game, which is my first foray into building something intended to be “played” – to be fun and challenging. Designing a game is super interesting, it’s almost like playing a game in itself, and eventually once you’ve built enough of it, you are indeed playing the game. Implementing game mechanics like scoring points, playing rounds, and thinking about how to make something fun and challenging vs. efficient and useful is super enlightening. The best part is that this app sits in the middle – it’s fun, but also useful and repeated play gives you real value in your actual life.
Posted 4 months back at Ruby5
You got your Python in my Ruby! RMagick news, linotype, a U.S. State font, mail_room, Code Triage, Party Foul, and the voice stylings of your #rubyloco compatriots on this edition of Ruby5
Listen to this episode on Ruby5
This episode is sponsored by New Relic
NewRelic just keeps getting better. You can now monitor your instances from an iphone app. How James Bond is that?!?! Deploy today and get a free t-shirt. http://NewRelic.com
Topaz
Topaz as a ruby implementation written in python. Its primary goals are simplicity and performance.
RMagick
After a long 'island of stability' RMagic is out with a small patch, largely to fix installation issues with ruby 1.9.3 and ImageMagick 6.8+
Skeptik
Skeptick is an all-purpose DSL for building and running ImageMagick commands. It helps you build any transformations, from trivial resizes to complex mask algorithms and free drawing. In a nutshell, Skeptick is nothing more than a string manipulator and a process spawner. That's all it's meant to be. However, with Skeptick you get quite a few advantages over using plain shell-out or other libraries.
linotype
Linotype is a letterpress-like game engine for Ruby. Apparently Chris is a letterpress fan and found this fascinating.
Stately
The cool kids at Intridea are at it again... this time they've released a glyph font composed of images of all the U.S. States. They fit together seamlessly too, so you can easily use it to build things like the infamous 'red state/blue state' maps at election time.
mail_room
What a beautifully simple idea. mail_room will sit idly by, polling an imap connection, and will post received messages to a delivery url of your choosing.
Code Triage
Have you ever wanted to contribute to open source, but just been daunted by jumping in? Check out Code Triage... you list a few projects you like, and it sends you occasional issues you might be able to help with. Imagine a world where all the companies that profited from open source dedicated a small amount of their resources to doing this...
Party Foul
Party Foul just hit its 1.0 release! With PArty Foul, you can automatically use exceptions your application generates to create github issues.
Posted 4 months back at The Pug Automatic
The other day, I wanted to extract some validation logic from an Active Record model into its own class.
Initially I tried Rails’ validates_with and ActiveModel::Validator.
It went something like this:
<figure class="code">
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| class MyModel < ActiveRecord::Base
validates_with Validator
end
class MyModel
class Validator < ActiveModel::Validator
def validate(record)
@record = record
validate_not_bad
end
private
def validate_not_bad
record.errors.add_to_base("Bad!") if bad?
end
def bad?
properties.include?(:evil) || properties.include?(:nasty)
end
def properties
@properties ||= record.some_expensive_lookup
end
def record
@record
end
end
end
|
</figure>
I wanted private helper methods, and I didn’t want to pass the record around as method arguments to each, so I treated the validator as a regular object, though Rails only offered me a validate method, not an initializer.
But it soon became apparent that it indeed wasn’t the regular object I hoped for. In my tests, the memoized properties from one validation run would still be around when validating a second time.
The validator was not initialized once per validation run, as one might expect, but only once when the class loads.
So I rewrote it as a plain old Ruby object (“PORO”) with a minimum of boilerplate and glue, and as far as I can tell, it works better, with less magic and less surprises:
<figure class="code">
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| class MyModel < ActiveRecord::Base
validate do |record|
Validator.new(record).validate
end
end
class MyModel
class Validator
def initialize(record)
@record = record
end
def validate
validate_not_bad
end
private
# The exact same private methods.
end
end
|
</figure>
After a brief look at the Rails Validator code, I suspect the class is defensible as a base for Rails’ built-in validations, but it doesn’t seem worthwhile to build your own validators around it, to me. But please let me know if I’m missing something.