Project

cfror

0.0
No commit activity in last 3 years
No release in over 3 years
Cfror add custom fields to rails model.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Development

~> 1.3

Runtime

~> 4.2
 Project Readme

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