Skip to content
Browse files

initial beta version

  • Loading branch information...
0 parents commit 6b782bc9b5b0f38edcffa15656271cdb07d56f21 @elvuel committed Mar 21, 2011
Showing with 939 additions and 0 deletions.
  1. 0 .gitignore
  2. +22 −0 README.rdoc
  3. +404 −0 gen_model_doc.rake
  4. +289 −0 templates/css/style.css
  5. +52 −0 templates/index_template.erb
  6. +172 −0 templates/model_template.erb
0 .gitignore
No changes.
22 README.rdoc
@@ -0,0 +1,22 @@
+== Rails model doc generator(Rake Task)
+
+ * Beta version!!! Currently no support, no warranty *.
+
+== Usage & Example
+ Default:
+ # Copy templates folder to your app/doc
+ cp -R templates/ path/to/your_app/doc
+
+ # Generate config file for generator
+ rake elvuel:config APP=model_doc_name
+
+ # Setting your models folder in the config file.
+ key => :folders(Array)
+
+ # Generate docs
+ rake elvuel:gen_dbdoc_files FORCE_RELOAD=true
+
+== Author
+
+ Elvuel(elvuel@gmail.com)
+ Copyright (c) 2011 elvuel(http://elvuel.com), released under the MIT license
404 gen_model_doc.rake
@@ -0,0 +1,404 @@
+require 'fileutils'
+namespace :elvuel do
+
+ # Fetch all has*** :as => item polymorphic associations
+ def store_polymorphic_as_associations(models)
+ reflects = []
+ models.each do |model|
+ model.reflections.each do |key, association|
+ reflects << association if association.send(:options).has_key?(:as) #only polymorphic
+ end
+ end
+
+ @polymorphic_as_reflections = reflects
+ end
+
+ # Model belongs_to :item, :polymorphic => true, get all item's class
+ def get_polymorphic_as_associations_classes(model, reflection)
+ classes = []
+ @polymorphic_as_reflections.each do |poly_as_reflection|
+ if poly_as_reflection.send(:options)[:as] == reflection.send(:name)
+ if poly_as_reflection.active_record.send(:compute_type, poly_as_reflection.send(:class_name)).to_s == model.to_s
+ classes << poly_as_reflection.active_record.to_s.constantize
+ end
+ end
+ end
+ classes
+ end
+
+ # Simple quoting for the default column value
+ def quote(value)
+ case value
+ when NilClass then
+ "NULL"
+ when TrueClass then
+ "TRUE"
+ when FalseClass then
+ "FALSE"
+ when Float, Fixnum, Bignum then
+ value.to_s
+ # BigDecimals need to be output in a non-normalized form and quoted.
+ when BigDecimal then
+ value.to_s('F')
+ else
+ value.inspect
+ end
+ end
+
+ # Use the column information in an ActiveRecord class
+ # to create a comment block containing a line for
+ # each column. The line contains the column name,
+ # the type (and length), and any optional attributes
+ def get_schema_info(klass, options)
+ info = []
+ klass.columns.each do |col|
+ attrs = []
+ attrs << "default(#{quote(col.default)})" unless col.default.nil?
+ attrs << "not null" unless col.null
+ attrs << "primary key" if col.name == klass.primary_key
+
+ col_type = col.type.to_s
+ if col_type == "decimal"
+ col_type << "(#{col.precision}, #{col.scale})"
+ else
+ col_type << "(#{col.limit})" if col.limit
+ end
+
+ # Check out if we got a geometric column
+ # and print the type and SRID
+ if col.respond_to?(:geometry_type)
+ attrs << "#{col.geometry_type}, #{col.srid}"
+ end
+
+ # Check if the column has indices and print "indexed" if true
+ # If the indice include another colum, print it too.
+ if options[:simple_indexes] # Check out if this column is indexed
+ indices = klass.connection.indexes(klass.table_name)
+ if indices = indices.select { |ind| ind.columns.include? col.name }
+ indices.each do |ind|
+ ind = ind.columns.reject! { |i| i == col.name }
+ attrs << (ind.length == 0 ? "indexed" : "indexed => [#{ind.join(", ")}]")
+ end
+ end
+ end
+ info << { :name => col.name, :type => col_type, :attrs => attrs.join(", "), :human_name => klass.respond_to?(:human_attribute_name) ? klass.human_attribute_name(col.name) : col.name }
+ end
+ info
+ end
+
+ # Get table indexes info
+ def get_index_info(klass)
+ index_info = []
+ indexes = klass.connection.indexes(klass.table_name)
+ indexes.each do |index|
+ index_info << { :name => index.name, :columns => index.columns.join(", "), :unique => (index.unique ? "UNIQUE" : "NO") }
+ end
+ index_info
+ end
+
+ # Get association info
+ def get_association_info(klass)
+ reflections = klass.reflections
+ associations = []
+ unless reflections.empty?
+ reflections.each do |key, association|
+ begin
+ info = {}
+
+ info[:name]= key.to_s
+ info[:type]= association.class.to_s.gsub(/ActiveRecord|Reflection|::/, "")
+ info[:macro]= association.send(:macro).to_s
+
+ # ActiveRecord::Reflection::ThroughReflection[::AssociationReflection|::AggregateReflection]
+ association_classes = []
+
+ case info[:type]
+
+ when "Through"
+ # TODO ThroughReflection details!
+ association_class = association.active_record.send(:compute_type, association.send(:class_name))
+ association_classes << association_class
+ info[:through]= association.through_reflection.klass.to_s
+ # if association.send(:options).has_key?(:source_type)
+ # association_classes << association.send(:class_name)
+ # else
+
+ # end
+ when "Association"
+ if association.send(:options).has_key?(:polymorphic)
+ association_classes = get_polymorphic_as_associations_classes(klass, association)
+ else
+ if association.send(:options).has_key?(:class_name)
+ association_classes << association.send(:class_name).constantize
+ else
+ association_classes << association.active_record.send(:compute_type, association.send(:class_name))
+ end
+ end
+ when "Aggregate"
+ if association.send(:options).has_key?(:class_name)
+ association_classes << association.send(:class_name).constantize
+ else
+ association_classes << association.active_record.send(:compute_type, association.send(:class_name))
+ end
+ end
+
+ info[:association_classes]= association_classes.map(&:to_s)
+ info[:foreign_key]= association.send(:association_foreign_key).to_s
+ info[:primary_key]= association.send(:primary_key_name).to_s
+ info[:options]= association.send(:options).inspect if association.respond_to?(:options)
+
+ associations << info
+ rescue
+ @exception_associations[klass.to_s] ||= []
+ @exception_associations[klass.to_s] << "#{association.send(:macro).to_s} :#{key}"
+ end
+ end
+ end
+ associations
+ end
+
+ # Get all ActiveRecord named_scopes
+ def get_named_scope_info(klass)
+ klass.respond_to?(:scopes) ? klass.scopes.keys.map(&:to_s) : []
+ end
+
+ # Get model singleton methods
+ def get_singleton_methods_info(klass)
+ klass.singleton_methods(false).map(&:to_s)
+ end
+
+ # check ruby version from github.com/jgoizueta/modalsupport
+ def ruby_version?(cmp, v)
+ rv = Gem::Version.create(RUBY_VERSION.dup)
+ v = Gem::Version.create(v)
+ if cmp.to_sym==:'~>'
+ rv = rv.release
+ rv >= v && rv < v.bump
+ else
+ rv.send(cmp, v)
+ end
+ end
+
+ # check ruby platform from github.com/jgoizueta/modalsupport
+ def ruby_platform_is?(platform)
+ ruby_platform = ruby_version?(:<, '1.9.0') ? PLATFORM : RUBY_PLATFORM
+ case platform
+ when :unix
+ ruby_platform =~ /linux|darwin|freebsd|netbsd|solaris|aix|hpux|cygwin/
+ when :linux
+ ruby_platform =~ /linux/
+ when :osx, :darwin
+ ruby_platform =~ /darwin/
+ when :bsd
+ ruby_platform =~ /freebsd|netbsd/
+ when :cygwin
+ ruby_platform =~ /cygwin/
+ when :windows
+ ruby_platform =~ /mswin32|mingw32/
+ when :mswin32
+ ruby_platform =~ /mswin32/
+ when :mingw32
+ ruby_platform =~ /mingw32/
+ when :java
+ ruby_platform =~ /java/
+ else
+ raise RuntimeError, "Invalid platform specifier"
+ end ? true : false
+ end
+
+ desc 'genenrate config file'
+ task :config do
+ config = {}
+ config['app'] = ENV['APP'] || "app"
+ config["template_folder"] = "doc/templates"
+
+ FileUtils.mkdir_p config['template_folder']
+
+ config['output_folder'] = "doc/model_db"
+ config['index'] = { 'template' => "#{config['template_folder']}/index_template.erb", 'output' => config['output_folder'] }
+ config['model'] = { 'template' => "#{config['template_folder']}/model_template.erb", 'output' => config['output_folder'] }
+ config['models_yml'] = "#{config['template_folder']}/models.yml"
+ config['folders'] = [ "app/models" ]
+ config['config'] = ENV['CONFIG'] || "config/db_doc_gen.yml"
+
+ File.open(config['config'],'w') do |f|
+ if config.respond_to?(:ya2yaml)
+ f.write config.ya2yaml(:syck_compatible => true)
+ else
+ f.write config.to_yaml
+ end
+ end
+ puts "Please setup your config file '#{config['config']}'"
+ end
+
+ desc 'preset'
+ task :preset do
+ @config ||= YAML.load_file(ENV['CONFIG'] || "config/db_doc_gen.yml")
+ @exception_associations ||= {}
+ end
+
+ desc 'generate doc files'
+ task :gen_dbdoc_files => :preset do
+ FileUtils.mkdir_p @config['output_folder']
+ FileUtils.cp_r @config["template_folder"] + "/css", @config['output_folder']
+
+ Rake::Task["elvuel:load_model_info"].invoke if ENV['FORCE_RELOAD']
+
+ # gen index file
+ doc = YAML.load_file(@config["models_yml"])
+
+ @app_name = @config["app"]
+ # except Symbol :exception_associations
+ models = doc.keys.reject { |key| key.is_a? Symbol }
+ @alphabetic_indexes = models.inject({}) do |hash, value|
+ alpha_key = value[0].chr.upcase
+ hash[alpha_key] ||= []
+ hash[alpha_key] << doc[value][:name]
+ hash
+ end
+ File.open("#{@config["index"]["output"]}/index.html", "w") { |f| f.write ERB.new(File.read("#{@config["index"]["template"]}")).result }
+ models.each do |model|
+ @model = model
+ @inherits = doc[model][:super_classes]
+ @defined_in = doc[model][:defined_in].split(",")
+ @database = doc[model][:database]
+ @table_name = doc[model][:table_name]
+ @human_name = doc[model][:human_name]
+ @schema = doc[model][:table_schema]
+ @db_indexes = doc[model][:table_indexes]
+ @associations = doc[model][:associations]
+ @named_scopes = doc[model][:named_scopes]
+ @singleton_methods = doc[model][:singleton_methods]
+
+ File.open("#{@config["model"]["output"]}/#{model.underscore.gsub("/", "_")}.html", "w") { |f| f.write ERB.new(File.read("#{@config["model"]["template"]}")).result }
+ end
+
+ unless doc[:exception_associations].empty?
+ content = []
+ doc[:exception_associations].each do |key, value|
+ content << "#{key}: #{value.join(", ")}"
+ end
+ File.open("#{@config["model"]["output"]}/exception_associations.log", "w") do |f|
+ f.write content.join("\n")
+ end
+ end
+ puts "Docs generated."
+ end
+
+ desc 'load models info'
+ task :load_model_info => [:preset, :environment] do
+
+ FileUtils.touch @config['models_yml']
+ FileUtils.rm @config['models_yml']
+
+ # set i18n locale
+ I18n.locale = :zh if defined? I18n
+
+ model_folders = @config["folders"]
+
+ # require all files under 'models folder'
+ model_folders.each { |folder| Dir.glob(File.join(folder, "**", "*.rb")).each{ |file| require file } }
+
+ # get all ActiveRecord::Base subclasses
+ models = []
+ ObjectSpace.each_object(Class) do |klass|
+ if klass.ancestors.include?(ActiveRecord::Base)
+ unless klass.abstract_class?
+ models << klass if klass != ActiveRecord::Base
+ end
+ end
+ end
+
+ # @polymorphic_as_reflections init
+ store_polymorphic_as_associations(models)
+
+ # get all files which contain class define
+ # TODO diff platform model class define fetch!
+ if ruby_platform_is? :linux
+ find_cmd = "find ./ \\( -path './vendor/rails*' -o -path './test*' -o -path './db/migrate*' -o -path './rspec*' \\) -a -prune -o -type f -name \"*.rb\" | xargs grep -n -E \"[ \\t\\f\\v]*class[ \\f\\n\\r\\t\\v]+[A-Z]+\\S*[ \\f\\n\\r\\t\\v]+<\""
+ elsif ruby_platform_is? :darwins
+ find_cmd = "find ./ -type f -name \"*.rb\" | xargs grep -n -E \"[ \\t\\f\\v]*class[ \\f\\n\\r\\t\\v]+[A-Z].*[ \\f\\n\\r\\t\\v]+<\""
+ else
+ # ...
+ find_cmd = "echo ''"
+ end
+
+ all_class_defines = `#{find_cmd}`.split("\n")
+
+ doc = {}
+
+ models.each_with_index do |model, index|
+ key = model.to_s
+
+ puts "#{key}-#{index}"
+ doc[key] = {}
+ doc[key][:name] = key
+
+ table_exist = begin
+ model.send(:columns)
+ true
+ rescue
+ false
+ end
+
+ if table_exist
+ if model.columns.empty? # tableless
+ doc[key][:database] = "Tableless(none)"
+ doc[key][:table_name] = "Tableless(none)"
+ doc[key][:table_schema] = "Tableless(none)"
+ doc[key][:table_indexes] = "Tableless(none)"
+ else
+ doc[key][:database] = model.connection.current_database
+ doc[key][:table_name] = model.table_name
+ doc[key][:table_schema] = get_schema_info(model, :simple_indexes => true)
+ doc[key][:table_indexes] = get_index_info(model)
+ end
+ else
+ doc[key][:database] = model.connection.current_database
+ doc[key][:table_name] = model.table_name
+ doc[key][:table_schema] = [{ :name => "table #{model.connection.current_database}.#{model.table_name} doesn't exist", :type => "NULL", :attrs => "NULL", :human_name => "NULL" }]
+ doc[key][:table_indexes] = [{ :name => "table #{model.connection.current_database}.#{model.table_name} doesn't exist", :columns => "NULL", :unique => "NULL" }]
+ end
+
+ # I18n human name
+ doc[key][:human_name] = model.respond_to?(:human_name)? model.human_name : key
+ # all associations
+ doc[key][:associations] = get_association_info(model)
+ # all named scopes
+ doc[key][:named_scopes] = get_named_scope_info(model)
+ doc[key][:singleton_methods] = get_singleton_methods_info(model)
+
+ # super class
+ super_classes = []
+ sp_klass = model.superclass
+ begin
+ super_classes << sp_klass.to_s if sp_klass != Object
+ sp_klass = sp_klass.superclass
+ sp_klass = nil if sp_klass and (sp_klass == ActiveRecord::Base)
+ end while sp_klass
+
+ doc[key][:super_classes] = super_classes
+
+ # declaration file
+ reg_last = Regexp.new("class\\s+#{key.split("::").last}\\s+<")
+ reg_full = Regexp.new("class\\s+#{key}\\s+<")
+ paths = all_class_defines.grep(reg_last).concat(all_class_defines.grep(reg_full)).uniq
+
+ doc[key][:defined_in] = paths.collect{ |line| "#{line.split(":")[0]} - line:(#{line.split(":")[1]})" }
+
+ end
+
+ doc[:exception_associations] = @exception_associations
+
+ File.open(@config["models_yml"],'w') do |f|
+ if doc.respond_to?(:ya2yaml)
+ f.write doc.ya2yaml(:syck_compatible => true)
+ else
+ f.write doc.to_yaml
+ end
+ end
+
+ puts "Model info loaded."
+ end
+
+end
289 templates/css/style.css
@@ -0,0 +1,289 @@
+body{padding:0 20px;font-family:"Lucida Sans","Lucida Grande",Verdana,Arial,sans-serif;font-size:13px}body.frames{padding:0 5px}h1{font-size:25px;margin:1em 0 .5em;padding-top:4px;border-top:1px dotted #d5d5d5}h1.noborder{border-top:0;margin-top:0;padding-top:4px}h1.title{margin-bottom:10px}h1.alphaindex{margin-top:0;font-size:22px}h2{padding:0;padding-bottom:3px;border-bottom:1px #aaa solid;font-size:1.4em;margin:1.8em 0 .5em}h2 small{font-weight:400;font-size:.7em;display:block;float:right}.clear{clear:both}.inline{display:inline}.inline p:first-child{display:inline}.docstring h1,.docstring h2,.docstring h3,.docstring h4{padding:0;border:0;border-bottom:1px dotted #bbb}.docstring h1{font-size:1.2em}.docstring h2{font-size:1.1em}.docstring h3,.docstring h4{font-size:1em;border-bottom:0;padding-top:10px}.summary_desc .object_link,.docstring .object_link{font-family:monospace}#filecontents li>p,.docstring li>p{margin:0}#filecontents ul,.docstring ul{padding-left:20px}#filecontents dl,.docstring dl{border:1px solid #ccc}#filecontents dt,.docstring dt{background:#ddd;font-weight:700;padding:3px 5px}#filecontents dd,.docstring dd{padding:5px 0;margin-left:18px}#filecontents dd>p,.docstring dd>p{margin:0}.note{color:#222;-moz-border-radius:3px;-webkit-border-radius:3px;background:#e3e4e3;border:1px solid #d5d5d5;padding:7px 10px;display:block}.note.todo{background:#ffffc5;border-color:#ececaa}.note.returns_void{background:#efefef}.note.deprecated{background:#ffe5e5;border-color:#e9dada}.note.private{background:#ffffc5;border-color:#ececaa}.note.title{text-transform:lowercase;padding:1px 5px;font-size:.9em;font-family:"Lucida Sans","Lucida Grande",Verdana,Arial,sans-serif;display:inline}.summary_signature+.note.title{margin-left:7px}h1 .note.title{font-size:.5em;font-weight:400;padding:3px 5px;position:relative;top:-3px;text-transform:capitalize}.note.title.constructor{color:#fff;background:#6a98d6;border-color:#6689d6}.note.title.writeonly{color:#fff;background:#45a638;border-color:#2da31d}.note.title.readonly{color:#fff;background:#6a98d6;border-color:#6689d6}.note.title.private{background:#d5d5d5;border-color:#c5c5c5}.discussion .note{margin-top:6px}.discussion .note:first-child{margin-top:0}h3.inherited{font-style:italic;font-family:"Lucida Sans","Lucida Grande",Verdana,Arial,sans-serif;font-weight:400;padding:0;margin:0;margin-top:12px;margin-bottom:3px;font-size:13px}p.inherited{padding:0;margin:0;margin-left:25px}dl.box{width:520px;font-size:1em}dl.box dt{float:left;display:block;width:100px;margin:0;text-align:right;font-weight:700;border:1px solid #aaa;border-width:1px 0 0 1px;padding:6px 0;padding-right:10px}dl.box dd{float:left;display:block;width:380px;margin:0;padding:6px 0;padding-right:20px;border:1px solid #aaa;border-width:1px 1px 0 0}dl.box .last{border-bottom:1px solid #aaa}table .info{width:600px;background:#eee}dl.box .r1{background:#eee}ul.toplevel{list-style:none;padding-left:0;font-size:1.1em}#files{padding-left:15px;font-size:1.1em}#files{padding:0}#files li{list-style:none;display:inline;padding:7px 12px;line-height:35px}dl.constants{margin-left:40px}dl.constants dt{font-weight:700;font-size:1.1em;margin-bottom:5px}dl.constants dd{width:75%;white-space:pre;font-family:monospace;margin-bottom:18px}.summary_desc{margin-left:32px;display:block;font-family:sans-serif}.summary_desc tt{font-size:.9em}dl.constants .note{padding:2px 6px;padding-right:12px;margin-top:6px}dl.constants .docstring{margin-left:32px;font-size:.9em;font-weight:400}dl.constants .tags{padding-left:32px;font-size:.9em;line-height:.8em}dl.constants .discussion *:first-child{margin-top:0}dl.constants .discussion *:last-child{margin-bottom:0}.method_details{border-top:1px dotted #aaa;margin-top:15px;padding-top:0}.method_details.first{border:0}p.signature{font-size:1.1em;font-weight:400;font-family:Monaco,Consolas,Courier,monospace;padding:6px 10px;margin-top:18px;background:#e5e8ff;border:1px solid #d8d8e5;-moz-border-radius:3px;-webkit-border-radius:3px}p.signature tt{font-family:Monaco,Consolas,Courier,monospace}p.signature .overload{display:block}p.signature .extras{font-weight:400;font-family:sans-serif;color:#444;font-size:1em}p.signature .aliases{display:block;font-weight:400;font-size:.9em;font-family:sans-serif;margin-top:0;color:#555}p.signature .aliases .names{font-family:Monaco,Consolas,Courier,monospace;font-weight:700;color:#000;font-size:1.2em}.tags h3{font-size:1em;margin-bottom:0}.tags ul{margin-top:5px;padding-left:30px;list-style:square}.tags ul li{margin-bottom:3px}.tags ul .name{font-family:monospace;font-weight:700}.tags ul .note{padding:3px 6px}.tags{margin-bottom:12px}.tags .examples h3{margin-bottom:10px}.tags .examples h4{padding:0;margin:0;margin-left:15px;font-weight:700;font-size:.9em}.tags .overload .overload_item{list-style:none;margin-bottom:25px}.tags .overload .overload_item .signature{padding:2px 8px;background:#e5e8ff;border:1px solid #d8d8e5;-moz-border-radius:3px;-webkit-border-radius:3px}.tags .overload .signature{margin-left:-15px;font-family:monospace;display:block;font-size:1.1em}.tags .overload .docstring{margin-top:15px}.defines{display:none}#method_missing_details .notice.this{position:relative;top:-8px;color:#888;padding:0;margin:0}.showSource{font-size:.9em}.showSource a:link,.showSource a:visited{text-decoration:none;color:#666}#content a:link,#content a:visited{text-decoration:none;color:#05a}#content a:hover{background:#ffffa5}.docstring{margin-right:6em}ul.summary{list-style:none;font-family:monospace;font-size:1em;line-height:1.5em}ul.summary body {
+ padding: 0 20px;
+ font-family: "Lucida Sans", "Lucida Grande", Verdana, Arial, sans-serif;
+ font-size: 13px;
+}
+body.frames { padding: 0 5px; }
+h1 { font-size: 25px; margin: 1em 0 0.5em; padding-top: 4px; border-top: 1px dotted #d5d5d5; }
+h1.noborder { border-top: 0px; margin-top: 0; padding-top: 4px; }
+h1.title { margin-bottom: 10px; }
+h1.alphaindex { margin-top: 0; font-size: 22px; }
+h2 {
+ padding: 0;
+ padding-bottom: 3px;
+ border-bottom: 1px #aaa solid;
+ font-size: 1.4em;
+ margin: 1.8em 0 0.5em;
+}
+h2 small { font-weight: normal; font-size: 0.7em; display: block; float: right; }
+.clear { clear: both; }
+.inline { display: inline; }
+.inline p:first-child { display: inline; }
+.docstring h1, .docstring h2, .docstring h3, .docstring h4 { padding: 0; border: 0; border-bottom: 1px dotted #bbb; }
+.docstring h1 { font-size: 1.2em; }
+.docstring h2 { font-size: 1.1em; }
+.docstring h3, .docstring h4 { font-size: 1em; border-bottom: 0; padding-top: 10px; }
+.summary_desc .object_link, .docstring .object_link { font-family: monospace; }
+
+/* style for <ul> */
+#filecontents li > p, .docstring li > p { margin: 0px; }
+#filecontents ul, .docstring ul { padding-left: 20px; }
+/* style for <dl> */
+#filecontents dl, .docstring dl { border: 1px solid #ccc; }
+#filecontents dt, .docstring dt { background: #ddd; font-weight: bold; padding: 3px 5px; }
+#filecontents dd, .docstring dd { padding: 5px 0px; margin-left: 18px; }
+#filecontents dd > p, .docstring dd > p { margin: 0px; }
+
+.note {
+ color: #222;
+ -moz-border-radius: 3px; -webkit-border-radius: 3px;
+ background: #e3e4e3; border: 1px solid #d5d5d5; padding: 7px 10px;
+ display: block;
+}
+.note.todo { background: #ffffc5; border-color: #ececaa; }
+.note.returns_void { background: #efefef; }
+.note.deprecated { background: #ffe5e5; border-color: #e9dada; }
+.note.private { background: #ffffc5; border-color: #ececaa; }
+.note.title { text-transform: lowercase; padding: 1px 5px; font-size: 0.9em; font-family: "Lucida Sans", "Lucida Grande", Verdana, Arial, sans-serif; display: inline; }
+.summary_signature + .note.title { margin-left: 7px; }
+h1 .note.title { font-size: 0.5em; font-weight: normal; padding: 3px 5px; position: relative; top: -3px; text-transform: capitalize; }
+.note.title.constructor { color: #fff; background: #6a98d6; border-color: #6689d6; }
+.note.title.writeonly { color: #fff; background: #45a638; border-color: #2da31d; }
+.note.title.readonly { color: #fff; background: #6a98d6; border-color: #6689d6; }
+.note.title.private { background: #d5d5d5; border-color: #c5c5c5; }
+.discussion .note { margin-top: 6px; }
+.discussion .note:first-child { margin-top: 0; }
+
+h3.inherited {
+ font-style: italic;
+ font-family: "Lucida Sans", "Lucida Grande", Verdana, Arial, sans-serif;
+ font-weight: normal;
+ padding: 0;
+ margin: 0;
+ margin-top: 12px;
+ margin-bottom: 3px;
+ font-size: 13px;
+}
+p.inherited {
+ padding: 0;
+ margin: 0;
+ margin-left: 25px;
+}
+
+dl.box {
+ width: 520px;
+ font-size: 1em;
+}
+dl.box dt {
+ float: left;
+ display: block;
+ width: 100px;
+ margin: 0;
+ text-align: right;
+ font-weight: bold;
+ border: 1px solid #aaa;
+ border-width: 1px 0px 0px 1px;
+ padding: 6px 0;
+ padding-right: 10px;
+}
+dl.box dd {
+ float: left;
+ display: block;
+ width: 380px;
+ margin: 0;
+ padding: 6px 0;
+ padding-right: 20px;
+ border: 1px solid #aaa;
+ border-width: 1px 1px 0 0;
+}
+dl.box .last {
+ border-bottom: 1px solid #aaa;
+}
+table .info {
+ width: 600px;
+ background: #eee;
+}
+dl.box .r1 { background: #eee; }
+
+ul.toplevel { list-style: none; padding-left: 0; font-size: 1.1em; }
+#files { padding-left: 15px; font-size: 1.1em; }
+
+#files { padding: 0; }
+#files li { list-style: none; display: inline; padding: 7px 12px; line-height: 35px; }
+
+dl.constants { margin-left: 40px; }
+dl.constants dt { font-weight: bold; font-size: 1.1em; margin-bottom: 5px; }
+dl.constants dd { width: 75%; white-space: pre; font-family: monospace; margin-bottom: 18px; }
+
+.summary_desc { margin-left: 32px; display: block; font-family: sans-serif; }
+.summary_desc tt { font-size: 0.9em; }
+dl.constants .note { padding: 2px 6px; padding-right: 12px; margin-top: 6px; }
+dl.constants .docstring { margin-left: 32px; font-size: 0.9em; font-weight: normal; }
+dl.constants .tags { padding-left: 32px; font-size: 0.9em; line-height: 0.8em; }
+dl.constants .discussion *:first-child { margin-top: 0; }
+dl.constants .discussion *:last-child { margin-bottom: 0; }
+
+.method_details { border-top: 1px dotted #aaa; margin-top: 15px; padding-top: 0; }
+.method_details.first { border: 0; }
+p.signature {
+ font-size: 1.1em; font-weight: normal; font-family: Monaco, Consolas, Courier, monospace;
+ padding: 6px 10px; margin-top: 18px;
+ background: #e5e8ff; border: 1px solid #d8d8e5; -moz-border-radius: 3px; -webkit-border-radius: 3px;
+}
+p.signature tt { font-family: Monaco, Consolas, Courier, monospace; }
+p.signature .overload { display: block; }
+p.signature .extras { font-weight: normal; font-family: sans-serif; color: #444; font-size: 1em; }
+p.signature .aliases { display: block; font-weight: normal; font-size: 0.9em; font-family: sans-serif; margin-top: 0px; color: #555; }
+p.signature .aliases .names { font-family: Monaco, Consolas, Courier, monospace; font-weight: bold; color: #000; font-size: 1.2em; }
+
+.tags h3 { font-size: 1em; margin-bottom: 0; }
+.tags ul { margin-top: 5px; padding-left: 30px; list-style: square; }
+.tags ul li { margin-bottom: 3px; }
+.tags ul .name { font-family: monospace; font-weight: bold; }
+.tags ul .note { padding: 3px 6px; }
+.tags { margin-bottom: 12px; }
+
+.tags .examples h3 { margin-bottom: 10px; }
+.tags .examples h4 { padding: 0; margin: 0; margin-left: 15px; font-weight: bold; font-size: 0.9em; }
+
+.tags .overload .overload_item { list-style: none; margin-bottom: 25px; }
+.tags .overload .overload_item .signature {
+ padding: 2px 8px;
+ background: #e5e8ff; border: 1px solid #d8d8e5; -moz-border-radius: 3px; -webkit-border-radius: 3px;
+}
+.tags .overload .signature { margin-left: -15px; font-family: monospace; display: block; font-size: 1.1em; }
+.tags .overload .docstring { margin-top: 15px; }
+
+.defines { display: none; }
+
+#method_missing_details .notice.this { position: relative; top: -8px; color: #888; padding: 0; margin: 0; }
+
+.showSource { font-size: 0.9em; }
+.showSource a:link, .showSource a:visited { text-decoration: none; color: #666; }
+
+#content a:link, #content a:visited { text-decoration: none; color: #05a; }
+#content a:hover { background: #ffffa5; }
+.docstring { margin-right: 6em; }
+
+ul.summary {
+ list-style: none;
+ font-family: monospace;
+ font-size: 1em;
+ line-height: 1.5em;
+}
+ul.summary a:link, ul.summary a:visited {
+ text-decoration: none; font-size: 1.1em;
+}
+ul.summary li { margin-bottom: 5px; }
+.summary .summary_signature {
+ padding: 1px 10px;
+ background: #eaeaff; border: 1px solid #dfdfe5;
+ -moz-border-radius: 3px; -webkit-border-radius: 3px;
+}
+.summary_signature:hover { background: #eeeeff; cursor: pointer; }
+ul.summary.compact li { display: inline-block; margin: 0px 5px 0px 0px; line-height: 2.6em;}
+ul.summary.compact .summary_signature { padding: 5px 7px; padding-right: 4px; }
+#content .summary_signature:hover a:link,
+#content .summary_signature:hover a:visited {
+ background: transparent;
+ color: #48f;
+}
+
+p.inherited a { font-family: monospace; font-size: 0.9em; }
+p.inherited { word-spacing: 5px; font-size: 1.2em; }
+
+p.children { font-size: 1.2em; }
+p.children a { font-size: 0.9em; }
+p.children strong { font-size: 0.8em; }
+p.children strong.modules { padding-left: 5px; }
+
+ul.fullTree { display: none; padding-left: 0; list-style: none; margin-left: 0; margin-bottom: 10px; }
+ul.fullTree ul { margin-left: 0; padding-left: 0; list-style: none; }
+ul.fullTree li { text-align: center; padding-top: 18px; padding-bottom: 12px; }
+ul.fullTree li:first-child { padding-top: 0; background: transparent; }
+ul.fullTree li:last-child { padding-bottom: 0; }
+.showAll ul.fullTree { display: block; }
+.showAll .inheritName { display: none; }
+
+#search { position: absolute; right: 14px; top: 0px; }
+#search a:link, #search a:visited {
+ display: block; float: left; margin-right: 4px;
+ padding: 8px 10px; text-decoration: none; color: #05a;
+ border: 1px solid #d8d8e5;
+ -moz-border-radius-bottomleft: 3px; -moz-border-radius-bottomright: 3px;
+ -webkit-border-bottom-left-radius: 3px; -webkit-border-bottom-right-radius: 3px;
+ background: #eaf0ff;
+ -webkit-box-shadow: -1px 1px 3px #ddd;
+}
+#search a:hover { background: #f5faff; color: #06b; }
+#search a.active {
+ background: #568; padding-bottom: 20px; color: #fff; border: 1px solid #457;
+ -moz-border-radius-topleft: 5px; -moz-border-radius-topright: 5px;
+ -webkit-border-top-left-radius: 5px; -webkit-border-top-right-radius: 5px;
+}
+#search a.inactive { color: #999; }
+.frames #search { display: none; }
+.inheritanceTree, .toggleDefines { float: right; }
+
+#menu { font-size: 1.3em; color: #bbb; top: -5px; position: relative; }
+#menu .title, #menu a { font-size: 0.7em; }
+#menu .title a { font-size: 1em; }
+#menu .title { color: #555; }
+#menu a:link, #menu a:visited { color: #333; text-decoration: none; border-bottom: 1px dotted #bbd; }
+#menu a:hover { color: #05a; }
+#menu .noframes { display: none; }
+.frames #menu .noframes { display: inline; float: right; }
+
+#footer { margin-top: 15px; border-top: 1px solid #ccc; text-align: center; padding: 7px 0; color: #999; }
+#footer a:link, #footer a:visited { color: #444; text-decoration: none; border-bottom: 1px dotted #bbd; }
+#footer a:hover { color: #05a; }
+
+#listing ul.alpha { font-size: 1.1em; }
+#listing ul.alpha { margin: 0; padding: 0; padding-bottom: 10px; list-style: none; }
+#listing ul.alpha li.letter { font-size: 1.4em; padding-bottom: 10px; }
+#listing ul.alpha ul { margin: 0; padding-left: 15px; }
+#listing ul small { color: #666; font-size: 0.7em; }
+
+li.r1 { background: #f0f0f0; }
+li.r2 { background: #fafafa; }
+
+#search_frame {
+ z-index: 9999;
+ background: #fff;
+ display: none;
+ position: absolute;
+ top: 36px;
+ right: 18px;
+ width: 500px;
+ height: 80%;
+ overflow-y: scroll;
+ border: 1px solid #999;
+ border-collapse: collapse;
+ -webkit-box-shadow: -7px 5px 25px #aaa;
+ -moz-box-shadow: -7px 5px 25px #aaa;
+ -moz-border-radius: 2px;
+ -webkit-border-radius: 2px;
+}
+
+#content ul.summary li.deprecated .summary_signature a:link,
+#content ul.summary li.deprecated .summary_signature a:visited { text-decoration: line-through; font-style: italic; }
+
+#toc {
+ padding: 20px; padding-right: 30px; border: 1px solid #ddd; float: right; background: #fff; margin-left: 20px; margin-bottom: 20px;
+ max-width: 300px;
+ -webkit-box-shadow: -2px 2px 6px #bbb;
+ -moz-box-shadow: -2px 2px 6px #bbb;
+ z-index: 5000;
+ position: relative;
+}
+#toc.nofloat { float: none; max-width: none; border: none; padding: 0; margin: 20px 0; -webkit-box-shadow: none; -moz-box-shadow: none; }
+#toc.nofloat.hidden { padding: 0; background: 0; margin-bottom: 5px; }
+#toc .title { margin: 0; }
+#toc ol { padding-left: 1.8em; }
+#toc li { font-size: 1.1em; line-height: 1.7em; }
+#toc > ol > li { font-size: 1.1em; font-weight: bold; }
+#toc ol > ol { font-size: 0.9em; }
+#toc ol ol > ol { padding-left: 2.3em; }
+#toc ol + li { margin-top: 0.3em; }
+#toc.hidden { padding: 10px; background: #f6f6f6; -webkit-box-shadow: none; -moz-box-shadow: none; }
+#filecontents h1 + #toc.nofloat { margin-top: 0; }-border-radius:3px;-webkit-border-radius:3px}.summary_signature:hover{background:#eef;cursor:pointer}ul.summary.compact li{display:inline-block;margin:0 5px 0 0;line-height:2.6em}ul.summary.compact .summary_signature{padding:5px 7px;padding-right:4px}#content .summary_signature:hover a:link,#content .summary_signature:hover a:visited{background:transparent;color:#48f}p.inherited a{font-family:monospace;font-size:.9em}p.inherited{word-spacing:5px;font-size:1.2em}p.children{font-size:1.2em}p.children a{font-size:.9em}p.children strong{font-size:.8em}p.children strong.modules{padding-left:5px}ul.fullTree{display:none;padding-left:0;list-style:none;margin-left:0;margin-bottom:10px}ul.fullTree ul{margin-left:0;padding-left:0;list-style:none}ul.fullTree li{text-align:center;padding-top:18px;padding-bottom:12px}ul.fullTree li:first-child{padding-top:0;background:transparent}ul.fullTree li:last-child{padding-bottom:0}.showAll ul.fullTree{display:block}.showAll .inheritName{display:none}#search{position:absolute;right:14px;top:0}#search a:link,#search a:visited{display:block;float:left;margin-right:4px;padding:8px 10px;text-decoration:none;color:#05a;border:1px solid #d8d8e5;-moz-border-radius-bottomleft:3px;-moz-border-radius-bottomright:3px;-webkit-border-bottom-left-radius:3px;-webkit-border-bottom-right-radius:3px;background:#eaf0ff;-webkit-box-shadow:-1px 1px 3px #ddd}#search a:hover{background:#f5faff;color:#06b}#search a.active{background:#568;padding-bottom:20px;color:#fff;border:1px solid #457;-moz-border-radius-topleft:5px;-moz-border-radius-topright:5px;-webkit-border-top-left-radius:5px;-webkit-border-top-right-radius:5px}#search a.inactive{color:#999}.frames #search{display:none}.inheritanceTree,.toggleDefines{float:right}#menu{font-size:1.3em;color:#bbb;top:-5px;position:relative}#menu .title,#menu a{font-size:.7em}#menu .title a{font-size:1em}#menu .title{color:#555}#menu a:link,#menu a:visited{color:#333;text-decoration:none;border-bottom:1px dotted #bbd}#menu a:hover{color:#05a}#menu .noframes{display:none}.frames #menu .noframes{display:inline;float:right}#footer{margin-top:15px;border-top:1px solid #ccc;text-align:center;padding:7px 0;color:#999}#footer a:link,#footer a:visited{color:#444;text-decoration:none;border-bottom:1px dotted #bbd}#footer a:hover{color:#05a}#listing ul.alpha{font-size:1.1em}#listing ul.alpha{margin:0;padding:0;padding-bottom:10px;list-style:none}#listing ul.alpha li.letter{font-size:1.4em;padding-bottom:10px}#listing ul.alpha ul{margin:0;padding-left:15px}#listing ul small{color:#666;font-size:.7em}li.r1{background:#f0f0f0}li.r2{background:#fafafa}#search_frame{z-index:9999;background:#fff;display:none;position:absolute;top:36px;right:18px;width:500px;height:80%;overflow-y:scroll;border:1px solid #999;border-collapse:collapse;-webkit-box-shadow:-7px 5px 25px #aaa;-moz-box-shadow:-7px 5px 25px #aaa;-moz-border-radius:2px;-webkit-border-radius:2px}#content ul.summary li.deprecated .summary_signature a:link,#content ul.summary li.deprecated .summary_signature a:visited{text-decoration:line-through;font-style:italic}#toc{padding:20px;padding-right:30px;border:1px solid #ddd;float:right;background:#fff;margin-left:20px;margin-bottom:20px;max-width:300px;-webkit-box-shadow:-2px 2px 6px #bbb;-moz-box-shadow:-2px 2px 6px #bbb;z-index:5000;position:relative}#toc.nofloat{float:none;max-width:none;border:none;padding:0;margin:20px 0;-webkit-box-shadow:none;-moz-box-shadow:none}#toc.nofloat.hidden{padding:0;background:0;margin-bottom:5px}#toc .title{margin:0}#toc ol{padding-left:1.8em}#toc li{font-size:1.1em;line-height:1.7em}#toc>ol>li{font-size:1.1em;font-weight:700}#toc ol>ol{font-size:.9em}#toc ol ol>ol{padding-left:2.3em}#toc ol+li{margin-top:.3em}#toc.hidden{padding:10px;background:#f6f6f6;-webkit-box-shadow:none;-moz-box-shadow:none}#filecontents h1+#toc.nofloat{margin-top:0}
52 templates/index_template.erb
@@ -0,0 +1,52 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <meta name="Content-Type" content="text/html; charset=UTF-8"/>
+ <title>Index</title>
+ <link rel="stylesheet" href="css/style.css" type="text/css" media="screen" charset="utf-8"/>
+</head>
+<body>
+<div id="menu">
+ <a href="index.html" target="_top">Index</a> &raquo;
+ <span class="title"><%= Time.now %></span>
+</div>
+<div id="search">
+ <a id="class_list_link" href="index.html">Index</a>
+</div>
+<div id="content"><h1 class="noborder title">Documentation for <%= @app_name %></h1>
+
+ <div id="listing">
+ <h1 class="alphaindex">Alphabetic Index</h1>
+
+ <div class="clear"></div>
+ <h2>Listing A-Z</h2>
+ <table>
+ <tr>
+ <td valign='top' width="33%">
+ <% @alphabetic_indexes.keys.sort.each_with_index do |key, index| %>
+ <ul id="alpha_<%= key %>" class="alpha">
+ <li class="letter"><%= key %></li>
+ <ul>
+ <% @alphabetic_indexes[key].sort.each do |info| %>
+ <li>
+ <span class='object_link'><a href="<%= info.underscore.gsub("/", "_") %>.html" title="<%= info %> (Model)"><%= info %></a></span>
+ </li>
+ <% end %>
+ </ul>
+ </ul>
+ <% if (index +1) % 5 == 0 %>
+ </td>
+ <td valign='top' width="33%">
+ <% end %>
+ <% end %>
+ </td>
+ </tr>
+ </table>
+ </div>
+</div>
+<div id="footer">
+ <%= Time.now %> by Elvuel (elvuel@gmail.com)
+</div>
+</body>
+</html>
172 templates/model_template.erb
@@ -0,0 +1,172 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <meta name="Content-Type" content="text/html; charset=UTF-8"/>
+ <title>Model: <%= @model %></title>
+ <link rel="stylesheet" href="css/style.css" type="text/css" media="screen" charset="utf-8"/>
+</head>
+<body>
+<div id="header">
+ <div id="menu">
+ <a href="index.html" target="_top">Index</a> &raquo;
+ <span class="title"><%= Time.now.strftime("%Y-%m-%d") %></span>
+ </div>
+ <div id="search">
+ <a id="class_list_link" href="index.html">Index</a>
+ </div>
+ <div class="clear"></div>
+</div>
+<div id="content"><h1>Model: <%= @model %>(<%= @human_name %>)
+</h1>
+ <dl class="box">
+ <dt class="r1">Inherits:</dt>
+ <dd class="r1">
+ <% @inherits.each do |item| %>
+ <span class="inheritName">
+ <% if item != "ActiveRecord::Base" %>
+ <a href='<%= item.underscore.gsub("/", "_")%>.html'><%= item %></a>
+ <% else %>
+ <%= item %>
+ <% end %>
+ </span>
+ <% end %>
+ </dd>
+ <dt class="r2">Defined in:</dt>
+ <dd class="r2">
+ <% @defined_in.each do |locate| %>
+ <span class="inheritName"><%= locate %></span><br/>
+ <% end %>
+ </dd>
+ <dt class="r2">Database:</dt>
+ <dd class="r2"><%= @database %></dd>
+ <dt class="r2 last">Table name:</dt>
+ <dd class="r2 last"><%= @table_name %></dd>
+ </dl>
+ <div class="clear"></div>
+ <h2>Table schema</h2>
+
+ <div class="docstring">
+ <table width="70%" align="left" bgcolor="#999" cellpadding="0" cellspacing="1">
+ <tr bgcolor="#eee">
+ <th height="35">Column name</th>
+ <th>Column type</th>
+ <th>Column attr</th>
+ <th>Human name</th>
+ </tr>
+ <% if @schema.is_a? Array %>
+
+ <% @schema.each do |item| %>
+ <tr bgcolor="#fff">
+ <td height="30"><%= item[:name] %></td>
+ <td><%= item[:type] %></td>
+ <td><%= item[:attrs] %></td>
+ <td><%= item[:human_name] %></td>
+ </tr>
+ <% end %>
+ <% else %>
+ <tr bgcolor="#fff">
+ <td height="30" colspan="4" align="center"><%= @schema %></td>
+ </tr>
+ <% end %>
+
+ </table>
+ </div>
+ <div class="clear"></div>
+ <h2>Table index</h2>
+
+ <div class="docstring">
+ <table width="70%" align="left" bgcolor="#999" cellpadding="0" cellspacing="1">
+ <tr bgcolor="#eee">
+ <th height="35">Name</th>
+ <th>Column</th>
+ <th>Unique?</th>
+ </tr>
+ <% @db_indexes.each do |index| %>
+ <tr bgcolor="#fff">
+ <td height="30"><%= index[:name] %></td>
+ <td><%= index[:columns] %></td>
+ <td><%= index[:unique] %></td>
+ </tr>
+ <% end %>
+
+ </table>
+ </div>
+ <div class="clear"></div>
+ <h2>Model association</h2>
+
+ <div class="docstring">
+ <table width="100%" align="left" bgcolor="#999" cellpadding="0" cellspacing="1">
+ <tr bgcolor="#eee">
+ <th>Macro</th>
+ <th height="35">Association key</th>
+ <th>Association(model)</th>
+ <th>Foreign key</th>
+ <th>Primary key</th>
+ <th>Options</th>
+ </tr>
+ <% @associations.each do |info| %>
+ <tr bgcolor="#fff">
+ <td><%= info[:macro] %>(<%= info[:type] %>)
+ <% if info[:type] == "through" %>Through =>
+ <a href="<%= info[:through].underscore.gsub("/", "_") %>.html"><%= info[:through] %></a>
+ <% end %>
+ </td>
+ <td height="30"><%= info[:name] %></td>
+ <td>
+ <% if info[:association_classes] %>
+ <% info[:association_classes].each do |item| %>
+ <a href="<%= item.underscore.gsub("/", "_") %>.html"><%= item %></a>
+ <br/>
+ <% end %>
+ <% end %>
+ </td>
+ <td><%= info[:foreign_key] %></td>
+ <td><%= info[:primary_key] %></td>
+ <td><%= info[:options] %></td>
+ </tr>
+ <% end %>
+ </table>
+ </div>
+ <div class="clear"></div>
+ <h2>Named scope(AR)</h2>
+
+ <div class="docstring">
+ <ul class="summary">
+
+ <li class="public ">
+ <% @named_scopes.each do |ns| %>
+ <span class="summary_signature"><%= ns %></span>
+ <% end %>
+ </li>
+
+ </ul>
+
+ </div>
+ <div class="clear"></div>
+ <h2>Singleton methods</h2>
+
+ <div class="docstring">
+ <ul class="summary">
+
+ <li class="public ">
+ <% @singleton_methods.each do |sm| %>
+ <span class="summary_signature"><%= sm %></span>
+ <% end %>
+ </li>
+
+ </ul>
+
+ </div>
+ <div class="clear"></div>
+ <h2>Summary</h2>
+
+ <div class="docstring">
+ </div>
+ <div class="clear"></div>
+</div>
+<div id="footer">
+ <%= Time.now %> by Elvuel (elvuel@gmail.com)
+</div>
+</body>
+</html>

0 comments on commit 6b782bc

Please sign in to comment.
Something went wrong with that request. Please try again.