LibXML — empty nodes (and the libxml-rails gem)

I was going to title this post “LibXML — how to fail right out of the box” but then thought a more accurately description of the problem might be better.  There is something I don’t understand about the open source community: its tolerance.  I encountered this problem right out of the gate:

The XML:

<?xml version="1.0" encoding="UTF-8"?>
<root_node>
<elem1 attr1="val1" attr2="val2"/>
<elem2 attr1="val1" attr2="val2"/>
<elem3 attr="baz">
  <elem4/>
  <elem5>
    <elem6>Content for element 6</elem6>
  </elem5>
</elem3>
</root_node>

The resulting root children:

libxml-1

 

 

 

 

Do tell me why whitespace and CRLF’s are seen as empty nodes?  Do tell me why this is absurd behavior is tolerated as the default?  I had to google for some indication as to what’s going on.  This fixes the problem (Ruby code):

doc = XML::Document.file('foo.xml', :
  encoding => XML::Encoding::UTF_8, 
  :options => LibXML::XML::Parser::Options::NOBLANKS)

Other than that, it looks like a decent enough package, though I haven’t explored it further.

The libxml-ruby gem

The libxml-ruby gem worked fine, but I did have to, as the documentation says, copy three binaries into the one of the directories in the Windows path.  Happily, the gem comes with the precompiled binaries – that’s a real help and kudos to the gem authors for providing the MinGW32 binaries.

Add routes in Rails dynamically at runtime

After poking around some more, I answered my question in my previous post — “when I add a route at runtime, do they add to the route list or replace existing ones?”  The answer is that they are added to the route list.  So here’s the code to add the route once by checking to see if it already exists.  If not, the route is added, if it does exist, we verify that the controller, method, and verb are identical.

  # Add the route if it doesn't exist.
  # Example: add_route('post', 'sign_in', 'users', 'sign_in') 
  def add_route(verb, url, controller, method)
    route = controller + '_' + method
    if Rails.application.routes.named_routes.routes[route.to_sym].nil?
      Rails.application.routes.disable_clear_and_finalize = true
      Rails.application.routes.draw do
        meth = method(verb.downcase)
        meth.call("/#{url}" => "#{controller}##{method}", :as => "#{controller}_#{method}")
      end
    else
      # Check that we're adding the same verb and route.
      # TODO: Verify the URL is the same too.
      route_verb = Rails.application.routes.named_routes.routes[route.to_sym].verb
      unless route_verb.to_s.upcase =~ /#{verb.upcase}/
        raise "Attempting to use a different verb #{verb} for route #{controller}_#{method}."
      end
    end
  end

 

 

Routes, form_for, the HTML form tag, and dynamic routes

Part 4 of n of the series “Never write HTML, Javascript, or CSS again”

Let’s look at a simple form_for:

<%= form_for @user do |f| %>
  <%= f.label :name %>:
  <%= f.text_field :name %><br />
  <%= f.submit %>
<% end %>

Interestingly, the default behavior does not require a route for the
button (which by default is labelled “Create User”) to work.  When we click
on this button, Rails calls the “create” method:

class UsersController < ApplicationController
  def create
    # ... do something
  end
end

That’s pretty interesting.  Now let’s tweak the form_for a
bit by introducing a desired action:

<%= form_for @user, :url => { :action => "upload" } do |f| %>
  <%= f.label :name %>:
  <%= f.text_field :name %><br />
  <%= f.submit %>
<% end %>

Now that we’re specifying a URL (and what, you may ask, does this have to do
with the action?) we now need a route.  If we don’t provide a route, we get
this message:

No route matches {:action=>"upload", :controller=>"home"}

Interesting again, because Rails is also telling us the controller that it
expects to handle the route “/upload”.  Because I created the view for this
demo page in home and therefore we also have a HomeController defining the page
(I called it “test2”):

class HomeController < ApplicationController
  def test2
    @user = User.new()
  end
end

and, the accompanying route:

get "/test2" => "home#test2"

we can assume that Rails is making a stab at the controller because that’s
the controller that invoked the page.  Rails is making the assumption that
the controller should be HomeController.  Interestingly, it did NOT make
this assumption earlier.

So, let’s provide this route:

post "/upload" => "home#upload"

and the accompanying method (in HomeController):

def upload
  # do something
end

and all is well.

But we don’t want this route vectoring off to the HomeController.  We
want it to go to the UsersController like it was before we started finagling
with the URL.  So we provide an override:

<%= form_for @user, :url => { :controller => "users", :action => "upload" } do |f| %>
  <%= f.label :name %>:
  <%= f.text_field :name %><br />
  <%= f.submit %>
<% end %>

and yes, we would expect to get:

No route matches {:controller=>"users", :action=>"upload"}

so once again, we finagle the route:

post "/upload" => "users#upload"

and write the method in UsersController:

class UsersController < ApplicationController
  def upload
    # Do something
  end
end

and again all is well.

But let’s look at the generated HTML now.  It looks like this:

<form accept-charset="UTF-8" action="/upload" class="new_user" id="new_user" method="post">
  <label for="user_name">Name</label>:
  <input id="user_name" name="user[name]" size="30" type="text" /><br />
  <input name="commit" type="submit" value="Create User" />
</form>

Notice there is nothing that indicates the controller in the markup.

So, what Rails is doing, when it parses the form_for tag, is it is matching
what is on the right-hand side of the route action with what you specify in the
:url => { } hash.  Now, wait a minute.  This seems
unnecessarily invasive.  Why should form_for care what controller is
handling the action route?  It’s not like you can specify two different
controllers for the same route!  Ironically, if you do, Rails doesn’t
throw an exception
(at least 3.2.17, which is what I’m testing with) — in
fact, it will always use the first matching route’s controller and method
mapping!

Now, let’s make matters more interesting.  The Airity DSL isn’t going to
generate a form_for Rails tag, it will be generating the HTML form
tag, like in the HTML example above.  And since there’s no controller being
specified in the HTML, the controller that receives the “action” will be
determined by the route.  If I say home#upload, then the HomeController#upload
function is called, if I say "users#upload, then the UsersController#upload
function is called.

So why does Rails introduce this complexity of making an assumption of what
the default route is (and making an incorrect assumption) and secondly,
requiring a controller hash, which it then enforces that the route is mapped to
the same controller?  These conflicts that can occur between the route
definition and the form_for tag can make for (quite unnecessary, in my opinion)
heaps of confusion.  First, it shouldn’t make any assumptions, second, it
should require a controller hash, and third, it should just let the route
specify the controller.

However!

In my DSL, why not define the controller and method that should handle the
button click?  (Notice we’re dealing with the form tag, not the input
button — the page that receives the data is determined by the action
attribute in the
form tag.)  Why should I have to edit the routes.rb file
when I create forms in the DSL?  This adds complexity and unnecessary
decoupling of action with the controller and method that handles the action.
Unfortunately, the solutions are non-trivial.  The basic solution, the “catch
all
“, can have undesirable side-effects.  A

more robust solution
by Michael Lang is Rails 4 dependent — in other words, it looks like
that there is no common solution (other than the “catch all”, that is Rails
version independent.

Then I stumbled across

this post
which looks to me like it would be Rails agnostic, as it is adds a
route to the routes table with Rails.application.routes.draw do…end.
Looking at this implementation, it looks very similar to what Michael Lang’s
Rails4 dynamic router is doing.  So let’s give it a try.

In the DSL, I want to specify the Sign In action:

html_dsl.form("user", {action: 'sign_in'}) do

and after modifying the DSL code a bit to handle this new hash, we can see
the generated HTML:

<form id="new_user" action="/sign_in" class="new_user" method="post">

and indeed, clicking on the Sign In button gives us the expected routing
error:

No route matches [POST] "/sign_in"

Now we try to specify the handler, let’s hard-code it for the moment:

def sign_in_markup(html_dsl, fz_dsl, styles)
  Rails.application.routes.draw do
    post "/sign_in" => "users#sign_in", :as => "users_sign_in"
  end

  html_dsl.div({id: 'sign_in_page', styles: ['display: none']}) do
    html_dsl.form("user", {action: 'sign_in'}) do
      ....

what you’ll notice is that this initially works.  But do a refresh on
the home page, and you get:

No route matches [GET] "/"

Whoops!  What happened to the routes that were defined in
routes.rb?

If we add the mysterious
Rails.application.routes.disable_clear_and_finalize = true before the “do”
statement, lo-and-behold, it works!

That was remarkably simple, yet a few questions remain — how exactly is this
working, are there unintentional side-effects, does this work with Rails 4, and
are these route additions cumulative, or do they replace any existing
definition?

I’m sure these questions will be explored in more depth at some point!

Recaptcha, Foundation, Sass, and the “different prefix” error

Recaptcha and Foundation

After creating a test project, I finally connected the dots that my inclusion of foundation_and_overrides.scss was causing the problem with the rendering of the recaptcha block (see my previous post.)  A quick google search led me here where DinkoMiletic provides a good solution:

#recaptcha_area input[type="text"] {
  display: inline-block;
  height: auto;
}

Disturbingly, “thedeerchild” writes: ” It’s not really feasible to pull out a core part of our styling due to third party compatibility issues, and it’s fairly easy to overwrite the Foundation styles to fix reCaptcha.”

Now, personally, I think that’s a cop out.  If you’re going to write something as “foundational” (pun intended) as a presentation layer styler, then it is damn well your responsibility when some third party component has compatibility issues, especially one so prevalent as Google’s recaptcha.   Now, granted, I’m not using the most recent version of Foundation, so perhaps the issue has been fixed, but at a minimum, I think these kinds of compatibility issues should be easily found in the documentation, not buried in some forum post.

SASS and the “Different Prefix” Error

However, when I set up my new test project, I started getting the following error:

ActionView::Template::Error (different prefix: "C:/" and "E:/rails-projects/airity/app/assets/stylesheets"

I have never, until this morning, had a problem running rails projects on a different drive.  Now, this morning, this happens.  As others suggested, moving the project back to the C drive where I RailsInstaller did its thing is a solution.  While I came across a few different “solutions” (none of which worked and some of which required forking the sass-rails gem), the only solution that worked and allowed me to keep the project on the desired drive was to use a simlink.

Because this problem appears to be common with other gems (like bootstrap-sass, read here), I am definitely interested in a global solution rather than something that fixes the problem for just one issue.  The mklink command was for some reason undecipherable to me, so as per a suggestion here, I downloaded Directory Linker and filled in:

Link Location: the new folder name on the C drive that I want.  In my case “c:\rails-projects\airity”

Link To: The existing folder name on my E drive.  In my case “e:\rails-projects\airity”

Click Go!, it does it’s thing in a second or so, then verify that the project files and folders exist on the C drive.

After reloading the project in RubyMine from the C drive, lo-and-behold, no more problems with the prefix.

Now if I could only bill the 4 hours I spent this morning working around problems that someone else caused by some update (which I was completely unable to track down, even after setting specific versions of sass and sass-rails based on my laptop version numbers).

Then again, those people that have suffered through this and found solutions which I then used, well, they should be the ones getting the money.

So, now it’s almost 3PM and I’m finally able to get to what I wanted to at 9 this morning (yes, there was lunch and grocery run in between.)  And yes, the reCAPTCHA block now works with Foundation:

recap1

Never write HTML, Javascript, or CSS again — Part 3 of n

As you’ll notice in the my website which I’m using as a test case  for The Great DSL Experiment, there’s a reCAPTCHA block on the new user registration page (http://needsandgifts.herokuapp.com/new_user).  Let’s have some fun getting that working in my Ruby DSL:

In the original website, the markup was:

<%= recaptcha_tags %>

which is just a function call that generates some HTML.

We first need to add the required gem:

gem 'recaptcha', :require => 'recaptcha/rails'

and then run “bundle install”

We also need to set up the API keys, for which instructions are here: https://github.com/ambethia/recaptcha/blob/master/README.rdoc

Since I don’t want to share my keys, the .gitignore file gets updated with:

config/initializers/recaptcha.rb

We also need to:

include Recaptcha::ClientHelper

in the Ruby code to be able to explicitly call recaptcha_tags in Ruby rather than from the ERB view (Rails obviously is doing some magic here.)

Now we can obtain the markup that needs to be embedded:

recaptcha_html = recaptcha_tags()

I want the recaptcha block on its own row, centered, which I’ll specify like this (remember I’m using Foundation Zurb):

airity1

 

 

 

and lastly, I’ll need to implement the “inject” function.  For this, I’ll also update clifton_lib (https://github.com/cliftonm/clifton_lib) to implement an HtmlFragment (derived from XmlNode and not to be found in the .NET equivalent of the XmlDocument and supporting classes) which is going to have a special case handler in XmlDocument#write_nodes.

Now, I’m not very thrilled with how this looks when you start reducing the width of the window — I did all my testing in full-screen width.  Something to explore and also to figure out why Foundation’s small-centered isn’t doing anything.

The result is:

airity2

 

 

 

 

 

 

As usual, you can view this live here.

And as usual, besides the centering bothering me, the bottom of the reCAPTCHA block looks really bad as well.  The number of small details is growing!  Probably a good time to address them next.

Never write HTML, Javascript, or CSS again — Part 2 of n

Some HTML, CSS, and Javascript DSL Examples

In this post, I want to show you how I:

  • added some CSS-DSL to make the checkbox and label look more pleasing by aligning vertically the checkbox
  • Fixed the HTML-DSL to embed links in the label’s text
  • Wrote the supporting Javascript in the DSL

First, we start with a basic DSL implementation:

valign2

This renders as:

valign

which bothers me because the checkbox seems elevated above the text.  So let’s fix that first.  I’m going to specify a style called “checkbox-valign” using the CSS DSL:

valign3

which has a very simple definition:

valign4

 

 

resulting in a more pleasing alignment of the checkbox:

valign8

Next, I want to use the DSL to embed some HTML into the label’s text so that “Privacy Policy” and “Terms and Conditions” become clickable links.  I’ll write it with a couple helper functions (instead of embedding the function directly in the string) to make it clearer:

valign5

The helper functions call return the HTML generated by passing an expression to the DSL’s inline function:

valign6

In the HtmlDsl class, the inline function is implemented as follows:

valign7

Finally, we need some Javascript (in DSL of course) to deal with clicking on the links and showing the correct text and activating the correct link in the sidebar,
leveraging some functions we’ve already written in the DSL as well.

valign9

 

 

 

 

 

 

Now, when we click on the links in the “I acknowledge…” label, we get taken to the appropriate text.

Note that the notable “problem” with this is, since these are not separate pages, pressing “Back” on the browser doesn’t take you back to the registration page.

Try it live here.  Click on the “Register” menu item in the menu bar, then click on the links “Privacy Policy” and “Terms and Conditions” in the text next to the right of the checkbox.