forked from formtastic/formtastic
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request formtastic#721 from jcorcuera/formtasticGH-717
FormGenerator is Back! (should close formtastic#717)
- Loading branch information
Showing
4 changed files
with
227 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,105 @@ | |||
# encoding: utf-8 | |||
module Formtastic | |||
# Generates a Formtastic form partial based on an existing model. It will not overwrite existing | |||
# files without confirmation. | |||
# | |||
# @example | |||
# $ rails generate formtastic:form Post | |||
# @example Copy the partial code to the pasteboard rather than generating a partial | |||
# $ rails generate formtastic:form Post --copy | |||
# @example Return HAML output instead of default template engine | |||
# $ rails generate formtastic:form Post --haml | |||
# @example Generate a form for specific model attributes | |||
# $ rails generate formtastic:form Post title:string body:text | |||
# @example Generate a form for a specific controller | |||
# $ rails generate formtastic:form Post --controller admin/posts | |||
class FormGenerator < Rails::Generators::NamedBase | |||
desc "Generates a Formtastic form partial based on an existing model." | |||
|
|||
argument :name, :type => :string, :required => true, :banner => 'MODEL_NAME' | |||
argument :attributes, :type => :array, :default => [], :banner => 'attribute attribute' | |||
|
|||
source_root File.expand_path('../../../templates', __FILE__) | |||
|
|||
class_option :template_engine | |||
|
|||
class_option :copy, :type => :boolean, :default => false, :group => :formtastic, | |||
:desc => 'Copy the generated code the clipboard instead of generating a partial file."' | |||
|
|||
class_option :controller, :type => :string, :default => false, :group => :formtastic, | |||
:desc => 'Generate for custom controller/view path - in case model and controller namespace is different, i.e. "admin/posts"' | |||
|
|||
def create_or_show | |||
@attributes = reflected_attributes if @attributes.empty? | |||
|
|||
engine = options[:template_engine] | |||
|
|||
if options[:copy] | |||
template = File.read("#{self.class.source_root}/_form.html.#{engine}") | |||
erb = ERB.new(template, nil, '-') | |||
generated_code = erb.result(binding).strip rescue nil | |||
puts "The following code has been copied to the clipboard, just paste it in your views:" if save_to_clipboard(generated_code) | |||
puts generated_code || "Error: Nothing generated. Does the model exist?" | |||
else | |||
empty_directory "app/views/#{controller_path}" | |||
template "_form.html.#{engine}", "app/views/#{controller_path}/_form.html.#{engine}" | |||
end | |||
end | |||
|
|||
protected | |||
|
|||
def controller_path | |||
@controller_path ||= if options[:controller] | |||
options[:controller].underscore | |||
else | |||
name.underscore.pluralize | |||
end | |||
end | |||
|
|||
def reflected_attributes | |||
columns = content_columns | |||
columns += association_columns | |||
end | |||
|
|||
def model | |||
@model ||= name.camelize.constantize | |||
end | |||
|
|||
# Collects content columns (non-relation columns) for the current class. | |||
# Skips Active Record Timestamps. | |||
def content_columns | |||
model.content_columns.select do |column| | |||
!Formtastic::Helpers::InputsHelper::SKIPPED_COLUMNS.include? column.name.to_sym | |||
end | |||
end | |||
|
|||
# Collects association columns (relation columns) for the current class. Skips polymorphic | |||
# associations because we can't guess which class to use for an automatically generated input. | |||
def association_columns | |||
model.reflect_on_all_associations(:belongs_to).select do |association_reflection| | |||
association_reflection.options[:polymorphic] != true | |||
end | |||
end | |||
|
|||
def save_to_clipboard(data) | |||
return unless data | |||
|
|||
begin | |||
case RUBY_PLATFORM | |||
when /win32/ | |||
require 'win32/clipboard' | |||
::Win32::Clipboard.data = data | |||
when /darwin/ # mac | |||
`echo "#{data}" | pbcopy` | |||
else # linux/unix | |||
`echo "#{data}" | xsel --clipboard` || `echo "#{data}" | xclip` | |||
end | |||
rescue LoadError | |||
false | |||
else | |||
true | |||
end | |||
end | |||
|
|||
end | |||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -1,8 +1,8 @@ | |||
= semantic_form_for @<%= singular_name %> do |f| | = semantic_form_for @<%= singular_name %> do |f| | ||
= f.inputs do | = f.inputs do | ||
<% attributes.each do |attribute| -%> | <%- attributes.each do |attribute| -%> | ||
= f.input :<%= attribute.name %> | = f.input :<%= attribute.name %> | ||
<% end -%> | <%- end -%> | ||
|
|
||
= f.buttons do | = f.buttons do | ||
= f.commit_button | = f.commit_button |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -1,8 +1,8 @@ | |||
= semantic_form_for @<%= singular_name %> do |f| | = semantic_form_for @<%= singular_name %> do |f| | ||
= f.inputs do | = f.inputs do | ||
<% attributes.each do |attribute| -%> | <%- attributes.each do |attribute| -%> | ||
= f.input :<%= attribute.name %> | = f.input :<%= attribute.name %> | ||
<% end -%> | <%- end -%> | ||
|
|
||
= f.buttons do | = f.buttons do | ||
= f.commit_button | = f.commit_button |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,118 @@ | |||
require 'spec_helper' | |||
|
|||
# Generators are not automatically loaded by Rails | |||
require 'generators/formtastic/form/form_generator' | |||
|
|||
describe Formtastic::FormGenerator do | |||
|
|||
include FormtasticSpecHelper | |||
|
|||
# Tell the generator where to put its output (what it thinks of as Rails.root) | |||
destination File.expand_path("../../../../../tmp", __FILE__) | |||
|
|||
before do | |||
@output_buffer = '' | |||
prepare_destination | |||
mock_everything | |||
::Post.stub!(:reflect_on_all_associations).with(:belongs_to).and_return([ | |||
mock('reflection', :name => :author, :options => {}, :klass => ::Author, :macro => :belongs_to), | |||
mock('reflection', :name => :reviewer, :options => {:class_name => 'Author'}, :klass => ::Author, :macro => :belongs_to), | |||
mock('reflection', :name => :main_post, :options => {}, :klass => ::Post, :macro => :belongs_to), | |||
mock('reflection', :name => :attachment, :options => {:polymorphic => true}, :macro => :belongs_to), | |||
]) | |||
end | |||
|
|||
describe 'without model' do | |||
it 'should raise Thor::RequiredArgumentMissingError' do | |||
lambda { run_generator }.should raise_error(Thor::RequiredArgumentMissingError) | |||
end | |||
end | |||
|
|||
describe 'with existing model' do | |||
it 'should not raise an exception' do | |||
lambda { run_generator %w(Post) }.should_not raise_error(Thor::RequiredArgumentMissingError) | |||
end | |||
end | |||
|
|||
describe 'with attributes' do | |||
before { run_generator %w(Post title:string author:references) } | |||
|
|||
describe 'render only the specified attributes' do | |||
subject { file('app/views/posts/_form.html.erb') } | |||
it { should exist } | |||
it { should contain "<%= f.input :title %>" } | |||
it { should contain "<%= f.input :author %>" } | |||
it { should_not contain "<%= f.input :main_post %>" } | |||
end | |||
end | |||
|
|||
describe 'without attributes' do | |||
before { run_generator %w(Post) } | |||
|
|||
subject { file('app/views/posts/_form.html.erb') } | |||
|
|||
describe 'content_columns' do | |||
it { should contain "<%= f.input :title %>" } | |||
it { should contain "<%= f.input :body %>" } | |||
it { should_not contain "<%= f.input :created_at %>" } | |||
it { should_not contain "<%= f.input :updated_at %>" } | |||
end | |||
|
|||
describe 'reflection_on_association' do | |||
it { should contain "<%= f.input :author %>" } | |||
it { should contain "<%= f.input :reviewer %>" } | |||
it { should contain "<%= f.input :main_post %>" } | |||
it { should_not contain "<%= f.input :attachment %>" } | |||
end | |||
end | |||
|
|||
describe 'with template engine option' do | |||
describe 'erb' do | |||
before { run_generator %w(Post --template-engine erb) } | |||
|
|||
describe 'app/views/posts/_form.html.erb' do | |||
subject { file('app/views/posts/_form.html.erb') } | |||
it { should exist } | |||
it { should contain "<%= semantic_form_for @post do |f| %>" } | |||
end | |||
end | |||
|
|||
describe 'haml' do | |||
before { run_generator %w(Post --template-engine haml) } | |||
|
|||
describe 'app/views/posts/_form.html.haml' do | |||
subject { file('app/views/posts/_form.html.haml') } | |||
it { should exist } | |||
it { should contain "= semantic_form_for @post do |f|" } | |||
end | |||
end | |||
|
|||
describe 'slim' do | |||
before { run_generator %w(Post --template-engine slim) } | |||
|
|||
describe 'app/views/posts/_form.html.slim' do | |||
subject { file('app/views/posts/_form.html.slim') } | |||
it { should exist } | |||
it { should contain "= semantic_form_for @post do |f|" } | |||
end | |||
end | |||
end | |||
|
|||
describe 'with copy option' do | |||
before { run_generator %w(Post --copy) } | |||
|
|||
describe 'app/views/posts/_form.html.erb' do | |||
subject { file('app/views/posts/_form.html.erb') } | |||
it { should_not exist } | |||
end | |||
end | |||
|
|||
describe 'with controller option' do | |||
before { run_generator %w(Post --controller admin/posts) } | |||
|
|||
describe 'app/views/admin/posts/_form.html.erb' do | |||
subject { file('app/views/admin/posts/_form.html.erb') } | |||
it { should exist } | |||
end | |||
end | |||
end |