Permalink
Browse files

first . . .

  • Loading branch information...
0 parents commit 04d07bf33c6e0e9719c99ac6eaa69bc656cb2ab2 @mkristian committed Sep 9, 2010
@@ -0,0 +1,5 @@
+h1. ROO like generators for RAILS
+
+p. this is inspired by "www.springsource.org/roo":http://www.springsource.org/roo which allows to manage models and their views during the whole lifecycle of the related classes/files.
+
+
@@ -0,0 +1,3 @@
+Description:
+
+Examples:
@@ -0,0 +1,174 @@
+module Roo
+ module ActiveRecord
+ class ValidationsFile
+
+ class Field
+
+ attr_accessor :name, :validators
+
+ def initialize(name)
+ @name = name.to_sym
+ @validators = {}
+ end
+
+ def self.get(name)
+ name = name.to_sym
+ get!(name) || @fields[name] = Field.new(name)
+ end
+
+ def self.get!(name)
+ name = name.to_sym
+ @fields ||= {}
+ @fields[name]
+ end
+
+ def self.add(name, field)
+ name = name.to_sym
+ @fields ||= {}
+ @fields[name] = field
+ end
+
+ def self.remove(name)
+ name = name.to_sym
+ @fields ||= {}
+ @fields.delete(name)
+ end
+
+ def self.all
+ @fields ||= {}
+ @fields.values
+ end
+
+ def self.clear
+ all.clear
+ end
+
+ def add(validator, args = {})
+ validator = @validators[validator.to_sym] ||= {}
+ validator.merge!(args)
+ validator.delete_if { |k, v| v.nil? }
+ end
+
+ def remove(validator)
+ @validators.delete(validator.to_sym)
+ end
+
+ def rename(new_name)
+ self.class.remove(@name)
+ @name = new_name
+ self.class.add(@name, self)
+ end
+ end
+
+ def initialize(module_const)
+ self.class.send(:include, module_const)
+ @module_const = module_const
+ end
+
+ def dump(dir)
+ parts = @module_const.to_s.split("::")
+ parts.each do |part|
+ part.replace(part.underscore)
+ end
+ file = File.join(dir, parts) + ".rb"
+ File.open(file, 'w') do |o|
+ o.puts("# this file gets modified by rails-roo generators")
+ o.puts("# no lamda, block or Proc allowed")
+ o.puts("module #{@module_const}")
+ o.puts(" def self.included(model)")
+ Field.all.each do |f|
+ f.validators.each do |k,v|
+ o.print(" model.#{k} :#{f.name}")
+ if v && v.size > 0
+ vv = v.inspect
+ vv = vv[1, (vv.size - 2)]
+ o.puts(", #{vv}")
+ else
+ o.puts
+ end
+ end
+ end
+ o.puts(" end")
+ o.puts("end")
+ end
+ file
+ end
+
+ def self.method_missing(method, *args)
+ name = args.shift
+ Field.get(name).add(method, *args)
+ end
+
+ private
+
+ def to_boolean(b)
+ (b.is_a?(String) && b.downcase == "true") || b == true
+ end
+
+ public
+
+ def remove(name)
+ Field.remove(name)
+ end
+
+ def rename(new_name, old_name)
+ if(new_name != old_name)
+ Field.get(old_name).rename(new_name)
+ true
+ else
+ false
+ end
+ end
+
+ def unique(name, unique = true)
+ if to_boolean(unique)
+ Field.get(name).add(:validates_uniqueness_of)
+ else
+ Field.get(name).remove(:validates_uniqueness_of)
+ end
+ end
+
+ def numerical(name, type)
+ return unless type # type is required
+ if [:short, :long, :integer, :float, :decimal, :double, :number, :fixnum].member?(type.to_sym)
+ Field.get(name).add(:validates_numericality_of)
+ else
+ Field.get(name).remove(:validates_numericality_of)
+ end
+ end
+
+ def presence(name, presence = true)
+ if to_boolean(presence)
+ Field.get(name).add(:validates_presence_of)
+ else
+ Field.get(name).remove(:validates_presence_of)
+ end
+ end
+
+ def format(name, format = nil)
+ if format
+ Field.get(name).add(:validates_format_of, :with => /#{format}/)
+ else
+ Field.get(name).remove(:validates_format_of)
+ end
+ end
+
+ def length(name, min = nil, max = nil)
+ f = Field.get(name)
+ min = (min || 0).to_i
+ max = (max || 0).to_i
+ if min == 0 && max == 0
+ f.remove(:validates_length_of)
+ else
+ f.add(:validates_length_of, :is => nil, :maximum => nil, :minimum => nil, :in => nil, :within => nil, :in => nil)
+ if(max == min)
+ f.add(:validates_length_of, :is => max)
+ else
+ f.add(:validates_length_of, :maximum => max) if max && max > 0
+ f.add(:validates_length_of, :minimum => min) if min && min > 0
+ end
+ end
+ end
+ end
+ end
+end
@@ -0,0 +1,24 @@
+require 'rails/generators/active_record'
+require 'rails/generators/active_record/migration/migration_generator'
+require 'roo/base'
+require 'roo/active_record/validations_file'
+
+module Roo
+ module Generators
+ class AddFieldGenerator < Roo::Generators::Base
+
+ argument :field, :type => :string, :required => true, :banner => "field:type"
+
+ parameters_argument
+
+ def create_migration_file
+ add_source_root
+ init_new_column_options(@field)
+ verify_field_missing(@new_field.name)
+ init_new_index_options
+ create_roo_migration_file("add", @new_field.name, "remove")
+ rewrite_validations_file
+ end
+ end
+ end
+end
@@ -0,0 +1,147 @@
+require 'rails/generators/active_record'
+require 'rails/generators/active_record/migration/migration_generator'
+require 'roo/active_record/validations_file'
+module Roo
+ module Generators
+ class Base < ::ActiveRecord::Generators::Base
+ arguments.clear # clear name argument from NamedBase
+ argument :model, :type => :string, :required => true, :banner => "model"
+ def name # set alias so NamedBase uses the model as its name
+ @model.replace(@model.singularize)
+ end
+
+ def self.parameters_argument
+ argument :parameters, :type => :hash, :default => {}, :banner => "index:name index:true unique:true min:123 max:128 nullable:false default:blablub"
+ end
+
+ def self.migration_exists?(dirname, file_name)
+ nil # TODO use option to define behaviour
+ end
+
+ def self.model_class(name = ARGV[0])
+ return unless name
+ begin
+ require name.singularize.underscore
+ rescue LoadError
+ raise "model '#{name}' does not exists"
+ end
+ class_name = name.singularize.camelize.to_s
+
+ # Split the class from its module nesting
+ nesting = class_name.split('::')
+ last_name = nesting.pop
+
+ # Hack to limit const_defined? to non-inherited on 1.9
+ extra = []
+ extra << false unless Object.method(:const_defined?).arity == 1
+
+ # Extract the last Module in the nesting
+ last = nesting.inject(Object) do |last, nest|
+ break unless last.const_defined?(nest, *extra)
+ last.const_get(nest)
+ end
+ if last && last.const_defined?(last_name.camelize, *extra)
+ last.const_get(last_name.camelize)
+ end
+ end
+
+ protected
+
+ attr_reader :migration_action, :migration_alternate_action, :new_field, :new_column_options, :old_field, :old_column_options, :new_index_options, :old_index_options
+
+ def add_source_root(reference = __FILE__)
+ self.class.source_root(File.join(File.dirname(reference), "templates"))
+ end
+
+ def verify_field_exists(field)
+ field_sym = field.to_sym
+ begin
+ raise "field '#{field_sym}' does not exist for model '#{self.class.model_class}'" if self.class.model_class && !self.class.model_class.new.respond_to?(field_sym)
+ rescue ::ActiveRecord::ActiveRecordError
+ # assume the field is missing
+ raise "field '#{field_sym}' does not exist for model '#{self.class.model_class}'"
+ end
+ end
+
+ def verify_field_missing(field)
+ field_sym = field.to_sym
+ begin
+ raise "field '#{field_sym}' already exists for model '#{self.class.model_class}'" if self.class.model_class && self.class.model_class.new.respond_to?(field_sym)
+ rescue ::ActiveRecord::ActiveRecordError
+ # assume the field is missing
+ end
+ end
+
+ def init_old_column_options(name)
+ column = self.class.model_class.columns.detect { |c| c.name == name }
+ raise "column '#{name}' does not exist" if column.nil?
+
+ @old_field = Rails::Generators::GeneratedAttribute.new(column.name, column.type)
+ @old_column_options = {}
+ @old_column_options[:limit] = column.limit if column.limit
+ @old_column_options[:default] = column.default if column.default
+ @old_column_options[:null] = column.null if column.null
+ #TODO precision + scale for decimal
+ end
+
+ def init_new_column_options(field)
+ @new_field = Rails::Generators::GeneratedAttribute.new(field.sub(/\:.*/, ''), field.sub(/.*\:/, ''))
+ @new_column_options = {}
+ @parameters ||= {}
+ @new_column_options[:limit] = @parameters["max"] if @parameters["max"] && @parameters["max"].to_i > 0
+ @new_column_options[:default] = @parameters["default"] if @parameters["default"] && @parameters["default"] != ''
+ @new_column_options[:null] = "true" != @parameters["presence"]
+ #TODO precision + scale for decimal
+ end
+
+ def init_old_index_options
+ @old_index_options = nil
+ end
+
+ def init_new_index_options
+ @new_index_options = {}
+ unless @parameters["unique"].nil?
+ @new_index_options[:unique] = @parameters["unique"] if @parameters["unique"] == 'true'
+ end
+ if @parameters["index"].is_a?(String) && (@parameters["index"] =~ /(true|false)/).nil?
+ @new_index_options[:name] = @parameters["index"]
+ elsif @parameters["index"] != "true" && @new_index_options.size == 0
+ @new_index_options = nil
+ end
+ end
+
+ def rewrite_validations_file(remove = false)
+ orm = Rails.application.config.generators.options[:rails][:orm]
+ val = Roo.const_get(orm.to_s.camelize.constantize.to_s)::ValidationsFile.new(self.class.model_class(self.class.model_class.to_s + "Validation"))
+ if remove
+ val.remove(@new_field.name)
+ else
+ if @new_fieldname && @old_fieldname
+ val.rename(@new_fieldname, @old_fieldname)
+ elsif @parameters
+ val.length(@new_field.name, @parameters["min"], @parameters["max"])
+ val.presence(@new_field.name, @parameters["presence"])
+ val.unique(@new_field.name, @parameters["unique"])
+ val.format(@new_field.name, @parameters["format"])
+ val.numerical(@new_field.name, @new_field.type)
+ end
+ end
+ file = val.dump(File.join('app', 'models', class_path))
+ log "rewrite", file
+ end
+
+ def create_roo_migration_file(action, name_middle, alternate_action = nil)
+ @migration_action = action
+ @migration_alternate_action = alternate_action ? alternate_action : action
+ @table_name = @model.pluralize
+
+ # remove the restriction of migration to allow serveral files with the
+ # same postfix
+ name = "#{action}_#{name_middle}_#{action == 'add'? "to" : "from"}_#{file_name.pluralize}"
+ count = Dir.glob("db/migrate/*_#{name}_*.rb").size
+
+ migration_template "migration.rb", "db/migrate/#{name}_#{count}.rb"
+ end
+ end
+ end
+end
@@ -0,0 +1,27 @@
+require 'rails/generators/active_record'
+require 'rails/generators/active_record/migration/migration_generator'
+require 'roo/base'
+
+module Roo
+ module Generators
+ class ChangeFieldGenerator < Roo::Generators::Base
+
+ argument :field, :type => :string, :required => true, :banner => "field:type"
+
+ parameters_argument
+
+ attr_reader :new_field, :new_column_options, :old_field, :old_column_options, :index_options
+
+ def create_migration_file
+ add_source_root
+ init_new_column_options(@field)
+ verify_field_exists(@new_field.name)
+ init_old_column_options(@new_field.name)
+ init_new_index_options
+ init_old_index_options
+ create_roo_migration_file("change", @new_field.name)
+ rewrite_validations_file
+ end
+ end
+ end
+end
Oops, something went wrong.

0 comments on commit 04d07bf

Please sign in to comment.