Showing posts with label rails. Show all posts
Showing posts with label rails. Show all posts

Wednesday, April 28, 2010

Adding Rails Counter Cache - Two Stage Release

I recently added a counter cache to an existing has_many/belongs_to association. I am using a hosted Rails application (heroku)

I went through all the steps:

1) create a migration adding a table_name_count column to the counting table, and initializing table_name_count to the the proper value

2) add :counter_cache => true to my :belongs_to association

3) make use of .size instead of .count for counting the associations

4) write proper unit tests to verify the counter is updated

It all worked - until deployment. The migration did not fail, but my counter fields remained 0.

Odd, it all worked locally. Must be an issue with my host - NOT.

Here is the skinny:

Specifying :counter_cache => true makes the counter field read only. Updating it does not cause an error, the model can still be saved, and modified? returns true. However, the field does not get saved - it is a transient change.

It wasn't a problem locally, because I had done these in the proper order. With git as my interface to heroku, all these steps were effectively compressed into one.

To fix this, I had to deploy this change in two stages:

A) Migration, initializing the value
B) Model and other code updates.

And it worked.

Thursday, November 5, 2009

Authlogic and Functional Tests

After adding Authlogic to my Rails application, functional tests failed with errors like
Expected response to be a <:success>, but was <302>
This is because the controllers now require a valid session. Here are the steps to easliy accomplish this:

1. Create a named user in fixtures/users.yml, as described in docs. I called my user auto
auto:
username: autotester
email: whatever@whatever.com
password_salt: <%= salt = Authlogic::Random.hex_token %>
crypted_password: <%= Authlogic::CryptoProviders::Sha512.encrypt("autouserpw" + salt) %>
persistence_token: <%= Authlogic::Random.hex_token %>


2. Activate AuthLogic at setup time of test
setup :activate_authlogic
within each ControllerTest class (ActionController::TestCase)

3. Add helper to create a session in test_helper.rb
class ActionController::TestCase
def within_session(user = :auto)
authlogic_session = UserSession.create(users(user))
yield if block_given?
authlogic_session.destroy
end
end


4. Put a within_session block around the areas of a test which need it. Change tests like this:
test "should get index" do
get :index
assert_response :success
assert_not_nil assigns(:releases)
end

To tests like this:
test "should get index" do
within_session do
get :index
assert_response :success
assert_not_nil assigns(:releases)
end
end

And that's it.

Tuesday, October 27, 2009

How to reset the Rails Console

Editing a model while running the Rails console? Don't quit and restart it, you can reset the console.

Its simple:

reload!

Thursday, September 24, 2009

Rails ApplicationController Not Found

Upgrading a 2.2 project to 2.3 and beyond. I get an error:

NameError (uninitialized constant ApplicationController):

This is because the default file for the ApplicationController has changed from application.rb to the normalized application_controller.rb

If you are using svn, do

svn rename app/controllers/application.rb app/controllers/application_controller.rb

And in your commit comment, remember the schmitzer.

Wednesday, July 29, 2009

Java Web Services using BPEL

I've been exploring the Web Services implementation in Java, specifically within the GlassFish container.

I ran across this screencast of a Simple BPEL Process with a Web Service Binding. I'm curious what you think of this screencast. Many RESTful Rails and SOAP/ASP.NET developers will laugh at what has to be done to build this "simple" process.

Here are my takes:

A simply insane number of mouse clicks to create the process. I tried counting all the necessary mouse clicks and came up with:
* Create BPEL module project: 9 clicks
* Import XSDs: 14 clicks
* Create WSDL: 18 clicks
* Create BPEL process: 54 clicks
* Create and deploy composite application project: 26 clicks
Or a quick 121 clicks total. I was absolutely stunned at how complex this is. If this seems normal to you, find an ASP.NET web service screencast, or Creating a weblog in 15 minutes, which uses Ruby on Rails.

Tight coupling between the technology and the tool makes for heavy reliance on a vendor. Watching this video, could anybody create this without Netbeans? Recently the NHIN Connect project decided to drop GlassFish ESB and BPEL. Part of the reason cited was that the integrated solution could not keep up with the individual components - the integrated tool could not keep up with GlassFish, open ESB, and Netbeans.

How long would Hello World take in this process? Probably 20 minutes - unacceptable.

BPEL and WS-* seem to be a perfect match for each other. Developers hate the complexity of WS-* (nicknamrf WS-Death Star) , and similarly hate all the overhead/configuration in a Java project. Put two hated technologies together, and then you've got something.

Tuesday, July 14, 2009

New Plugin for Rails

Inspired by Actual Events

Since we often see organizations which do little to improve themselves. I have written a new Rails plugin to help them along the way.

If you have a Rails project with a bunch of code that does not work well together, this plugin is for you.

The plugin, called Acta as Manager, basically does nothing. Leave it in place for 2 1/2 years, then check and see if your project has improved. When the inevitable inevitably happens, blame the plugin and uninstall it.

FAQ
Q) Where can I get Acta as Manager?
A) I wouldn't try github or rubyforge, as they may actually have code there which does something. /dev/null is a better repository for a do-nothing plugin.

Q) Is Acta as Manager Open Source?
A) Since there is no source, I guess not. You can't legally modify nothing without permission.

Q) What does Acta as Manager do?
A) Acta as Manager does nothing.

Q) How does Acta as Manager help my bad project?
A) Your project will fail, and you will waste time finding something to blame for a post-mortem. Acta as Manager provides exactly that, prepackaged for you. Effectively, it makes useless work more efficient.

Q) How do I install Acta as Manager?
A) ./script/plugin install acta_as_manager

Wednesday, May 20, 2009

Migrating schema to test

Rather than running migrations, do


rake db:schema:dump
rake db:test:load

Wednesday, February 25, 2009

Rails Authenticity Tokens

Rails uses authenticity tokens to verify that requests which modify data are coming from an authentic session with the requester.

When you use form_for in your view, a hidden authenticity_token is inserted for you, containing a long string used to validate your session. In certain conditions, notably AJAX, you may not use form_for and may get an InvalidAuthenticityToken exception, thrown from verify_authenticity_token.

Many articles suggest turning off protect_from_forgery where you need to, using a :only option. I would think twice before doing this. Instead, work to get a valid authenticity token into your request.

First, if you have a form handy, you have to put it into your prototype Ajax.Request as a parameter:
token = el.getAttribute('value')
new Ajax.Request('/lesson_plans/update/'+parent_id, {
method: 'put',
parameters: 'course_sequence=' + newval + "&authenticity_token=" + token
});

Here, the authenticity token is taken from element el, and added as a parameter to the Ajax.Request.

If you do not have a form handy, you can add your own, using the helper form_authenticity_token in your view:

<div token="<%= protect_against_forgery?() ? form_authenticity_token() : "" %>">


This will produce an authenticity token (identical to one which would appear in a form) and places it in a token attribute of a div if forgery protection is activated. Using a method similar to above, the attribute may be extracted and added to the request.

Happy coding

Monday, January 26, 2009

The Satisfaction of Instant Feedback

Everything I Know, I Learned From Rails, Part III - The Satisfaction of Instant Feedback

This is the third article in a series to try to convince more developers to learn Ruby on Rails, even if they are not involved in web programming. See also part II.

As developers we have come to expect a great deal of pain in our lives. It stated when we deciphered cryptic error messages from our first C compiler, continued through poorly documented tools, and then extended into just using our operating system. Later, developing for APIs like Win32 quickly turn into trial and error sessions. Long periods without progress.

Things just don't have to be this way. Rails taught me this. While installing Rails was less than trivial for cygwin, it certainly keeps things moving.

When you create a new rails program, using the rails command, it is ready to run. Immediately. Run script/server and it is a go. It doesn't do anything, but it is configured for me. I'm ready to develop it, Rails does not get in the way.

Immediately I know the installation worked. I see this immediate feedback everywhere I look
  • Scaffolds - ready to run out of the box

  • Generated tests - ready included by the included Rakefile

  • Database migrations - forward and backward, generated for me ready for rake db:migrate<

  • Models - immediately accessible from the console (post migration). Test data generated too.

  • Controller actions usable via a standard URL.

  • Views, properly named already routed by the controller's default action


And of course, code changes immediately reparsed in the development environment. But there is more. Gems and plugins, with a single command they are ready to use. No restarting Windows.

This instant feedback loop is simply addictive. Productive too - keeps my attention span locked in to the development of the project. I want my customers to get this instant feedback as well, so they are addicted to my product, find it so easy to use. No waiting. No trial and error.

Install and have the reference demo running in 20 seconds. That's where Rails has put the bar.

Wednesday, January 21, 2009

The Sweet Spot

Everything I Know, I Learned From Rails, Part II - The Sweet Spot Between Frameworks and APIs

This is the second article in a series to try to convince more developers to learn Ruby on Rails, even if they are not involved in web programming. See also part I and part III.

Rails is described as a Model-View-Controller (MVC) web application framework. It also contains a number of Application Programming Interfaces, or APIs.

What's the difference? Frameworks impose a structure on our program in order to make development easy in a complex paradigm. Frameworks say "implement interface X and overide the doSomething() method". An example of a framework would be the Microsoft Foundation Classes (MFC) for Windows development. Frameworks are great, except when the user needs to do something outside of the framework's capabilities.

An example of this which I encountered in my career was (not sure if it still this way) introducing an MFC split view with separate documents in each view pane. Was not possible as a view references one document. (Author's note: pls don't slam me on this point - at the time, had no other way to show two documents in my view).

APIs, on the other hand, are considerably lower level, offering more flexibility than frameworks, at a cost of complexity moving from the producer to the consumer. An API many of us are familiar with is the Java Swing API. You can do pretty much anything you want, but good luck figuring out how to correctly incorporate the JTree into your application.

I've often said APIs give you what you want, while Frameworks tell you what you want. There is a parallel in the software application space too. Applications can be written as a tool to aid the wisdom of the smart user, or can be the expert system which tells the user what's what.

As developers, we always get squeezed between the limitations imposed by a framework, and the difficulty of using an API in a efficient and robust manner which is maintainable across the future releases of the library. In fear, we wrap the API in a facade which we can adapt to new releases or fix improper usage of it. In essence, we turn APIs into into Frameworks ourselves.

Rails has found, in my opinion, a way to get the best of both worlds. Rails starts as a minimalist framework, doing basic request routing. It also has a series of APIs for building out your functionality.

Rather than impose a structural framework, Rails provides more of a functional framework. Using the metaprogramming of Ruby, rails generates missing methods at runtime, including defining model classes based on a table's structure, and generating find_by methods as they are called. The framework writes itself as it is running. That way, it stays lean and does as little for you as possible, and limits you as little as possible.

In addition to runtime code generation, Rails also comes with a series of static code generators to give you a leg up on fulfilling your part of the framework. Developers need to write model, view and controller classes, and there are generators which output each, plus the necessary tests for each.

While we may not all be able to put metaprogramming into our frameworks in the same manner Rails does, there still are lessons to be learned here. Not all of the restrictions we impose via frameworks are necessary. We can still help users by providing APIs alongside our frameworks, generating code (and tests with it) that use these APIs, but not providing the whole solution to your users, ever.

Tuesday, January 20, 2009

The Cost Of Choice

Everything I Know, I Learned From Rails, Part I - The Cost of Choice

This is the first article in a series to try to convince more developers to learn Ruby on Rails, even if they are not involved in web programming. See also part II.

Rails constantly preaches the idea of Convention over Configuration, or con/con. By way of con/con, Rails makes decisions for you. Some of these include directory names and layout, database table and column names, and others.

To quiet the naysayers, there is the claim that you can customize these defaults to your liking. But before this happens, somebody asks the question "Why?" and it doesn't happen. Nobody really knows if you can customize these or not :) As a result, a great many of the Rails applications use the default conventions.

Choice is expensive. Extremely expensive. Is there anything more costly than being able to make choices? Think about it. Here are a few time costs
  • Setting our various PATH variables, accommodating the directory structure of a new library we've included in our project
  • Figuring out where this project builds to
  • Looking at stored procedures to determine mapping to relational tables
  • Determining which directory/registry to put our configuration files in
  • Learning how to configure/configuring/reconfiguring/rolling out configuration/synchronizing configuration
  • Added complexity in our code due to options in our software
  • Added complexity in our tools due to the above
And the list goes on. But, this is only the direct costs of choice. There is also a second, even more expensive indirect cost. The indirect cost is not what we have to do because of choice, but what we don't do because of choice.
  • Reusing code is difficult, we'll just write ourselves
  • Testing code is difficult, we'll just hope it works
  • Automating anything is difficult, we'll do it by hand
  • That tool would take a minute to write and an hour to make work, I'll pass
When we move from a configurable environment to a con/con environment, things change noticeably. Configuration is automatic. Things are easy to find. Names are easy to remember. Code gets reused. Test are simple. Automation happens. Tools are easy to write. We make fewer decisions about things which don't impact our final product, and more which do. The Schmitzer becomes happy.

Monday, January 5, 2009

Ten Predictions for 2009

Rails Hosting Specialists Take Market Share...
finding they can cut costs more due to convention over configuration.

Bill Evjen Writes a Book About Something Other Than .NET...
signaling the beginning of the end of C#.

Zune Works All Year Long...
but thinks Feb 30 is a valid date.

Open Source Investment Increases...
as companies move to cut costs by leveraging community development.

Static Languages Get Dynamic Envy...
try to add closure, open classes, and metaprogramming but fall way short.

Ruby is Forked...
so many times, we won't be able to count. Thanks, Dave.

Technical Patent Mess Gets Even Worse...
as the 1 bit gets patented, hosing us all.

Ruby GUIs Become Real...
but not with FxRuby or Shoes, but Monkeybars on JRuby.

Steve Jobs Shows Up at MacWorld...
but Gizmodo claims he is a fake.

Yet Another MVC Framework Emerges...
but Rails still wins.

Friday, December 26, 2008

How to Configure Rails Routes Containing a Dot, Period!

I had trouble matching a rails route containing a dot, like the following:
http://localhost:3000/stations/103.3
I created a route in routes.rb defined to match a radio station by frequency:
map.connect 'stations/:frequency', :controller => 'stations', :action => :show
Which I thought would do the trick, but instead I get the dreaded:
No route matches "/stations/103.3" with {:method=>:get}
But it works without the dot (for AM stations)
http://localhost:3000/stations/1120
What gives?

The problem, I found in Rails bug reports, is that this is desired behavior. The dot is, by default considered separator, just like a slash. One way to fix this is to specify the pattern for frequency as anything which is not a slash:
map.connect 'stations/:frequency', :controller => 'stations', :action => :show, :frequency => /[^\/]+/
Which does the trick

Tuesday, December 2, 2008

Rails 2.2 - undefined method 'write_inheritable_attribute'

After installing Rails 2.2, I got what appears to be a common problem, where write_inheritable_attribute is no longer found.

$ rails test
/usr/lib/ruby/gems/1.8/gems/rails-2.2.2/bin/../lib/rails_generator/options.rb:32:in `default_options': undefined method `write_inheritable_attribute' for Rails::Generator::Base:Class (NoMethodError)
from /usr/lib/ruby/gems/1.8/gems/rails-2.2.2/bin/../lib/rails_generator/base.rb:90
from /usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in `gem_original_require'
from /usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in `require'
from /usr/lib/ruby/gems/1.8/gems/rails-2.2.2/bin/../lib/rails_generator.rb:34
from /usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in `gem_original_require'
from /usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in `require'
from /usr/lib/ruby/gems/1.8/gems/rails-2.2.2/bin/rails:13
from /usr/bin/rails:19:in `load'
from /usr/bin/rails:19

But this is clearly defined in activesupport-2.2.2. If you encounter this problem, check to see if you can load activesupport in irb:

$ irb
irb(main):001:0> require 'rubygems'
=> true
irb(main):002:0> require 'activesupport'
LoadError: no such file to load -- iconv
from /usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in `gem_original_require'
from /usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in `require'
from /usr/lib/ruby/gems/1.8/gems/activesupport-2.2.2/lib/active_support/inflector.rb:3
from /usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in `gem_original_require'
from /usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in `require'
from /usr/lib/ruby/gems/1.8/gems/activesupport-2.2.2/lib/active_support.rb:26
from /usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in `gem_original_require'
from /usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in `require'
from /usr/lib/ruby/gems/1.8/gems/activesupport-2.2.2/lib/activesupport.rb:1
from /usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:36:in `gem_original_require'
from /usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:36:in `require'
from (irb):2


If you see "LoadError: no such file to load -- iconv" your problem is a missing libiconv, which supports internationalization in Rails 2.2.

The rails wiki has the instructions on how to install and build iconv if you need them.

Hope this helps....

Update: For Debian, I got the same error, but had a different solution.