strapyourself.in and flouri.sh
Rendering views without a web request in rails
Why the heck do you want to do that?
Views are very well integrated into the rails framework, but they're only typically rendered when an http request comes in. ActionMailer is the main exception, but what if you want to render a view for use in another backend application? These days, document fragments are being used everywhere, and often times I'll need rendered HTML for a use other than just sending it back to the requesting user.
A solution: instantiate a controller and view
Controllers are just objects, and so are views. We can instantiate a controller, instantiate a view, then point the view to the controller and we're ready to go. The only think we're missing is the session and the request objects, but not every view needs those. I used this once for updating a facebook profile using a backgroundrb worker:
class FakeView < ActionView::Base include SomeHelper include SomeOtherHelper end class FakeController < ActionController::Base def render_some_view action_view = FakeView.new(File.join(RAILS_ROOT, "app", "views"), {}) action_view.instance_variable_set("@controller", self) markup = action_view.render(:partial => 'facebook/your_profile') end end
By subclasses ActionView::Base, you can mix helpers into the view class, making their
methods available.
Another solution: use the test framework!
The rails TestProcess is the only place where views are rendered. If you really want to simulate a real experience when rendering a view, use the test process. First, you'll need an actual controller with an actual action you want to render in it, like this one:
class FacebookController before_filter :login_required def your_profile end end
Then, we create and instante a test as follows:
class FacebookTest include ActionController::TestProcess attr_reader :response def initialize require_dependency 'application' unless defined?(ApplicationController) @controller = UserScheduleEntryController.new @request = ActionController::TestRequest.new @response = ActionController::TestResponse.new end def render_your_profile(user) @controller.instance_variable_set("@user", user) # bypass login required get :your_profile @response end end test = FacebookTest.new test.render_your_profile(user) markup = test.response.body
The require_dependency was something I threw in because backgroundrb didn't
have some of the
required classes loaded at that point, it may not be something you need in your application.
Tremble at my fast internet speeds
Ever since moving to San Francisco, I can't believe how fast internet access can be here! Check it out:
The sample above was taken at my house... middle of the day... using a cable modem with Comcast over 802.11n wireless!
Duplicate Migrations in Rails
Why we need duplicate migrations
Have you ever been working on a large project, and had people check in migrations with the same numbers? It's happened to me probably no less than 10 times in the last year. In each case, the situation is recoverable, but sometimes requires a lot of manual rolling back of specific migrations on possibly several machines. Then you have to renumber all the migrations after the conflict, of course.
An even worse situation is when a project is branched and remerged. For example, you might want to branch out several complicated features from trunk for a few weeks, then bring them back when complete. Assuming you create 2 feature branches (for adding profiles and friends to your users), you could end up with something like this:
- 036_modify_users_to_include_first_name.rb
- 037_create_profiles.rb
- 037_create_friendships.rb
- 037_fix_a_bug.rb
- 038_add_timestamps_to_friendships.rb
- 038_modify_accounts_to_limit_length.rb
- 039_modify_users_to_include_gender.rb
In the above situation, the person merging the two branches has a very difficult situation ahead. Everyone working on the project is probably on revision 37 (profiles branch), 38 (friends branch), or 39 (trunk). The safe way to proceed with traditional rails migrations is to force all machines be migrated down to 36. No new migrations can be added while the migrations are then renumbered so they range from 036 to 042. Finally, all users can update from trunk and run rake db:migrate. Of course, people often forget to migrate down, and end up stuck in the middle of a sequence of migrations that has been renumbered (I am so tired of reversing migrations by hand).
Solution: Allowing duplicate migration version numbers
In the above example, the 3 migrations numbered 37 are not dependent in any way. Because they had to be developed independently, duplicate version numbers are very rarely dependent. For this reason, we beleive that it is usually safe to create a "partial ordering" of migrations rather than an exact ordering. In this partial ordering (which can be represented as a lattice), migrations with the same version number will be run in an arbitrary order:
Since all of the dependencies in the above lattice flow downward, we can satisfy the partial ordering by running the migrations alphabetically by filename, alphabetizing them first by version number and then by class name. This will only work if we can make the assumption that when new migrations are added, they can only be dependent on those with smaller version numbers.
How the plugin works
Traditional rails schema_info table cannot hold enough information to keep track of
which migrations have been run, so we need to adopt a new schema format, which we place in a new
schema_infos table:
In this new schema, every record represents a migration that has been run. By traversing this table, we can get an accurate picture of the state of the system, and decide which migration to run next.
If we want to migrate to version 10, for example, we create an alphabetical listing of migrations up
to and including version 10(s). Then we traverse that list in order, running "up" on
migrations which have not been previously run, and inserting a record into schema_infos.
Finally, we create a list of migrations with version numbers
larger than 10, and run "down" on those in reverse alphabetical order, removing the entries
in schema_info.
A little under the hood
Below is the main migrate function. It does exactly what is discussed in the previous section:
def migrate_with_duplicates migration_classes_before(@target_version).each do |(version, migration_class)| next if schema_information_contains?(migration_class) ActiveRecord::Base.logger.info "Migrating up #{migration_class} (#{version})" migration_class.migrate(:up) insert_schema_information(migration_class) end migration_classes_after(@target_version).each do |(version, migration_class)| next if !schema_information_contains?(migration_class) ActiveRecord::Base.logger.info "Migrating down #{migration_class} (#{version})" migration_class.migrate(:down) remove_schema_information(migration_class) end end
What would be even better...
I've always wanted to write a migration system based on partial orderings where dependencies are
explicit, and version numbers are history. Such a system would work nicely on top of the new
schema_infos table format. The tricky part would be how to state the dependencies without
forcing the migration author to work too hard.
Download
From the ELC plugin repository: http://wush.net/svn/public/plugins/duplicate_migrations
To install:./script/plugin install -x http://wush.net/svn/public/plugins/duplicate_migrations
(installing automatically creates the schema_infos table and populates it, but does NOT delete your old schema_info table... don't panic!)
Originally posted on ELC's blog
Ruby gems and plugins
Syntax highlighting Mephistro admin side
Using CodePress to make writing blog entries easier
After I got Metphisto working with Ultraviolet syntax highlighting, I decided to make writing blog entries easier! I already had some experience with my TicTacToe project using CodePress for real-time syntax highlighting. From there, I was able to modify Mephisto to use syntax highlighting while I was writing my articles... the goal being to make the HTML easier to read and easier to write:
The first step was to install CodePress from codepress.org into the mephistro public directory. I ended up putting the css in /stylesheets, and the javascript in /javascripts... big mistake! Codepress likes to be kept all together (probably best in /public), and you shouldn't have to rewrite any of the paths in codepress.html or codepress.js, like I did.
The second step was to include codepress.js in your layout.
Then I made the following modifications to /app/views/admin/articles/_form.rhtml replacing
only the dd tag that defines the "body" textarea:
<dd><%= form.text_area :body, :class => 'fat codepress html linenumbers-off', :rows => 25 %> <br/><%= check_box_tag "enable_codepress", "1", true, :onchange => "article_body.toggleEditor();" %> Use codepress for syntax highlighting <%= hidden_field_tag "article[body_hidden]", @article.body, :id => "article_body_hidden" %> </dd>
Just changing the class of the text_area is enough to active codepress... pretty cool huh? The
checkbox is cool too, allowing you to turn codepress on/off on the fly.
Unfortunately, I had to do some craziness with a hidden field. Codepress does not submit the edited
data with the form, like you'd expect. Because it creates an design-mode iframe, the browser does not
submit your code the way you want it to. However, it provides an object on which you can call a method
to get the unformatted code. We can copy the code into the hidden field with an onsubmit
in /app/views/admin/articles/new.rhtml and
/app/views/admin/articles/edit.rhtml by adding an :onsubmit option to the form like so:
{:id => 'article-form', :multipart => true, :onsubmit => "document.getElementById('article_body_hidden').value = article_body.getCode();"}
Of course, we've now got a hidden field named article[body_hidden] which contains
the actual body, rather than the regular field article[body], so we need to deal with that somewhere.
I chose /app/models/article.rb, adding the following public method:
def body_hidden=(newval) self.body = newval end
This is still mostly untested, but I'm really enjoying it so far. Uploading an asset inline might not submit the body of the article anymore, so you'll have to a similar piece of javascript to copy the value from the codepress object or lose your changes when you upload an image.
Writing view helpers with 'yield'
Ever wanted to pass in blocks of view code to a helper? Think of the power! Say we want to make a helper method which surrounds whatever block you pass in with a firefox html designer iframe, here's how I'd imagine a user would want to use it:
<% html_editor(:width => 600, :height => 400) do %> <p><b>This html should be editable by the user!</b></p> <% end %>
Here's how you could write such a helper function:
class ApplicationHelper def html_editor(options = {}, &block) concat("<IFRAME WIDTH=#{options[:width] || 400} HEIGHT=#{options[:height || 200]} ID=myEditor>") yield concat("</IFRAME>") concat("<script>frames.myEditor.document.designMode = 'On'</script>") end end
Notice that I pass the block in as the last parameter "&block", but I never use it in the function! Ruby doesn't require that you pass the name of the block in when you yield, because it only supports the passing in of 0 or 1 blocks to a function (hence it always knows which one you're talking about). I like to pass it in as a named method parameter because it makes the method signature more clear.
Now let's improve on our initial version, by passing an object back to the view. This is exactly the way form_for passes back a |form| object to the view. Our updated use case is as follows:
<% html_editor(:width => 600, :height => 400) do |editor| %> <p><b>This html should be editable by the user!</b></p> <% editor.command_button("Bold", "bold") %> <% end %>
The "command_button" method above should insert a standard html button with the text "Bold" that executes the "bold" command on the selected text range inside the html editor. Let's see how this can be implemented:
class JsHtmlEditor attr_reader :output def command_button(name, command) @output ||= "" @output << "<button onclick='javascript: frames.myEditor.document.selection.createRange().execCommand(\"#{command}\")'> #{name}</button>\n" end end module ApplicationHelper def html_editor(options = {}, &block) editor = JsHtmlEditor.new("myEditor") concat("<IFRAME WIDTH=#{options[:width] || 400} HEIGHT=#{options[:height || 200]} ID='myEditor'>") yield editor concat("</IFRAME>") concat("<script>frames.myEditor.document.designMode = 'On'</script>") concat(editor.output) unless editor.output.blank? # stick the user buttons at the bottom end end
Pretty cool huh? When we create a new editor and yield it, we make its methods accessible to the view block. In this example, the user can execute editor.command_button whereever they want inside the block, but we collect the output of those statements and force them all to the bottom of the iframe (where they probably belong).
Another common use of blocks inside view helpers is simply to control whether or not they get yielded at all. If you don't call yield, the block never executes and never gets rendered. Often times we have complex conditions determining when a certain block of code should be rendered, and this approach can clean that up immensely.
Ultraviolet syntax highlighting in Mephisto
Being a mac rails user, I love textmate. Naturally, I went crazy when I found out that Ultraviolet is a syntax highlighting gem for ruby that reads textmate theme and textmate syntax files to create xhtml highlighted code. After using this in my tictactoe project, I integrated it into mephisto. Because I wanted to use the filter:code tag that's normally used by coderay, I did this by deleting all the files in the filtered_column_code_macro/lib plugin except code_macro.rb, which I replaced as follows
require 'uv' class CodeMacro < FilteredColumn::Macros::Base def self.filter(attributes, inner_text = '', text = '') lang = attributes.delete(:lang) || "ruby_on_rails" theme = attributes.delete(:theme) || "cobalt" begin Uv.parse( inner_text, "xhtml", lang, false, theme) rescue RAILS_DEFAULT_LOGGER.warn "UltraViolet Error: #{$!.message}" RAILS_DEFAULT_LOGGER.debug $!.backtrace.join("\n") end end end
Then I copied in the xhtml CSS files for Ultraviolet xhtml rendering (as shown in the UV instructions) into my stylesheets directory. I included the themes I liked into my layout.liquid as follows:
<link rel="stylesheet" href="/stylesheets/uv/blackboard.css" type="text/css" media="screen" /> <link rel="stylesheet" href="/stylesheets/uv/dawn.css" type="text/css" media="screen" /> <link rel="stylesheet" href="/stylesheets/uv/cobalt.css" type="text/css" media="screen" />
Finally, you have to remove a reference to 'include "coderay"' in environment.rb. For each code block in your blog, you can change the theme (there's about 12 included themes):
import java.net; public class Bob extends BobParent implements Bobliness { private final int SOMETHING=0; public static void main(String[] args) { System.out.println("Hello world!" + 5.5) } }
UV comes with syntax highlightinb for 158 languages:
actionscript, active4d_html, active4d_ini, active4d_library, active4d, ada, antlr, apache, applescript, asp, asp_vb.net, bibtex, blog_html, blog_markdown, blog_textile, blog_text, build, bulletin_board, cake, camlp4, cm, coldfusion, context_free, css_experimental, css, cs, csv, c, c++, diff, dokuwiki, dot, doxygen, d, dylan, eiffel, erlang, fortran, f-script, fxscript, greasemonkey, gri, groovy, gtdalt, gtd, haml, haskell, html-asp, html_django, html_for_asp.net, html_mason, html_rails, html, html_tcl, icalendar, inform, ini, installer_distribution_script, io, javaproperties, javascript_+_prototype_bracketed, javascript_+_prototype, javascript, java, jquery_javascript, json, languagedefinition, latex_beamer, latex_log, latex_memoir, latex, lexflex, lighttpd, lilypond, lisp, literate_haskell, logo, logtalk, lua, macports_portfile, mail, makefile, man, markdown, mediawiki, mel, mips, mod_perl, modula-3, moinmoin, mootools, movable_type, m, multimarkdown, objective-c, objective-c++, ocamllex, ocaml, ocamlyacc, opengl, pascal, perl, php, plain_text, pmwiki, postscript, processing, prolog, property_list, python_django, python, qmake_project, qt_c++, quake3_config, ragel, r_console, rd_r_documentation, regexp, regular_expressions_oniguruma, regular_expressions_python, release_notes, remind, restructuredtext, rez, r, ruby_experimental, ruby_on_rails, ruby, s5, scheme, scilab, setext, shell-unix-generic, slate, smarty, sql_rails, sql, ssh-config, standard_ml, strings_file, subversion_commit_message, sweave, swig, tcl, template_toolkit, tex_math, tex, textile, tsv, twiki, txt2tags, vectorscript, xhtml_1.0, xml_strict, xml, xsl, yaml, yui_javascript
Welcome to flouri.sh
I finally have a blog!
Anyone who knows me knows that I've despised blogs my entire life. However, I feel it's time to move on from my backwards ideals and embrace a new technology. I divided it into these sections:
- Blog: a running timeline of the next 3 sections
- Personal: information about my life.
- Projects: full projects that I've done outside my current job.
- Technology: smaller technological tidbits not related to work.
- Work: things that I've done at work.
I chose Mephisto as my blogging system because it's rails based. I've already made several modifications which I'm going to blog about in later posts.