Skip to content
Browse files

added the fundations of the Merb Open Source Book

  • Loading branch information...
1 parent b980fd5 commit ecc5f5d9e00a6fcb75286678d178f67a72ac0ce4 @mattetti committed Dec 1, 2008
Showing with 19,682 additions and 0 deletions.
  1. +14 −0 README.markdown
  2. +35 −0 Rakefile
  3. +2 −0 app/controllers/application.rb
  4. +13 −0 app/controllers/exceptions.rb
  5. +24 −0 app/controllers/pages.rb
  6. +5 −0 app/helpers/global_helpers.rb
  7. +5 −0 app/helpers/pages_helper.rb
  8. +63 −0 app/views/exceptions/not_acceptable.html.erb
  9. +3 −0 app/views/exceptions/not_found.html.erb
  10. +34 −0 app/views/layout/application.html.erb
  11. +28 −0 app/views/pages/index.html.erb
  12. +1 −0 autotest/discover.rb
  13. +149 −0 autotest/merb.rb
  14. +165 −0 autotest/merb_rspec.rb
  15. +31 −0 bin/autospec
  16. +31 −0 bin/edit_json.rb
  17. +31 −0 bin/erubis
  18. +31 −0 bin/gpgen
  19. +31 −0 bin/maruku
  20. +31 −0 bin/marutex
  21. +31 −0 bin/merb
  22. +31 −0 bin/mongrel_rails
  23. +31 −0 bin/rackup
  24. +31 −0 bin/rake
  25. +31 −0 bin/rake2thor
  26. +31 −0 bin/spec
  27. +31 −0 bin/thor
  28. +3 −0 book-content/0-front-matter/0-foreword.markdown
  29. +21 −0 book-content/0-front-matter/1-preface.markdown
  30. +9 −0 book-content/0-front-matter/2-contributors.markdown
  31. +47 −0 book-content/1-introduction/1-ruby.markdown
  32. +1 −0 book-content/1-introduction/2-merb.markdown
  33. +1 −0 book-content/1-introduction/3-datamapper.markdown
  34. +1 −0 book-content/1-introduction/4-rspec.markdown
  35. +13 −0 config/dependencies.yml
  36. +15 −0 config/environments/development.rb
  37. +10 −0 config/environments/production.rb
  38. +11 −0 config/environments/rake.rb
  39. +10 −0 config/environments/staging.rb
  40. +12 −0 config/environments/test.rb
  41. +51 −0 config/init.rb
  42. +11 −0 config/rack.rb
  43. +9 −0 config/router.rb
  44. +1,362 −0 doc/rdoc/generators/merb_generator.rb
  45. +640 −0 doc/rdoc/generators/template/merb/api_grease.js
  46. +37 −0 doc/rdoc/generators/template/merb/index.html.erb
  47. +252 −0 doc/rdoc/generators/template/merb/merb.css
  48. +351 −0 doc/rdoc/generators/template/merb/merb.rb
  49. +492 −0 doc/rdoc/generators/template/merb/merb_doc_styles.css
  50. +2,515 −0 doc/rdoc/generators/template/merb/prototype.js
  51. BIN gems/cache/abstract-1.0.0.gem
  52. BIN gems/cache/cgi_multipart_eof_fix-2.5.0.gem
  53. BIN gems/cache/daemons-1.0.10.gem
  54. BIN gems/cache/erubis-2.6.2.gem
  55. BIN gems/cache/extlib-0.9.8.gem
  56. BIN gems/cache/fastthread-1.0.1.gem
  57. BIN gems/cache/gem_plugin-0.2.3.gem
  58. BIN gems/cache/json_pure-1.1.3.gem
  59. BIN gems/cache/maruku-0.5.9.gem
  60. BIN gems/cache/merb-assets-1.0.3.gem
  61. BIN gems/cache/merb-core-1.0.3.gem
  62. BIN gems/cache/mime-types-1.15.gem
  63. BIN gems/cache/mongrel-1.1.5.gem
  64. BIN gems/cache/nokogiri-1.0.6.gem
  65. BIN gems/cache/rack-0.4.0.gem
  66. BIN gems/cache/rake-0.8.3.gem
  67. BIN gems/cache/rspec-1.1.11.gem
  68. BIN gems/cache/syntax-1.0.0.gem
  69. BIN gems/cache/thor-0.9.8.gem
  70. BIN gems/cache/webrat-0.3.2.gem
  71. +3 −0 gems/gems/abstract-1.0.0/ChangeLog
  72. +57 −0 gems/gems/abstract-1.0.0/README.txt
  73. +48 −0 gems/gems/abstract-1.0.0/abstract.gemspec
  74. +75 −0 gems/gems/abstract-1.0.0/lib/abstract.rb
  75. +1,331 −0 gems/gems/abstract-1.0.0/setup.rb
  76. +91 −0 gems/gems/abstract-1.0.0/test/test.rb
  77. +14 −0 gems/gems/cgi_multipart_eof_fix-2.5.0/CHANGELOG
  78. +55 −0 gems/gems/cgi_multipart_eof_fix-2.5.0/LICENSE
  79. +6 −0 gems/gems/cgi_multipart_eof_fix-2.5.0/Manifest
  80. +40 −0 gems/gems/cgi_multipart_eof_fix-2.5.0/README
  81. +44 −0 gems/gems/cgi_multipart_eof_fix-2.5.0/cgi_multipart_eof_fix.gemspec
  82. +127 −0 gems/gems/cgi_multipart_eof_fix-2.5.0/lib/cgi_multipart_eof_fix.rb
  83. +59 −0 gems/gems/cgi_multipart_eof_fix-2.5.0/test/test_cgi_multipart_eof_fix.rb
  84. +29 −0 gems/gems/daemons-1.0.10/LICENSE
  85. +223 −0 gems/gems/daemons-1.0.10/README
  86. +84 −0 gems/gems/daemons-1.0.10/Rakefile
  87. +126 −0 gems/gems/daemons-1.0.10/Releases
  88. +6 −0 gems/gems/daemons-1.0.10/TODO
  89. +56 −0 gems/gems/daemons-1.0.10/examples/call/call.rb
  90. +55 −0 gems/gems/daemons-1.0.10/examples/call/call_monitor.rb
  91. +20 −0 gems/gems/daemons-1.0.10/examples/daemonize/daemonize.rb
  92. +17 −0 gems/gems/daemons-1.0.10/examples/run/ctrl_crash.rb
  93. +16 −0 gems/gems/daemons-1.0.10/examples/run/ctrl_exec.rb
  94. +15 −0 gems/gems/daemons-1.0.10/examples/run/ctrl_exit.rb
  95. +17 −0 gems/gems/daemons-1.0.10/examples/run/ctrl_keep_pid_files.rb
  96. +16 −0 gems/gems/daemons-1.0.10/examples/run/ctrl_monitor.rb
  97. +16 −0 gems/gems/daemons-1.0.10/examples/run/ctrl_multiple.rb
  98. +12 −0 gems/gems/daemons-1.0.10/examples/run/ctrl_normal.rb
  99. +16 −0 gems/gems/daemons-1.0.10/examples/run/ctrl_ontop.rb
  100. +43 −0 gems/gems/daemons-1.0.10/examples/run/ctrl_optionparser.rb
  101. +25 −0 gems/gems/daemons-1.0.10/examples/run/ctrl_proc.rb
  102. +101 −0 gems/gems/daemons-1.0.10/examples/run/ctrl_proc.rb.output
  103. +22 −0 gems/gems/daemons-1.0.10/examples/run/ctrl_proc_multiple.rb
  104. +2 −0 gems/gems/daemons-1.0.10/examples/run/ctrl_proc_multiple.rb.output
  105. +17 −0 gems/gems/daemons-1.0.10/examples/run/ctrl_proc_simple.rb
  106. +12 −0 gems/gems/daemons-1.0.10/examples/run/myserver.rb
  107. +14 −0 gems/gems/daemons-1.0.10/examples/run/myserver_crashing.rb
  108. +30 −0 gems/gems/daemons-1.0.10/examples/run/myserver_crashing.rb.output
  109. +8 −0 gems/gems/daemons-1.0.10/examples/run/myserver_exiting.rb
  110. +283 −0 gems/gems/daemons-1.0.10/lib/daemons.rb
  111. +372 −0 gems/gems/daemons-1.0.10/lib/daemons/application.rb
  112. +152 −0 gems/gems/daemons-1.0.10/lib/daemons/application_group.rb
  113. +117 −0 gems/gems/daemons-1.0.10/lib/daemons/cmdline.rb
  114. +134 −0 gems/gems/daemons-1.0.10/lib/daemons/controller.rb
  115. +263 −0 gems/gems/daemons-1.0.10/lib/daemons/daemonize.rb
  116. +28 −0 gems/gems/daemons-1.0.10/lib/daemons/exceptions.rb
  117. +127 −0 gems/gems/daemons-1.0.10/lib/daemons/monitor.rb
  118. +101 −0 gems/gems/daemons-1.0.10/lib/daemons/pid.rb
  119. +111 −0 gems/gems/daemons-1.0.10/lib/daemons/pidfile.rb
  120. +10 −0 gems/gems/daemons-1.0.10/lib/daemons/pidmem.rb
  121. +1,360 −0 gems/gems/daemons-1.0.10/setup.rb
  122. +717 −0 gems/gems/erubis-2.6.2/CHANGES.txt
  123. +20 −0 gems/gems/erubis-2.6.2/MIT-LICENSE
  124. +102 −0 gems/gems/erubis-2.6.2/README.txt
  125. +6 −0 gems/gems/erubis-2.6.2/benchmark/Makefile
  126. +314 −0 gems/gems/erubis-2.6.2/benchmark/bench.rb
  127. +141 −0 gems/gems/erubis-2.6.2/benchmark/bench_context.yaml
  128. +4 −0 gems/gems/erubis-2.6.2/benchmark/templates/_footer.html
  129. +52 −0 gems/gems/erubis-2.6.2/benchmark/templates/_header.html
  130. +29 −0 gems/gems/erubis-2.6.2/benchmark/templates/bench_erb.rhtml
  131. +29 −0 gems/gems/erubis-2.6.2/benchmark/templates/bench_erubis.rhtml
  132. +29 −0 gems/gems/erubis-2.6.2/benchmark/templates/bench_eruby.rhtml
  133. +11 −0 gems/gems/erubis-2.6.2/bin/erubis
  134. +11 −0 gems/gems/erubis-2.6.2/contrib/erubis
  135. +132 −0 gems/gems/erubis-2.6.2/contrib/erubis-run.rb
  136. +153 −0 gems/gems/erubis-2.6.2/contrib/inline-require
  137. +105 −0 gems/gems/erubis-2.6.2/doc-api/classes/ActionView.html
  138. +181 −0 gems/gems/erubis-2.6.2/doc-api/classes/ActionView/TemplateHandlers/ErubisHandler.html
  139. +101 −0 gems/gems/erubis-2.6.2/doc-api/classes/ERB.html
  140. +353 −0 gems/gems/erubis-2.6.2/doc-api/classes/Erubis.html
  141. +175 −0 gems/gems/erubis-2.6.2/doc-api/classes/Erubis/ArrayBufferEnhancer.html
  142. +120 −0 gems/gems/erubis-2.6.2/doc-api/classes/Erubis/ArrayBufferEruby.html
  143. +174 −0 gems/gems/erubis-2.6.2/doc-api/classes/Erubis/ArrayEnhancer.html
  144. +120 −0 gems/gems/erubis-2.6.2/doc-api/classes/Erubis/ArrayEruby.html
  145. +112 −0 gems/gems/erubis-2.6.2/doc-api/classes/Erubis/Basic.html
  146. +327 −0 gems/gems/erubis-2.6.2/doc-api/classes/Erubis/Basic/Converter.html
  147. +130 −0 gems/gems/erubis-2.6.2/doc-api/classes/Erubis/Basic/Engine.html
  148. +215 −0 gems/gems/erubis-2.6.2/doc-api/classes/Erubis/BiPatternEnhancer.html
  149. +120 −0 gems/gems/erubis-2.6.2/doc-api/classes/Erubis/BiPatternEruby.html
  150. +386 −0 gems/gems/erubis-2.6.2/doc-api/classes/Erubis/CGenerator.html
  151. +113 −0 gems/gems/erubis-2.6.2/doc-api/classes/Erubis/CommandOptionError.html
  152. +344 −0 gems/gems/erubis-2.6.2/doc-api/classes/Erubis/Context.html
  153. +283 −0 gems/gems/erubis-2.6.2/doc-api/classes/Erubis/Converter.html
  154. +150 −0 gems/gems/erubis-2.6.2/doc-api/classes/Erubis/DeleteIndentEnhancer.html
  155. +120 −0 gems/gems/erubis-2.6.2/doc-api/classes/Erubis/DeleteIndentEruby.html
  156. +126 −0 gems/gems/erubis-2.6.2/doc-api/classes/Erubis/Ec.html
  157. +126 −0 gems/gems/erubis-2.6.2/doc-api/classes/Erubis/Ejava.html
  158. +126 −0 gems/gems/erubis-2.6.2/doc-api/classes/Erubis/Ejavascript.html
  159. +305 −0 gems/gems/erubis-2.6.2/doc-api/classes/Erubis/Engine.html
  160. +126 −0 gems/gems/erubis-2.6.2/doc-api/classes/Erubis/Eperl.html
  161. +126 −0 gems/gems/erubis-2.6.2/doc-api/classes/Erubis/Ephp.html
  162. +175 −0 gems/gems/erubis-2.6.2/doc-api/classes/Erubis/ErboutEnhancer.html
  163. +120 −0 gems/gems/erubis-2.6.2/doc-api/classes/Erubis/ErboutEruby.html
  164. +117 −0 gems/gems/erubis-2.6.2/doc-api/classes/Erubis/ErubisError.html
  165. +132 −0 gems/gems/erubis-2.6.2/doc-api/classes/Erubis/Eruby.html
Sorry, we could not display the entire diff because too many files (1,828) changed.
View
0 README
Sorry, we could not display the changes to this file because there were too many other changes to display.
View
14 README.markdown
@@ -0,0 +1,14 @@
+== the Merb Book ==
+
+This book is an open source book written by the Merb community.
+
+
+
+=== Legal ===
+
+The content of this book is licensed under the [Creative Commons Attribution-Noncommercial-Share Alike 3.0 license](http://creativecommons.org/licenses/by-nc-sa/3.0/us/)
+
+The source code of the application is dual licensed under the MIT and GPL licenses:
+
+ * [MIT](http://www.opensource.org/licenses/mit-license.php)
+ * [GPL](http://www.gnu.org/licenses/gpl.html)
View
35 Rakefile
@@ -0,0 +1,35 @@
+require 'rubygems'
+require 'rake/rdoctask'
+
+require 'merb-core'
+require 'merb-core/tasks/merb'
+
+include FileUtils
+
+# Load the basic runtime dependencies; this will include
+# any plugins and therefore plugin rake tasks.
+init_env = ENV['MERB_ENV'] || 'rake'
+Merb.load_dependencies(:environment => init_env)
+
+# Get Merb plugins and dependencies
+Merb::Plugins.rakefiles.each { |r| require r }
+
+# Load any app level custom rakefile extensions from lib/tasks
+tasks_path = File.join(File.dirname(__FILE__), "lib", "tasks")
+rake_files = Dir["#{tasks_path}/*.rake"]
+rake_files.each{|rake_file| load rake_file }
+
+desc "Start runner environment"
+task :merb_env do
+ Merb.start_environment(:environment => init_env, :adapter => 'runner')
+end
+
+require 'spec/rake/spectask'
+require 'merb-core/test/tasks/spectasks'
+desc 'Default: run spec examples'
+task :default => 'spec'
+
+##############################################################################
+# ADD YOUR CUSTOM TASKS IN /lib/tasks
+# NAME YOUR RAKE FILES file_name.rake
+##############################################################################
View
2 app/controllers/application.rb
@@ -0,0 +1,2 @@
+class Application < Merb::Controller
+end
View
13 app/controllers/exceptions.rb
@@ -0,0 +1,13 @@
+class Exceptions < Merb::Controller
+
+ # handle NotFound exceptions (404)
+ def not_found
+ render :format => :html
+ end
+
+ # handle NotAcceptable exceptions (406)
+ def not_acceptable
+ render :format => :html
+ end
+
+end
View
24 app/controllers/pages.rb
@@ -0,0 +1,24 @@
+class Pages < Application
+
+ def index
+ render
+ end
+
+ def show
+ chapter = params[:chapter]
+ page_name = params[:page_name]
+
+ page_file = find_page_file(chapter, page_name)
+ raise NotFound unless page_file
+
+ text = File.open(page_file).read
+ render Maruku::new(text).to_html
+ end
+
+ private
+ # If no page name is being passed, the first page is being returned
+ def find_page_file(chapter, page_name, format="markdown")
+ Dir["#{Merb.root}/book-content/*-#{chapter}/*-#{page_name||"*"}.#{format}"].entries.first
+ end
+
+end
View
5 app/helpers/global_helpers.rb
@@ -0,0 +1,5 @@
+module Merb
+ module GlobalHelpers
+ # helpers defined here available to all views.
+ end
+end
View
5 app/helpers/pages_helper.rb
@@ -0,0 +1,5 @@
+module Merb
+ module PagesHelper
+
+ end
+end # Merb
View
63 app/views/exceptions/not_acceptable.html.erb
@@ -0,0 +1,63 @@
+<div id="container">
+ <div id="header-container">
+ <img src="/images/merb.jpg" />
+ <!-- <h1>Mongrel + Erb</h1> -->
+ <h2>pocket rocket web framework</h2>
+ <hr />
+ </div>
+
+ <div id="left-container">
+ <h3>Exception:</h3>
+ <p><%= request.exceptions.first.message %></p>
+ </div>
+
+ <div id="main-container">
+ <h3>Why am I seeing this page?</h3>
+ <p>Merb couldn't find an appropriate content_type to return,
+ based on what you said was available via provides() and
+ what the client requested.</p>
+
+ <h3>How to add a mime-type</h3>
+ <pre><code>
+ Merb.add_mime_type :pdf, :to_pdf, %w[application/pdf], &quot;Content-Encoding&quot; =&gt; &quot;gzip&quot;
+ </code></pre>
+ <h3>What this means is:</h3>
+ <ul>
+ <li>Add a mime-type for :pdf</li>
+ <li>Register the method for converting objects to PDF as <code>#to_pdf</code>.</li>
+ <li>Register the incoming mime-type "Accept" header as <code>application/pdf</code>.</li>
+ <li>Specify a new header for PDF types so it will set <code>Content-Encoding</code> to gzip.</li>
+ </ul>
+
+ <h3>You can then do:</h3>
+ <pre><code>
+ class Foo &lt; Application
+ provides :pdf
+ end
+ </code></pre>
+
+ <h3>Where can I find help?</h3>
+ <p>If you have any questions or if you can't figure something out, please take a
+ look at our <a href="http://merbivore.com/"> project page</a>,
+ feel free to come chat at irc.freenode.net, channel #merb,
+ or post to <a href="http://groups.google.com/group/merb">merb mailing list</a>
+ on Google Groups.</p>
+
+ <h3>What if I've found a bug?</h3>
+ <p>If you want to file a bug or make your own contribution to Merb,
+ feel free to register and create a ticket at our
+ <a href="http://merb.lighthouseapp.com/">project development page</a>
+ on Lighthouse.</p>
+
+ <h3>How do I edit this page?</h3>
+ <p>You can change what people see when this happens by editing <tt>app/views/exceptions/not_acceptable.html.erb</tt>.</p>
+
+ </div>
+
+ <div id="footer-container">
+ <hr />
+ <div class="left"></div>
+ <div class="right">&copy; 2008 the merb dev team</div>
+ <p>&nbsp;</p>
+ </div>
+</div>
View
3 app/views/exceptions/not_found.html.erb
@@ -0,0 +1,3 @@
+<div class="error">we couldn't find the page you requested, you might want to check on the index</div>
+
+<%= link_to "Table of Content", url(:toc) %>
View
34 app/views/layout/application.html.erb
@@ -0,0 +1,34 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-us" lang="en-us">
+ <head>
+ <title>The Merb Open Source Book</title>
+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
+ <link rel="stylesheet" href="/stylesheets/master.css" type="text/css" media="screen" charset="utf-8" />
+ </head>
+ <body>
+ <p id="warning">This book is work in progress, feel free to <a href="http://github.com/mattetti/merb-book">contribute</a></p>
+ <%#= message[:notice] %>
+
+ <div id="book-title"><a href="/">The Merb Open Source Book</a></div>
+ <div id="book">
+ <div class="toc-link"><a href="/">table of contents</a></div>
+
+ <div id="table-of-contents">
+ <%= catch_content :toc %>
+ </div>
+
+ <div id="book-content">
+ <%= catch_content :for_layout %>
+ </div>
+
+ </div>
+
+ <div id="footer">
+ <span>The content of this book is licensed under the
+ <a href="http://creativecommons.org/licenses/by-nc-sa/3.0/us/">Creative Commons Attribution-Non Commercial-Share Alike 3.0 license</a></span>
+
+ <p>The source code of the book application is dual licensed under the <a href="http://www.opensource.org/licenses/mit-license.php">MIT</a>
+ and <a href="http://www.gnu.org/licenses/gpl.html">GPL</a> licenses </p>
+ </div>
+ </body>
+</html>
View
28 app/views/pages/index.html.erb
@@ -0,0 +1,28 @@
+<h1>Table of Contents</h1>
+
+<div id="intro">
+ <p>Welcome to the Merb Open Source Book. The purpose of this book is simple: guide beginner and advance users through the Merb Framework.</p>
+ <p>In all honesty, This book won't answer all your questions about Merb, Ruby and unfortunately won't cover all the use case. However will the help of the Ruby Community, we will be able to cover most of what you need to use Merb on a daily basis.</p>
+ <p>If you are learning through this material, or if already use Merb frequently, <strong>please consider helping improve the content of this book</strong></p>
+</div>
+
+<ul id="toc">
+ <li>
+ <a href="/front-matter">Front Matter</a>
+ <ul>
+ <li><a href="/front-matter/foreword">Foreword by Yehuda Katz</a></li>
+ <li><a href="/front-matter/preface">Preface by Matt Aimonetti</a></li>
+ <li><a href="/front-matter/contributors">Contributors</a></li>
+ </ul>
+ </li>
+ <li>
+ <a href="/introduction">Introduction</a>
+ <ul>
+ <li><a href="/introduction/ruby">Ruby language</a></li>
+ <li><a href="/introduction/merb">Merb web framework</a></li>
+ <li><a href="/introduction/datamapper">DataMapper ORM</a></li>
+ <li><a href="/introduction/rspec">RSpec testing framework</a></li>
+ </ul>
+ </li>
+
+</ul>
View
1 autotest/discover.rb
@@ -0,0 +1 @@
+Autotest.add_discovery { "merb" }
View
149 autotest/merb.rb
@@ -0,0 +1,149 @@
+# Adapted from Autotest::Rails
+require 'autotest'
+
+class Autotest::Merb < Autotest
+
+ # +model_tests_dir+:: the directory to find model-centric tests
+ # +controller_tests_dir+:: the directory to find controller-centric tests
+ # +view_tests_dir+:: the directory to find view-centric tests
+ # +fixtures_dir+:: the directory to find fixtures in
+ attr_accessor :model_tests_dir, :controller_tests_dir, :view_tests_dir, :fixtures_dir
+
+ def initialize
+ super
+
+ initialize_test_layout
+
+ # Ignore any happenings in these directories
+ add_exception %r%^\./(?:doc|log|public|tmp)%
+
+ # Ignore any mappings that Autotest may have already set up
+ clear_mappings
+
+ # Any changes to a file in the root of the 'lib' directory will run any
+ # model test with a corresponding name.
+ add_mapping %r%^lib\/.*\.rb% do |filename, _|
+ files_matching Regexp.new(["^#{model_test_for(filename)}$"])
+ end
+
+ # Any changes to a fixture will run corresponding view, controller and
+ # model tests
+ add_mapping %r%^#{fixtures_dir}/(.*)s.yml% do |_, m|
+ [
+ model_test_for(m[1]),
+ controller_test_for(m[1]),
+ view_test_for(m[1])
+ ]
+ end
+
+ # Any change to a test or test will cause it to be run
+ add_mapping %r%^test/(unit|models|integration|controllers|views|functional)/.*rb$% do |filename, _|
+ filename
+ end
+
+ # Any change to a model will cause it's corresponding test to be run
+ add_mapping %r%^app/models/(.*)\.rb$% do |_, m|
+ model_test_for(m[1])
+ end
+
+ # Any change to the global helper will result in all view and controller
+ # tests being run
+ add_mapping %r%^app/helpers/global_helpers.rb% do
+ files_matching %r%^test/(views|functional|controllers)/.*_test\.rb$%
+ end
+
+ # Any change to a helper will run it's corresponding view and controller
+ # tests, unless the helper is the global helper. Changes to the global
+ # helper run all view and controller tests.
+ add_mapping %r%^app/helpers/(.*)_helper(s)?.rb% do |_, m|
+ if m[1] == "global" then
+ files_matching %r%^test/(views|functional|controllers)/.*_test\.rb$%
+ else
+ [
+ view_test_for(m[1]),
+ controller_test_for(m[1])
+ ]
+ end
+ end
+
+ # Changes to views result in their corresponding view and controller test
+ # being run
+ add_mapping %r%^app/views/(.*)/% do |_, m|
+ [
+ view_test_for(m[1]),
+ controller_test_for(m[1])
+ ]
+ end
+
+ # Changes to a controller result in its corresponding test being run. If
+ # the controller is the exception or application controller, all
+ # controller tests are run.
+ add_mapping %r%^app/controllers/(.*)\.rb$% do |_, m|
+ if ["application", "exception"].include?(m[1])
+ files_matching %r%^test/(controllers|views|functional)/.*_test\.rb$%
+ else
+ controller_test_for(m[1])
+ end
+ end
+
+ # If a change is made to the router, run all controller and view tests
+ add_mapping %r%^config/router.rb$% do # FIX
+ files_matching %r%^test/(controllers|views|functional)/.*_test\.rb$%
+ end
+
+ # If any of the major files governing the environment are altered, run
+ # everything
+ add_mapping %r%^test/test_helper.rb|config/(init|rack|environments/test.rb|database.yml)% do # FIX
+ files_matching %r%^test/(unit|models|controllers|views|functional)/.*_test\.rb$%
+ end
+ end
+
+private
+
+ # Determines the paths we can expect tests or specs to reside, as well as
+ # corresponding fixtures.
+ def initialize_test_layout
+ self.model_tests_dir = "test/unit"
+ self.controller_tests_dir = "test/functional"
+ self.view_tests_dir = "test/views"
+ self.fixtures_dir = "test/fixtures"
+ end
+
+ # Given a filename and the test type, this method will return the
+ # corresponding test's or spec's name.
+ #
+ # ==== Arguments
+ # +filename+<String>:: the file name of the model, view, or controller
+ # +kind_of_test+<Symbol>:: the type of test we that we should run
+ #
+ # ==== Returns
+ # String:: the name of the corresponding test or spec
+ #
+ # ==== Example
+ #
+ # > test_for("user", :model)
+ # => "user_test.rb"
+ # > test_for("login", :controller)
+ # => "login_controller_test.rb"
+ # > test_for("form", :view)
+ # => "form_view_spec.rb" # If you're running a RSpec-like suite
+ def test_for(filename, kind_of_test)
+ name = [filename]
+ name << kind_of_test.to_s if kind_of_test == :view
+ name << "test"
+ return name.join("_") + ".rb"
+ end
+
+ def model_test_for(filename)
+ [model_tests_dir, test_for(filename, :model)].join("/")
+ end
+
+ def controller_test_for(filename)
+ [controller_tests_dir, test_for(filename, :controller)].join("/")
+ end
+
+ def view_test_for(filename)
+ [view_tests_dir, test_for(filename, :view)].join("/")
+ end
+
+end
View
165 autotest/merb_rspec.rb
@@ -0,0 +1,165 @@
+# Adapted from Autotest::Rails, RSpec's autotest class, as well as merb-core's.
+require 'autotest'
+
+class RspecCommandError < StandardError; end
+
+# This class maps your application's structure so Autotest can understand what
+# specs to run when files change.
+#
+# Fixtures are _not_ covered by this class. If you change a fixture file, you
+# will have to run your spec suite manually, or, better yet, provide your own
+# Autotest map explaining how your fixtures are set up.
+class Autotest::MerbRspec < Autotest
+ def initialize
+ super
+
+ # Ignore any happenings in these directories
+ add_exception %r%^\./(?:doc|log|public|tmp|\.git|\.hg|\.svn|framework|gems|schema|\.DS_Store|autotest|bin|.*\.sqlite3)%
+ # Ignore SCM directories and custom Autotest mappings
+ %w[.svn .hg .git .autotest].each { |exception| add_exception(exception) }
+
+ # Ignore any mappings that Autotest may have already set up
+ clear_mappings
+
+ # Anything in /lib could have a spec anywhere, if at all. So, look for
+ # files with roughly the same name as the file in /lib
+ add_mapping %r%^lib\/(.*)\.rb% do |_, m|
+ files_matching %r%^spec\/#{m[1]}%
+ end
+
+ add_mapping %r%^spec/(spec_helper|shared/.*)\.rb$% do
+ all_specs
+ end
+
+ # Changing a spec will cause it to run itself
+ add_mapping %r%^spec/.*\.rb$% do |filename, _|
+ filename
+ end
+
+ # Any change to a model will cause it's corresponding test to be run
+ add_mapping %r%^app/models/(.*)\.rb$% do |_, m|
+ spec_for(m[1], 'model')
+ end
+
+ # Any change to global_helpers will result in all view and controller
+ # tests being run
+ add_mapping %r%^app/helpers/global_helpers\.rb% do
+ files_matching %r%^spec/(views|controllers|helpers)/.*_spec\.rb$%
+ end
+
+ # Any change to a helper will cause its spec to be run
+ add_mapping %r%^app/helpers/((.*)_helper(s)?)\.rb% do |_, m|
+ spec_for(m[1], 'helper')
+ end
+
+ # Changes to a view cause its spec to be run
+ add_mapping %r%^app/views/(.*)/% do |_, m|
+ spec_for(m[1], 'view')
+ end
+
+ # Changes to a controller result in its corresponding spec being run. If
+ # the controller is the exception or application controller, all
+ # controller specs are run.
+ add_mapping %r%^app/controllers/(.*)\.rb$% do |_, m|
+ if ["application", "exception"].include?(m[1])
+ files_matching %r%^spec/controllers/.*_spec\.rb$%
+ else
+ spec_for(m[1], 'controller')
+ end
+ end
+
+ # If a change is made to the router, run controller, view and helper specs
+ add_mapping %r%^config/router.rb$% do
+ files_matching %r%^spec/(controllers|views|helpers)/.*_spec\.rb$%
+ end
+
+ # If any of the major files governing the environment are altered, run
+ # everything
+ add_mapping %r%^config/(init|rack|environments/test).*\.rb|database\.yml% do
+ all_specs
+ end
+ end
+
+ def failed_results(results)
+ results.scan(/^\d+\)\n(?:\e\[\d*m)?(?:.*?Error in )?'([^\n]*)'(?: FAILED)?(?:\e\[\d*m)?\n(.*?)\n\n/m)
+ end
+
+ def handle_results(results)
+ @failures = failed_results(results)
+ @files_to_test = consolidate_failures(@failures)
+ @files_to_test.empty? && !$TESTING ? hook(:green) : hook(:red)
+ @tainted = !@files_to_test.empty?
+ end
+
+ def consolidate_failures(failed)
+ filters = Hash.new { |h,k| h[k] = [] }
+ failed.each do |spec, failed_trace|
+ if f = test_files_for(failed).find { |f| f =~ /spec\// }
+ filters[f] << spec
+ break
+ end
+ end
+ filters
+ end
+
+ def make_test_cmd(specs_to_runs)
+ [
+ ruby,
+ "-S",
+ spec_command,
+ add_options_if_present,
+ files_to_test.keys.flatten.join(' ')
+ ].join(' ')
+ end
+
+ def add_options_if_present
+ File.exist?("spec/spec.opts") ? "-O spec/spec.opts " : ""
+ end
+
+ # Finds the proper spec command to use. Precendence is set in the
+ # lazily-evaluated method spec_commands. Alias + Override that in
+ # ~/.autotest to provide a different spec command then the default
+ # paths provided.
+ def spec_command(separator=File::ALT_SEPARATOR)
+ unless defined?(@spec_command)
+ @spec_command = spec_commands.find { |cmd| File.exists?(cmd) }
+
+ raise RspecCommandError, "No spec command could be found" unless @spec_command
+
+ @spec_command.gsub!(File::SEPARATOR, separator) if separator
+ end
+ @spec_command
+ end
+
+ # Autotest will look for spec commands in the following
+ # locations, in this order:
+ #
+ # * default spec bin/loader installed in Rubygems
+ # * any spec command found in PATH
+ def spec_commands
+ [File.join(Config::CONFIG['bindir'], 'spec'), 'spec']
+ end
+
+private
+
+ # Runs +files_matching+ for all specs
+ def all_specs
+ files_matching %r%^spec/.*_spec\.rb$%
+ end
+
+ # Generates a path to some spec given its kind and the match from a mapping
+ #
+ # ==== Arguments
+ # match<String>:: the match from a mapping
+ # kind<String>:: the kind of spec that the match represents
+ #
+ # ==== Returns
+ # String
+ #
+ # ==== Example
+ # > spec_for('post', :view')
+ # => "spec/views/post_spec.rb"
+ def spec_for(match, kind)
+ File.join("spec", kind + 's', "#{match}_spec.rb")
+ end
+end
View
31 bin/autospec
@@ -0,0 +1,31 @@
+#!/usr/bin/env ruby
+#
+# This file was generated by Merb's GemManagement
+#
+# The application 'rspec' is installed as part of a gem, and
+# this file is here to facilitate running it.
+
+begin
+ require 'minigems'
+rescue LoadError
+ require 'rubygems'
+end
+
+# use gems dir if ../gems exists - eg. only for ./bin/autospec
+if File.directory?(gems_dir = File.join(File.dirname(__FILE__), '..', 'gems'))
+ $BUNDLE = true; Gem.clear_paths; Gem.path.replace([File.expand_path(gems_dir)])
+ ENV["PATH"] = "#{File.dirname(__FILE__)}:#{gems_dir}/bin:#{ENV["PATH"]}"
+ if (local_gem = Dir[File.join(gems_dir, "specifications", "rspec-*.gemspec")].last)
+ version = File.basename(local_gem)[/-([\.\d]+)\.gemspec$/, 1]
+ end
+end
+
+version ||= ">= 0"
+
+if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
+ version = $1
+ ARGV.shift
+end
+
+gem 'rspec', version
+load 'autospec'
View
31 bin/edit_json.rb
@@ -0,0 +1,31 @@
+#!/usr/bin/env ruby
+#
+# This file was generated by Merb's GemManagement
+#
+# The application 'json_pure' is installed as part of a gem, and
+# this file is here to facilitate running it.
+
+begin
+ require 'minigems'
+rescue LoadError
+ require 'rubygems'
+end
+
+# use gems dir if ../gems exists - eg. only for ./bin/edit_json.rb
+if File.directory?(gems_dir = File.join(File.dirname(__FILE__), '..', 'gems'))
+ $BUNDLE = true; Gem.clear_paths; Gem.path.replace([File.expand_path(gems_dir)])
+ ENV["PATH"] = "#{File.dirname(__FILE__)}:#{gems_dir}/bin:#{ENV["PATH"]}"
+ if (local_gem = Dir[File.join(gems_dir, "specifications", "json_pure-*.gemspec")].last)
+ version = File.basename(local_gem)[/-([\.\d]+)\.gemspec$/, 1]
+ end
+end
+
+version ||= ">= 0"
+
+if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
+ version = $1
+ ARGV.shift
+end
+
+gem 'json_pure', version
+load 'edit_json.rb'
View
31 bin/erubis
@@ -0,0 +1,31 @@
+#!/usr/bin/env ruby
+#
+# This file was generated by Merb's GemManagement
+#
+# The application 'erubis' is installed as part of a gem, and
+# this file is here to facilitate running it.
+
+begin
+ require 'minigems'
+rescue LoadError
+ require 'rubygems'
+end
+
+# use gems dir if ../gems exists - eg. only for ./bin/erubis
+if File.directory?(gems_dir = File.join(File.dirname(__FILE__), '..', 'gems'))
+ $BUNDLE = true; Gem.clear_paths; Gem.path.replace([File.expand_path(gems_dir)])
+ ENV["PATH"] = "#{File.dirname(__FILE__)}:#{gems_dir}/bin:#{ENV["PATH"]}"
+ if (local_gem = Dir[File.join(gems_dir, "specifications", "erubis-*.gemspec")].last)
+ version = File.basename(local_gem)[/-([\.\d]+)\.gemspec$/, 1]
+ end
+end
+
+version ||= ">= 0"
+
+if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
+ version = $1
+ ARGV.shift
+end
+
+gem 'erubis', version
+load 'erubis'
View
31 bin/gpgen
@@ -0,0 +1,31 @@
+#!/usr/bin/env ruby
+#
+# This file was generated by Merb's GemManagement
+#
+# The application 'gem_plugin' is installed as part of a gem, and
+# this file is here to facilitate running it.
+
+begin
+ require 'minigems'
+rescue LoadError
+ require 'rubygems'
+end
+
+# use gems dir if ../gems exists - eg. only for ./bin/gpgen
+if File.directory?(gems_dir = File.join(File.dirname(__FILE__), '..', 'gems'))
+ $BUNDLE = true; Gem.clear_paths; Gem.path.replace([File.expand_path(gems_dir)])
+ ENV["PATH"] = "#{File.dirname(__FILE__)}:#{gems_dir}/bin:#{ENV["PATH"]}"
+ if (local_gem = Dir[File.join(gems_dir, "specifications", "gem_plugin-*.gemspec")].last)
+ version = File.basename(local_gem)[/-([\.\d]+)\.gemspec$/, 1]
+ end
+end
+
+version ||= ">= 0"
+
+if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
+ version = $1
+ ARGV.shift
+end
+
+gem 'gem_plugin', version
+load 'gpgen'
View
31 bin/maruku
@@ -0,0 +1,31 @@
+#!/usr/bin/env ruby
+#
+# This file was generated by Merb's GemManagement
+#
+# The application 'maruku' is installed as part of a gem, and
+# this file is here to facilitate running it.
+
+begin
+ require 'minigems'
+rescue LoadError
+ require 'rubygems'
+end
+
+# use gems dir if ../gems exists - eg. only for ./bin/maruku
+if File.directory?(gems_dir = File.join(File.dirname(__FILE__), '..', 'gems'))
+ $BUNDLE = true; Gem.clear_paths; Gem.path.replace([File.expand_path(gems_dir)])
+ ENV["PATH"] = "#{File.dirname(__FILE__)}:#{gems_dir}/bin:#{ENV["PATH"]}"
+ if (local_gem = Dir[File.join(gems_dir, "specifications", "maruku-*.gemspec")].last)
+ version = File.basename(local_gem)[/-([\.\d]+)\.gemspec$/, 1]
+ end
+end
+
+version ||= ">= 0"
+
+if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
+ version = $1
+ ARGV.shift
+end
+
+gem 'maruku', version
+load 'maruku'
View
31 bin/marutex
@@ -0,0 +1,31 @@
+#!/usr/bin/env ruby
+#
+# This file was generated by Merb's GemManagement
+#
+# The application 'maruku' is installed as part of a gem, and
+# this file is here to facilitate running it.
+
+begin
+ require 'minigems'
+rescue LoadError
+ require 'rubygems'
+end
+
+# use gems dir if ../gems exists - eg. only for ./bin/marutex
+if File.directory?(gems_dir = File.join(File.dirname(__FILE__), '..', 'gems'))
+ $BUNDLE = true; Gem.clear_paths; Gem.path.replace([File.expand_path(gems_dir)])
+ ENV["PATH"] = "#{File.dirname(__FILE__)}:#{gems_dir}/bin:#{ENV["PATH"]}"
+ if (local_gem = Dir[File.join(gems_dir, "specifications", "maruku-*.gemspec")].last)
+ version = File.basename(local_gem)[/-([\.\d]+)\.gemspec$/, 1]
+ end
+end
+
+version ||= ">= 0"
+
+if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
+ version = $1
+ ARGV.shift
+end
+
+gem 'maruku', version
+load 'marutex'
View
31 bin/merb
@@ -0,0 +1,31 @@
+#!/usr/bin/env ruby
+#
+# This file was generated by Merb's GemManagement
+#
+# The application 'merb-core' is installed as part of a gem, and
+# this file is here to facilitate running it.
+
+begin
+ require 'minigems'
+rescue LoadError
+ require 'rubygems'
+end
+
+# use gems dir if ../gems exists - eg. only for ./bin/merb
+if File.directory?(gems_dir = File.join(File.dirname(__FILE__), '..', 'gems'))
+ $BUNDLE = true; Gem.clear_paths; Gem.path.replace([File.expand_path(gems_dir)])
+ ENV["PATH"] = "#{File.dirname(__FILE__)}:#{gems_dir}/bin:#{ENV["PATH"]}"
+ if (local_gem = Dir[File.join(gems_dir, "specifications", "merb-core-*.gemspec")].last)
+ version = File.basename(local_gem)[/-([\.\d]+)\.gemspec$/, 1]
+ end
+end
+
+version ||= ">= 0"
+
+if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
+ version = $1
+ ARGV.shift
+end
+
+gem 'merb-core', version
+load 'merb'
View
31 bin/mongrel_rails
@@ -0,0 +1,31 @@
+#!/usr/bin/env ruby
+#
+# This file was generated by Merb's GemManagement
+#
+# The application 'mongrel' is installed as part of a gem, and
+# this file is here to facilitate running it.
+
+begin
+ require 'minigems'
+rescue LoadError
+ require 'rubygems'
+end
+
+# use gems dir if ../gems exists - eg. only for ./bin/mongrel_rails
+if File.directory?(gems_dir = File.join(File.dirname(__FILE__), '..', 'gems'))
+ $BUNDLE = true; Gem.clear_paths; Gem.path.replace([File.expand_path(gems_dir)])
+ ENV["PATH"] = "#{File.dirname(__FILE__)}:#{gems_dir}/bin:#{ENV["PATH"]}"
+ if (local_gem = Dir[File.join(gems_dir, "specifications", "mongrel-*.gemspec")].last)
+ version = File.basename(local_gem)[/-([\.\d]+)\.gemspec$/, 1]
+ end
+end
+
+version ||= ">= 0"
+
+if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
+ version = $1
+ ARGV.shift
+end
+
+gem 'mongrel', version
+load 'mongrel_rails'
View
31 bin/rackup
@@ -0,0 +1,31 @@
+#!/usr/bin/env ruby
+#
+# This file was generated by Merb's GemManagement
+#
+# The application 'rack' is installed as part of a gem, and
+# this file is here to facilitate running it.
+
+begin
+ require 'minigems'
+rescue LoadError
+ require 'rubygems'
+end
+
+# use gems dir if ../gems exists - eg. only for ./bin/rackup
+if File.directory?(gems_dir = File.join(File.dirname(__FILE__), '..', 'gems'))
+ $BUNDLE = true; Gem.clear_paths; Gem.path.replace([File.expand_path(gems_dir)])
+ ENV["PATH"] = "#{File.dirname(__FILE__)}:#{gems_dir}/bin:#{ENV["PATH"]}"
+ if (local_gem = Dir[File.join(gems_dir, "specifications", "rack-*.gemspec")].last)
+ version = File.basename(local_gem)[/-([\.\d]+)\.gemspec$/, 1]
+ end
+end
+
+version ||= ">= 0"
+
+if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
+ version = $1
+ ARGV.shift
+end
+
+gem 'rack', version
+load 'rackup'
View
31 bin/rake
@@ -0,0 +1,31 @@
+#!/usr/bin/env ruby
+#
+# This file was generated by Merb's GemManagement
+#
+# The application 'rake' is installed as part of a gem, and
+# this file is here to facilitate running it.
+
+begin
+ require 'minigems'
+rescue LoadError
+ require 'rubygems'
+end
+
+# use gems dir if ../gems exists - eg. only for ./bin/rake
+if File.directory?(gems_dir = File.join(File.dirname(__FILE__), '..', 'gems'))
+ $BUNDLE = true; Gem.clear_paths; Gem.path.replace([File.expand_path(gems_dir)])
+ ENV["PATH"] = "#{File.dirname(__FILE__)}:#{gems_dir}/bin:#{ENV["PATH"]}"
+ if (local_gem = Dir[File.join(gems_dir, "specifications", "rake-*.gemspec")].last)
+ version = File.basename(local_gem)[/-([\.\d]+)\.gemspec$/, 1]
+ end
+end
+
+version ||= ">= 0"
+
+if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
+ version = $1
+ ARGV.shift
+end
+
+gem 'rake', version
+load 'rake'
View
31 bin/rake2thor
@@ -0,0 +1,31 @@
+#!/usr/bin/env ruby
+#
+# This file was generated by Merb's GemManagement
+#
+# The application 'thor' is installed as part of a gem, and
+# this file is here to facilitate running it.
+
+begin
+ require 'minigems'
+rescue LoadError
+ require 'rubygems'
+end
+
+# use gems dir if ../gems exists - eg. only for ./bin/rake2thor
+if File.directory?(gems_dir = File.join(File.dirname(__FILE__), '..', 'gems'))
+ $BUNDLE = true; Gem.clear_paths; Gem.path.replace([File.expand_path(gems_dir)])
+ ENV["PATH"] = "#{File.dirname(__FILE__)}:#{gems_dir}/bin:#{ENV["PATH"]}"
+ if (local_gem = Dir[File.join(gems_dir, "specifications", "thor-*.gemspec")].last)
+ version = File.basename(local_gem)[/-([\.\d]+)\.gemspec$/, 1]
+ end
+end
+
+version ||= ">= 0"
+
+if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
+ version = $1
+ ARGV.shift
+end
+
+gem 'thor', version
+load 'rake2thor'
View
31 bin/spec
@@ -0,0 +1,31 @@
+#!/usr/bin/env ruby
+#
+# This file was generated by Merb's GemManagement
+#
+# The application 'rspec' is installed as part of a gem, and
+# this file is here to facilitate running it.
+
+begin
+ require 'minigems'
+rescue LoadError
+ require 'rubygems'
+end
+
+# use gems dir if ../gems exists - eg. only for ./bin/spec
+if File.directory?(gems_dir = File.join(File.dirname(__FILE__), '..', 'gems'))
+ $BUNDLE = true; Gem.clear_paths; Gem.path.replace([File.expand_path(gems_dir)])
+ ENV["PATH"] = "#{File.dirname(__FILE__)}:#{gems_dir}/bin:#{ENV["PATH"]}"
+ if (local_gem = Dir[File.join(gems_dir, "specifications", "rspec-*.gemspec")].last)
+ version = File.basename(local_gem)[/-([\.\d]+)\.gemspec$/, 1]
+ end
+end
+
+version ||= ">= 0"
+
+if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
+ version = $1
+ ARGV.shift
+end
+
+gem 'rspec', version
+load 'spec'
View
31 bin/thor
@@ -0,0 +1,31 @@
+#!/usr/bin/env ruby
+#
+# This file was generated by Merb's GemManagement
+#
+# The application 'thor' is installed as part of a gem, and
+# this file is here to facilitate running it.
+
+begin
+ require 'minigems'
+rescue LoadError
+ require 'rubygems'
+end
+
+# use gems dir if ../gems exists - eg. only for ./bin/thor
+if File.directory?(gems_dir = File.join(File.dirname(__FILE__), '..', 'gems'))
+ $BUNDLE = true; Gem.clear_paths; Gem.path.replace([File.expand_path(gems_dir)])
+ ENV["PATH"] = "#{File.dirname(__FILE__)}:#{gems_dir}/bin:#{ENV["PATH"]}"
+ if (local_gem = Dir[File.join(gems_dir, "specifications", "thor-*.gemspec")].last)
+ version = File.basename(local_gem)[/-([\.\d]+)\.gemspec$/, 1]
+ end
+end
+
+version ||= ">= 0"
+
+if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
+ version = $1
+ ARGV.shift
+end
+
+gem 'thor', version
+load 'thor'
View
3 book-content/0-front-matter/0-foreword.markdown
@@ -0,0 +1,3 @@
+## Foreword
+
+By Yehuda Katz, Merb lead developer
View
21 book-content/0-front-matter/1-preface.markdown
@@ -0,0 +1,21 @@
+# Preface
+
+Back when there were no Merb stack, no locked API, no organized documentation and the code base was changing often [Matthew Ford](http://github.com/deimos1986) decided to work on an open source book with a simple goal: help courageous Rubyists who wanted to live on the edge and decided to try Merb, DataMapper and RSpec.
+
+Since then, Merb matured a lot, getting up and running became way easier and Merb decided to offer an **opionated** version of the stack using Merb, DataMapper and RSpec!
+
+As an early Merb developer myself, I really enjoyed having access to Matt's book and I even contributed to the early version of book. Since then, I joined the Merb team and with the rest of the I focused on the big 1.0 release which we did successfully in two steps, the first Release Candidate during [MerbCamp 2008](http://merbcamp.com) and then 1.0 final during [RubyConf 2008](http://rubyconf.org).
+
+Having a great framework is great, but has Jason Seifer from [RailsEnvy.com](http://railsenvy.com) said to me after 1.0 was released:
+
+> Merb has some awesome code documentation, the code is so easy to read, but the user documentation is still lacking.
+
+Jason was right, our user documentation was not that great. Of course we have the [wiki](http://wiki.merbivore.com) and a bunch of books available out there but I have to say that looking at other frameworks such as [Django](http://www.djangobook.com/) the entire core team knew we had to do something.
+
+Ford's efforts were noticed early on and he was offered a deal to write a [Merb book for Apress](http://www.apress.com/book/view/9781430218234). As you can imagine, it became a bit hard for Matt to write a book for a publisher, have a full time job, contribute to various Open Source projects, keep up with Merb and finally make sure the open source book he started a while back would stay up to date was pretty challenging. On top of that, many things changed pre 1.0 and a good part of the existing content needed a full rewrite.
+
+That's why the Merb team decided it was time to get involved and to get the community focused on a centralized, user oriented book.
+
+I am really glad that the Merb team decided to take responsibility for this book and that we will make sure it stays well organized, up to date and relevant. However, this book, very much like Merb, won't grow and evolve much without the community help. So, please, send us corrections, new examples, new chapters so this book can help the community has a whole.
+
+[Matt Aimonetti](http://merbist.com), Merb Core team member
View
9 book-content/0-front-matter/2-contributors.markdown
@@ -0,0 +1,9 @@
+# Contributors
+
+Without the following contributors this book would not have possible. Thank you all!
+
+**In alphabetical order:**
+
+* [Matt Aimonetti](http://merbist.com)
+* [Matthew Ford](http://github.com/deimos1986)
+* [Yehuda Katz](http://yehudakatz.com)
View
47 book-content/1-introduction/1-ruby.markdown
@@ -0,0 +1,47 @@
+## Ruby
+
+![Ruby](/images/ruby-header.gif){: .no-border}
+
+
+**Reference website:** [http://ruby-lang.org](http://ruby-lang.org){: .reference}
+
+> Coding in Ruby makes me happy because it’s one of the shortest paths between my brain and a computer. I can think of something and I can express it very succinctly and typically fairly elegantly in Ruby without all the kind of extraneous fluff that you need in most other languages.
+> [Dave Thomas, author of "Programming Ruby"](http://pragdave.pragprog.com/){: .quote-author}
+{: cite=http://www.infoq.com/interviews/ruby-rails-dave-thomas .lead-quote}
+
+It would be a crime to start talking about the Merb framework without first discussing the very reason why Merb is so flexible, powerful and fast: **Ruby**.
+
+### Origin ###{: #origin}
+![Yukihiro Matsumoto](/images/Yukihiro_Matsumoto.jpg){: .left}
+Ruby is an open source dynamic, reflective, general purpose object-oriented programming language written in the mid-1990s by Japanese software architect [Yukihiro "Matz" Matsumoto-san ( まつもとゆきひろ)](http://en.wikipedia.org/wiki/Yukihiro_Matsumoto).
+
+Ruby has focuses on simplicity and productivity. It has an elegant syntax that is natural to read and easy to write.
+
+Matz borrowed ideas and idions from some of his favorite programming languages (Perl, Smalltalk, Eiffel, Ada, and Lisp) to form a new language that balanced functional programming with imperative programming.
+
+The result is an attractive language which feels very natural. In the Ruby community we often refer to the POLS, Principle of Least Surprise. The concept behind this principle is very simple, if you know a minimum of Ruby, you should not be surprised by how the language will react.
+
+### Adoption ###{: #adoption}
+According to the [TIOBE index](http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html), Ruby ranks in the top 10 programming languages used worldwide. Much of the growth is attributed to the popularity of software written in Ruby, particularly the [Ruby on Rails web framework](http://rubyonrails.org).
+
+### Key Elements of the language ###{: #key-elements}
+
+> I wanted a scripting language that was more powerful than Perl, and more object-oriented than Python
+> Matz
+{: cite=http://www.linuxdevcenter.com/pub/a/linux/2001/11/29/ruby.html}
+
+* Everything is an object
+* Everything can be extended/modified
+* High code readability
+
+To learn more about the Ruby language, check the [Official Ruby language website](http://www.ruby-lang.org/en/about)
+
+### Merb and Ruby ###{: #merb-and-ruby}
+
+Merb tries to say as close as possible to the Ruby language itself. That's why it's important to understand what people call the "ruby Way".
+
+During RubyConf 2008, Matz made a comment about Merb:
+
+> Merb has a bright future for the people who are not satisfied by the fixed ways in Rails. I think that Merb will give users more freedom in a Ruby-ish way of programming
+> [Matz, Author of the Ruby programming language](http://ruby-lang.org/){: .quote-author}
+{: cite=http://merbist.com/2008/11/09/merb-1-0-released/}
View
1 book-content/1-introduction/2-merb.markdown
@@ -0,0 +1 @@
+## Merb
View
1 book-content/1-introduction/3-datamapper.markdown
@@ -0,0 +1 @@
+## DataMapper
View
1 book-content/1-introduction/4-rspec.markdown
@@ -0,0 +1 @@
+## RSpec
View
13 config/dependencies.yml
@@ -0,0 +1,13 @@
+---
+- merb-core (= 1.0.3, runtime)
+- extlib (>= 0.9.8, runtime)
+- erubis (>= 2.6.2, runtime)
+- rake (>= 0, runtime)
+- json_pure (>= 0, runtime)
+- rspec (>= 0, runtime)
+- rack (>= 0, runtime)
+- mime-types (>= 0, runtime)
+- thor (>= 0.9.7, runtime)
+- webrat (>= 0.3.1, development)
+- maruku (>= 0.5.9, runtime)
+- merb-assets (= 1.0.3, runtime)
View
15 config/environments/development.rb
@@ -0,0 +1,15 @@
+Merb.logger.info("Loaded DEVELOPMENT Environment...")
+Merb::Config.use { |c|
+ c[:exception_details] = true
+ c[:reload_templates] = true
+ c[:reload_classes] = true
+ c[:reload_time] = 0.5
+ c[:ignore_tampered_cookies] = true
+ c[:log_auto_flush ] = true
+ c[:log_level] = :debug
+
+ c[:log_stream] = STDOUT
+ c[:log_file] = nil
+ # Or redirect logging into a file:
+ # c[:log_file] = Merb.root / "log" / "development.log"
+}
View
10 config/environments/production.rb
@@ -0,0 +1,10 @@
+Merb.logger.info("Loaded PRODUCTION Environment...")
+Merb::Config.use { |c|
+ c[:exception_details] = false
+ c[:reload_classes] = false
+ c[:log_level] = :error
+
+ c[:log_file] = Merb.root / "log" / "production.log"
+ # or redirect logger using IO handle
+ # c[:log_stream] = STDOUT
+}
View
11 config/environments/rake.rb
@@ -0,0 +1,11 @@
+Merb.logger.info("Loaded RAKE Environment...")
+Merb::Config.use { |c|
+ c[:exception_details] = true
+ c[:reload_classes] = false
+ c[:log_auto_flush ] = true
+
+ c[:log_stream] = STDOUT
+ c[:log_file] = nil
+ # Or redirect logging into a file:
+ # c[:log_file] = Merb.root / "log" / "development.log"
+}
View
10 config/environments/staging.rb
@@ -0,0 +1,10 @@
+Merb.logger.info("Loaded STAGING Environment...")
+Merb::Config.use { |c|
+ c[:exception_details] = false
+ c[:reload_classes] = false
+ c[:log_level] = :error
+
+ c[:log_file] = Merb.root / "log" / "staging.log"
+ # or redirect logger using IO handle
+ # c[:log_stream] = STDOUT
+}
View
12 config/environments/test.rb
@@ -0,0 +1,12 @@
+Merb.logger.info("Loaded TEST Environment...")
+Merb::Config.use { |c|
+ c[:testing] = true
+ c[:exception_details] = true
+ c[:log_auto_flush ] = true
+ # log less in testing environment
+ c[:log_level] = :error
+
+ #c[:log_file] = Merb.root / "log" / "test.log"
+ # or redirect logger using IO handle
+ c[:log_stream] = STDOUT
+}
View
51 config/init.rb
@@ -0,0 +1,51 @@
+# Go to http://wiki.merbivore.com/pages/init-rb
+
+# Specify a specific version of a dependency
+# dependency "RedCloth", "> 3.0"
+
+dependency "maruku", ">= 0.5.9"
+dependency "merb-assets", "~> 1.0"
+
+# use_orm :none
+use_test :rspec
+use_template_engine :erb
+
+Merb::Config.use do |c|
+ c[:use_mutex] = false
+ #c[:session_store] = 'cookie' # can also be 'memory', 'memcache', 'container', 'datamapper
+
+ # cookie session store configuration
+ #c[:session_secret_key] = '8cea7e76dcb6c428521f22c4943b4e69430a85fa' # required for cookie session store
+ #c[:session_id_key] = '_merb-book_session_id' # cookie session id key, defaults to "_session_id"
+end
+
+Merb::BootLoader.before_app_loads do
+ # This will get executed after dependencies have been loaded but before your app's classes have loaded.
+end
+
+Merb::BootLoader.after_app_loads do
+ # This will get executed after your app's classes have been loaded.
+ MaRuKu::In::Markdown.register_block_extension(
+ :regexp => /<(sidebar|note):".*?">/,
+ :handler => proc do |doc, src, context|
+ first_line = src.shift_line
+ first_line =~ /<(sidebar|note):"(.*?)">/
+ kind, subkind = $1, $2
+ lines = ["<p class='#{kind}_head'>#{$2}</p>"]
+
+ while src.cur_line && src.cur_line !~ /<\/(sidebar|note)>/
+ line = src.shift_line
+ lines.push line
+ end
+
+ src.shift_line
+
+ src = MaRuKu::In::Markdown::BlockLevelParser::LineSource.new(lines)
+ children = doc.parse_blocks(src)
+
+ context.push doc.md_el(:div, children, {}, {:class => "#{kind} #{subkind}"})
+
+ true
+ end
+ )
+end
View
11 config/rack.rb
@@ -0,0 +1,11 @@
+# use PathPrefix Middleware if :path_prefix is set in Merb::Config
+if prefix = ::Merb::Config[:path_prefix]
+ use Merb::Rack::PathPrefix, prefix
+end
+
+# comment this out if you are running merb behind a load balancer
+# that serves static files
+use Merb::Rack::Static, Merb.dir_for(:public)
+
+# this is our main merb application
+run Merb::Rack::Application.new
View
9 config/router.rb
@@ -0,0 +1,9 @@
+Merb.logger.info("Compiling routes...")
+Merb::Router.prepare do
+
+ match('/table-of-content').to(:controller => 'pages', :action => 'index').name(:toc)
+ match("/:chapter(/:page_name)").to(:controller => 'pages', :action => 'show').name(:page)
+ #default_routes
+
+ match('/').to(:controller => 'pages', :action => 'index')
+end
View
1,362 doc/rdoc/generators/merb_generator.rb
@@ -0,0 +1,1362 @@
+
+# # We're responsible for generating all the HTML files
+# from the object tree defined in code_objects.rb. We
+# generate:
+#
+# [files] an html file for each input file given. These
+# input files appear as objects of class
+# TopLevel
+#
+# [classes] an html file for each class or module encountered.
+# These classes are not grouped by file: if a file
+# contains four classes, we'll generate an html
+# file for the file itself, and four html files
+# for the individual classes.
+#
+# Method descriptions appear in whatever entity (file, class,
+# or module) that contains them.
+#
+# We generate files in a structure below a specified subdirectory,
+# normally +doc+.
+#
+# opdir
+# |
+# |___ files
+# | |__ per file summaries
+# |
+# |___ classes
+# |__ per class/module descriptions
+#
+# HTML is generated using the Template class.
+#
+
+require 'ftools'
+
+require 'rdoc/options'
+require 'rdoc/template'
+require 'rdoc/markup/simple_markup'
+require 'rdoc/markup/simple_markup/to_html'
+require 'cgi'
+
+module Generators
+
+ # Name of sub-direcories that hold file and class/module descriptions
+
+ FILE_DIR = "files"
+ CLASS_DIR = "classes"
+ CSS_NAME = "stylesheet.css"
+
+
+ ##
+ # Build a hash of all items that can be cross-referenced.
+ # This is used when we output required and included names:
+ # if the names appear in this hash, we can generate
+ # an html cross reference to the appropriate description.
+ # We also use this when parsing comment blocks: any decorated
+ # words matching an entry in this list are hyperlinked.
+
+ class AllReferences
+ @@refs = {}
+
+ def AllReferences::reset
+ @@refs = {}
+ end
+
+ def AllReferences.add(name, html_class)
+ @@refs[name] = html_class
+ end
+
+ def AllReferences.[](name)
+ @@refs[name]
+ end
+
+ def AllReferences.keys
+ @@refs.keys
+ end
+ end
+
+
+ ##
+ # Subclass of the SM::ToHtml class that supports looking
+ # up words in the AllReferences list. Those that are
+ # found (like AllReferences in this comment) will
+ # be hyperlinked
+
+ class HyperlinkHtml < SM::ToHtml
+ # We need to record the html path of our caller so we can generate
+ # correct relative paths for any hyperlinks that we find
+ def initialize(from_path, context)
+ super()
+ @from_path = from_path
+
+ @parent_name = context.parent_name
+ @parent_name += "::" if @parent_name
+ @context = context
+ end
+
+ # We're invoked when any text matches the CROSSREF pattern
+ # (defined in MarkUp). If we fine the corresponding reference,
+ # generate a hyperlink. If the name we're looking for contains
+ # no punctuation, we look for it up the module/class chain. For
+ # example, HyperlinkHtml is found, even without the Generators::
+ # prefix, because we look for it in module Generators first.
+
+ def handle_special_CROSSREF(special)
+ name = special.text
+ if name[0,1] == '#'
+ lookup = name[1..-1]
+ name = lookup unless Options.instance.show_hash
+ else
+ lookup = name
+ end
+
+ if /([A-Z].*)[.\#](.*)/ =~ lookup
+ container = $1
+ method = $2
+ ref = @context.find_symbol(container, method)
+ else
+ ref = @context.find_symbol(lookup)
+ end
+
+ if ref and ref.document_self
+ "<a href=\"index.html?a=#{ref.aref}&name=#{name}\">#{name}</a>"
+ else
+ name #it does not need to be a link
+ end
+ end
+
+
+ # Generate a hyperlink for url, labeled with text. Handle the
+ # special cases for img: and link: described under handle_special_HYPEDLINK
+ def gen_url(url, text)
+ if url =~ /([A-Za-z]+):(.*)/
+ type = $1
+ path = $2
+ else
+ type = "http"
+ path = url
+ url = "http://#{url}"
+ end
+
+ if type == "link"
+ url = path
+ end
+
+ if (type == "http" || type == "link") && url =~ /\.(gif|png|jpg|jpeg|bmp)$/
+ "<img src=\"#{url}\">"
+ elsif (type == "http" || type == "link")
+ "<a href=\"#{url}\" target=\"_blank\">#{text}</a>"
+ else
+ "<a href=\"#\" onclick=\"jsHref('#{url}');\">#{text.sub(%r{^#{type}:/*}, '')}</a>"
+
+ end
+ end
+
+ # And we're invoked with a potential external hyperlink mailto:
+ # just gets inserted. http: links are checked to see if they
+ # reference an image. If so, that image gets inserted using an
+ # <img> tag. Otherwise a conventional <a href> is used. We also
+ # support a special type of hyperlink, link:, which is a reference
+ # to a local file whose path is relative to the --op directory.
+
+ def handle_special_HYPERLINK(special)
+ url = special.text
+ gen_url(url, url)
+ end
+
+ # HEre's a hypedlink where the label is different to the URL
+ # <label>[url]
+ #
+
+ def handle_special_TIDYLINK(special)
+ text = special.text
+ # unless text =~ /(\S+)\[(.*?)\]/
+ unless text =~ /\{(.*?)\}\[(.*?)\]/ or text =~ /(\S+)\[(.*?)\]/
+ return text
+ end
+ label = $1
+ url = $2
+ gen_url(url, label)
+ end
+
+ end
+
+
+
+ #####################################################################
+ #
+ # Handle common markup tasks for the various Html classes
+ #
+
+ module MarkUp
+
+ # Convert a string in markup format into HTML. We keep a cached
+ # SimpleMarkup object lying around after the first time we're
+ # called per object.
+
+ def markup(str, remove_para=false)
+ return '' unless str
+ unless defined? @markup
+ @markup = SM::SimpleMarkup.new
+
+ # class names, variable names, file names, or instance variables
+ @markup.add_special(/(
+ \b([A-Z]\w*(::\w+)*[.\#]\w+) # A::B.meth
+ | \b([A-Z]\w+(::\w+)*) # A::B..
+ | \#\w+[!?=]? # #meth_name
+ | \b\w+([_\/\.]+\w+)+[!?=]? # meth_name
+ )/x, :CROSSREF)
+
+ # external hyperlinks
+ @markup.add_special(/((link:|https?:|mailto:|ftp:|www\.)\S+\w)/, :HYPERLINK)
+
+ # and links of the form <text>[<url>]
+ @markup.add_special(/(((\{.*?\})|\b\S+?)\[\S+?\.\S+?\])/, :TIDYLINK)
+ # @markup.add_special(/\b(\S+?\[\S+?\.\S+?\])/, :TIDYLINK)
+
+ end
+ unless defined? @html_formatter
+ @html_formatter = HyperlinkHtml.new(self.path, self)
+ end
+
+ # Convert leading comment markers to spaces, but only
+ # if all non-blank lines have them
+
+ if str =~ /^(?>\s*)[^\#]/
+ content = str
+ else
+ content = str.gsub(/^\s*(#+)/) { $1.tr('#',' ') }
+ end
+
+ res = @markup.convert(content, @html_formatter)
+ if remove_para
+ res.sub!(/^<p>/, '')
+ res.sub!(/<\/p>$/, '')
+ end
+ res
+ end
+
+
+ def style_url(path, css_name=nil)
+ css_name ||= CSS_NAME
+ end
+
+ # Build a webcvs URL with the given 'url' argument. URLs with a '%s' in them
+ # get the file's path sprintfed into them; otherwise they're just catenated
+ # together.
+
+ def cvs_url(url, full_path)
+ if /%s/ =~ url
+ return sprintf( url, full_path )
+ else
+ return url + full_path
+ end
+ end
+ end
+
+
+ #####################################################################
+ #
+ # A Context is built by the parser to represent a container: contexts
+ # hold classes, modules, methods, require lists and include lists.
+ # ClassModule and TopLevel are the context objects we process here
+ #
+ class ContextUser
+
+ include MarkUp
+
+ attr_reader :context
+
+ def initialize(context, options)
+ @context = context
+ @options = options
+
+ end
+
+ # convenience method to build a hyperlink # Where's the DRY in this?? Put this in the template where it belongs
+ def href(link, cls, name)
+ %{"<a href=\"#\" onclick=\"jsHref('#{link}');\" class=\"#{cls}\">#{name}</a>"}
+ end
+
+ # Create a list of HtmlMethod objects for each method
+ # in the corresponding context object. If the @options.show_all
+ # variable is set (corresponding to the <tt>--all</tt> option,
+ # we include all methods, otherwise just the public ones.
+
+ def collect_methods
+ list = @context.method_list
+ unless @options.show_all
+ list = list.find_all {|m| m.visibility == :public || m.visibility == :protected || m.force_documentation }
+ end
+ @methods = list.collect {|m| HtmlMethod.new(m, self, @options) }
+ end
+
+ # Build a summary list of all the methods in this context
+ def build_method_summary_list(path_prefix="")
+ collect_methods unless @methods
+ meths = @methods.sort
+ res = []
+ meths.each do |meth|
+ res << {
+ "name" => CGI.escapeHTML(meth.name),
+ "aref" => meth.aref,
+ "href" => meth.path
+ }
+ end
+ res
+ end
+
+
+ # Build a list of aliases for which we couldn't find a
+ # corresponding method
+ def build_alias_summary_list(section)
+ values = []
+ @context.aliases.each do |al|
+ next unless al.section == section
+ res = {
+ 'old_name' => al.old_name,
+ 'new_name' => al.new_name,
+ }
+ if al.comment && !al.comment.empty?
+ res['desc'] = markup(al.comment, true)
+ end
+ values << res
+ end
+ values
+ end
+
+ # Build a list of constants
+ def build_constants_summary_list(section)
+ values = []
+ @context.constants.each do |co|
+ next unless co.section == section
+ res = {
+ 'name' => co.name,
+ 'value' => CGI.escapeHTML(co.value)
+ }
+ res['desc'] = markup(co.comment, true) if co.comment && !co.comment.empty?
+ values << res
+ end
+ values
+ end
+
+ def build_requires_list(context)
+ potentially_referenced_list(context.requires) {|fn| [fn + ".rb"] }
+ end
+
+ def build_include_list(context)
+ potentially_referenced_list(context.includes)
+ end
+
+ # Build a list from an array of <i>Htmlxxx</i> items. Look up each
+ # in the AllReferences hash: if we find a corresponding entry,
+ # we generate a hyperlink to it, otherwise just output the name.
+ # However, some names potentially need massaging. For example,
+ # you may require a Ruby file without the .rb extension,
+ # but the file names we know about may have it. To deal with
+ # this, we pass in a block which performs the massaging,
+ # returning an array of alternative names to match
+
+ def potentially_referenced_list(array)
+ res = []
+ array.each do |i|
+ ref = AllReferences[i.name]
+ # if !ref
+ # container = @context.parent
+ # while !ref && container
+ # name = container.name + "::" + i.name
+ # ref = AllReferences[name]
+ # container = container.parent
+ # end
+ # end
+
+ ref = @context.find_symbol(i.name)
+ ref = ref.viewer if ref
+
+ if !ref && block_given?
+ possibles = yield(i.name)
+ while !ref and !possibles.empty?
+ ref = AllReferences[possibles.shift]
+ end
+ end
+ h_name = CGI.escapeHTML(i.name)
+ if ref and ref.document_self
+ path = ref.path
+ res << { "name" => h_name, "href" => path }
+ else
+ res << { "name" => h_name, "href" => "" }
+ end
+ end
+ res
+ end
+
+ # Build an array of arrays of method details. The outer array has up
+ # to six entries, public, private, and protected for both class
+ # methods, the other for instance methods. The inner arrays contain
+ # a hash for each method
+
+ def build_method_detail_list(section)
+ outer = []
+
+ methods = @methods.sort
+ for singleton in [true, false]
+ for vis in [ :public, :protected, :private ]
+ res = []
+ methods.each do |m|
+ if m.section == section and
+ m.document_self and
+ m.visibility == vis and
+ m.singleton == singleton
+ row = {}
+ if m.call_seq
+ row["callseq"] = m.call_seq.gsub(/->/, '&rarr;')
+ else
+ row["name"] = CGI.escapeHTML(m.name)
+ row["params"] = m.params
+ end
+ desc = m.description.strip
+ row["m_desc"] = desc unless desc.empty?
+ row["aref"] = m.aref
+ row["href"] = m.path
+ row["m_seq"] = m.seq
+ row["visibility"] = m.visibility.to_s
+
+ alias_names = []
+ m.aliases.each do |other|
+ if other.viewer # won't be if the alias is private
+ alias_names << {
+ 'name' => other.name,
+ 'href' => other.viewer.path,
+ 'aref' => other.viewer.aref
+ }
+ end
+ end
+ unless alias_names.empty?
+ row["aka"] = alias_names
+ end
+
+ #if @options.inline_source
+ code = m.source_code
+ row["sourcecode"] = code if code
+ #else
+ # code = m.src_url
+ #if code
+ # row["codeurl"] = code
+ # row["imgurl"] = m.img_url
+ #end
+ #end
+ res << row
+ end
+ end
+ if res.size > 0
+ outer << {
+ "type" => vis.to_s.capitalize,
+ "category" => singleton ? "Class" : "Instance",
+ "methods" => res
+ }
+ end
+ end
+ end
+ outer
+ end
+
+ # Build the structured list of classes and modules contained
+ # in this context.
+
+ def build_class_list(level, from, section, infile=nil)
+ res = ""
+ prefix = "&nbsp;&nbsp;::" * level;
+
+ from.modules.sort.each do |mod|
+ next unless mod.section == section
+ next if infile && !mod.defined_in?(infile)
+ if mod.document_self
+ res <<
+ prefix <<
+ "Module " <<
+ href(mod.viewer.path, "link", mod.full_name) <<
+ "<br />\n" <<
+ build_class_list(level + 1, mod, section, infile)
+ end
+ end
+
+ from.classes.sort.each do |cls|
+ next unless cls.section == section
+ next if infile && !cls.defined_in?(infile)
+ if cls.document_self
+ res <<
+ prefix <<
+ "Class " <<
+ href(cls.viewer.path, "link", cls.full_name) <<
+ "<br />\n" <<
+ build_class_list(level + 1, cls, section, infile)
+ end
+ end
+
+ res
+ end
+
+ def document_self
+ @context.document_self
+ end
+
+ def diagram_reference(diagram)
+ res = diagram.gsub(/((?:src|href)=")(.*?)"/) {
+ $1 + $2 + '"'
+ }
+ res
+ end
+
+
+ # Find a symbol in ourselves or our parent
+ def find_symbol(symbol, method=nil)
+ res = @context.find_symbol(symbol, method)
+ if res
+ res = res.viewer
+ end
+ res
+ end
+
+ # create table of contents if we contain sections
+
+ def add_table_of_sections
+ toc = []
+ @context.sections.each do |section|
+ if section.title
+ toc << {
+ 'secname' => section.title,
+ 'href' => section.sequence
+ }
+ end
+ end
+
+ @values['toc'] = toc unless toc.empty?
+ end
+
+
+ end
+
+ #####################################################################
+ #
+ # Wrap a ClassModule context
+
+ class HtmlClass < ContextUser
+
+ @@c_seq = "C00000000"
+
+ attr_reader :path
+
+ def initialize(context, html_file, prefix, options)
+ super(context, options)
+ @@c_seq = @@c_seq.succ
+ @c_seq = @@c_seq
+ @html_file = html_file
+ @is_module = context.is_module?
+ @values = {}
+
+ context.viewer = self
+
+ @path = http_url(context.full_name, prefix)
+
+ collect_methods
+
+ AllReferences.add(name, self)
+ end
+
+ # return the relative file name to store this class in,
+ # which is also its url
+ def http_url(full_name, prefix)
+ path = full_name.dup
+ if path['<<']
+ path.gsub!(/<<\s*(\w*)/) { "from-#$1" }
+ end
+ File.join(prefix, path.split("::")) + ".html"
+ end
+
+ def seq
+ @c_seq
+ end
+
+ def aref
+ @c_seq
+ end
+
+ def scope
+ a = @context.full_name.split("::")
+ if a.length > 1
+ a.pop
+ a.join("::")
+ else
+ ""
+ end
+ end
+
+ def name
+ @context.full_name.gsub("#{scope}::", '')
+ end
+
+ def full_name
+ @context.full_name
+ end
+
+ def parent_name
+ @context.parent.full_name
+ end
+
+ def write_on(f)
+ value_hash
+ template = TemplatePage.new(RDoc::Page::BODY,
+ RDoc::Page::CLASS_PAGE,
+ RDoc::Page::METHOD_LIST)
+ template.write_html_on(f, @values)
+ end
+
+ def value_hash
+ class_attribute_values
+ add_table_of_sections
+
+ @values["charset"] = @options.charset
+ @values["style_url"] = style_url(path, @options.css)
+
+ # Convert README to html