Creating FormTag as Shopify using Liquid Block

If you using Liquid Filter do you know the Shopify is reference to Ecommerce platform on the web and they created and using Liquid Markup

To create like the same behave using simple form ou form_for on the RoR using the Liquid do you must need to created a Liquid Block

In shopify’s explanation {% form ‘example’ %} is informed it only generate elements html to submit but looking carefully the shopify’s form tag does more than that.

a) add  the value post when the render is back

b) add “error” on the class element in the inputs

Screen Shot 2015-12-27 at 13.54.35

This is the way I generate html input with my form block

#/customers/new.html.liquid

{% form 'form_customer' %}
form id=create_customer method=post action=/customers accept-charset=UTF-8
  input type=hidden value={{ _csrf_token }} name=authenticity_token/
  input name=_method type=hidden value=post
  input type=hidden value= name=utf8/
  input name=customer[name] title=Please enter your username (at least 3 characters) type=text class=form-control placeholder=Enter name required minlength=3
{% endform %}

#form_tag.rb on gist

require 'i18n'

class FormTag < Liquid::Block
  Syntax     = /(#{Liquid::QuotedFragment})\s*(by\s*(\d+))?/
  def initialize(tag_name, markup, tokens)
     @param_name = Liquid::Expression.parse(markup).name.gsub(form_, )
     super
  end

  def form_nested(params, index, object, html_name)
    attribute = index.gsub(_attributes, )
    object = object.send(attribute)
    params[index].each do |index, key|
      html_name = html_name << [#{index}]
      element = @doc.at(input[@name='#{html_name}'])
      form_nested(params[index], index, object, html_name) if index.include?(attributes)
      form_error(object, index, element)
    end
  end

  def form_error(object, index, element)
    if object.respond_to?(index) and object.errors[index].any? and not element.nil?
      klass = element.get_attribute(:class)
      element.set_attribute(:class, error #{klass})
    end
  end

  def render(context)
    params = context.registers[:controller].env[rack.request.form_hash]
    unless params.nil?
      @object = context.registers[:action_view].assigns[@param_name]
      @doc = Hpricot(super)  # calling `super` returns the content of the block
      params[@param_name].each do |index, key|
        if params[_method].downcase == post
          html_name = #{@param_name}[#{index}]
          unless @object.valid?
            if index.include?(attributes)
              form_nested(params[@param_name], index, @object, html_name)
            else
              element = @doc.at(input[@name='#{html_name}'])
              form_error(@object, index, element)
            end
          end
          @doc.search(input[@name='#{@param_name}[#{index}]']).each do |element|
            element.set_attribute(:value, key) unless element.get_attribute(:type) == radio
          end
        else
          next
        end
      end
      @doc
    else
      super
    end
  end
end

Liquid::Template.register_tag('form', FormTag)

File Uploads with Directory in Rails with CarrierWave and Ancestry

Screen Shot 2015-11-22 at 17.59.34

This guide will help you to create structure of directories in your application. In my application is need to create this structure because I had to have files with same name. exp: “all.css, sub/all.css”

I assume that I already know how configure Carrierwave on rails. If not you might have look at this material CarrierWave File Uploads by Ryan Bates

In this tutorial I using this gems:

gem 'carrierwave'
gem 'mini_magick'
gem 'ancestry'

I’m using the inspinia template admin. To more information https://wrapbootstrap.com/theme/inspinia-responsive-admin-theme-WB0R5L90S

Screen Shot 2015-11-22 at 16.22.52.png

models/asset_folder.rb

class AssetFolder < ActiveRecord::Base 
  has_ancestry cache_depth: true 
  validates :name, presence: true  validates :name, format: { with: /\A[a-zA-Z]+\z/ } 
  has_many :assets, :class_name => "Imentore::Asset", :foreign_key => "folder_id
end

models/asset.rb

class Asset < ActiveRecord::Base

belongs_to :folder, :class_name =&gt; Imentore::AssetFolder, :foreign_key =&gt; "folder_id"

attr_accessor :filename
mount_uploader :file, FileUploader

end

app/uploaders/file_uploader.rb

require 'carrierwave/processing/mime_types'

class FileUploader < CarrierWave::Uploader::Base
  include CarrierWave::MimeTypes
  include CarrierWave::MiniMagick
  process :set_content_type
  process :save_content_type_and_size_in_model

  def save_content_type_and_size_in_model
    model.content_type = file.content_type if file.content_type
  end
# Choose what kind of storage to use for this uploader:
storage :file
  def store_dir
    if model.folder.is_root?
      "uploads/#{model.class.to_s.underscore}/#{mounted_as} /#{model.folder.id}/#{model.folder.name.to_underscore}/"
     else
       folders = model.folder.ancestors.map{|x| x.name.downcase}.join('/')
       "uploads/#{model.class.to_s.underscore}/#{mounted_as}    /#{model.folder.id}/#{folders}/#{model.folder.name.to_underscore}"
    end
  end
end

 

Important Thing

I create the initialize below to execute when the new user as created
current_user.folders.create(name: “root”, store: store)

#In my default initialize I created a root directory because this will produce same as this bellow
“uploads/asset/file/1/root”

app/views/asset_folders/index.html.slim

#nestable2.dd
ol.dd-list
- @folders.each do |folder|
li.dd-item data-id="2"
.dd-handle.dd-nodrag
span.pull-right
= link_to t('folders', :default =&gt; t("helpers.links.folders")), new_asset_folders_path(parent_id: folder.id), :class=&gt;'btn btn-outline btn-info btn-xs',
'
= link_to t('files', :default =&gt; t("helpers.links.files")),  new_asset_folder_asset_path(folder), :class =&gt; 'btn btn-outline btn-info btn-xs',
span.label.label-info
i.fa.fa-folder
= folder.name
= render "actions", folder:folder
- unless params[:action] == "new"
.row
.hr-line-dashed
.form-group
.col-sm-4.col-sm-offset-1
= link_to new_asset_folder<span class="helper">_path</span>(parent_id: @folder.id), :class=&gt;'btn btn-primary' do
i.fa.fa-plus
i.fa.fa-plus
'
= I18n.t(:new, scope: 'helpers.submit', model: model_class.model_name.human)

app/controlers/asset_folders_controller.rb  #index

def index
@folders = AssetFolder.roots
@folder = params[:parent_id] ? AssetFolder.find(params[:parent_id]) : AssetFolder.first
respond_with(@folders)
end

app/views/asset_folders/new.html.slim

= simple_form_for @folder, :html =&gt; { :class =&gt; "form-horizontal", id: "my-awesome-dropzone" }, wrapper: :horizontal_form, wrapper_mappings: { check_boxes: :horizontal_radio_and_checkboxes, radio_buttons: :horizontal_radio_and_checkboxes, file: :horizontal_file_input, boolean: :horizontal_boolean } do |f|
= f.input :name
= f.input :parent_id, as: :hidden, input_html:{value: f.object.parent.try(:id)}
= f.input :parent, as: :string, input_html:{value: f.object.parent.try(:name)}, disabled: true
button.btn.btn-white data-dismiss="modal" type="button"  Close
= f.button :submit, t(:save), :class =&gt; 'btn btn-primary'

app/controlers/asset_folders_controller.rb  #new

def new
  @folder = AssetFolder.new(parent_id: params[:parent_id])
  respond_with(@folder)
end

app/controlers/asset_folders_controller.rb  #create

def create
  @folder = AssetFolder.new(asset_folder_params)
  flash[:notice] = 'Imentore::AssetFolder was successfully created.' if @folder.save
  @folders = AssetFolder.roots
  respond_with(@folder, location: admin_asset_folders_path)
end

Screen Shot 2015-11-22 at 18.31.18.png

Need more information please let me known or if this helped you.
Breno