Rails conf 2007
It's official I will be speaking at Rails Conf 2007!
Rails conf bio
Stephen Becker IV is a Cancer with a a smooth demeanor and a voice like velour. His turn-ons are long walks on the beach, futurama and all night caffeine induced bing programming sessions. His turn-offs are stupidity and the lack of velour clothing currently in stores. Presently working for Vonage as a rails developer in New Jersey.
Rails Conf linkKomodo 4
I use
Komodo IDE at work and at home I would always use
Rad Rails. That was the case until I started running 64bit suse. Rad Rails will not work for me, and it is not worth my effort to figure it out. Also Komodo just released 4.0!
I was sad at first because I did not see a price for a personal edition. So I am just the demo version of 4.0 and I found they have a free version which they call Komodo Edit. Found
here ! I have yet to install the free version but if its close enough to the pay version I am set.
There is a big list some where of the features for komodo but there are only a few things that draw me to this product. Number 1 with out a doubt is the toolbox. I store bits of repeatable code and make them hot keys. This makes writing unit test, change logs, formatted rdocs comments so easy. I have also created a package for the tool box that includes the common RoR actions like rad rails has. That can be downloaded from
here. Number 2 is the tab complete. In 4.0 you have to turn on the tab complete because its bound with different keys. I think they did a good job at guessing which word I want. They have an open that strips the whitspaces at the end of lines which includes those killer windows ^M's.
It is missing a few things that rad rails has. One is the hot key to take you from the controller to the view and back again. The auto formatting for indentation is very nice because sometimes i get out of control.
Rails generate generators
Creating a generator for rails is simple enough.[1] You have to create a directory in lib or vendor and call it generators. Then you create ruby class (Name)Generator that extends Rails::Generator::NamedBase or Rails::Generator. You create a method called manifest that will do the actions to for your generator.
In the manifest you call record with a block. Somethings you can do in the block?
</pre>
<br><br>
Simple enough so lets script it! Thats right I created a generator to create generators. <br>
<pre>
sbecker@funtime-theme-park:~/rails/gens$ ruby script/generate generator test
create lib/generators/test
create lib/generators/test/templates
create lib/generators/test/templates/controllers
create lib/generators/test/templates/tests
create lib/generators/test/templates/views
create lib/generators/test/templates/models
create lib/generators/test/templates/helpers
create lib/generators/test/templates/libs
create lib/generators/test/USAGE
create lib/generators/test1/test_generator.rb
The script creates the correct file structure,the generator.rb file and the templates folder with some default folders. If you do not need all these folders then delete them. I think it helps keep to the templates grouped together.
To create the generator I have two templates and the manifest. The templates are for the USAGE file which is from the controller USAGE, and a basic manifest which has example calls that i listed above. The manifest for the generator is simple.
class GeneratorGenerator < Rails::Generator::NamedBase
attr_accessor :gen_path,:gen_name
def initialize(*runtime_args)
super(*runtime_args)
raise "You need a Generator name" if runtime_args.first.size<1
@gen_name=runtime_args.first.first
raise " You already have a generator called #{gen_name}" if File.directory?(RAILS_ROOT+"/lib/generators/#{gen_name.downcase}")
@actions = @args.empty? ? [] : Array(@args)
@gen_path=@gen_name.downcase
end
def manifest
record do |m|
m.directory File.join('lib','generators', gen_path)
m.directory File.join('lib','generators', gen_path,'templates')
m.directory File.join('lib','generators', gen_path,'templates','controllers')
m.directory File.join('lib','generators', gen_path,'templates','tests')
m.directory File.join('lib','generators', gen_path,'templates','views')
m.directory File.join('lib','generators', gen_path,'templates','models')
m.directory File.join('lib','generators', gen_path,'templates','helpers')
m.directory File.join('lib','generators', gen_path,'templates','libs')
m.template 'USAGE', File.join('lib', 'generators',gen_path,"USAGE")
m.template 'generator.rb', File.join('lib', 'generators',gen_path,"#{gen_name}_generator.rb")
end
end
end
I will add the generator code to my svn soon.
Useful sites:
[1]
http://wiki.rubyonrails.org/rails/pages/UnderstandingGenerators
[2]
http://www.aidanf.net/rails/creating-your-own-generators-in-railsRails conf
Today was a fun day at Rails conf. I liked both my sessions I went to. I was a big fan of capistrano and now look forward to version
2.0. I am also looking forward to the sound features of the newest
scriptaculous.
I give my talk tomorrow and I am very excited. I am also exited about portland. I like what i have seen of it so far.
Aftermath
I think the talk went well. I did not see a lot of people leaving. I will have to wait for the flood of blog posts. Which should be a lot because the room was packed. Some links to chew on
CampFire transcript and
Delicious page for vonage. Hopefully more to come soon...
Rails http head
As it turns out rails implements HTTP head calls for you. A fellow programmer was creating rails functional tests and using rcow noticed that we did not write test for the head command. He then found out that the functional test in rails when creating the mock response object includes the body like a normal get call. We reread the spec for http head one more time just in case we both thought incorrectly. So, I fired up the test using code komodo's ruby debugger. for the next 2 hours we stepped in the tes setup code and the rails frame work. We stepped in a lot of code until we hit the trunk/vendor/rails/actionpack/lib/action_controller/request.rb:line 16
def method
@request_method ||= (!parameters[:_ethod].blank? && @env['REQUEST_METHOD'] == 'POST') ?
parameters[:_method].to_s.downcase.to_sym :
@env['REQUEST_METHOD'].downcase.to_sym
@request_method == :head ? :get : @request_method
end
I did kept stepping in but I never found the code that takes care of the head body problem. We tested the head call using curl and it works, but the test returns the body like a normal get call.
So it must be code that is not ran for the testing frame work. Ihope to find the other code for the head call but I am having a hard time learning how to debug live rails apps.
Read up on your http head request
here it is useful for caching. We all know what caching can lead to.
Just something i thought was cool.
First rails patch
I have posted my first rails patch. I will see how much I am laughed at and then decide on doing it again. I submitted a patch to exclude the directories of frameworks that are not needed. When I was stepping through the boot of rails I noticed it had a TODO next to it. The link is
here. I hope they dont laugh to much.
Rails log parser
I started working on a rails log parser but there appears to be a very nice one on
ruby forge. Since they have a lot of nice things i will not even finish the code I have in the SVN. From the little work I did with the log parser I did find out that %13.91 of my logged server requests had stack traces, and that %51.23 of the logged session ids are uniq. So I will check out the ruby forge project and see how that goes.
Action mailer recieving attchments
So I posted before about receiving voice mail and having populate on the internet. Well step one is checking the mail. As it turns out my email for my server is stored in a local dir. This is really good news because it appears that action mailer does not check email. First I need to add a receive method to my mail model:
def receive(email)
email
end
Done! But you say it does nothing? I know. I hate changing code in models and then restarting the server. For now my logic will stay in the controller:
def test
x=Notifier.receive(File.read("/home/giggles/realybadsite/1184671181.V9I760a7f6.popeyemail.com"))
File.open(Dir.pwd+"/tmp/"+x.attachments.first.original_filename,"w+"){|file|
file << x.attachments.first.gets
}
end
Now that I have access to the mail object and I can save the attached file I can now store the necessary data for my website.
For a list of methods on the TMail object try this website
http://railsmanual.com/class/TMail::Mail
TMailRails upload file test
I wrote a rails service that can take a file as the posted params or data in the raw body. Like the good developer I am I wanted to create unit test for this functionally. Unsure how I could do this I search the Google for terms including but not limited to upload test file post rails with no good results. I did find fixture_file_upload which I now question what it really does.
I should have known the solution from odd test mishaps I have had before.
When you set params in the testing frame work they are passed as is.
For example:
x = Tempfile.new()
x.write = temp_file_data
x.rewind
post :service_x,:file=>x,value=>"x"
This will set param[:file] to a Tempfile like a normal mutil-part form post would. If you did not find this out already Rails will not always return you a Tempfile for posted files. If the file is small enough it will be a StringIO object. If you are reading the contents they will function the same real_data = params[:file].read . If you plan on resending the data you might want to check the kind_of? on the object that was passed in.
anyway good to know stuff.
Rjs partials
I was asked if one can do partial rjs files by a coworker recently. Some of our RJS code is becoming large and difficult to maintained. I figured why should RJS be different then the other templates. So I started up my test rails application. This is very filled with 1/2 crazy ideas and 1/2 unsure syntax examples. So I added two calls rjs1 and rjs2. I created a link_to_remote[1] on rjs1 pointing to rjs2.
I did nothing in rjs2 but created an rjs2.rjs file in side of my views directory. My rsj2.rjs file looks like
page.replace_html "one","one"
page<<"#{render(:partial=>"moo")}"
_moo.rjs looks like
page.replace_html "two","two"
I understand that render will return the JavaScript of the rjs rendering. I also understand that I need to use page<< to append the JavaScript to the call like I would use <%= %> in an RHTML file. Would appear to work, but it seems off.
What I do not understand is why firebug[2] only shows me the first replace call in the response.
try {
Element.update("one", "one");
} catch (e) { alert('RJS error:\n\n' + e.toString()); alert('Element.update(\"one\", \"one\");'); throw e }
What it is close to what I expect to but not complete. This is what I think I should see:
try {
Element.update("one", "one");
try {
Element.update("two", "two");
} catch (e) { alert('RJS error:\n\n' + e.toString()); alert('Element.update(\"two\", \"two\");'); throw e }
} catch (e) { alert('RJS error:\n\n' + e.toString()); alert('Element.update(\"one\", \"one\");'); throw e }
Note the nested try catch is only there because of the rjs debug setting. So I changed debug to false and restarted the server. I now see this in firebug:
Element.update("one", "one");
Element.update("two", "two");
I am happy because this can help clean out view logic from the controller RJS calls, but I am also concerned that the improper display in firebug[3] can have a negative impact on developing JavaScript. I am also thinking that this can be done in a different way.
[1]
http://api.rubyonrails.org/classes/ActionView/Helpers/PrototypeHelper.html
[2]
http://www.getfirebug.com/
[3]
Firebug display issueRjs extensions
If you find your self using the page<< command a lot extend the page object.
module ActionView
module Helpers
module PrototypeHelper
class JavaScriptGenerator
module GeneratorMethods
def becker
assign 'window.location.href', "http://stephenbeckeriv.com"
end
end
end
end
end
end
Save this in a ruby file in the lib directory and then require "file_name" in environment.rb after the boot line.
Now you can call this from your rjs template.
page.becker
I do not have any good code examples. I am not a big javascript person which is why I am happy I can extend it in ruby.
Page render rjs
I recently posted about using RJS partials. I could of found out how to do this by searching the google. I found a nice
site that sums up a peep code screencast.
I still think that this is an ugly way of doing this. It also has an extra debug statement that makes it look messy. So like my rjs extension example I created a partial render method. Odd little side note: I can not name my method render. I am looking in to why.
module ActionView
module Helpers
module PrototypeHelper
class JavaScriptGenerator
module GeneratorMethods
def partial(*args)
reset_rjs_debug = ActionView::Base.debug_rjs
ActionView::Base.debug_rjs = false
@lines << "#{render(*args)}"
ActionView::Base.debug_rjs = reset_rjs_debug
end
end
end
end
end
end
Now your rjs script looks like
page.partial(:partial=>"moo",{:local=>"var"})
I do not like how the arguments repeat the partial. It would make more sense if I could call my method render (page.render). I also could do more with the arguments.
partial(name,*args)
...
@lines << "#{render(:partial=>name,*args)}"
I did not test this but it should work. Maybe there is a problem if no *args are passed in.
Rails rendering
No doubt any one looking at rails for speed will find that rendering sucks up a fair bit of time. I have two ideas to change the rendering system. The first is to remove the benchmarking code for production. I have no proof that this will make things faster. I am just sick of seeing it in my profiling log. The second idea I have is allowing the user of rails to change out rendering engines. For example using
markaby instead of ERB or libxml(c lib) instead of XMLbuilder. I think using different libs could yield a faster render. If not with a c lib replacement then with another lib yet to be written.
Login generator
I have posted about my ruby on rails generator that generates generators before. When I created this I did not know the generator code that well. Sadly this is still the case. I used generators for my one task saving me from created a nearly similar files over and over again. But Becker could you have not metaprogramed it? No each model had a common idea and interface but in the end the code was very different. I could have reduced the code and created a mixin for most of the methods, but it's a little late for that. Also I do not think I want to create a mixin for code that should be changing a lot.
Long story shorter I created some generators. I found my self creating another rails app for my websites and I am writing the login code over and over again. I thought why not generate? Then I thought why has someone not done this? But they
have and, each of the generators work. They all have good attributes and bad ones but, none of them are 100% what I wanted. So I am going to make my own login generator. I will post it in my svn when its ok. I also realize the literature on generators is lacking. I also feel that the amount of generators that have been created is very low. It might be the most under used feature in RoR.
Simple examples
Today a friend asked me how hard it would to create an inventory site for items. I asked him about the database and we came to the conclusion it was a single table problem. With in 10 mins I had a new rails app with migrations and scaffolding. Normally I do not like scaffolding but for a single table ugly app it works very well. After another 20 minutes I added a login and changed the form to use drop downs. Rails is by no means perfect, but it does make little things easy.
Kevin bacon search engine
I have come up with a new concept for search engines!
Kevin Bacon Page Ranks:
It takes a site and determines how many degrees from Kevin! That is your page rank. Or the inverse of that number. Depends if you like Kevin or not. The engine will have to be flexible enough to apply the same concept to other things like sports websites.
I am going to start mocking up the Kevin Bacon Page Ranker tonight in Rails.
Rails and soap4r
Recently at work I was tasked with making rails work with soap4r. There is a ticket on the soap4r trac[1] and in the Rails trac[2]. There are a wide range of solutions. I personally try to avoid changing core code if I can. With that in mind I have created two solutions.
In both solutions you need to not have soap4r install in the default gem folder. So gem install it in the Rails layout.
#since having soap4r installed will break all rail projects remove it.
sudo gem uninstall soap4r
sudo gem install --install-dir ~/rails_dir/vendor/gems soap4r
You can update the gem and remove it if needed. I see two negatives to this solution. One is that when I run IRB I do not have soap4r. This is solved by having it also installed in the normal gem dir on my dev box. Two there is going to be extra bandwidth with coping your rails code around. This can also be solved by installing it on your production boxes. If you have more then one prod box that could be a pain to remember to do.
Solution 1:
Set the GEM_PATH in the rails project:
Setting gem_path after require rubygems is a great way to keep soap4r in one project. The gem library is to use once you find the documentation. For some reason it is not listed on the ruby-doc website but you should have a local version of the docs.
In your environment.rb file add
ENV['GEM_PATH'] = "#{File.dirname(__FILE__)}/../../../../gems"
require "rubygems"
Gem.clear_paths
or the location you installed the gems to. Then gem install your little hard way.
Solution 2:
Use rubygem classes:
rubygems is just another ruby class to load other ruby classes. Its one of the reasons we are in this mess to begin with. The good news is I have searched for example code and found nothing useful
gems = Gem::SourceIndex.from_gems_in("~/local_gems")
spec = gems.search('soap4r').last
spec.require_paths.unshift spec.bindir if spec.bindir
soap_path = ""
require_paths = spec.require_paths.map do |path|
tmp = File.join spec.full_gem_path, path
soap_path = tmp if tmp.include?("/lib")
tmp
end
sitelibdir = Config::CONFIG['sitelibdir']
$LOAD_PATH.insert($:.index(sitelibdir), *require_paths)
require File.join(path,"soap","soap")
As you can see most of it is just from the gem require process. All I added was the SourceIndex and soap path pieces. This code was my first fix. I tried like heck to set the gem load path but never made the connection to where it was first loading rubygems. This solution fails with requiring a gem, using versions, and preventing the gem from being loaded again. All things that can be fixed but I did not post the code. I am thinking about extending rubygem to allow you say gem name,revision,location.
[1] http://dev.ctor.org/soap4r/ticket/433
[2] http://dev.rubyonrails.org/ticket/10001Email rendering
I have convinced ActionController to render email templates for me. This allows me to use all the ActionView HTML helpers.