Track your BJCP scoresheets

by Dean

I am at the beginning of pulling all the recipe bits together. Like some of the older brewing software available, you will be able to track each time you brew a particular recipe. I am calling each separate time a “brew session” and each recipe can have many brew sessions.

Part of the fun in making beer is participating in competitions. The BJCP sponsors hundreds of competitions each year and has given us generous permission to use their style guidelines in BrewSession. The results from each judge can be an invaluable tool for improving your beer. Tonight I set up the relationship that will let you enter and track each score sheet you receive. Each time you brew a beer you can attach any number of score sheets to that session. Wouldn’t it be cool if you could:

  • … track your recipe’s score variation over time?
  • … average the scores for all your sessions?
  • … view other member’s scores and recipes for the same style?
  • … check out how the judge scored other member’s brews in the same competition?

I am really excited about sharing recipes with members and seeing what links we can make.

–Dean

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

Posted by: Dean @ 1:46 am

Categories: Dean, features |

3 Comments »

 Hey, my first rails plugin

by Dean

Ruby on Rails allows for plugins to extend its functionality. My first attempt at one is nothing special but I am going to blog about it anyway :-D

I needed a way to check if a number is within a range before saving a record to the database. Rails has a number of good validations built in, but not one that does that. In two or three hours, I was able to piece together a simple plugin that adds “validates_range_of” to my AR models. The code is a little ugly until I can get Greg to install a syntax highlighting plugin for this blog.

  1. module ActiveRecord
  2. module Validations
  3. module ClassMethods
  4. def validates_range_of(*attrs)
  5. options = {  :on => :save  }
  6. options.update(attrs.pop) if attrs.last.is_a?(Hash)
  7. attrs.flatten!
  8. unless options[:message]
  9. if options[:maximum] && options[:minimum]
  10. options[:message] = ‘must be betweeen ‘ + options[:minimum].to_s + ‘ and ‘ + options[:maximum].to_s
  11. elsif options[:maximum]
  12. options[:message] = ‘must be less than or equal to ‘ + options[:maximum].to_s
  13. elsif options[:minimum]
  14. options[:message] = ‘must be greater than or equal to ‘ + options[:minimum].to_s
  15. else
  16. raise ArgumentError, ‘Range unspecified.  Specify the :maximum and/or :minimum.’
  17. end
  18. end
  19.  
  20. validates_numericality_of( attrs, options )
  21.  
  22. validates_each(attrs, options) do |record, attr_name, value|
  23. if ((!options[:maximum].nil?) && (!options[:minimum].nil?)) &&
  24. (value > options[:maximum] || value < options[:minimum])
  25. record.errors.add( attr_name, options[:message] )
  26. elsif (!options[:maximum].nil?) && value > options[:maximum]
  27. record.errors.add( attr_name, options[:message] )
  28. elsif (!options[:minimum].nil?) && value < options[:minimum]
  29. record.errors.add( attr_name, options[:message] )
  30. end
  31. end
  32. end
  33. end
  34. end
  35. end

As you can see this plugin extends AR::Validations. So my models can use validates_range_of to check minimum and maximum values. Let’s look at the BJCP Scoresheet I needed it for:

  1. class BjcpScoresheet < ActiveRecord::Base
  2. belongs_to :brew_session
  3. validates_associated :brew_session
  4. validates_range_of :brew_session_id, { :minimum => 1, :message => ‘does not belong to a brew session’ }
  5. validates_range_of :aroma_score, { :minimum => 1, :maximum => 12, :only_integer => true }
  6. validates_range_of :appearance_score, { :minimum => 1, :maximum => 3, :only_integer => true }
  7. validates_range_of :flavor_score, { :minimum => 1, :maximum => 20, :only_integer => true }
  8. validates_range_of :mouthfeel_score, { :minimum => 1, :maximum => 10, :only_integer => true }
  9. validates_range_of :impression_score, { :minimum => 1, :maximum => 10, :only_integer => true }

How nice - reusable user input validation.

–Dean

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

Posted by: Dean @ 1:46 am

Categories: Dean, code, ruby |

2 Comments »

 Obligatory new blogger post

by Dean

I'm not a blogger, I just like looking under-sexedI’m happy to see we’re getting some traffic and, being a new blogger, I want to just crow a little about it.

(9:43:00 AM) Greg: we suddenly have 89 unique visitors to the blog. At the beginning of the month we had 0.
(9:43:28 AM) Uh, me: very nice
(9:43:57 AM) Greg: and for our home page we had 42 visitors for Jan two days ago, now we have 64
(9:44:19 AM) Uh, me: yay - more traffic

Thanks to the HBD post I made for bringing us traffic. Greg is also driving people here from the Northern Brewer forums.

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

Posted by: Dean @ 10:21 am

Categories: Dean |

No Comments »

 Of all the things I miss….

by Dean

Before the death of my drives I had a flexible Measurement class written up. As most of you know brewing involves all kinds of measurement - hop weights, boil volume, bitterness, etc. The class served as a base that more specific classes would inherit and provided the framework for converting between different measurement systems. For example, the SpecificGravity class would inherit Measurement and provide the conversion code to switch between SG and Plato.

The database would save the scalar and units and instantiate an aggregation of these two pieces. When the units changed, back into the database it went with the new values. It was very easy to work with, but took me quite a bit of time to write.

“Why not use one of the libraries already available?” you might ask. The short answer is that they do not fit my needs. Firstly, none of them produced objects that I could stuff in the database as a aggregation - most are intended as great extensions to various number classes. Secondly, and most importantly, none of them dealt with Bitterness, Color or Specific Gravity, which is really why I need a measurement class. Lastly, most of them only handled linear transformations:

m2 = a * m1 + b

To convert from SG to Plato you need to use a cubic polynomial, ugh.

So I wrote my own, then re-wrote it, and it was beautiful. Now I am re-engineering the whole thing. In addition, I’ll have to figure out where I got the Plato to SG reverse conversion. I remember trying to solve the cubic equation, then finding something that actually worked.

It is a little faster-going because I wrote it all before, but I had some tr1cky 31337 code in there that I will have to figure out again.

Always make sure your backups are working and current.

–Dean

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

Posted by: Dean @ 9:51 pm

Categories: Dean, code, disaster, ruby |

1 Comment »

 Progress report

by Dean

‘Been working hard on BrewSession for a few days and I’d like to tell my readers (or is it reader?) about it. I got the hop data Greg compiled and the BJCP style guidelines back into the database. It feels like an accomplishment because this data is critical to meaningful brewing software. Next up is to move my spreadsheet of yeast information into the database and re-compile the data I had about extras. Today is a holiday in the US so I will be hard at work on that. Our biggest data hurdle is finding malt information and putting it into a consistent format. Greg is working hard on that. Once all our data is loaded and backed up I believe BrewSession will be around one quarter complete.

–Dean

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

Posted by: Dean @ 10:37 am

Categories: Dean |

No Comments »

Our Sponsors