class ScaffoldingSandbox include ActionView::Helpers::ActiveRecordHelper attr_accessor :singular_name, :suffix, :model_instance def sandbox_binding binding end def default_input_block Proc.new { |record, column| " #{input(record, column.name)}\n" } end end class ListHeadScaffoldingSandbox < ScaffoldingSandbox def default_input_block Proc.new { |record, column| "#{column.human_name}" } end end class ListLineScaffoldingSandbox < ScaffoldingSandbox def default_input_block Proc.new { |record, column| "#{record} #{column.human_name}" } end end class ShowScaffoldingSandbox < ScaffoldingSandbox def default_input_block Proc.new { |record, column| " #{record} #{column.human_name}" } end end class ActionView::Helpers::InstanceTag def to_input_field_tag(field_type, options={}) field_meth = "#{field_type}_field" case field_meth when 'text_field' %Q[] when 'hidden_field' %Q[] when 'password_field' %Q[] end end def to_text_area_tag(options = {}) %Q[] end def to_date_select_tag(options = {}) %Q[todo render date select] end def to_datetime_select_tag(options = {}) %Q[todo render date select] end end module Rails module Generator module Commands class Create < Base def multi_include_template(relative_source, relative_destination, template_options = {}, multi_assign_options = {}) options = template_options.dup options[:assigns] ||= {} multi_assign_options.each do |k,v| options[:assigns][k] = render_template_part(v) end template(relative_source, relative_destination, options) end end class Destroy < RewindBase def multi_include_template(relative_source, relative_destination, template_options = {}, multi_assign_options = {}) end end class List < Base def multi_include_template(relative_source, relative_destination, template_options = {}, multi_assign_options = {}) str = "" multi_assign_options.each do |k,v| str << v[:insert] << ' ' end logger.template "#{str} inside #{relative_destination}" end end class Update < Create def multi_include_template(relative_source, relative_destination, template_options = {}, multi_assign_options = {}) begin dest_file = destination_path(relative_destination) source_to_update = File.readlines(dest_file).join rescue Errno::ENOENT logger.missing relative_destination return end multi_assign_options.each do |k,v| template_options = v logger.refreshing "#{template_options[:insert].gsub(/\.rhtml/,'')} inside #{relative_destination}" begin_mark = Regexp.quote(template_part_mark(template_options[:begin_mark], template_options[:mark_id])) end_mark = Regexp.quote(template_part_mark(template_options[:end_mark], template_options[:mark_id])) # Refreshing inner part of the template with freshly rendered part. rendered_part = render_template_part(template_options) source_to_update.gsub!(/#{begin_mark}.*?#{end_mark}/m, rendered_part) end File.open(dest_file, 'w') { |file| file.write(source_to_update) } end end end end end class MasterviewGenerator < Rails::Generator::NamedBase attr_reader :controller_name, :controller_class_path, :controller_file_path, :controller_class_nesting, :controller_class_nesting_depth, :controller_class_name, :controller_singular_name, :controller_plural_name alias_method :controller_file_name, :controller_singular_name alias_method :controller_view_dir_name, :controller_singular_name alias_method :controller_masterview_name, :controller_singular_name alias_method :controller_table_name, :controller_plural_name def initialize(runtime_args, runtime_options = {}) super @controller_name = args.shift || @name base_name, @controller_class_path, @controller_file_path, @controller_class_nesting, @controller_class_nesting_depth = extract_modules(@controller_name) @controller_class_name_without_nesting, @controller_singular_name, @controller_plural_name = inflect_names(base_name) if @controller_class_nesting.empty? @controller_class_name = @controller_class_name_without_nesting else @controller_class_name = "#{@controller_class_nesting}::#{@controller_class_name_without_nesting}" end end def manifest record do |m| # Depend on model generator but skip if the model exists. m.dependency 'model', [singular_name], :collision => :skip, :skip_migration => true # Check for class naming collisions. m.class_collisions controller_class_path, "#{controller_class_name}Controller", "#{controller_class_name}ControllerTest", "#{controller_class_name}Helper" # Controller, helper, views, and test directories. m.directory File.join('app/controllers', controller_class_path) m.directory File.join('app/helpers', controller_class_path) m.directory File.join('app/views', controller_class_path, controller_view_dir_name) m.directory 'app/views/masterview' m.directory File.join('test/functional', controller_class_path) # Controller class, functional test, helper, and views. m.template 'controller.rb', File.join('app/controllers', controller_class_path, "#{controller_file_name}_controller.rb") m.template 'functional_test.rb', File.join('test/functional', controller_class_path, "#{controller_file_name}_controller_test.rb") m.template 'helper.rb', File.join('app/helpers', controller_class_path, "#{controller_file_name}_helper.rb") # MasterView m.template 'mvpreview.js', File.join('public/javascripts', "mvpreview.js") m.multi_include_template "masterview.rhtml","app/views/masterview/#{controller_masterview_name}.html", {}, { 'form_inclusion' => { :insert => 'form_scaffold.rhtml', :sandbox => lambda { create_multi_sandbox('ScaffoldingSandbox') }, :begin_mark => 'form', :end_mark => 'eoform', :mark_id => singular_name }, 'list_head_inclusion' => { :insert => 'fields_scaffold.rhtml', :sandbox => lambda { create_multi_sandbox('ListHeadScaffoldingSandbox') }, :begin_mark => 'listhead', :end_mark => 'eolisthead', :mark_id => singular_name }, 'list_line_inclusion' => { :insert => 'list_line_scaffold.rhtml', :sandbox => lambda { create_multi_sandbox('ListLineScaffoldingSandbox') }, :begin_mark => 'listline', :end_mark => 'eolistline', :mark_id => singular_name }, 'show_inclusion' => { :insert => 'show_scaffold.rhtml', :sandbox => lambda { create_multi_sandbox('ShowScaffoldingSandbox') }, :begin_mark => 'show', :end_mark => 'eoshow', :mark_id => singular_name } } m.template 'style.css', 'public/stylesheets/scaffold.css' end end protected # Override with your own usage banner. def banner "Usage: #{$0} masterview ModelName [ControllerName] [action, ...]" end def scaffold_views %w(list show new edit destroy) end def scaffold_actions scaffold_views + %w(index) end def unscaffolded_actions args - scaffold_actions end def suffix "_#{singular_name}" if options[:suffix] end def model_name class_name.demodulize end def create_sandbox sandbox = ScaffoldingSandbox.new sandbox.singular_name = singular_name begin sandbox.model_instance = model_instance sandbox.instance_variable_set("@#{singular_name}", sandbox.model_instance) rescue ActiveRecord::StatementInvalid => e logger.error "Before updating scaffolding from new DB schema, try creating a table for your model (#{class_name})" raise SystemExit end sandbox.suffix = suffix sandbox end def create_multi_sandbox( sandbox_class_name ) sandbox = eval("#{sandbox_class_name}.new") sandbox.singular_name = singular_name begin sandbox.model_instance = model_instance sandbox.instance_variable_set("@#{singular_name}", sandbox.model_instance) rescue ActiveRecord::StatementInvalid => e logger.error "Before updating scaffolding from new DB schema, try creating a table for your model (#{class_name})" raise SystemExit end sandbox.suffix = suffix sandbox end def model_instance base = class_nesting.split('::').inject(Object) do |base, nested| break base.const_get(nested) if base.const_defined?(nested) base.const_set(nested, Module.new) end unless base.const_defined?(@class_name_without_nesting) base.const_set(@class_name_without_nesting, Class.new(ActiveRecord::Base)) end class_name.constantize.new end end