Skip to content

Commit

Permalink
Merge pull request #1150 from larrymjordan/input-generators
Browse files Browse the repository at this point in the history
Add custom input generator support
  • Loading branch information
justinfrench committed Oct 27, 2015
2 parents 4e757bd + 03a1ead commit 4697a1d
Show file tree
Hide file tree
Showing 4 changed files with 205 additions and 2 deletions.
18 changes: 16 additions & 2 deletions README.textile
Expand Up @@ -539,8 +539,7 @@ h2. Modified & Custom Inputs

You can modify existing inputs, subclass them, or create your own from scratch. Here's the basic process:

* Create a file in @app/inputs@ with a filename ending in @_input.rb@. For example, @app/inputs/hat_size_input.rb@. Formtastic will automatically look in @app/inputs@ and find the file.
* In that file, declare a classname ending in @Input@. For example, @class HatSizeInput@. It must have a @to_html@ method for rendering.
* Run the input generator and provide your custom input name. For example, @rails generate formtastic:input hat_size@. This creates the file @app/inputs/hat_size_input.rb@. You can also provide namespace to input name like @rails generate formtastic:input foo/custom@ or @rails generate formtastic:input Foo::Custom@, this will create the file @app/inputs/foo/custom_input.rb@ in both cases.
* To use that input, leave off the word "input" in your @as@ statement. For example, @f.input(:size, :as => :hat_size)@

Specific examples follow.
Expand All @@ -558,6 +557,13 @@ To modify the behavior of @StringInput@, subclass it in a new file, @app/inputs/
end
</pre>

Another way to modify behavior is by using the input generator:
<pre>
rails generate formtastic:input string --extend
</pre>

This generates the file @app/inputs/string_input.rb@ with its respective content class.

You can use your modified version with @:as => :string@.

h3. Creating New Inputs Based on Existing Ones
Expand All @@ -572,6 +578,14 @@ To create your own new types of inputs based on existing inputs, the process is
end
</pre>

You can also extend existing input behavior by using the input generator:

<pre>
rails generate formtastic:input FlexibleText --extend string
</pre>

This generates the file @app/inputs/flexible_text_input.rb@ with its respective content class.

You can use your new input with @:as => :flexible_text@.

h3. Creating New Inputs From Scratch
Expand Down
46 changes: 46 additions & 0 deletions lib/generators/formtastic/input/input_generator.rb
@@ -0,0 +1,46 @@
module Formtastic

# Modify existing inputs, subclass them, or create your own from scratch.
# @example
# !!!shell
# $ rails generate formtastic:input HatSize

# @example Define input name using underscore convention
# !!!shell
# $ rails generate formtastic:input hat_size

# @example Override an existing input behavior
# !!!shell
# $ rails generate formtastic:input string --extend

# @example Extend an existing input behavior
# !!!shell
# $ rails generate formtastic:input FlexibleText --extend string
class InputGenerator < Rails::Generators::NamedBase

argument :name, :type => :string, :required => true, :banner => 'FILE_NAME'

source_root File.expand_path('../../../templates', __FILE__)

class_option :extend

def create
normalize_file_name
define_extension_sentence
template "input.rb", "app/inputs/#{name.underscore}_input.rb"
end

protected

def normalize_file_name
name.chomp!("Input") if name.ends_with?("Input")
name.chomp!("_input") if name.ends_with?("_input")
name.chomp!("input") if name.ends_with?("input")
end

def define_extension_sentence
@extension_sentence = "< Formtastic::Inputs::#{name.camelize}Input" if options[:extend] == "extend"
@extension_sentence ||= "< Formtastic::Inputs::#{options[:extend].camelize}Input" if options[:extend]
end
end
end
19 changes: 19 additions & 0 deletions lib/generators/templates/input.rb
@@ -0,0 +1,19 @@
class <%= name.camelize %>Input <%= @extension_sentence %>
<%- if !options[:extend] -%>
include Formtastic::Inputs::Base
<%- end -%>

<%- if !options[:extend] || (options[:extend] == "extend") -%>
def to_html
# Add your custom input definition here.
<%- if options[:extend] == "extend" -%>
super
<%- end -%>
end
<%- else -%>
def input_html_options
# Add your custom input extension here.
end
<%- end -%>
end
124 changes: 124 additions & 0 deletions spec/generators/formtastic/input/input_generator_spec.rb
@@ -0,0 +1,124 @@
require 'spec_helper'

require 'generators/formtastic/input/input_generator'

describe Formtastic::InputGenerator do
include FormtasticSpecHelper

destination File.expand_path("../../../../../tmp", __FILE__)

before do
prepare_destination
end

after do
FileUtils.rm_rf(File.expand_path("../../../../../tmp", __FILE__))
end

describe 'without file name' do
it 'should raise Thor::RequiredArgumentMissingError' do
lambda { run_generator }.should raise_error(Thor::RequiredArgumentMissingError)
end
end

describe "input generator with underscore definition" do
before { run_generator %w(hat_size)}

describe 'generate an input in its respective folder' do
subject{ file('app/inputs/hat_size_input.rb')}
it { should exist}
it { should contain "class HatSizeInput"}
it { should contain "def to_html"}
it { should contain "include Formtastic::Inputs::Base"}
it { should_not contain "super"}
end
end

describe "input generator with camelcase definition" do
before { run_generator %w(HatSize)}

describe 'generate an input in its respective folder' do
subject{ file('app/inputs/hat_size_input.rb')}
it { should exist}
it { should contain "class HatSizeInput"}
end
end

describe "input generator with camelcase Input name sufixed" do
before { run_generator %w(HatSizeInput)}

describe 'generate an input in its respective folder' do
subject{ file('app/inputs/hat_size_input.rb')}
it { should exist}
it { should contain "class HatSizeInput"}
end
end

describe "input generator with underscore _input name sufixed" do
before { run_generator %w(hat_size_input)}

describe 'generate an input in its respective folder' do
subject{ file('app/inputs/hat_size_input.rb')}
it { should exist}
it { should contain "class HatSizeInput"}
end
end

describe "input generator with underscore input name sufixed" do
before { run_generator %w(hat_sizeinput)}

describe 'generate an input in its respective folder' do
subject{ file('app/inputs/hat_size_input.rb')}
it { should exist}
it { should contain "class HatSizeInput"}
end
end

describe "override an existing input using extend" do
before { run_generator %w(string --extend)}

describe 'app/inputs/string_input.rb' do
subject{ file('app/inputs/string_input.rb')}
it { should exist }
it { should contain "class StringInput < Formtastic::Inputs::StringInput" }
it { should contain "def to_html" }
it { should_not contain "include Formtastic::Inputs::Base" }
it { should contain "super" }
it { should_not contain "def input_html_options" }
end
end

describe "extend an existing input" do
before { run_generator %w(FlexibleText --extend string)}

describe 'app/inputs/flexible_text_input.rb' do
subject{ file('app/inputs/flexible_text_input.rb')}
it { should contain "class FlexibleTextInput < Formtastic::Inputs::StringInput" }
it { should contain "def input_html_options" }
it { should_not contain "include Formtastic::Inputs::Base" }
it { should_not contain "def to_html" }
end
end

describe "provide a slashed namespace" do
before { run_generator %w(stuff/foo)}

describe 'app/inputs/stuff/foo_input.rb' do
subject{ file('app/inputs/stuff/foo_input.rb')}
it {should exist}
it { should contain "class Stuff::FooInput" }
it { should contain "include Formtastic::Inputs::Base" }
end
end

describe "provide a camelized namespace" do
before { run_generator %w(Stuff::Foo)}

describe 'app/inputs/stuff/foo_input.rb' do
subject{ file('app/inputs/stuff/foo_input.rb')}
it {should exist}
it { should contain "class Stuff::FooInput" }
it { should contain "include Formtastic::Inputs::Base" }
end
end
end

0 comments on commit 4697a1d

Please sign in to comment.