5

I'm looking for some advice on designing the above for a RoR application that basically has two sections:

1) Admin Form Builder 2) End User Form (that was created by the admin in step 1)

I've prototyped the JQuery form (end result) I want and it uses "progressive disclosure" (similar to Yehuda Katz's Bamboo example in the JQuery in Action book), so if you click one item another item might open (eg click the "registration checkbox" and another registration sub-form is displayed). I also have the rails application actually building a couple of inputs as described. However, I feel like I'm not really utilizing the dynamic capabilities of Ruby. For example, here's a slice of one of my module methods that builds a particular simple html input:

module Levin 
  module EventReg 
    module Jquery

  def self.simple_registration_text(input_params)
    raise ArgumentError if input_params.nil?
    ip_id = input_params[:input_div_id] 
    simple_reg_input_html = '<p><label for="'
    simple_reg_input_html << "#{ip_id + '_label_id'}\">"
    ........<A BUNCH MORE CODE TO BUILD INPUT>........
    simple_reg_input_html << ' class="required"' unless input_params[:is_required].eql?("0")    
    simple_reg_input_html << '/></p>'
  end

Basically, the administrator can name the input item and I use that for the div id's, etc. Given that I have many input types (select drop downs, textareas, inputs with children like an input type text with a checkbox that, when checked, opens a data grid, that can add/remove items via a dialog popup form, etc.), I'd like to avoid "hard-wiring" build methods like in my example above. Also, it would be great to allow the caller to be responsible for BOTH the input value choices (which is already happening when they pass input params parameter), and ALSO the builder itself.

I'm very new to functional programming but I'm thinking of something to the affect of the caller passing in a "builder" lambda that builds the specific form input they need using the input params leading to:

builder_lambda.call(input_params)

But when I think about this, it seems the lambda will look very similar to what the Jquery module is already doing. Yes, the caller will have the ability to create a custom builder to their liking, but then what is my module method gonna really do! What value will it provide?

I will also sometimes need to connect these inputs to specific event handlers. Perhaps there could be an array of lamdas, a builder and an add event handler like:

builder_lambda.call(input_params)

add_event_handlers_lambda.call(input_params)

Am I on the right track? Sorry if I'm rambling on, but I'm obviously having trouble decomposing this problem into a "Ruby-istic" solution and ideas for a more dynamic approach would be greatly appreciated.

EDIT: This was helpful for me: Obie Fernandez DSL Development PDF

4
  • @Rob, I am working on a similar project, and the link you've given was very helpful. Cheers! Commented Apr 15, 2010 at 6:32
  • 4
    Your sample code is extremely "un-Ruby-istic" but I had a feeling you knew that. I highly recommend Obie's book "The Rails 3 Way". My best advice for being more "Ruby-istic" is to work backwards. Build your first form using no custom code. Then build your second form and maybe a third. Once you see where your duplication will be then refactor. When you start to refactor, write the API first. Think "how do I want this to work" before you think "how will the guts of this work". Even if you aren't writing tests first (!!!) it's still good to have this mentality. Your code will be cleaner. Commented Mar 7, 2011 at 0:38
  • 1
    +1 for linking the presentation. Excellent read. Commented Jun 23, 2011 at 19:22
  • You can put a string across multiple lines. That will make your code a lot easier to read. Commented Jul 17, 2011 at 0:29

1 Answer 1

1

It might sound to counter your idea, but I think you are struggling with this because part of what you want is reinventing the wheel (creating a meta-language where the frameworks you're using already provide the tools).

My approach would be something along the lines:

Create HTML templates for your partials (in your view) using some kind of a javascript template engine (my choice: Handlebars or jQuery.tmpl ). No builders yet.

<script id="form-partial-registration" type="text/x-handlebars-template">
 <div class="registration_field field">
  <label for="{{field_id}}" id="label_for_{{field_id}}" class="label registration"></label>
  <input id="{{field_id}}" />
 </div>
</script> 

then in your javascript simply compile the template

var form_builder = {templates:{}};
function compile_template(template_id, key) {
   form_builder.templates[key] = Handlebars.compile($("#" + template_id).html());
}
function render_template(template_id, data) {
   form_builder.templates[key](data);
}

// other stuff

compile_template('form-partial-registration', 'registration')

then you have a collection of every piece of HTML code needed for your forms, clearly written in HTML instead of javascript. Then use them:

function build_some_form(has_registration, has_expiration, event_handlers) {
  var form_content = [];
  var form_prefix = '...some_prefix...'

  // something optional
  if (has_registration) { 
    form_content.push( render_template('registration', {field_id:form_prefix + '_reg'}) )
  }

  // another optional 
  if (has_expiration) { 
    form_content.push( render_template('expiration', {field_id:form_prefix + '_exp'}) )
  }

  // put it into the form
  $('#some_form').append(form_content.join(''));


  // hook up handlers, in this example for onclick only
  for(var k in event_handlers) {
    $('#some_form #' + form_prefix + '_' + k ).click(event_handlers[k]);
  }
}

build_some_form(true, false, {reg: function() { alert('registration clicked') })

My point is: try focusing on the "business logic" of your application, and aim for simplicity for the rest (to paraphrase Joel Spolsky: care deeply for your core competence and try to outsource/fake the rest).

Of course this is just a very rough draft, the approach I would take and refine it from here (there are a couple of repetitions here, that could be refactored).

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.