Skip to content

sunchess/cfror

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

30 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Custom Fields for Ruby on Rails

Add custom fields functional to your Rails app!

Installation

  gem 'cfror'
  bundle
  rake db:migrate

And now you can put to model string "include Cfror::Fields".

  class Site < ActiveRecord::Base
    include Cfror::Fields
  end

Put to controller

  def new
    @site = Site.new
    gon.fields = @site.fields #see the gon gem
  end

  def edit
    @site = Site.find(params[:id])
    gon.fields = @site.fields
  end

Cfror::Field.field_types contains all support types. This is a simple AR enum.

And in view (real slim and angular example)

   = form_for @site do |f|
      fieldset
        section
          label.input
            = f.text_field :title

        h3.padding-20 Fields
        fieldset ng-repeat="field in fields" ng-hide="field._destroy" ng-init='pidx = $index'

          = f.fields_for :fields, Cfror::Field.new, child_index: '{{pidx}}'do |ef|
            = ef.hidden_field :id, 'ng-if'=>"field.id", 'value'=>'{{field.id}}'
            = ef.hidden_field :_destroy, 'ng-if'=>"field._destroy", 'value'=>'{{field._destroy}}'

            section
              = ef.label :title, class: 'label'
              label.input
                /human title
                = ef.text_field :title, 'ng-model' => 'field.title'

            section
              /machin title
              = ef.label :name, class: 'label'
              label.input
                = ef.text_field :name, 'ng-model' => 'field.name'

            section
              = ef.label :field_type, class: 'label'
              label.select
                /t - translate types
                = ef.select :field_type, Cfror::Field.field_types.map{|k, v| [t("cfror.field_type.#{k}"), k]}, {}, {'ng-model' => 'field.field_type'}


            /add select options only for type option
            section.no-margin[ng-if="field.field_type == 'option'"]

              h3 Options

              div ng-repeat="select_option in field.select_options = (field.select_options || [{}])" ng-hide="select_option._destroy" ng-init='cidx = $index'
                = ef.fields_for :select_options, Cfror::SelectOption.new, child_index: '{{cidx}}' do |df|
                  = df.hidden_field :id, 'ng-if'=>"select_option.id", 'value'=>'{{select_option.id}}'
                  = df.hidden_field :_destroy, 'ng-if'=>"select_option._destroy", 'value'=>'{{select_option._destroy}}'

                  .row
                    .col-sm-5.col-md-4.pr20px
                      section
                        = df.label :body, class: 'label'
                        label.input
                          = df.text_field :body, 'ng-model' => 'select_option.body'
                    .col-sm-2.col-md-2
                      label.label
                        | &nbsp;
                      a.btn.btn-danger.btn-plus.mr10px ng-show="$parent.field.select_options.length>1" ng-click="select_option._destroy = true" -
              a.btn.btn-success ng-show="$last" ng-click="$parent.field.select_options.push({})" Add

          a.btn.btn-danger.pull-right ng-show="fields.length>1" ng-click="field._destroy = true" Delete
        footer
          a.btn.btn-success ng-click="fields.push({})" Add one


      = f.submit

Cfror::Field has Integer, String, Text, Image, Date, Datetime and Set column types

Create and update are regular

Controller

  # POST /sites
  # POST /sites.json
  def create
    @site = Site.new(site_params)

    respond_to do |format|
      if @site.save
        format.html { redirect_to @site, notice: 'Created!' }
        format.json { render :show, status: :created, location: @site }
      else
        format.html { render :new }
        format.json { render json: @site.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /sites/1
  # PATCH/PUT /sites/1.json
  def update
    respond_to do |format|
      if @site.update(site_params)
        @site.save_groups(params[:children])
        format.html { redirect_to @site, notice: 'Updated!' }
        format.json { render :show, status: :ok, location: @site }
      else
        format.html { render :edit }
        format.json { render json: @site.errors, status: :unprocessable_entity }
      end
    end
  end

Add content

In your content model put include Cfror::Data

class Publication < ActiveRecord::Base
  include Cfror::Data

  belongs_to :site
end

In form view

  = form_for @publication, html: {class: 'smart-form'} do |f|
    = f.hidden_field :site_id

    - @site.fields.order(:id).each do |field|
        fieldset
          section
            = render partial: 'layouts/cfror/field', locals:{field: field, model: @publication}

The partial layouts/cfror/field contains fields generator. Feel free to change it.

  <label>
    <%= field.title %>
    <br/>
    <%- case field.field_type
       when 'integer' %>
         <input type="number" name="cfror_fields[<%= field.id %>]" value="<%= field.integers.find_by(dataable: model).try(:body) %>" id="cfror_fields_<%=field.id%>" class="cfror_number_input"/>
      <%- when 'string' %>
         <input type="text" name="cfror_fields[<%= field.id %>]" value="<%= field.strings.find_by(dataable: model).try(:body) %>" id="cfror_fields_<%=field.id%>" class="cfror_text_input"/>
      <%- when 'boolean' %>
         <input type="checkbox" name="cfror_fields[<%= field.id %>]" value="<%= field.booleans.find_by(dataable: model).try(:body) %>" id="cfror_fields_<%=field.id%>" class="cfror_checkbox_input"/>
      <%- when 'text' %>
         <textarea name="cfror_fields[<%= field.id %>]" class="cfror_textarea" id="cfror_fields_<%=field.id%>"><%= field.texts.find_by(dataable: model).try(:body) %></textarea>
      <%- when 'date' %>
         <input type="date" name="cfror_fields[<%= field.id %>]" value="<%= field.dates.find_by(dataable: model).try(:body) %>" id="cfror_fields_<%=field.id%>" class="cfror_date_input"/>
      <%- when 'datetime' %>
         <input type="date" name="cfror_fields[<%= field.id %>]" value="<%= field.datetimes.find_by(dataable: model).try(:body) %>" id="cfror_fields_<%=field.id%>" class="cfror_datetime_input"/>
      <%- when 'image' %>
         <input type="file" name="cfror_fields[<%= field.id %>]" value="<%= field.images.find_by(dataable: model).try(:body) %>" id="cfror_fields_<%=field.id%>" class="cfror_file_input"/>
      <%- when 'option' %>
        <%-  value_option = field.options.find_by(dataable: model).try(:select_option_id) %>
        <select name="cfror_fields[<%= field.id %>" id="cfror_fields_<%=field.id%>" class="cfror_select">
          <% field.select_options.order(:id).each do |option|  %>
            <% if value_option and option.id.to_i == value_option.to_i %>
              <option value="<%= option.id %>" selected='selected'><%= option.body %></option>
            <% else %>
              <option value="<%= option.id %>"><%= option.body %></option>
            <% end %>
          <% end %>
        </select>
    <% end %>
  </label>

And create or update method you should add your_model.save_cfror_fields(params[:cfror_fields])

 # POST /publications
  # POST /publications.json
  def create
    @publication = Publication.new(publication_params)

    respond_to do |format|
      if @publication.save
        @publication.save_cfror_fields(params[:cfror_fields])

        format.html { redirect_to @publication, notice: 'Publication was successfully created.' }
        format.json { render :show, status: :created, location: @publication }
      else
        format.html { render :new }
        format.json { render json: @publication.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /publications/1
  # PATCH/PUT /publications/1.json
  def update
    respond_to do |format|
      if @publication.update(publication_params)
        @publication.save_cfror_fields(params[:cfror_fields])

        format.html { redirect_to @publication, notice: 'Publication was successfully updated.' }
        format.json { render :show, status: :ok, location: @publication }
      else
        format.html { render :edit }
        format.json { render json: @publication.errors, status: :unprocessable_entity }
      end
    end
  end

Show data

Show view

  - @publication.value_fields_for(:site).each do |field|
    p
      = field.title
      br
      = field.value_object.body
      /or if you need only values
      = field.value

The method value_fields_for method sets value_object on each object. The argument is a symbol of relation fields contains model. I.e. there is belongs_to :site in Publication model.

Images processing

If you want to have CarrierWave images processing you mast create app/uploaders/cfror/image_uploader.rb file and put CarrierWave code like below.

# encoding: utf-8
class Cfror::ImageUploader
  # Include RMagick or MiniMagick support:
  # include CarrierWave::RMagick
   include CarrierWave::MiniMagick


   # Create different versions of your uploaded files:
   version :thumb do
     process :resize_to_fit => [50, 50]
   end
end

The file is regular CarrierWave uploader.

TODO

  • Make a view jquery helper for rendering nested models
  • Make fields helper by field types
  • Tests

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create new Pull Request