The Missing Beer Styles, Making it into BrewSession

by gregr

Sticke Alt, Australian Sparkling Ale, Leipziger Gose, Classic American Cream Ale, Imperial Lager.  What do all these beer styles have in common?  Done guessing?  Hint: They’re not in the Beer Judges Certification Program Style Guidelines.  Nor are a lot of other lesser-known beer styles.  The BJCP Style Guide is the main source of style guidelines used by home and microbrewers in the US, and beyond.  But it has its purpose, educating beer judges.  For a brewer, it’s just not quite complete.

As noted previously, we’ll be including the FULL BJCP Beer Style Guidelines in BrewSession.  However, lately, as I’ve brewed my last few batches of beer that didn’t fall into a BJCP specified style, I started thinking about those “missing styles”.

Certainly there are many, many modern and ancient styles that aren’t in the BJCP style guidelines.  Some are just too obscure, some are even commercially extinct.  But there are some styles that, still, deserve to be recognized … somehow.

Some of these obscure styles even get brief mentions in the BJCP Style Guide as part of another similar or contrasting style.  Others are mentioned in BJCP Style Category 23A which is purposely labeled a “catch-all” category of beers that don’t have their own category.  Wouldn’t it be nice to have some guidelines to help you brew all those styles that haven’t made it into the official BJCP Style Guide?

There are well over 20 styles that are of major interest to the home and micro-brewer that I think need to be honored with a style guideline.  So, recently I started researching and posting about several of these “missing” styles on my own blog, “Food, Beer & Buffoonery” .  I started with two varieties of German Altbier that are different enough from the standard Alt to deserve mention: Sticke Ale and Münster Alt.

I’ll be periodically adding more posts about other non-BJCP beer styles that are of interest to us brewers.  I’ll also be continually updating previous style posts as new information comes in.  With all of these, I’ll be “attempting” (with big help from many of you) to create a BJCP-like style description for each of these, so that we can eventually include these “missing styles” into BrewSession. Then, when you have the guidelines at your fingertips, you’ll be able to accurately brew that Sticke Alt, that Australian Sparkling Ale, that Leipziger Gose, that Classic American Cream Ale …

Upcoming styles in the series will include, but are not limited to: Kellerbier, Gose, Wiess, Honey Beers, Classic American Cream Ale, Czech Dark Lager, English Pale Mild, Scottish 90/-, American Stock Ale, English Strong Ale, Non-alcoholic “Beer”, Malt Liquor, Australian Sparkling Ale, Imperial/Double Red Ale, Imperial/Double Brown Ale, Imperial Lager, Imperial Pilsner, Imperial Porter, Rye IPA, Dark American Wheat/Rye.

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

Posted by: gregr @ 9:28 pm

Categories: Greg, features, related |

No Comments »

 Extending acts_as_commentable

by Dean

acts_as_commentable is a nice little ruby on rails plugin. It extends your ActiveRecord classes giving them comments. We are going to use comments on all kinds of things, starting with recipes, of course. However, AAC lacks a critical feature: the ability for users to approve comments before they are displayed. In this post I am going to run through extending AAC using acts_as_state_machine.

The first thing I did (and do to all the plugins we use) was pistonize the plugin so I could hack on it without fear of getting my changes destroyed.

I start off simply here by adding two states to the Comment model: :pending and :approved.

  1. class Comment < ActiveRecord::Base
  2.   # The first element of this array is the initial state
  3.   VALID_STATES = [ :pending, :approved ]
  4.   acts_as_state_machine :initial => VALID_STATES[0]
  5.  
  6.   event :approve do
  7.     transitions :from => :pending, :to => :approved
  8.   end
  9.  
  10.   VALID_STATES.each do |_state|
  11.     # Define _state as a state
  12.     state _state
  13.   end
  14.  
  15.   # More code snipped
  16. end

Now we are going to write some real code, so here comes a little RSpec. aac provides three class methods:

  1. class Comment < ActiveRecord::Base
  2.   class << self
  3.   # Helper class method to lookup all comments assigned
  4.   # to all commentable types for a given user.
  5.   def find_comments_by_user(user)
  6.  
  7.   # Helper class method to look up all comments for
  8.   # commentable class name and commentable id.
  9.   def find_comments_for_commentable(commentable_str, commentable_id)
  10.  
  11.   # Helper class method to look up a commentable object
  12.   # given the commentable class name and id
  13.   def find_commentable(commentable_str, commentable_id)
  14.   end

Since it didn’t come with Test::Unit or RSpec tests I wrote up some test for these methods.

  1. describe Comment, "class methods" do
  2.   fixtures :comments, :recipes, :users
  3.   it "should find comments by user" do
  4.     Comment.find_comments_by_user( comments(:comment_one).user ).should all_belong_to( comments(:comment_one).user )
  5.   end
  6.  
  7.   # This could be more specific
  8.   it "should find comments for a particular class" do
  9.     Comment.find_comments_for_commentable( Comments(:comment_one).commentable_type, comments(:comment_one).commentable_id ).should be_an_instance_of(Array)
  10.   end
  11.  
  12.   it "should find all comments for a particular class" do
  13.     # I happen to know that comment_one is a recipe comment
  14.     Comment.find_commentable( "Recipe", comments(:comment_one).commentable_id ).should be_an_instance_of(Recipe)
  15.   end
  16.  
  17. end

If you are confused by should all_belong_to then you should check out my previous post. With these specs out of the way we can go on to adding more new code.

  1.  it "should find approved comments by user" do
  2.     Comment.find_approved_comments_by_user( comments(:comment_one).user ).should all_be_in_state("approved")
  3.   end
  4.  
  5.   it "should find pending comments by user" do
  6.     Comment.find_pending_comments_by_user( comments(:comment_one).user ).should all_be_in_state("pending")
  7.   end
  8. end

Now, normally you would write one spec at a time, but I think I would bore my readers, so I combined these two. Also take note that I am using another custom RSpec matcher all_be_in_state(). It looks a lot like all_belong_to(), so I leave its implementation as an exercise to the reader (unless I can get another blog post out of it). To get these tests to pass I add a few lines of code:

  1.  VALID_STATES.each do |_state|
  2.     # Define _state as a state
  3.     state _state
  4.  
  5.     # Add Comment.find__comments methods
  6.     ( class << self; self; end ).instance_eval do
  7.       define_method "find_#{_state}_comments_by_user" do |_user|
  8.         find_in_state( :all, _state, :conditions => ["user_id = ?", _user.id], :order => "created_at DESC" )
  9.       end
  10.     end
  11.   end

I am not a method_missing kind of guy, and prefer the dynamic-method metaprogramming style. This lot of code defines class methods at runtime that find Comments in specific states. I am actually using whytheluckystiff’s metaid to hide some of the meta-junk, but I thought I should spell it out here for clarity.

Well, now we have a Comment class with two states and code to limit finds to cmments in a specific state. Right now, that is all I have. Here is the full code for the Comment class and the RSpec. You will see another custom RSpec matcher here, require_a().

  1. class Comment < ActiveRecord::Base
  2.  
  3.   # The first element of this array is the initial state
  4.   VALID_STATES = [ :pending, :approved ]
  5.  
  6.   acts_as_state_machine :initial => VALID_STATES[0]
  7.  
  8.   belongs_to :commentable, :polymorphic => true
  9.   belongs_to :user
  10.  
  11.   event :approve do
  12.     transitions :from => :pending, :to => :approved
  13.   end
  14.  
  15.   validates_associated :user
  16.   validates_presence_of :comment, :commentable_id, :commentable_type, :state,                           :user_id
  17.  
  18.   VALID_STATES.each do |_state|
  19.     # Define _state as a state
  20.     state _state
  21.  
  22.     # Add Comment.find_<state>_comments methods
  23.     meta_def "find_#{_state}_comments_by_user" do |_user|
  24.       find_in_state( :all, _state, :conditions => ["user_id = ?", _user.id],
  25.                      :order => "created_at DESC" )
  26.     end
  27.   end
  28.  
  29.   class < < self
  30.  
  31.     # Helper class method to look up a commentable object
  32.     # given the commentable class name and id
  33.     def find_commentable(commentable_str, commentable_id)
  34.       commentable_str.constantize.find(commentable_id)
  35.     end
  36.  
  37.     # This could be refactored into find_<state>_comments_by_user (somehow)
  38.     def find_comments_by_user(_user)
  39.       find( :all, :conditions => ["user_id = ?", _user.id],
  40.             :order => "created_at DESC" )
  41.     end
  42.  
  43.     # Helper class method to look up all comments for
  44.     # commentable class name and commentable id.
  45.     def find_comments_for_commentable(commentable_str, commentable_id)
  46.       find( :all,
  47.             :conditions => [ "commentable_type = ? and commentable_id = ?",
  48.                              commentable_str, commentable_id ],
  49.             :order => "created_at DESC" )
  50.     end
  51.  
  52.   end
  53.  
  54. end</state>
  1. require File.dirname(__FILE__) + ‘/../../../../spec/spec_helper’
  2.  
  3. module CommentSpecHelper
  4.  
  5. end
  6.  
  7. describe Comment do
  8.  
  9.   fixtures :comments
  10.  
  11.   include CommentSpecHelper
  12.  
  13.   before(:each) do
  14.     @comment = Comment.new
  15.   end
  16.  
  17.   it "should start out in pending state" do
  18.     @comment.state.should == "pending"
  19.   end
  20.  
  21.   it "sould transition to approved" do
  22.     @comment = comments(:pending_comment)
  23.     @comment.approve!
  24.     @comment.state.should == "approved"
  25.   end
  26.  
  27.   it "should require a comment" do
  28.     @comment.should require_a(:comment)
  29.   end
  30.  
  31.   it "should require a commentable_id" do
  32.     @comment.should require_a(:commentable_id)
  33.   end
  34.  
  35.   it "should require a commentable_type" do
  36.     @comment.should require_a(:commentable_type)
  37.   end
  38.  
  39.   it "should require a state" do
  40.     @comment.should require_a(:state)
  41.   end
  42.  
  43.   it "should require a user_id" do
  44.     @comment.should require_a(:user_id)
  45.   end
  46.  
  47. end
  48.  
  49. describe Comment, "class methods" do
  50.  
  51.   fixtures :comments, :recipes, :users
  52.  
  53.   it "should find all comments for a particular class" do
  54.     # I happen to know that comment_one is a recipe comment
  55.     Comment.find_commentable( "Recipe", comments(:comment_one).commentable_id ).should be_an_instance_of(Recipe)
  56.   end
  57.  
  58.   it "should find comments by user" do
  59.     Comment.find_comments_by_user( comments(:comment_one).user ).should all_belong_to( comments(:comment_one).user )
  60.   end
  61.  
  62.   it "should find approved comments by user" do
  63.     Comment.find_approved_comments_by_user( comments(:comment_one).user ).should all_be_in_state("approved")
  64.   end
  65.  
  66.   it "should find pending comments by user" do
  67.     Comment.find_pending_comments_by_user( comments(:comment_one).user ).should all_be_in_state("pending")
  68.   end
  69.  
  70.   # This could be more specific
  71.   it "should find comments for a particular class" do
  72.     Comment.find_comments_for_commentable( comments(:comment_one).commentable_type, comments(:comment_one).commentable_id ).should be_an_instance_of(Array)
  73.   end
  74.  
  75. end

–Dean

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

Posted by: Dean @ 8:48 pm

Categories: Dean, code, ruby |

No Comments »

 belong_to RSpec matcher

by Dean

I was extending acts_as_commentable and needed a good RSpec test to check the returned objects from its finder methods belonged to the correct user. For example, Comment.find_comments_by_user( :some_user ) should all belong_to :some_user. I’ll be darned if that doesn’t look like a RSpec description. Since there is no all_belong_to matcher, I wrote one.

  1. module ActiveRecordValidations
  2.   class BelongTo
  3.     def initialize(expected)
  4.       @expected = expected
  5.     end
  6.  
  7.     def matches?(args)
  8.       args.all? do |target|
  9.         @target = target
  10.         @target.send(@expected.class.to_s.downcase) == @expected
  11.       end
  12.     end
  13.  
  14.     def failure_message
  15.       "expected #{@target.inspect} to all belong to #{@expected}"
  16.     end
  17.  
  18.     def negative_failure_message
  19.       "expected #{@target.inspect} not to all belong to #{@expected}"
  20.     end
  21.   end
  22.  
  23.   def belong_to(expected)
  24.     BelongTo.new( [expected] )
  25.   end
  26.  
  27.   def all_belong_to(expected)
  28.     BelongTo.new( expected )
  29.   end
  30. end

The matches? method takes an array of objects and goes through them with all? checking that they have a belongs_to the expected thing. Using the example above, each comment object returned by Comment.find_comments_by_user would get tested if comment.user== @user.

–Dean

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

Posted by: Dean @ 4:59 pm

Categories: Dean, code, ruby |

No Comments »

Our Sponsors