From 6baa7faf1d70ce106716102997e20f02d51de0b6 Mon Sep 17 00:00:00 2001 From: Thomas Stankus Jr Date: Fri, 8 Aug 2008 00:21:31 -0400 Subject: [PATCH] TinyMCE plugin generation. --- .gitignore | 1 + README | 7 +- TODO | 2 + generators/aftfilr/aftfilr_generator.rb | 81 ++++- .../controllers/aftfilr_controller.rb | 3 + .../controllers/categories_controller.rb | 0 .../templates/tinymce_plugin/css/aftfilr.css | 0 .../templates/tinymce_plugin/dialog.htm | 41 +++ .../templates/tinymce_plugin/editor_plugin.js | 72 +++++ .../templates/tinymce_plugin/img/aftfilr.png | Bin 0 -> 4358 bytes .../templates/tinymce_plugin/img/category.png | Bin 0 -> 3840 bytes .../templates/tinymce_plugin/js/dialog.js | 15 + .../templates/tinymce_plugin/langs/en.js | 3 + .../templates/tinymce_plugin/langs/en_dlg.js | 3 + test/aftfilr_generator_test.rb | 74 +++++ test/aftfilr_generator_tinymce_plugin_test.rb | 26 ++ test/aftfilr_test.rb | 8 - test/generator_test_helper.rb | 288 ++++++++++++++++++ test/test_helper.rb | 6 + 19 files changed, 617 insertions(+), 13 deletions(-) create mode 100644 .gitignore create mode 100644 TODO create mode 100644 generators/aftfilr/templates/controllers/aftfilr_controller.rb create mode 100644 generators/aftfilr/templates/controllers/categories_controller.rb create mode 100644 generators/aftfilr/templates/tinymce_plugin/css/aftfilr.css create mode 100644 generators/aftfilr/templates/tinymce_plugin/dialog.htm create mode 100644 generators/aftfilr/templates/tinymce_plugin/editor_plugin.js create mode 100644 generators/aftfilr/templates/tinymce_plugin/img/aftfilr.png create mode 100644 generators/aftfilr/templates/tinymce_plugin/img/category.png create mode 100644 generators/aftfilr/templates/tinymce_plugin/js/dialog.js create mode 100644 generators/aftfilr/templates/tinymce_plugin/langs/en.js create mode 100644 generators/aftfilr/templates/tinymce_plugin/langs/en_dlg.js create mode 100644 test/aftfilr_generator_test.rb create mode 100644 test/aftfilr_generator_tinymce_plugin_test.rb delete mode 100644 test/aftfilr_test.rb create mode 100644 test/generator_test_helper.rb create mode 100644 test/test_helper.rb diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e43b0f9 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.DS_Store diff --git a/README b/README index 5c766f9..fd61925 100644 --- a/README +++ b/README @@ -1,8 +1,9 @@ Aftfilr ======= -Introduction goes here. - +A generator for creating a TinyMCE plugin for inserting links to server +documents. Works similarly to the built-in scaffold generator, but does not +generate a test suite or helpers at this time. Example ======= @@ -10,4 +11,4 @@ Example Example goes here. -Copyright (c) 2008 [name of plugin creator], released under the MIT license +Copyright (c) 2008 TJ Stankus, released under the MIT license. diff --git a/TODO b/TODO new file mode 100644 index 0000000..52a975b --- /dev/null +++ b/TODO @@ -0,0 +1,2 @@ +Aftfilr task list + diff --git a/generators/aftfilr/aftfilr_generator.rb b/generators/aftfilr/aftfilr_generator.rb index 9bd5b1e..59599e6 100644 --- a/generators/aftfilr/aftfilr_generator.rb +++ b/generators/aftfilr/aftfilr_generator.rb @@ -1,8 +1,85 @@ class AftfilrGenerator < Rails::Generator::NamedBase + default_options :with_categories => false + + attr_reader :controller_name, + :controller_class_path, + :controller_file_path, + :controller_class_nesting, + :controller_class_nesting_depth, + :controller_class_name, + :controller_underscore_name, + :controller_singular_name, + :controller_plural_name + alias_method :controller_file_name, :controller_underscore_name + alias_method :controller_table_name, :controller_plural_name + + def initialize(runtime_args, runtime_options = {}) + super + + if @name == @name.pluralize && !options[:force_plural] + logger.warning "Plural version of the model detected, using singularized version. Override with --force-plural." + @name = @name.singularize + end + + @controller_name = @name.pluralize + + 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_underscore_name, @controller_plural_name = inflect_names(base_name) + @controller_singular_name=base_name.singularize + 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| - # m.directory "lib" - # m.template 'README', "README" + # Check for class collisions + m.class_collisions class_path, model_class_name #, controller_class_name + + # TinyMCE plugin + m.directory(tinymce_plugin_dir) + m.file 'tinymce_plugin/dialog.htm', File.join(tinymce_plugin_dir, 'dialog.htm') + m.file 'tinymce_plugin/editor_plugin.js', File.join(tinymce_plugin_dir, 'editor_plugin.js') + m.directory(File.join(tinymce_plugin_dir, 'css')) + m.file 'tinymce_plugin/css/aftfilr.css', File.join(tinymce_plugin_dir, 'css', "#{singular_name}.css") + m.directory(File.join(tinymce_plugin_dir, 'img')) + m.file 'tinymce_plugin/img/aftfilr.png', File.join(tinymce_plugin_dir, 'img', "#{singular_name}.png") + m.file 'tinymce_plugin/img/category.png', File.join(tinymce_plugin_dir, 'img', "category.png") + m.directory(File.join(tinymce_plugin_dir, 'js')) + m.file 'tinymce_plugin/js/dialog.js', File.join(tinymce_plugin_dir, 'js', "dialog.js") + m.directory(File.join(tinymce_plugin_dir, 'langs')) + m.file 'tinymce_plugin/langs/en.js', File.join(tinymce_plugin_dir, 'langs', "en.js") + m.file 'tinymce_plugin/langs/en_dlg.js', File.join(tinymce_plugin_dir, 'langs', "en_dlg.js") end end + + alias_method :model_class_name, :class_name + + def tinymce_plugin_dir + File.join('public/javascripts/tinymce/jscripts/tiny_mce/plugins', singular_name) + end + + def tinymce_dialog_name + class_name + 'Dialog' + end + + def tinymce_window_title + class_name.demodulize + ' Manager' + end + + def controller_index_path + class_path.empty? ? "/#{plural_name}" : "/#{class_path.join('/')}/#{plural_name}" + end + + protected + + def add_options!(opt) + opt.separator '' + opt.separator 'Options:' + opt.on("--with-categories", + "Add categories to be associated with the generated model.") { |v| options[:with_categories] = v } + end + end diff --git a/generators/aftfilr/templates/controllers/aftfilr_controller.rb b/generators/aftfilr/templates/controllers/aftfilr_controller.rb new file mode 100644 index 0000000..d3774c4 --- /dev/null +++ b/generators/aftfilr/templates/controllers/aftfilr_controller.rb @@ -0,0 +1,3 @@ +class <%= controller_class_name %>Controller < ApplicationController + +end \ No newline at end of file diff --git a/generators/aftfilr/templates/controllers/categories_controller.rb b/generators/aftfilr/templates/controllers/categories_controller.rb new file mode 100644 index 0000000..e69de29 diff --git a/generators/aftfilr/templates/tinymce_plugin/css/aftfilr.css b/generators/aftfilr/templates/tinymce_plugin/css/aftfilr.css new file mode 100644 index 0000000..e69de29 diff --git a/generators/aftfilr/templates/tinymce_plugin/dialog.htm b/generators/aftfilr/templates/tinymce_plugin/dialog.htm new file mode 100644 index 0000000..232c9a0 --- /dev/null +++ b/generators/aftfilr/templates/tinymce_plugin/dialog.htm @@ -0,0 +1,41 @@ + + + + <%= tinymce_window_title %> + + + + + + + + + + +
+ +

Insert a link to a document

+ + + + + + + +
+ <%- if options[:with_categories] -%> +
+ <%- else -%> +   + <%- end -%> +
+
+
+
+
+ + + diff --git a/generators/aftfilr/templates/tinymce_plugin/editor_plugin.js b/generators/aftfilr/templates/tinymce_plugin/editor_plugin.js new file mode 100644 index 0000000..7f8607c --- /dev/null +++ b/generators/aftfilr/templates/tinymce_plugin/editor_plugin.js @@ -0,0 +1,72 @@ +(function() { + // Load plugin specific language pack + tinymce.PluginManager.requireLangPack('<%= singular_name %>'); + + tinymce.create('tinymce.plugins.<%= model_class_name %>Plugin', { + /** + * Initializes the plugin, this will be executed after the plugin has been created. + * This call is done before the editor instance has finished it's initialization so use the onInit event + * of the editor instance to intercept that event. + * + * @param {tinymce.Editor} ed Editor instance that the plugin is initialized in. + * @param {string} url Absolute URL to where the plugin is located. + */ + init : function(ed, url) { + // Register the command so that it can be invoked by using tinyMCE.activeEditor.execCommand('mceExample'); + ed.addCommand('mce<%= model_class_name %>', function() { + ed.windowManager.open({ + file : url + '/dialog.htm', + width : 800, + height : 600, + inline : 1 + }, { + plugin_url : url, // Plugin absolute URL + }); + }); + + // Register button + ed.addButton('<%= singular_name %>', { + title : '<%= singular_name %>.desc', + cmd : 'mce<%= model_class_name %>', + image : url + '/img/<%= singular_name %>.gif' + }); + + // Add a node change handler, selects the button in the UI when a image is selected + ed.onNodeChange.add(function(ed, cm, n) { + cm.setActive('<%= singular_name %>', n.nodeName == 'IMG'); + }); + }, + + /** + * Creates control instances based in the incomming name. This method is normally not + * needed since the addButton method of the tinymce.Editor class is a more easy way of adding buttons + * but you sometimes need to create more complex controls like listboxes, split buttons etc then this + * method can be used to create those. + * + * @param {String} n Name of the control to create. + * @param {tinymce.ControlManager} cm Control manager to use inorder to create new control. + * @return {tinymce.ui.Control} New control instance or null if no control was created. + */ + createControl : function(n, cm) { + return null; + }, + + /** + * Returns information about the plugin as a name/value array. + * The current keys are longname, author, authorurl, infourl and version. + * + * @return {Object} Name/value array containing information about the plugin. + */ + getInfo : function() { + return { + longname : '<%= model_class_name %> plugin', + author : 'TJ Stankus', + authorurl : 'http://www.haikuwebdev.com', + version : "0.1" + }; + } + }); + + // Register plugin + tinymce.PluginManager.add('<%= singular_name %>', tinymce.plugins.<%= model_class_name %>Plugin); +})(); \ No newline at end of file diff --git a/generators/aftfilr/templates/tinymce_plugin/img/aftfilr.png b/generators/aftfilr/templates/tinymce_plugin/img/aftfilr.png new file mode 100644 index 0000000000000000000000000000000000000000..ac00374f90c5fa2338e417548e24a61003aba883 GIT binary patch literal 4358 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 AlK=n! literal 0 HcmV?d00001 diff --git a/generators/aftfilr/templates/tinymce_plugin/img/category.png b/generators/aftfilr/templates/tinymce_plugin/img/category.png new file mode 100644 index 0000000000000000000000000000000000000000..9c6409037fd4e71385bc762cbd9f335ee662c57a GIT binary patch literal 3840 zcmV+b5C8CqP)-9Re<0MW}$;2vHK0W)X8VQb?lqM{^DZSyZ>*1FS+(10GogDwSVZgPyHJcjjx^ExaqwBw$CoW z`-(Ea<{wUEzvxqU|MkzV%(}beY)|WFy;xV@Y9Q>#*58xW|5PV>W*v;Q(>?o>x!=OzypRPk{H;|LfZ)Zav>@acWzU9{Qzv z@#RnN{1U*zuEQ5ZV7&qI)7tj!drKlB_Aj4Xyk<82k2fEA`tf-98{bb(9XWMy>&Y|U z1z1>ExWIr>ADGEcYJ2&f@Bj9E0uY{k_lNZPyY0l+-<<#Lr(bV>^ugQi2YB$_J=x3s zF834I5+DP{UgkCqj9tBnsrEB>KOVmKF0<#~WZ}^(&bI%8JplGF$;H5m86tw+|GvFD z?tRZS4`i8}1Q$TWkO~XczI3c07;bP3377%_jmB7m2`5^QAvm)r5WynE2x$4)6wH@< z4S+WnEt#3vwmYqz6Php5JKgk4Gn=12__fxr|LTVhKW&B+L5B?(sL;ku$<+3(6K`*f zC8?@_3_Enl01?2k#*)fFi$DZbumBnn63oKT`3UMM#ZbVkG6pb3iaI5{HrDEqO=oWF zX7{?eS*{13zE}hX2An(VyFITfwR$oeqUf4?wKBp6XUuHK`&I%*hrAgyBe(uqO%AB(5jKF7yg?>y(HFW;V)>i-8|8VEKIRiBeRach2<%qVjMR5i>!g|2J0x z9|YAvwA?$xR1plsK&>=P3}ST-EAN*GF~KEuUfv%A z5&1C&JoQww2$9aHJXeV-U?kWuc(W>TD?nBO?E~urA2BYL6zc;g0%g89#=*J}I#|zR zlNqAX8G%|BckJ4=V|vK57aB0x?Rt!&I%R9%D`RYZWQLMfv^$tHte!wDz#_F;xb%(J*=DE$IBbAaxQ;9>c^|DUGORr7!S}u2_k6|cegsUh z{@-l9_lM~dP>pDen0my<%9NlD%^o(9f+#}3%u`FlhMV`uHUJhzBQRXdc_+@V$)l^3 zZdmV2-rF!Z?Sn6Y4!l30(>iKd08<(@L!6?85yM2uec#230-J3>ED-Tvfy9|yv7O5S z4(z_rfPPS{$YV1nr)TGeJK&MC)#*rV0X+0e+? zKG#6)G_-oCQ>aNv9|AF|Ifu+nvjbp$ltC5YOJwGplqw`8g={FH&4e^DvO1Vkk{Y;7 zaA~=W|~;$%*4#yzin~dRr^m93N}ok_6R= zX(_P=poS9BsMKr1$F~x zE-=6$zD&);gnoKEG#a(K@q-gNU;NS%^?IEny2SGu-fC%XX@OxvnX@QWK9dz$dHYn`Q5nfy&t2w zuy8>H76dR;>Dh?hSEWL~i?fLLk#48JOpz*A`tIm^X*54YXgxtA^PG6G;LA^V-u4Uq->M*}i2<1~^)pxbt-!Ophm>d} z0!0zqBQk20&X>tV2nFNy22VbDh)@3UpD;5sgZBaF9IGoUJa+IPlarI=dCqFHNwc|% z_nvd-&T-RCH}TM4ewIhS@)h25-*0lmwKe{9;WD~~CoNh$eyHHN#X7ZAL1m<@GIcm) zaw6eUfVsIJVL%Mh85|f4*Eao<7boOJ&Od+gO9ZW?6AdH@ZYj#wqFmWN`jES+O%X^A|~OK+!X)h|@Pq)M$` z$BEF(I~38e>zWB(ebpE-KaI4{!M1m@@zU#&*ad4;^_7%zVh&;%i7{t8;Oe@_(cA{D z)zC^)ycvk8luy{9O0%kQG%8L69|}(V-~=a5oWOfenx;7CE&x9^HpcVEj?rqhx&8Lr zSzcM>wl{4h8IQ!!f>waJJNeGxBN!3q1k@`2OGN||r%+F1+sE%|O|4OUT@G~HaW$x? zmWZfEoD7%5V5Yc4SZOvned-i>o)0{3>&wK-fUK5r_RLv6|GB^710TAd?>&2zn{GG@ znxpMAG`3vNw_m97Pk;X~8>VK6K2}LAWfV*yh?p5QlS{7HoNfe``ck@XG8dz$At^&% z?-yt#g%%f=IJ2}=MJ&B2@R4r}k(rqp9(>?4Y?|G{r#}7L*olwg+ox&79?w2M&ifzO zPxJgDwZ4}zz3;qfZ$glcYNZ(uk+-gLriX%B5EjkA6;dHm0f56jbdXH zTHmxWZUm#sUJ%rHpNuI1n&a|q={p#uYYMHM2x7y<=dyZVOI_E z77ds4-J@Nee4);!=>#)HqYw&8>O>u=kf;Nvmef?XY?htF-<<*+I8YuqNn0VrF330* z4N<`&A`yZxJCjncCoHdc(p0MQdc6S`4&{`Vkmn_9Z;zBYS|`sTLFvJ@MVx)`VgHDK+Hi9TvQUJdisKM25AImkVPR=vPRte+MAQ9BahI+`V?9y@t;{- z_1;f*&H9ad)gBI&6V8>2H0Xtg=027vqC@wnmR*?@xMT~?d5CSS)rBb|F_1*cM0KTP zqJo4fk7@y_0X4;CWv7=;Fui+cW1{c#Qbrf{8^HOZ8BNuX%yX(Hy!7Ny13#Gk42=$B zb=`nU9xMkA?G-tB^o8UTpZe_L>Hd3-(f|Q76A|$tgu+QANz93vtjVZV*R4gx2Gps> ziTFzY0=Q64-l&7S1Yc}mz5jbC=f`2C4%Z&Fu$=g_hEJLg-nsy$SM z-c#SHuZ|I7?zDJrsS#!yNj$pL%K!85On&a%jt+b^46|f-B2oE%bbb88x6EzXlw7jZ z%6ngXbfuL}MR)X2yLaUH@osgAud=z{_+50FnG+GU=F_k4ES>yD^W^cd?n>+StKRW} zf46=6ob2DPKkDA~fawpY7BJaA`ikj){WfvvOGocNFfVXmaCmsveDa#t94H?9*FU>? zHXVO_rY0Y~ = { + init : function() {}, + + insert : function() { + // APPTODO: Set link text, probably through an attribute like filename, which can be overridden in form field + link_text = document.forms[0].url.value; + link_html = '_controller part and an optional super class. + # The contents of the class source file is passed to a block. + def assert_generated_controller_for(name, parent = "ApplicationController") + assert_generated_class "app/controllers/#{name.to_s.underscore}_controller", parent do |body| + yield body if block_given? + end + end + + # Asserts that the given model was generated. + # It takes a name or symbol and an optional super class. + # The contents of the class source file is passed to a block. + def assert_generated_model_for(name, parent = "ActiveRecord::Base") + assert_generated_class "app/models/#{name.to_s.underscore}", parent do |body| + yield body if block_given? + end + end + + # Asserts that the given helper was generated. + # It takes a name or symbol without the _helper part. + # The contents of the module source file is passed to a block. + def assert_generated_helper_for(name) + assert_generated_module "app/helpers/#{name.to_s.underscore}_helper" do |body| + yield body if block_given? + end + end + + # Asserts that the given functional test was generated. + # It takes a name or symbol without the _controller_test part and an optional super class. + # The contents of the class source file is passed to a block. + def assert_generated_functional_test_for(name, parent = "ActionController::TestCase") + assert_generated_class "test/functional/#{name.to_s.underscore}_controller_test",parent do |body| + yield body if block_given? + end + end + + # Asserts that the given unit test was generated. + # It takes a name or symbol without the _test part and an optional super class. + # The contents of the class source file is passed to a block. + def assert_generated_unit_test_for(name, parent = "ActiveSupport::TestCase") + assert_generated_class "test/unit/#{name.to_s.underscore}_test", parent do |body| + yield body if block_given? + end + end + + # Asserts that the given file was generated. + # The contents of the file is passed to a block. + def assert_generated_file(path) + assert_file_exists(path) + File.open("#{RAILS_ROOT}/#{path}") do |f| + yield f.read if block_given? + end + end + + # asserts that the given file exists + def assert_file_exists(path) + assert File.exist?("#{RAILS_ROOT}/#{path}"), + "The file '#{RAILS_ROOT}/#{path}' should exist" + end + + # Asserts that the given class source file was generated. + # It takes a path without the .rb part and an optional super class. + # The contents of the class source file is passed to a block. + def assert_generated_class(path, parent = nil) + # FIXME: Sucky way to detect namespaced classes + if path.split('/').size > 3 + path =~ /\/?(\d+_)?(\w+)\/(\w+)$/ + class_name = "#{$2.camelize}::#{$3.camelize}" + else + path =~ /\/?(\d+_)?(\w+)$/ + class_name = $2.camelize + end + + assert_generated_file("#{path}.rb") do |body| + assert_match /class #{class_name}#{parent.nil? ? '':" < #{parent}"}/, body, "the file '#{path}.rb' should be a class" + yield body if block_given? + end + end + + # Asserts that the given module source file was generated. + # It takes a path without the .rb part. + # The contents of the class source file is passed to a block. + def assert_generated_module(path) + # FIXME: Sucky way to detect namespaced modules + if path.split('/').size > 3 + path =~ /\/?(\w+)\/(\w+)$/ + module_name = "#{$1.camelize}::#{$2.camelize}" + else + path =~ /\/?(\w+)$/ + module_name = $1.camelize + end + + assert_generated_file("#{path}.rb") do |body| + assert_match /module #{module_name}/, body, "the file '#{path}.rb' should be a module" + yield body if block_given? + end + end + + # Asserts that the given CSS stylesheet file was generated. + # It takes a path without the .css part. + # The contents of the stylesheet source file is passed to a block. + def assert_generated_stylesheet(path) + assert_generated_file("public/stylesheets/#{path}.css") do |body| + yield body if block_given? + end + end + + # Asserts that the given YAML file was generated. + # It takes a path without the .yml part. + # The parsed YAML tree is passed to a block. + def assert_generated_yaml(path) + assert_generated_file("#{path}.yml") do |body| + yaml = YAML.load(body) + assert yaml, 'YAML data missing' + yield yaml if block_given? + end + end + + # Asserts that the given fixtures YAML file was generated. + # It takes a fixture name without the .yml part. + # The parsed YAML tree is passed to a block. + def assert_generated_fixtures_for(name) + assert_generated_yaml "test/fixtures/#{name.to_s.underscore}" do |yaml| + yield yaml if block_given? + end + end + + # Asserts that the given views were generated. + # It takes a controller name and a list of views (including extensions). + # The body of each view is passed to a block. + def assert_generated_views_for(name, *actions) + actions.each do |action| + assert_generated_file("app/views/#{name.to_s.underscore}/#{action}") do |body| + yield body if block_given? + end + end + end + + def assert_generated_migration(name, parent = "ActiveRecord::Migration") + file = Dir.glob("#{RAILS_ROOT}/db/migrate/*_#{name.to_s.underscore}.rb").first + file = file.match(/db\/migrate\/[0-9]+_\w+/).to_s + assert_generated_class file, parent do |body| + assert_match /timestamps/, body, "should have timestamps defined" + yield body if block_given? + end + end + + # Asserts that the given migration file was not generated. + # It takes the name of the migration as a parameter. + def assert_skipped_migration(name) + migration_file = "#{RAILS_ROOT}/db/migrate/001_#{name.to_s.underscore}.rb" + assert !File.exist?(migration_file), "should not create migration #{migration_file}" + end + + # Asserts that the given resource was added to the routes. + def assert_added_route_for(name) + assert_generated_file("config/routes.rb") do |body| + assert_match /map.resources :#{name.to_s.underscore}/, body, + "should add route for :#{name.to_s.underscore}" + end + end + + # Asserts that the given methods are defined in the body. + # This does assume standard rails code conventions with regards to the source code. + # The body of each individual method is passed to a block. + def assert_has_method(body, *methods) + methods.each do |name| + assert body =~ /^ def #{name}(\(.+\))?\n((\n| .*\n)*) end/, "should have method #{name}" + yield(name, $2) if block_given? + end + end + + # Asserts that the given column is defined in the migration. + def assert_generated_column(body, name, type) + assert_match /t\.#{type.to_s} :#{name.to_s}/, body, "should have column #{name.to_s} defined" + end +end diff --git a/test/test_helper.rb b/test/test_helper.rb new file mode 100644 index 0000000..59b4766 --- /dev/null +++ b/test/test_helper.rb @@ -0,0 +1,6 @@ +ENV['RAILS_ENV'] = 'test' +ENV['RAILS_ROOT'] ||= File.dirname(__FILE__) + '/../../../..' +require 'test/unit' +require File.expand_path(File.join(ENV['RAILS_ROOT'], '/config/environment.rb')) +require 'rails_generator' +require File.join(File.dirname(__FILE__), 'generator_test_helper') \ No newline at end of file