Blog


    Form objects with nested forms 2- editing

    Continuing with my last form object experiment, I created a second form for editing a company and its offices. In keeping with the form object patterns I’d encountered on other projects, I split everything up into three classes- one for creating a new company and offices, another for editing an existing company and offices, and a third for behavior to be inherited by the other two.

    This is an experiment, so I’m not totally convinced that the different things I tried are necessarily the right solution. It’s currently in the “ok, this works alright” stage, and I plan to revisit the code later with a fresh (and more critical) perspective.

    To avoid pasting nearly 200 lines of code straight into this post, I put the three form objects in a gist. The repo itself can be found here.

    The first complication I ran into was that the edit form needed to be able to create new office rows as well as edit them. I originally placed the NewOfficeRow class inside the NewCompanyForm class, but that no longer made sense once I needed EditCompanyForm to have access to it as well. So, I moved it to the main CompanyForm.

    Next, the edit form needed to be able to differentiate between new and existing office rows, and either create or update accordingly. I’m not super thrilled by the current solution, which looks like this:

      def extract_params(params)
        super
    
        new_office_params = @office_params.select { |k, v| v[:id].blank? }
    
        @office_rows = office_rows.select { |row| row.persisted? } # remove NewOfficeRow instances
    
        @office_rows.each do |office_row|
          params = @office_params.select { |k, v| v[:id] == office_row.office.id.to_s }
          params = params.values.inject(:reduce)
          office_row.extract_params(params)
        end
    
        new_office_params.each do |k, v|
          params = v.merge(company: company)
          @office_rows << NewOfficeRow.new(params)
        end
      end
    

    I added two hidden fields to the office row form templates for rows used to edit existing offices. One of those hidden fields was for the office’s id (one of the first things I plan to revisit is to investigate better ways to communicate this information to the EditCompanyForm). Office row params are split up according to whether they contain this id param.

    (Note: Although hidden fields are not ideal from a security standpoint, I don’t think they present an issue here. The field value is only used to match up the params for a particular office with its EditOfficeRow instance. A set of office row params can only be used to edit an office object if the id param matches the id of one of the existing offices that was used to initialize the form, so if a user were to deliberately submit different value in this field, the worst that could happen is they could update a different office belonging to that company. Since the same thing could be achieved by modifying the office attribute fields in a totally permissible manner, I don’t think it is cause for concern. This experiment app doesn’t have any concept of users or ownership of companies, but obviously that would exist in a real-world implementation, and in that case, the users would still only be able to modify offices under their control.)

    Once the office row params are split up into new vs existing office groups, the NewOfficeRow instances are removed from @office_rows. Up til this point, NewOfficeRow is only used for extracting params and running validations. But, if an instance fails validation, the validations have to be re-run when the form is submitted again. In this case, there is no easy way to use a unique identifier to match up the submitted params with existing NewOfficeRow instances, so I just discard and re-create them from the new params, and add these new instances back into the @office_rows instance variable, after which the validations will be run against all rows.

    The next complication came about as a result of wanting the edit form to be able to remove existing offices. I considered using the ‘remove’ link to make an ajax call to delete the row’s office, but I wanted to keep as much of the logic in the form objects as possible. So I opted for a flag that could be used by the form to tell which rows to update, and which rows to delete.

    This is where the second hidden field comes in. Its default value is false, but clicking on the removal confirmation (a “Yes” link) triggers a binding that sets its value to true. When the form is submitted, the existing offices with a remove value of ‘true’ are deleted, and the rest are updated. Again, nothing submitted in this field is written to the database, and its value is only used if it is true, and then only used to delete that particular office.

    Next, I plan to use this as a basis for learning more about ReactJS. I’ve used it a little in the past, but that was a while ago. I’ve forgotten a lot of what I did and even at the time, I was far from comfortable with it. So, adding components to this is going to be my first step towards learning more.


    Fri 11 Mar 2016

    Testing and troubleshooting on mobile devices

    Recently, I was working on a project that had a lot of users who mostly interacted with the application using mobile devices (iOS being the most common). It was responsive, and all the external (customer) facing features worked really well, but internal users had some issues. They typically had a lot of interaction with forms rendered in Bootstrap modals, some of which broke down on mobile devices.

    When I first started to investigate, I turned to Chrome’s device emulator. While it’s an overall useful tool, I wasn’t able to reproduce most of these issues. So, I was going to have to use actual mobile devices to interact with my computer’s localhost. Fortunately, ngrok makes this super easy. Once installed, call the program and pass it a port number (ex. ./ngrok 3000) and it exposes your server to the internet and generates a url to access it from. It’s also useful if you want someone else to be able to access your local server.

    Now, you can start testing on whatever device you want. In this particular case, I needed to be able to inspect elements as they were rendered on my phone. This is where Safari’s device web inspector comes in. A good rundown of how to get that working if you don’t have it already can be found here.

    With these two tools, I was able to track down the issues and test various fixes much more easily than I would have been able to otherwise.


    Fri 19 Feb 2016

    Form objects with a variable number of nested forms

    I see questions about this pop up pretty regularly on StackOverflow. People want to create a form with variable numbers of nested forms. For example, someone may have a Company model which has_many :offices, and they want a single form which allows users to create their company along with however many offices their company has. Although Rails has accepts_nested_attributes_for, which can be used to solve this problem, I like to use form objects when dealing with complex forms. (For a good introduction to form objects, check out this blog post by Code Climate.)

    I learned this pattern for nested forms from a project I was working on a while back. Although it was being used to manage forms where the number of nested forms was set at the beginning, with a few changes, it can be used for forms where the user determines the number of nested forms.

    The idea is to create a class for the main form (the company in this case), and a separate class for the nested objects. In the view, a “Add Office” button can be used to append another nested form partial (however many times the user requires), and on submit, the form object iterates through the attributes for each office form row and creates a new Office for each.

    I put together a quick example project to make sure the below code works, but it’s just a demonstration of the concepts, so the code isn’t perfect.

    In the controller, the form’s #submit method is used to determine whether to redirect to the index page or re-render the form with errors:

    app/controllers/companies_controller.rb

      def create
        form = CompanyForm.new
    
        if form.submit(params[:company_form])
          redirect_to companies_path, notice: "Company created."
        else
          @form = form
          render :new
        end
      end
    

    That’s because #submit handles the Company and Office object validations instead of their respective models. It’s also responsible for creating those objects, when traditionally that would happen on the controller level:

    app/forms/company_form.rb

    class CompanyForm
    
      extend ActiveModel::Naming
      include ActiveModel::Conversion
      include ActiveModel::Validations
    
      attr_reader :name, :employee_count
    
      with_options presence: true do |required|
        required.validates :name
        required.validates :employee_count
      end
    
      def persisted?
        false
      end
    
      def office_rows
        @office_rows ||= [OfficeRow.new] # uhhhh...
      end
    
      def valid?
        validate_rows = @office_rows.all?(&:valid?)
        validate_form = super()
        validate_rows && validate_form
      end
    
      def submit(params)
        extract_params(params)
        if valid?
          persist!
          true
        else
          false
        end
      end
    
      def persist!
        @company = Company.new(name: name, employee_count: employee_count)
    
        @office_rows.each do |office_row|
          office = create_office(office_row)
          @company.offices << office
        end
    
        @company.save!
      end
    
      def extract_params(params)
        @name = params[:name]
        @employee_count = params[:employee_count]
        office_params = params[:office_rows]
    
        @office_rows = office_params.map do |k, office_attrs|
          OfficeRow.new(office_attrs)
        end
      end
    
      def create_office(office_row)
        office_row.save!
      end
    
      class OfficeRow
    
        attr_accessor :name, :city, :state, :employee_count
    
        include ActiveModel::Validations
    
        with_options presence: true do |required|
          required.validates :name 
          required.validates :city 
          required.validates :state 
          required.validates :employee_count
        end
    
        def initialize(params = {})
          @name = params[:name]
          @city = params[:city]
          @state = params[:state]
          @employee_count = params[:employee_count]
        end
    
        def save!
          Office.create!(name: name, city: city, state: state, employee_count: employee_count)
        end
      end
    end
    

    The #valid? method on the CompanyForm class overrides the #valid? method provided by the included ActiveModel::Validations module. First it calls #valid? on each instance of OfficeRow (where it is not overridden), which runs the validations, and then calls super() to run the CompanyForm validations. If both of those are true, a new company is created, and then new offices are created and associated with the new company. Once all that happens, #submit returns true and the user is redirected to the company index page. If any of the validations fail, the form is re-rendered with errors (just like a traditional #create action).

    I ran into an unexpected problem when it came to actually adding new office form partials to the form- even though there were multiple office forms on the page only one set of params was being submitted. Because I was copying the original office form to create the new one, the form fields were identical. Since Rails ignores duplicate parameter names, I had to modify some label and input attributes so they would be recognized as separate fields. I came across a this blog post, which demonstrated using regexes to increment the id numbers in the relevant form element attributes.

    In case anyone who wants to play around with the code or experiment with improvements, it can be found here. It doesn’t have any edit / update functionality yet, but I’ll be adding that soon so I can use the code for some more experiments.


    Thu 04 Feb 2016

    Turning old notes into new blog posts

    I set up this blog mostly to give myself a reason to organize my thoughts about interesting things I've learned or problems I've run into, and to serve as a sort of "developer diary". I keep a lot of notes and links for these things, but taking those and turning them into a blog post helps to solidify them in my mind, in the same way explaining something to someone else does. It's also a handy way to catalogue things I may want to refer back to, or document the way I was thinking about something at a particular point in time.

    With that in mind, I'm about to start the process of organizing some of the notes I've been keeping into actual, (semi-) coherent posts. Hopefully this will be the last time the time between posts approaches the 2-year mark.


    Mon 28 Dec 2015

    Codewars- Fluent Calculator

    This was a fun exercise. The description states: “The goal is to implement simple calculator which uses fluent syntax”. For example, Calc.new.one.plus.two should return 3. Each calculation is only one operation.

    To start out, I wrote some test methods to play with in IRB. This turned out to be a bit of a mistake, though, because this is what I came up with:

      def one
        if self.is_a? String
          instance_eval "#{self} 1"
        else
          1
        end
      end
    
      def two
        if self.is_a? String
          instance_eval "#{self} 2"
        else
          2
        end
      end
    
      def plus
        "#{self} +"
      end 
    

    It worked at first:

    1.9.3p484 :117 > one.plus.two
     => 3 
    

    But it broke as soon as I tried to write a class with those methods:

    1.9.3p484 :049 > Calc.new.one.plus.two
    NoMethodError: undefined method `plus' for 1:Fixnum
        from (irb):49
        from /Users/miles/.rvm/rubies/ruby-1.9.3-p484/bin/irb:12:in `<main>'
    

    I was confused, and after a fair bit of experiment and searching, I couldn’t find an answer, so I turned to the good folks of StackOverflow for guidance. In a matter of minutes, two people explained that when I defined those lone methods in IRB, I was defining them in the global namespace, which meant that they were being defined on the Object class and therefore being inherited by Fixnum and String.

    So, the whole approach had to be scrapped.

    As I was thinking about different ways this fluent calculator could be built, I decided I needed to read up a bit on method chain evaluation. A post by Jeff Kreeftmeijer, Method chaining and lazy evaluation in Ruby, gave me exactly what I needed, which was to have each method return self, the Calc object, instead of a String or Fixnum.

    class Calc # working test version
    
      def calc
        @calc ||= {}
      end
    
      def one
        if !calc[:operand_1].nil?
          return instance_eval "#{calc[:operand_1]} #{calc[:operator]} 1"
          # calc[:operand_1] calc[:operator]
        else
          calc[:operand_1] = 1
        end
        self
      end
    
      def two
        if !calc[:operand_1].nil?
          return instance_eval "#{calc[:operand_1]} #{calc[:operator]} 2"
        else
          calc[:operand_1] = 2
        end
        self
      end
    
      def plus
        calc[:operator] = '+'
        self
      end
    
    end
    

    It was working!

    1.9.3p484 :144 > Calc.new.one.plus.two
     => 3 
    

    Now, I had to add eight more number methods and three more operator methods to make a complete calculator. There was no way I wanted to have two large groups of nearly-identical methods, so I used define_method to do the work for me (I used a post by Jay Fields, Ruby: Dynamically Define Method, as a reference for this).

    Here’s what I ended up with:

    class Calc # complete working version
    
      nums = [:zero, :one, :two, :three, :four, :five, :six, :seven, :eight, :nine]
      ops = {:plus => '+', :minus => '-', :times => '*', :divided_by => '/'}
    
      def calc
        @calc ||= {}
      end
    
      define_method :sym_to_i do |sym|
        nums.index(sym)
      end
    
      nums.each do |method|
        define_method method do
          if !calc[:operand_1].nil?
            return instance_eval "#{calc[:operand_1]} #{calc[:operator]} #{sym_to_i(method)}"
          else
            calc[:operand_1] = sym_to_i(method)
          end
          self
        end
      end
    
      ops.each do |method, op|
        define_method method do
          calc[:operator] = op
          self
        end
      end
    
    end
    

    I’m sure there are improvements that could be made, but for now, I’m happy to have a working fluent calculator. It was a worthwhile exercise, and gave me some good practice in a couple of different areas.


    Sun 23 Mar 2014