Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

First cut at basic heap viewer functionality

  • Loading branch information...
commit aebea12973ca0c6ef48e1155a387c1091429529a 0 parents
Evan Phoenix authored
Showing with 16,815 additions and 0 deletions.
  1. +5 −0 .gitignore
  2. +29 −0 Gemfile
  3. +281 −0 README
  4. +7 −0 Rakefile
  5. +4 −0 app/controllers/application_controller.rb
  6. +88 −0 app/controllers/heap_viewer_controller.rb
  7. +2 −0  app/helpers/application_helper.rb
  8. +31 −0 app/helpers/heap_viewer_helper.rb
  9. +1 −0  app/views/heap_viewer/class_stats.html.erb
  10. +29 −0 app/views/heap_viewer/show.html.erb
  11. +39 −0 app/views/heap_viewer/show_class.html.erb
  12. +7 −0 app/views/heap_viewer/show_instances.html.erb
  13. +42 −0 app/views/heap_viewer/show_object.html.erb
  14. +18 −0 app/views/layouts/application.html.erb
  15. +4 −0 config.ru
  16. +46 −0 config/application.rb
  17. +13 −0 config/boot.rb
  18. +22 −0 config/database.yml
  19. +5 −0 config/environment.rb
  20. +19 −0 config/environments/development.rb
  21. +46 −0 config/environments/production.rb
  22. +32 −0 config/environments/test.rb
  23. +7 −0 config/initializers/backtrace_silencers.rb
  24. +10 −0 config/initializers/inflections.rb
  25. +5 −0 config/initializers/mime_types.rb
  26. +7 −0 config/initializers/secret_token.rb
  27. +8 −0 config/initializers/session_store.rb
  28. +5 −0 config/locales/en.yml
  29. +69 −0 config/routes.rb
  30. +7 −0 db/seeds.rb
  31. +2 −0  doc/README_FOR_APP
  32. +580 −0 lib/read_dump.rb
  33. 0  lib/tasks/.gitkeep
  34. +26 −0 public/404.html
  35. +26 −0 public/422.html
  36. +26 −0 public/500.html
  37. 0  public/favicon.ico
  38. BIN  public/images/rails.png
  39. BIN  public/images/spinner.gif
  40. +279 −0 public/index.html
  41. +371 −0 public/javascripts/app.js
  42. +225 −0 public/javascripts/application.js
  43. +965 −0 public/javascripts/controls.js
  44. +974 −0 public/javascripts/dragdrop.js
  45. +1,123 −0 public/javascripts/effects.js
  46. +6,240 −0 public/javascripts/jquery-1.4.2.js
  47. +4,874 −0 public/javascripts/prototype.js
  48. +118 −0 public/javascripts/rails.js
  49. +5 −0 public/robots.txt
  50. 0  public/stylesheets/.gitkeep
  51. +52 −0 public/stylesheets/application.css
  52. +6 −0 script/rails
  53. +9 −0 test/functional/heap_viewer_controller_test.rb
  54. +9 −0 test/performance/browsing_test.rb
  55. +13 −0 test/test_helper.rb
  56. +4 −0 test/unit/helpers/heap_viewer_helper_test.rb
  57. 0  vendor/plugins/.gitkeep
5 .gitignore
@@ -0,0 +1,5 @@
+*.swp
+.bundle
+db/*.sqlite3
+log/*.log
+tmp/**/*
29 Gemfile
@@ -0,0 +1,29 @@
+source 'http://rubygems.org'
+
+gem 'rails', '3.0.0.beta4'
+
+# Bundle edge Rails instead:
+# gem 'rails', :git => 'git://github.com/rails/rails.git'
+
+gem 'sqlite3-ruby', :require => 'sqlite3'
+
+# Use unicorn as the web server
+# gem 'unicorn'
+
+# Deploy with Capistrano
+# gem 'capistrano'
+
+# To use debugger
+# gem 'ruby-debug'
+
+# Bundle the extra gems:
+# gem 'bj'
+# gem 'nokogiri', '1.4.1'
+# gem 'sqlite3-ruby', :require => 'sqlite3'
+# gem 'aws-s3', :require => 'aws/s3'
+
+# Bundle gems for certain environments:
+# gem 'rspec', :group => :test
+# group :test do
+# gem 'webrat'
+# end
281 README
@@ -0,0 +1,281 @@
+== Welcome to Rails
+
+Rails is a web-application framework that includes everything needed to create
+database-backed web applications according to the Model-View-Control pattern.
+
+This pattern splits the view (also called the presentation) into "dumb"
+templates that are primarily responsible for inserting pre-built data in between
+HTML tags. The model contains the "smart" domain objects (such as Account,
+Product, Person, Post) that holds all the business logic and knows how to
+persist themselves to a database. The controller handles the incoming requests
+(such as Save New Account, Update Product, Show Post) by manipulating the model
+and directing data to the view.
+
+In Rails, the model is handled by what's called an object-relational mapping
+layer entitled Active Record. This layer allows you to present the data from
+database rows as objects and embellish these data objects with business logic
+methods. You can read more about Active Record in
+link:files/vendor/rails/activerecord/README.html.
+
+The controller and view are handled by the Action Pack, which handles both
+layers by its two parts: Action View and Action Controller. These two layers
+are bundled in a single package due to their heavy interdependence. This is
+unlike the relationship between the Active Record and Action Pack that is much
+more separate. Each of these packages can be used independently outside of
+Rails. You can read more about Action Pack in
+link:files/vendor/rails/actionpack/README.html.
+
+
+== Getting Started
+
+1. At the command prompt, create a new Rails application:
+ <tt>rails myapp</tt> (where <tt>myapp</tt> is the application name)
+
+2. Change directory to <tt>myapp</tt> and start the web server:
+ <tt>cd myapp; rails server</tt> (run with --help for options)
+
+3. Go to http://localhost:3000/ and you'll see:
+ "Welcome aboard: You're riding the Rails!"
+
+4. Follow the guidelines to start developing your application. You can find
+the following resources handy:
+
+* The Getting Started Guide: http://guides.rubyonrails.org/getting_started.html
+* Ruby on Rails Tutorial Book: http://www.railstutorial.org/
+
+
+== Web Servers
+
+By default, Rails will try to use Mongrel if it's installed when started with
+<tt>rails server</tt>, otherwise Rails will use WEBrick, the web server that
+ships with Ruby.
+
+Mongrel is a Ruby-based web server with a C component (which requires
+compilation) that is suitable for development. If you have Ruby Gems installed,
+getting up and running with mongrel is as easy as:
+ <tt>sudo gem install mongrel</tt>.
+
+You can find more info at: http://mongrel.rubyforge.org
+
+You can alternatively run Rails applications with other Ruby web servers, e.g.,
+{Thin}[http://code.macournoyer.com/thin/], {Ebb}[http://ebb.rubyforge.org/], and
+Apache with {mod_rails}[http://www.modrails.com/]. However, <tt>rails server</tt>
+doesn't search for or start them.
+
+For production use, often a web/proxy server, e.g., {Apache}[http://apache.org],
+{Nginx}[http://nginx.net/], {LiteSpeed}[http://litespeedtech.com/],
+{Lighttpd}[http://www.lighttpd.net/], or {IIS}[http://www.iis.net/], is deployed
+as the front end server with the chosen Ruby web server running in the back end
+and receiving the proxied requests via one of several protocols (HTTP, CGI, FCGI).
+
+
+== Debugging Rails
+
+Sometimes your application goes wrong. Fortunately there are a lot of tools that
+will help you debug it and get it back on the rails.
+
+First area to check is the application log files. Have "tail -f" commands
+running on the server.log and development.log. Rails will automatically display
+debugging and runtime information to these files. Debugging info will also be
+shown in the browser on requests from 127.0.0.1.
+
+You can also log your own messages directly into the log file from your code
+using the Ruby logger class from inside your controllers. Example:
+
+ class WeblogController < ActionController::Base
+ def destroy
+ @weblog = Weblog.find(params[:id])
+ @weblog.destroy
+ logger.info("#{Time.now} Destroyed Weblog ID ##{@weblog.id}!")
+ end
+ end
+
+The result will be a message in your log file along the lines of:
+
+ Mon Oct 08 14:22:29 +1000 2007 Destroyed Weblog ID #1!
+
+More information on how to use the logger is at http://www.ruby-doc.org/core/
+
+Also, Ruby documentation can be found at http://www.ruby-lang.org/. There are
+several books available online as well:
+
+* Programming Ruby: http://www.ruby-doc.org/docs/ProgrammingRuby/ (Pickaxe)
+* Learn to Program: http://pine.fm/LearnToProgram/ (a beginners guide)
+
+These two books will bring you up to speed on the Ruby language and also on
+programming in general.
+
+
+== Debugger
+
+Debugger support is available through the debugger command when you start your
+Mongrel or WEBrick server with --debugger. This means that you can break out of
+execution at any point in the code, investigate and change the model, and then,
+resume execution! You need to install ruby-debug to run the server in debugging
+mode. With gems, use <tt>sudo gem install ruby-debug</tt>. Example:
+
+ class WeblogController < ActionController::Base
+ def index
+ @posts = Post.find(:all)
+ debugger
+ end
+ end
+
+So the controller will accept the action, run the first line, then present you
+with a IRB prompt in the server window. Here you can do things like:
+
+ >> @posts.inspect
+ => "[#<Post:0x14a6be8
+ @attributes={"title"=>nil, "body"=>nil, "id"=>"1"}>,
+ #<Post:0x14a6620
+ @attributes={"title"=>"Rails", "body"=>"Only ten..", "id"=>"2"}>]"
+ >> @posts.first.title = "hello from a debugger"
+ => "hello from a debugger"
+
+...and even better, you can examine how your runtime objects actually work:
+
+ >> f = @posts.first
+ => #<Post:0x13630c4 @attributes={"title"=>nil, "body"=>nil, "id"=>"1"}>
+ >> f.
+ Display all 152 possibilities? (y or n)
+
+Finally, when you're ready to resume execution, you can enter "cont".
+
+
+== Console
+
+The console is a Ruby shell, which allows you to interact with your
+application's domain model. Here you'll have all parts of the application
+configured, just like it is when the application is running. You can inspect
+domain models, change values, and save to the database. Starting the script
+without arguments will launch it in the development environment.
+
+To start the console, run <tt>rails console</tt> from the application
+directory.
+
+Options:
+
+* Passing the <tt>-s, --sandbox</tt> argument will rollback any modifications
+ made to the database.
+* Passing an environment name as an argument will load the corresponding
+ environment. Example: <tt>rails console production</tt>.
+
+To reload your controllers and models after launching the console run
+<tt>reload!</tt>
+
+More information about irb can be found at:
+link:http://www.rubycentral.com/pickaxe/irb.html
+
+
+== dbconsole
+
+You can go to the command line of your database directly through <tt>rails
+dbconsole</tt>. You would be connected to the database with the credentials
+defined in database.yml. Starting the script without arguments will connect you
+to the development database. Passing an argument will connect you to a different
+database, like <tt>rails dbconsole production</tt>. Currently works for MySQL,
+PostgreSQL and SQLite 3.
+
+== Description of Contents
+
+The default directory structure of a generated Ruby on Rails application:
+
+ |-- app
+ | |-- controllers
+ | |-- helpers
+ | |-- models
+ | `-- views
+ | `-- layouts
+ |-- config
+ | |-- environments
+ | |-- initializers
+ | `-- locales
+ |-- db
+ |-- doc
+ |-- lib
+ | `-- tasks
+ |-- log
+ |-- public
+ | |-- images
+ | |-- javascripts
+ | `-- stylesheets
+ |-- script
+ | `-- performance
+ |-- test
+ | |-- fixtures
+ | |-- functional
+ | |-- integration
+ | |-- performance
+ | `-- unit
+ |-- tmp
+ | |-- cache
+ | |-- pids
+ | |-- sessions
+ | `-- sockets
+ `-- vendor
+ `-- plugins
+
+app
+ Holds all the code that's specific to this particular application.
+
+app/controllers
+ Holds controllers that should be named like weblogs_controller.rb for
+ automated URL mapping. All controllers should descend from
+ ApplicationController which itself descends from ActionController::Base.
+
+app/models
+ Holds models that should be named like post.rb. Models descend from
+ ActiveRecord::Base by default.
+
+app/views
+ Holds the template files for the view that should be named like
+ weblogs/index.html.erb for the WeblogsController#index action. All views use
+ eRuby syntax by default.
+
+app/views/layouts
+ Holds the template files for layouts to be used with views. This models the
+ common header/footer method of wrapping views. In your views, define a layout
+ using the <tt>layout :default</tt> and create a file named default.html.erb.
+ Inside default.html.erb, call <% yield %> to render the view using this
+ layout.
+
+app/helpers
+ Holds view helpers that should be named like weblogs_helper.rb. These are
+ generated for you automatically when using generators for controllers.
+ Helpers can be used to wrap functionality for your views into methods.
+
+config
+ Configuration files for the Rails environment, the routing map, the database,
+ and other dependencies.
+
+db
+ Contains the database schema in schema.rb. db/migrate contains all the
+ sequence of Migrations for your schema.
+
+doc
+ This directory is where your application documentation will be stored when
+ generated using <tt>rake doc:app</tt>
+
+lib
+ Application specific libraries. Basically, any kind of custom code that
+ doesn't belong under controllers, models, or helpers. This directory is in
+ the load path.
+
+public
+ The directory available for the web server. Contains subdirectories for
+ images, stylesheets, and javascripts. Also contains the dispatchers and the
+ default HTML files. This should be set as the DOCUMENT_ROOT of your web
+ server.
+
+script
+ Helper scripts for automation and generation.
+
+test
+ Unit and functional tests along with fixtures. When using the rails generate
+ command, template test files will be generated for you and placed in this
+ directory.
+
+vendor
+ External libraries that the application depends on. Also includes the plugins
+ subdirectory. If the app has frozen rails, those gems also go here, under
+ vendor/rails/. This directory is in the load path.
7 Rakefile
@@ -0,0 +1,7 @@
+# Add your own tasks in files placed in lib/tasks ending in .rake,
+# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
+
+require File.expand_path('../config/application', __FILE__)
+require 'rake'
+
+Rails::Application.load_tasks
4 app/controllers/application_controller.rb
@@ -0,0 +1,4 @@
+class ApplicationController < ActionController::Base
+ protect_from_forgery
+ layout 'application'
+end
88 app/controllers/heap_viewer_controller.rb
@@ -0,0 +1,88 @@
+require 'read_dump'
+
+class HeapViewerController < ApplicationController
+ def index
+ redirect_to :action => :show
+ end
+
+ def show
+ @session = HeapDumpSession.session
+ @histogram = @session.histogram
+ @classes = []
+
+ @session.decoder.Object.constant_table.each do |name, val|
+ if val.kind_of? Rubinius::HeapDump::DumpedObject
+ @classes << [name.data, val] if val.dump_kind_of? @session.decoder.Module
+ end
+ end
+
+ @classes.sort! { |a,b| a[0] <=> b[0] }
+ end
+
+ def show_class
+ id = params[:id].to_i
+
+ @session = HeapDumpSession.session
+ cls = @session.decoder.objects[id].as_module
+ @class = cls
+
+ if cls.dump_kind_of?(@session.decoder.Class)
+ @mod_or_class = "Class"
+ else
+ @mod_or_class = "Module"
+ end
+
+ @stats = @session.histogram[@class]
+
+ @classes = []
+ @values = []
+
+ cls.constant_table.each do |name, val|
+ if val.kind_of? Rubinius::HeapDump::DumpedObject
+ @classes << [name.data, val] if val.dump_kind_of? @session.decoder.Module
+ else
+ @values << [name.data, val]
+ end
+ end
+
+ @classes.sort! { |a,b| a[0] <=> b[0] }
+ @values.sort! { |a,b| a[0] <=> b[0] }
+
+ @instance_methods = []
+
+ cls.method_table.each do |name, method, vis|
+ @instance_methods << [name, method]
+ end
+
+ @instance_methods.sort! { |a,b| a[0] <=> b[0] }
+
+ raw = cls.direct_class_object
+ if raw.metaclass?
+ @class_methods = []
+
+ raw.method_table.each do |name, method, vis|
+ @class_methods << [name, method]
+ end
+
+ @class_methods.sort! { |a,b| a[0] <=> b[0] }
+ else
+ @class_methods = []
+ end
+ end
+
+ def show_instances
+ id = params[:id].to_i
+
+ @session = HeapDumpSession.session
+ cls = @session.decoder.objects[id].as_module
+ @class = cls
+
+ @instances = cls.all_instances
+ end
+
+ def show_object
+ id = params[:id].to_i
+ @session = HeapDumpSession.session
+ @object = @session.decoder.objects[id]
+ end
+end
2  app/helpers/application_helper.rb
@@ -0,0 +1,2 @@
+module ApplicationHelper
+end
31 app/helpers/heap_viewer_helper.rb
@@ -0,0 +1,31 @@
+module HeapViewerHelper
+ def show_object(obj)
+ cls = obj.class_object
+
+ case cls.name
+ when "Array"
+ sz = obj["@total"]
+ "#<Array id=#{obj.id} size=#{sz}>"
+ when "String"
+ total = obj["@num_bytes"]
+ data = obj["@data"].data.data[0, total]
+ "#<String id=#{obj.id} bytes=#{total} #{data.inspect}>"
+ else
+ "#<#{obj.class_object.name} id=#{obj.id}>"
+ end
+ end
+
+ def show_ref(ref)
+ sess = HeapDumpSession.session
+ case ref
+ when Rubinius::HeapDump::Reference
+ show_object sess.decoder.deref(ref)
+ when Rubinius::HeapDump::Symbol
+ "#<Symbol #{ref.data.inspect}>"
+ when Array
+ ref.map { |x| x.inspect }.join(", ")
+ else
+ ref.inspect
+ end
+ end
+end
1  app/views/heap_viewer/class_stats.html.erb
@@ -0,0 +1 @@
+
29 app/views/heap_viewer/show.html.erb
@@ -0,0 +1,29 @@
+<section id="classcontainer">
+ <div id="classlist">
+ <h1>Toplevel Classes</h1>
+
+ <ul>
+ <% @classes.each do |name, val| %>
+ <li><%= link_to name, :action => "show_class", :id => val.id %></li>
+ <% end %>
+ </ul>
+ </div>
+
+ <div id="classhistogram">
+ <h1>Class Usage</h1>
+ <table>
+ <tr>
+ <th>Objects</th>
+ <th>Class</th>
+ <th>Size (in bytes)</th>
+ </tr>
+ <% @histogram.each_sorted do |klass, entry| %>
+ <tr>
+ <td><%= entry.objects %></td>
+ <td><%= link_to klass.name, :action => "show_class", :id => klass.id %></td>
+ <td><%= entry.bytes %></td>
+ </tr>
+ <% end %>
+ </table>
+ </div>
+</section>
39 app/views/heap_viewer/show_class.html.erb
@@ -0,0 +1,39 @@
+<h1><%= @mod_or_class %>: <%= @class.name %></h1>
+
+<h2>Stats</h2>
+<ul>
+ <li>Number of instances: <b><%= @stats.objects %></b></li>
+ <li>Bytes used by instances: <b><%= @stats.bytes %></b></li>
+</ul>
+
+<h2><%= link_to "See All Instances", :action => "show_instances", :id => @class.id %></h2>
+
+<h2>Constants</h2>
+<h3>Modules</h3>
+<ul>
+<% @classes.each do |name, val| %>
+ <li><%= link_to name, :action => "show_class", :id => val.id %></li>
+<% end %>
+</ul>
+
+<h3>Values</h3>
+<ul>
+<% @values.each do |name, val| %>
+ <li><%= name %>: <%= val.inspect %></li>
+<% end %>
+</ul>
+
+<h2>Class Methods</h2>
+<ul>
+ <% @class_methods.each do |name, val| %>
+ <li><%= link_to name, :action => "show_object", :id => val.id %></li>
+ <% end %>
+</ul>
+
+<h2>Instance Methods</h2>
+<ul>
+ <% @instance_methods.each do |name, val| %>
+ <li><%= link_to name, :action => "show_object", :id => val.id %></li>
+ <% end %>
+</ul>
+</div>
7 app/views/heap_viewer/show_instances.html.erb
@@ -0,0 +1,7 @@
+<h1>All Instances of '<%= @class.name %>'</h1>
+
+<ul>
+ <% @instances.array.each do |obj| %>
+ <li><%= link_to show_object(obj), :action => "show_object", :id => obj.id %></li>
+ <% end %>
+</ul>
42 app/views/heap_viewer/show_object.html.erb
@@ -0,0 +1,42 @@
+<h1>#&lt;<%= link_to @object.class_object.name, :action => "show_class", :id => @object.class_object.id %> id=<%= @object.id %>&gt;</h1>
+
+<h2>Instance Data</h2>
+
+<table>
+ <tr>
+ <td>Class: </td>
+ <td><%= link_to @object.class_object.name, :action => "show_class", :id => @object.class_object.id %></td>
+ </tr>
+
+ <% @object.each_ivar do |name,val| %>
+ <tr>
+ <td><%= name %></td>
+ <td>
+ <%= val.kind_of?(Rubinius::HeapDump::Reference) ? link_to(show_ref(val), :action => "show_object", :id => val.id) : show_ref(val) %>
+ </td>
+ </tr>
+ <% end %>
+
+ <% if @object.data %>
+ <tr>
+ <td>data</td>
+ <td>
+ <% if @object.data.kind_of?(Rubinius::HeapDump::Decoder::Tuple) %>
+ <% @object.data.objects.each do |val| %>
+ <%= val.kind_of?(Rubinius::HeapDump::Reference) ? link_to(show_ref(val), :action => "show_object", :id => val.id) : show_ref(val) %><br>
+ <% end %>
+ <% else %>
+ <%= @object.data %>
+ <% end %>
+ </td>
+ </tr>
+ <% end %>
+</table>
+
+<h2>Referers</h2>
+
+<ul>
+ <% @object.referers.array.each do |obj| %>
+ <li><%= link_to show_object(obj), :action => "show_object", :id => obj.id %></li>
+ <% end %>
+</ul>
18 app/views/layouts/application.html.erb
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Gauge</title>
+ <%= stylesheet_link_tag :all %>
+ <%= javascript_include_tag :defaults %>
+ <%= csrf_meta_tag %>
+</head>
+<body>
+ <div class="header">
+ <h1>Gauge</h1>
+ <h2><%= link_to "Class List", :controller => "heap_viewer", :action => "show" %></h2>
+ </div>
+ <div class="body">
+ <%= yield %>
+ </div>
+</body>
+</html>
4 config.ru
@@ -0,0 +1,4 @@
+# This file is used by Rack-based servers to start the application.
+
+require ::File.expand_path('../config/environment', __FILE__)
+run Gauge::Application
46 config/application.rb
@@ -0,0 +1,46 @@
+require File.expand_path('../boot', __FILE__)
+
+require 'rails/all'
+
+# If you have a Gemfile, require the gems listed there, including any gems
+# you've limited to :test, :development, or :production.
+Bundler.require(:default, Rails.env) if defined?(Bundler)
+
+module Gauge
+ class Application < Rails::Application
+ # Settings in config/environments/* take precedence over those specified here.
+ # Application configuration should go into files in config/initializers
+ # -- all .rb files in that directory are automatically loaded.
+
+ # Add additional load paths for your own custom dirs
+ # config.load_paths += %W( #{config.root}/extras )
+
+ # Only load the plugins named here, in the order given (default is alphabetical).
+ # :all can be used as a placeholder for all plugins not explicitly named
+ # config.plugins = [ :exception_notification, :ssl_requirement, :all ]
+
+ # Activate observers that should always be running
+ # config.active_record.observers = :cacher, :garbage_collector, :forum_observer
+
+ # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
+ # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
+ # config.time_zone = 'Central Time (US & Canada)'
+
+ # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
+ # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
+ # config.i18n.default_locale = :de
+
+ # Configure generators values. Many other options are available, be sure to check the documentation.
+ # config.generators do |g|
+ # g.orm :active_record
+ # g.template_engine :erb
+ # g.test_framework :test_unit, :fixture => true
+ # end
+
+ # Configure the default encoding used in templates for Ruby 1.9.
+ config.encoding = "utf-8"
+
+ # Configure sensitive parameters which will be filtered from the log file.
+ config.filter_parameters += [:password]
+ end
+end
13 config/boot.rb
@@ -0,0 +1,13 @@
+require 'rubygems'
+
+# Set up gems listed in the Gemfile.
+gemfile = File.expand_path('../../Gemfile', __FILE__)
+begin
+ ENV['BUNDLE_GEMFILE'] = gemfile
+ require 'bundler'
+ Bundler.setup
+rescue Bundler::GemNotFound => e
+ STDERR.puts e.message
+ STDERR.puts "Try running `bundle install`."
+ exit!
+end if File.exist?(gemfile)
22 config/database.yml
@@ -0,0 +1,22 @@
+# SQLite version 3.x
+# gem install sqlite3-ruby (not necessary on OS X Leopard)
+development:
+ adapter: sqlite3
+ database: db/development.sqlite3
+ pool: 5
+ timeout: 5000
+
+# Warning: The database defined as "test" will be erased and
+# re-generated from your development database when you run "rake".
+# Do not set this db to the same as development or production.
+test:
+ adapter: sqlite3
+ database: db/test.sqlite3
+ pool: 5
+ timeout: 5000
+
+production:
+ adapter: sqlite3
+ database: db/production.sqlite3
+ pool: 5
+ timeout: 5000
5 config/environment.rb
@@ -0,0 +1,5 @@
+# Load the rails application
+require File.expand_path('../application', __FILE__)
+
+# Initialize the rails application
+Gauge::Application.initialize!
19 config/environments/development.rb
@@ -0,0 +1,19 @@
+Gauge::Application.configure do
+ # Settings specified here will take precedence over those in config/environment.rb
+
+ # In the development environment your application's code is reloaded on
+ # every request. This slows down response time but is perfect for development
+ # since you don't have to restart the webserver when you make code changes.
+ config.cache_classes = false
+
+ # Log error messages when you accidentally call methods on nil.
+ config.whiny_nils = true
+
+ # Show full error reports and disable caching
+ config.consider_all_requests_local = true
+ config.action_view.debug_rjs = true
+ config.action_controller.perform_caching = false
+
+ # Don't care if the mailer can't send
+ config.action_mailer.raise_delivery_errors = false
+end
46 config/environments/production.rb
@@ -0,0 +1,46 @@
+Gauge::Application.configure do
+ # Settings specified here will take precedence over those in config/environment.rb
+
+ # The production environment is meant for finished, "live" apps.
+ # Code is not reloaded between requests
+ config.cache_classes = true
+
+ # Full error reports are disabled and caching is turned on
+ config.consider_all_requests_local = false
+ config.action_controller.perform_caching = true
+
+ # Specifies the header that your server uses for sending files
+ config.action_dispatch.x_sendfile_header = "X-Sendfile"
+
+ # For nginx:
+ # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect'
+
+ # If you have no front-end server that supports something like X-Sendfile,
+ # just comment this out and Rails will serve the files
+
+ # See everything in the log (default is :info)
+ # config.log_level = :debug
+
+ # Use a different logger for distributed setups
+ # config.logger = SyslogLogger.new
+
+ # Use a different cache store in production
+ # config.cache_store = :mem_cache_store
+
+ # Disable Rails's static asset server
+ # In production, Apache or nginx will already do this
+ config.serve_static_assets = false
+
+ # Enable serving of images, stylesheets, and javascripts from an asset server
+ # config.action_controller.asset_host = "http://assets.example.com"
+
+ # Disable delivery errors, bad email addresses will be ignored
+ # config.action_mailer.raise_delivery_errors = false
+
+ # Enable threaded mode
+ # config.threadsafe!
+
+ # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
+ # the I18n.default_locale when a translation can not be found)
+ config.i18n.fallbacks = true
+end
32 config/environments/test.rb
@@ -0,0 +1,32 @@
+Gauge::Application.configure do
+ # Settings specified here will take precedence over those in config/environment.rb
+
+ # The test environment is used exclusively to run your application's
+ # test suite. You never need to work with it otherwise. Remember that
+ # your test database is "scratch space" for the test suite and is wiped
+ # and recreated between test runs. Don't rely on the data there!
+ config.cache_classes = true
+
+ # Log error messages when you accidentally call methods on nil.
+ config.whiny_nils = true
+
+ # Show full error reports and disable caching
+ config.consider_all_requests_local = true
+ config.action_controller.perform_caching = false
+
+ # Raise exceptions instead of rendering exception templates
+ config.action_dispatch.show_exceptions = false
+
+ # Disable request forgery protection in test environment
+ config.action_controller.allow_forgery_protection = false
+
+ # Tell Action Mailer not to deliver emails to the real world.
+ # The :test delivery method accumulates sent emails in the
+ # ActionMailer::Base.deliveries array.
+ config.action_mailer.delivery_method = :test
+
+ # Use SQL instead of Active Record's schema dumper when creating the test database.
+ # This is necessary if your schema can't be completely dumped by the schema dumper,
+ # like if you have constraints or database-specific column types
+ # config.active_record.schema_format = :sql
+end
7 config/initializers/backtrace_silencers.rb
@@ -0,0 +1,7 @@
+# Be sure to restart your server when you modify this file.
+
+# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
+# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
+
+# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
+# Rails.backtrace_cleaner.remove_silencers!
10 config/initializers/inflections.rb
@@ -0,0 +1,10 @@
+# Be sure to restart your server when you modify this file.
+
+# Add new inflection rules using the following format
+# (all these examples are active by default):
+# ActiveSupport::Inflector.inflections do |inflect|
+# inflect.plural /^(ox)$/i, '\1en'
+# inflect.singular /^(ox)en/i, '\1'
+# inflect.irregular 'person', 'people'
+# inflect.uncountable %w( fish sheep )
+# end
5 config/initializers/mime_types.rb
@@ -0,0 +1,5 @@
+# Be sure to restart your server when you modify this file.
+
+# Add new mime types for use in respond_to blocks:
+# Mime::Type.register "text/richtext", :rtf
+# Mime::Type.register_alias "text/html", :iphone
7 config/initializers/secret_token.rb
@@ -0,0 +1,7 @@
+# Be sure to restart your server when you modify this file.
+
+# Your secret key for verifying the integrity of signed cookies.
+# If you change this key, all old signed cookies will become invalid!
+# Make sure the secret is at least 30 characters and all random,
+# no regular words or you'll be exposed to dictionary attacks.
+Rails.application.config.secret_token = 'b2e7d6e4be46c59efc818bda1522b8884f5daeaa0d22e22757ece3edd7009a251887a88a005647d35a913ce8e6faee66877b975105904a89d9d60587922e66ba'
8 config/initializers/session_store.rb
@@ -0,0 +1,8 @@
+# Be sure to restart your server when you modify this file.
+
+Rails.application.config.session_store :cookie_store, :key => '_gauge_session'
+
+# Use the database for sessions instead of the cookie-based default,
+# which shouldn't be used to store highly confidential information
+# (create the session table with "rake db:sessions:create")
+# Rails.application.config.session_store :active_record_store
5 config/locales/en.yml
@@ -0,0 +1,5 @@
+# Sample localization file for English. Add more files in this directory for other locales.
+# See http://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points.
+
+en:
+ hello: "Hello world"
69 config/routes.rb
@@ -0,0 +1,69 @@
+Gauge::Application.routes.draw do |map|
+ get "heap_viewer/show_class"
+
+ get "heap_viewer/show_instances"
+
+ get "heap_viewer/show_object"
+
+ get "heap_viewer/show"
+
+ get "/heap_viewer" => "heap_viewer#show"
+
+
+ # The priority is based upon order of creation:
+ # first created -> highest priority.
+
+ # Sample of regular route:
+ # match 'products/:id' => 'catalog#view'
+ # Keep in mind you can assign values other than :controller and :action
+
+ # Sample of named route:
+ # match 'products/:id/purchase' => 'catalog#purchase', :as => :purchase
+ # This route can be invoked with purchase_url(:id => product.id)
+
+ # Sample resource route (maps HTTP verbs to controller actions automatically):
+ # resources :products
+
+ # Sample resource route with options:
+ # resources :products do
+ # member do
+ # get :short
+ # post :toggle
+ # end
+ #
+ # collection do
+ # get :sold
+ # end
+ # end
+
+ # Sample resource route with sub-resources:
+ # resources :products do
+ # resources :comments, :sales
+ # resource :seller
+ # end
+
+ # Sample resource route with more complex sub-resources
+ # resources :products do
+ # resources :comments
+ # resources :sales do
+ # get :recent, :on => :collection
+ # end
+ # end
+
+ # Sample resource route within a namespace:
+ # namespace :admin do
+ # # Directs /admin/products/* to Admin::ProductsController
+ # # (app/controllers/admin/products_controller.rb)
+ # resources :products
+ # end
+
+ # You can have the root of your site routed with "root"
+ # just remember to delete public/index.html.
+ # root :to => "welcome#index"
+
+ # See how all your routes lay out with "rake routes"
+
+ # This is a legacy wild controller route that's not recommended for RESTful applications.
+ # Note: This route will make all actions in every controller accessible via GET requests.
+ # match ':controller(/:action(/:id(.:format)))'
+end
7 db/seeds.rb
@@ -0,0 +1,7 @@
+# This file should contain all the record creation needed to seed the database with its default values.
+# The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).
+#
+# Examples:
+#
+# cities = City.create([{ :name => 'Chicago' }, { :name => 'Copenhagen' }])
+# Mayor.create(:name => 'Daley', :city => cities.first)
2  doc/README_FOR_APP
@@ -0,0 +1,2 @@
+Use this README file to introduce your application and point to useful places in the API for learning more.
+Run "rake doc:app" to generate API documentation for your models, controllers, helpers, and libraries.
580 lib/read_dump.rb
@@ -0,0 +1,580 @@
+module Rubinius
+ module HeapDump
+
+ class DumpedObject
+ def initialize(decoder, id, bytes, layout, klass, ivar_ref, ivars)
+ @decoder = decoder
+ @id = id
+ @bytes = bytes
+ @layout = layout
+ @ivars = ivars
+ @ivar_ref = ivar_ref
+ @klass = klass
+ end
+
+ attr_reader :decoder
+ attr_reader :id, :bytes, :layout, :ivars, :klass, :ivar_ref
+
+ def raw_ivar(name)
+ idx = @layout.index(name)
+ return nil unless idx
+
+ return @ivars[idx]
+ end
+
+ def [](name)
+ ref = raw_ivar(name)
+ if ref.kind_of? Reference
+ return @decoder.objects[ref.id]
+ end
+
+ return ref
+ end
+
+ def to_hash
+ hsh = {}
+ @layout.each_with_index do |name, idx|
+ hsh[name] = @ivars[idx]
+ end
+
+ hsh
+ end
+
+ def each_ivar
+ @layout.each_with_index do |name, idx|
+ yield name, @ivars[idx]
+ end
+ end
+
+ def direct_class_object
+ @klass_object ||= @decoder.objects[@klass.id].as_module
+ end
+
+ def dump_kind_of?(cls)
+ start = direct_class_object
+
+ while start
+ return true if start.object == cls.object
+ start = start.superclass
+ end
+
+ return false
+ end
+
+ def class_object
+ start = direct_class_object
+ return start unless start.metaclass?
+
+ start = start.superclass
+ cls = @decoder.Class
+
+ while start
+ return start if start.object.dump_kind_of?(cls) and !start.metaclass?
+ start = start.superclass
+ end
+
+ raise "Unable to figure out class"
+ end
+
+ def as_module
+ @as_module ||= DumpedModule.new(self)
+ end
+
+ def data
+ @ivars[@layout.size]
+ end
+
+ def inspect
+ "#<DumpedObject id=#{@id} klass=#{@klass} #{to_hash.inspect}>"
+ end
+
+ def referers
+ @referers ||= Objects.new(@decoder.find_references(@id))
+ end
+
+ def references?(other)
+ @ivars.each do |i|
+ return true if i.kind_of? Reference and i.id == other
+ end
+
+ if d = data and d.kind_of?(HeapDump::Decoder::Tuple)
+ d.objects.each do |i|
+ return true if i.kind_of? Reference and i.id == other
+ end
+ end
+
+ return false
+ end
+ end
+
+ class Specializer
+ def initialize(obj)
+ @object = obj
+ end
+
+ attr_reader :object
+
+ def ==(obj)
+ @object == obj || super
+ end
+
+ def method_missing(msg, *args)
+ if @object.respond_to?(msg)
+ @object.__send__(msg, *args)
+ else
+ super
+ end
+ end
+
+ def id
+ @object.id
+ end
+ end
+
+ class DumpedLookupTable < Specializer
+ def each
+ @object["@values"].data.objects.each do |ref|
+ if ref
+ obj = @object.decoder.deref(ref)
+ while obj
+ yield obj["@key"], obj["@value"]
+
+ obj = obj["@next"]
+ end
+ end
+ end
+ end
+
+ include Enumerable
+
+ def keys
+ map { |k,v| k.data }
+ end
+ end
+
+ class DumpedMethodTable < Specializer
+ def each
+ @object["@values"].data.objects.each do |ref|
+ if ref
+ obj = @object.decoder.deref(ref)
+ while obj
+ yield obj["@name"], obj["@method"], obj["@visibility"]
+
+ obj = obj["@next"]
+ end
+ end
+ end
+ end
+
+ include Enumerable
+
+ def keys
+ map { |k,v| k.data }
+ end
+ end
+
+ class DumpedModule < Specializer
+ def name
+ if sym = @object["@module_name"]
+ sym.data
+ end
+ end
+
+ def superclass
+ if obj = @object["@superclass"]
+ return obj.as_module
+ end
+
+ return nil
+ end
+
+ def constant_table
+ @constants ||= DumpedLookupTable.new(@object["@constants"])
+ end
+
+ def constants
+ constant_table.map do |key, val|
+ key.data
+ end
+ end
+
+ def method_table
+ @methods ||= DumpedMethodTable.new(@object["@method_table"])
+ end
+
+ def find_class(name)
+ name = name.to_s
+ constant_table.map do |key, val|
+ return val.as_module if key.data == name
+ end
+
+ nil
+ end
+
+ def constant(name)
+ name = name.to_s
+ constant_table.map do |key, val|
+ if key.data == name
+ if val.dump_kind_of?(@object.decoder.Module)
+ return val.as_module
+ else
+ return val
+ end
+ end
+ end
+
+ nil
+ end
+
+ def all_instances
+ ary = []
+ @object.decoder.objects.each do |obj|
+ ary << obj if obj.class_object.id == @object.id
+ end
+
+ return Objects.new(ary)
+ end
+
+ def metaclass?
+ !@object["@attached_instance"].nil?
+ end
+ end
+
+ Reference = Struct.new(:id)
+ Symbol = Struct.new(:id, :data)
+
+ class Symbol
+ def ==(other)
+ case other
+ when String
+ data == other
+ else
+ super
+ end
+ end
+
+ def <=>(other)
+ data <=> other.data
+ end
+
+ def to_s
+ data
+ end
+ end
+
+ class Decoder
+
+ Tuple = Struct.new(:objects)
+ Bytes = Struct.new(:data)
+
+ def initialize
+ @symbols = []
+ @objects = []
+ @layouts = []
+ end
+
+ attr_reader :symbols, :objects, :layouts
+
+ def deref(id)
+ if id.kind_of? Reference
+ @objects[id.id]
+ else
+ @objects[id]
+ end
+ end
+
+ def int
+ @f.read(4).unpack("N").first
+ end
+
+ def ints(n)
+ @f.read(4*n).unpack("N#{n}")
+ end
+
+ def short
+ @f.read(2).unpack("n").first
+ end
+
+ def char
+ @f.read(1)[0]
+ end
+
+ def decode_reference
+ subcmd = @f.read(1)[0]
+ case subcmd
+ when ?r
+ obj = Reference.new(int)
+ when ?x
+ ref = int
+ obj = @symbols[ref]
+ when ?s
+ id, sz = ints(2)
+ obj = Symbol.new(id, @f.read(sz))
+ @symbols[id] = obj
+ when ?f
+ obj = int
+ when ?t
+ obj = []
+ sz = int
+ sz.times do
+ obj << decode_reference
+ end
+
+ obj = Tuple.new(obj)
+ when ?b
+ obj = Bytes.new(@f.read(int))
+ when ?i
+ obj = case char
+ when 0
+ false
+ when 1
+ true
+ when 2
+ nil
+ end
+ else
+ raise "invalid sub code - #{subcmd}, #{subcmd.chr}"
+ end
+
+ return obj
+ end
+
+ def decode(file)
+ File.open(file) do |f|
+ magic = f.read(12)
+ if magic != "RBXHEAPDUMP\0"
+ raise "Invalid file format"
+ end
+
+ @f = f
+
+ version = int
+
+ unless version == 1
+ raise "Invalid version - #{version}"
+ end
+
+ while true
+ str = @f.read(1)
+ break unless str
+ cmd = str[0]
+
+ case cmd
+ when ?s
+ id, sz = ints(2)
+ str = @f.read(sz)
+ @symbols[id] = str
+ when ?o
+ id, bytes, layout, klass = ints(4)
+ ivar_ref = decode_reference
+ syms = short # , syms = @f.read(18).unpack("NNNNn")
+ ivars = []
+
+ syms.times do
+ ivars << decode_reference
+ end
+
+ if @objects[id]
+ raise "redefined object #{id}"
+ end
+
+ @objects[id] = DumpedObject.new(self, id, bytes,
+ @layouts[layout],
+ Reference.new(klass),
+ Reference.new(ivar_ref), ivars)
+ when ?l
+ id, sz = ints(2)
+ syms = ints(sz)
+ @layouts[id] = syms.map { |x| @symbols[x] }
+ when ?- # footer
+ @object = @objects[int].as_module
+ @klass = @objects[int].as_module
+ @module = @objects[int].as_module
+ else
+ raise "invalid code #{cmd}"
+ end
+ end
+ end
+
+ @included_module = @object.find_class("IncludedModule")
+ @f = nil
+ end
+
+ def Object
+ @object
+ end
+
+ def Class
+ @klass
+ end
+
+ def Module
+ @module
+ end
+
+ def all_objects
+ return Objects.new(@objects)
+ end
+
+ def find_references(id)
+ out = []
+ @objects.each do |o|
+ out << o if o.references?(id)
+ end
+
+ return out
+ end
+ end
+
+ class Histogram
+ def initialize(data)
+ @data = data
+ end
+
+ class Entry
+ def initialize(klass)
+ @klass = klass
+ @objects = 0
+ @bytes = 0
+ end
+
+ attr_reader :objects, :bytes
+
+ def inc(object)
+ @objects += 1
+ @bytes += object.bytes
+ end
+
+ def <=>(other)
+ @objects <=> other.objects
+ end
+ end
+
+ def self.by_class(objects)
+ histogram = Hash.new { |h,k| h[k] = Entry.new(k) }
+
+ objects.each do |o|
+ klass = o.class_object
+
+ if klass.name
+ histogram[klass].inc(o)
+ end
+ end
+
+ return Histogram.new(histogram)
+ end
+
+ def [](cls)
+ @data[cls]
+ end
+
+ def to_text
+ each_sorted do |klass, entry|
+ puts "%10d %s (%d bytes)" % [entry.objects, klass.name, entry.bytes]
+ end
+ end
+
+ def each_sorted
+ sorted = @data.to_a.sort_by { |x| x[1] }
+ sorted.reverse_each do |klass, entry|
+ yield klass, entry
+ end
+ end
+ end
+
+ class Objects
+ def initialize(ary)
+ @array = ary
+ end
+
+ attr_reader :array
+
+ def histogram
+ Histogram.by_class(@array)
+ end
+
+ def referers
+ ary = []
+
+ # Use a hash to speed up checking. Doing a #include? check on @array
+ # is pretty slow.
+ hsh = {}
+
+ @array.each do |obj|
+ hsh[obj.id] = true
+ end
+
+ @array[0].decoder.objects.each do |obj|
+ obj.ivars.each do |iv|
+ ary << obj if iv.kind_of?(Reference) && hsh[iv.id]
+ end
+ end
+
+ return Objects.new(ary.uniq)
+ end
+
+ def size
+ @array.size
+ end
+
+ def byte_size
+ @bytes ||= @array.inject(0) { |acc,o| acc + o.bytes }
+ end
+
+ def [](obj)
+ @array[obj]
+ end
+
+ def inspect
+ "#<#{self.class} size=#{size}>"
+ end
+ end
+ end
+end
+
+class HeapDumpSession
+ def initialize
+ puts "RELOADING..."
+ @file = ENV['DUMP']
+ if File.exists?("#{@file}.marshal")
+ @decoder = Marshal.load File.read("#{@file}.marshal")
+ else
+ @decoder = Rubinius::HeapDump::Decoder.new
+ @decoder.decode(@file)
+ File.open("#{@file}.marshal", "w") { |f| f << Marshal.dump(@decoder) }
+ end
+ @histogram = decoder.all_objects.histogram
+ end
+
+ attr_reader :decoder, :histogram
+
+ def self.session
+ @session ||= new
+ end
+
+ def test
+ decoder = Rubinius::HeapDump::Decoder.new
+
+ file = ARGV.shift
+
+ decoder.decode(file)
+
+ histogram = decoder.all_objects.histogram
+
+ histogram.to_text
+
+ rubinius = decoder.Object.constant("Rubinius")
+ tuple = rubinius.constant("Tuple")
+
+ instances = tuple.all_instances
+ p instances.array.size
+
+ ref = instances.referers
+ p ref.size
+ puts "#{ref.byte_size} bytes"
+
+ hist = ref.histogram
+ hist.to_text
+ end
+end
+
0  lib/tasks/.gitkeep
No changes.
26 public/404.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>The page you were looking for doesn't exist (404)</title>
+ <style type="text/css">
+ body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
+ div.dialog {
+ width: 25em;
+ padding: 0 4em;
+ margin: 4em auto 0 auto;
+ border: 1px solid #ccc;
+ border-right-color: #999;
+ border-bottom-color: #999;
+ }
+ h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
+ </style>
+</head>
+
+<body>
+ <!-- This file lives in public/404.html -->
+ <div class="dialog">
+ <h1>The page you were looking for doesn't exist.</h1>
+ <p>You may have mistyped the address or the page may have moved.</p>
+ </div>
+</body>
+</html>
26 public/422.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>The change you wanted was rejected (422)</title>
+ <style type="text/css">
+ body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
+ div.dialog {
+ width: 25em;
+ padding: 0 4em;
+ margin: 4em auto 0 auto;
+ border: 1px solid #ccc;
+ border-right-color: #999;
+ border-bottom-color: #999;
+ }
+ h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
+ </style>
+</head>
+
+<body>
+ <!-- This file lives in public/422.html -->
+ <div class="dialog">
+ <h1>The change you wanted was rejected.</h1>
+ <p>Maybe you tried to change something you didn't have access to.</p>
+ </div>
+</body>
+</html>
26 public/500.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>We're sorry, but something went wrong (500)</title>
+ <style type="text/css">
+ body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
+ div.dialog {
+ width: 25em;
+ padding: 0 4em;
+ margin: 4em auto 0 auto;
+ border: 1px solid #ccc;
+ border-right-color: #999;
+ border-bottom-color: #999;
+ }
+ h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
+ </style>
+</head>
+
+<body>
+ <!-- This file lives in public/500.html -->
+ <div class="dialog">
+ <h1>We're sorry, but something went wrong.</h1>
+ <p>We've been notified about this issue and we'll take a look at it shortly.</p>
+ </div>
+</body>
+</html>
0  public/favicon.ico
No changes.
BIN  public/images/rails.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  public/images/spinner.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
279 public/index.html
@@ -0,0 +1,279 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Ruby on Rails: Welcome aboard</title>
+ <style type="text/css" media="screen">
+ body {
+ margin: 0;
+ margin-bottom: 25px;
+ padding: 0;
+ background-color: #f0f0f0;
+ font-family: "Lucida Grande", "Bitstream Vera Sans", "Verdana";
+ font-size: 13px;
+ color: #333;
+ }
+
+ h1 {
+ font-size: 28px;
+ color: #000;
+ }
+
+ a {color: #03c}
+ a:hover {
+ background-color: #03c;
+ color: white;
+ text-decoration: none;
+ }
+
+
+ #page {
+ background-color: #f0f0f0;
+ width: 750px;
+ margin: 0;
+ margin-left: auto;
+ margin-right: auto;
+ }
+
+ #content {
+ float: left;
+ background-color: white;
+ border: 3px solid #aaa;
+ border-top: none;
+ padding: 25px;
+ width: 500px;
+ }
+
+ #sidebar {
+ float: right;
+ width: 175px;
+ }
+
+ #footer {
+ clear: both;
+ }
+
+
+ #header, #about, #getting-started {
+ padding-left: 75px;
+ padding-right: 30px;
+ }
+
+
+ #header {
+ background-image: url("images/rails.png");
+ background-repeat: no-repeat;
+ background-position: top left;
+ height: 64px;
+ }
+ #header h1, #header h2 {margin: 0}
+ #header h2 {
+ color: #888;
+ font-weight: normal;
+ font-size: 16px;
+ }
+
+
+ #about h3 {
+ margin: 0;
+ margin-bottom: 10px;
+ font-size: 14px;
+ }
+
+ #about-content {
+ background-color: #ffd;
+ border: 1px solid #fc0;
+ margin-left: -55px;
+ margin-right: -10px;
+ }
+ #about-content table {
+ margin-top: 10px;
+ margin-bottom: 10px;
+ font-size: 11px;
+ border-collapse: collapse;
+ }
+ #about-content td {
+ padding: 10px;
+ padding-top: 3px;
+ padding-bottom: 3px;
+ }
+ #about-content td.name {color: #555}
+ #about-content td.value {color: #000}
+
+ #about-content ul {
+ padding: 0;
+ list-style-type: none;
+ }
+
+ #about-content.failure {
+ background-color: #fcc;
+ border: 1px solid #f00;
+ }
+ #about-content.failure p {
+ margin: 0;
+ padding: 10px;
+ }
+
+
+ #getting-started {
+ border-top: 1px solid #ccc;
+ margin-top: 25px;
+ padding-top: 15px;
+ }
+ #getting-started h1 {
+ margin: 0;
+ font-size: 20px;
+ }
+ #getting-started h2 {
+ margin: 0;
+ font-size: 14px;
+ font-weight: normal;
+ color: #333;
+ margin-bottom: 25px;
+ }
+ #getting-started ol {
+ margin-left: 0;
+ padding-left: 0;
+ }
+ #getting-started li {
+ font-size: 18px;
+ color: #888;
+ margin-bottom: 25px;
+ }
+ #getting-started li h2 {
+ margin: 0;
+ font-weight: normal;
+ font-size: 18px;
+ color: #333;
+ }
+ #getting-started li p {
+ color: #555;
+ font-size: 13px;
+ }
+
+
+ #search {
+ margin: 0;
+ padding-top: 10px;
+ padding-bottom: 10px;
+ font-size: 11px;
+ }
+ #search input {
+ font-size: 11px;
+ margin: 2px;
+ }
+ #search-text {width: 170px}
+
+
+ #sidebar ul {
+ margin-left: 0;
+ padding-left: 0;
+ }
+ #sidebar ul h3 {
+ margin-top: 25px;
+ font-size: 16px;
+ padding-bottom: 10px;
+ border-bottom: 1px solid #ccc;
+ }
+ #sidebar li {
+ list-style-type: none;
+ }
+ #sidebar ul.links li {
+ margin-bottom: 5px;
+ }
+
+ </style>
+ <script type="text/javascript">
+ function about() {
+ info = document.getElementById('about-content');
+ if (window.XMLHttpRequest)
+ { xhr = new XMLHttpRequest(); }
+ else
+ { xhr = new ActiveXObject("Microsoft.XMLHTTP"); }
+ xhr.open("GET","rails/info/properties",false);
+ xhr.send("");
+ info.innerHTML = xhr.responseText;
+ info.style.display = 'block'
+ }
+
+ function prepend() {
+ search = document.getElementById('search-text');
+ text = search.value;
+ search.value = 'site:rubyonrails.org ' + text;
+ }
+
+ window.onload = function() {
+ document.getElementById('search-text').value = '';
+ }
+ </script>
+ </head>
+ <body>
+ <div id="page">
+ <div id="sidebar">
+ <ul id="sidebar-items">
+ <li>
+ <form id="search" action="http://www.google.com/search" method="get" onSubmit="prepend();">
+ <input type="hidden" name="hl" value="en" />
+ <input type="text" id="search-text" name="q" value="site:rubyonrails.org " />
+ <input type="submit" value="Search" /> the Rails site
+ </form>
+ </li>
+
+ <li>
+ <h3>Join the community</h3>
+ <ul class="links">
+ <li><a href="http://www.rubyonrails.org/">Ruby on Rails</a></li>
+ <li><a href="http://weblog.rubyonrails.org/">Official weblog</a></li>
+ <li><a href="http://wiki.rubyonrails.org/">Wiki</a></li>
+ </ul>
+ </li>
+
+ <li>
+ <h3>Browse the documentation</h3>
+ <ul class="links">
+ <li><a href="http://api.rubyonrails.org/">Rails API</a></li>
+ <li><a href="http://stdlib.rubyonrails.org/">Ruby standard library</a></li>
+ <li><a href="http://corelib.rubyonrails.org/">Ruby core</a></li>
+ <li><a href="http://guides.rubyonrails.org/">Rails Guides</a></li>
+ </ul>
+ </li>
+ </ul>
+ </div>
+
+ <div id="content">
+ <div id="header">
+ <h1>Welcome aboard</h1>
+ <h2>You&rsquo;re riding Ruby on Rails!</h2>
+ </div>
+
+ <div id="about">
+ <h3><a href="rails/info/properties" onclick="about(); return false">About your application&rsquo;s environment</a></h3>
+ <div id="about-content" style="display: none"></div>
+ </div>
+
+ <div id="getting-started">
+ <h1>Getting started</h1>
+ <h2>Here&rsquo;s how to get rolling:</h2>
+
+ <ol>
+ <li>
+ <h2>Use <code>rails generate</code> to create your models and controllers</h2>
+ <p>To see all available options, run it without parameters.</p>
+ </li>
+
+ <li>
+ <h2>Set up a default route and remove or rename this file</h2>
+ <p>Routes are set up in config/routes.rb.</p>
+ </li>
+
+ <li>
+ <h2>Create your database</h2>
+ <p>Run <code>rake db:migrate</code> to create your database. If you're not using SQLite (the default), edit <code>config/database.yml</code> with your username and password.</p>
+ </li>
+ </ol>
+ </div>
+ </div>
+
+ <div id="footer">&nbsp;</div>
+ </div>
+ </body>
+</html>
371 public/javascripts/app.js
@@ -0,0 +1,371 @@
+$.facebox.settings.overlay = false;
+
+$.fn.setupTree = function() {
+ return $(this).each(function(){
+ var tree = $(this);
+
+ if (tree.hasClass('grouped')) {
+ tree.css('margin-left', '45px');
+ }
+
+ tree.treeview({
+ collapsed: true,
+ animated: 'fast',
+ prerendered: false,
+ toggle: function(){
+ var $this = $(this);
+ if ($this.hasClass('loaded'))
+ return;
+
+ $this.addClass('loaded');
+ var sublist = $this.find('>ul');
+ var url = sublist.attr('url');
+
+ $.ajax({
+ url: url,
+ success: function(data) {
+ sublist.animate({height: '0'}, 'fast', function(){
+ var panel = $this.parents('div.panel:first');
+ var hash = panel.attr('url') || '';
+ panel.attr('url', hash + '\\\\TOGGLE=' + url.replace(/^\/dump\/.+?\//,''));
+ updateHash();
+ replayNext(panel);
+
+ sublist.remove();
+ var newlist = $(data);
+ newlist.css('display','none').appendTo($this);
+ tree.trigger('add', newlist);
+ newlist.animate({height:'toggle'}, 'fast');
+ });
+ },
+ error: function() {
+ $this.removeClass('loaded');
+ },
+ cache: false
+ });
+ }
+ });
+ });
+};
+
+$.fn.setupPanel = function(){
+ var numPanels = $('div.panel').length;
+
+ return $(this).each(function(){
+ var panel = $(this);
+
+ panel.find('ul').not('ul.nav, ul.list').setupTree();
+ if (numPanels > 1)
+ panel.addClass('additional');
+
+ panel.find('pre.prettyprint').prettify();
+ panel.find("abbr.timeago").timeago();
+
+ updateHash();
+ replayNext(panel);
+ });
+};
+
+$.fn.prettify = function(){
+ return $(this).each(function(){
+ var container = $(this);
+ if (container.hasClass('prettified'))
+ return;
+ container.addClass('prettified');
+ container.html(prettyPrintOne(container.html()));
+ });
+};
+
+var replayNext = function(panel){
+ var link = REPLAY.shift();
+ if (link) {
+ setTimeout(function(){
+ if (link.match(/^TOGGLE=/)) {
+ var ul = panel.find("ul[url*="+link.slice(7)+"]");
+ if (ul.length) {
+ ul.prevAll('div.hitarea:first').click();
+ }
+ } else if (link.match(/^CHANGE=/)) {
+ var select = $('select.group_key:last');
+ select.val(link.slice(7)).change();
+ } else {
+ var links = panel.find("a[href*='"+link+"']");
+ if (links.length > 0)
+ links.eq(0).click();
+ else
+ REPLAY = [];
+ }
+ }, 300);
+ }
+};
+
+var updateHash = function(){
+ var hash = '';
+
+ $('div.panel').each(function(){
+ hash += ($(this).attr('url') || '');
+ var current = $(this).find('a.current');
+ if (current.length > 0)
+ hash += ('\\\\' + current.attr('href').replace(/^\/dump\/.+?\//,''));
+ });
+
+ if (hash)
+ window.location.hash = hash;
+}
+
+var updateBodyWidth = function(){
+ var w = 0, num = 0;
+ $('div.panel').each(function(){
+ w += $(this).outerWidth();
+ num += 1;
+ });
+
+ $('body').width(w + $(window).width()/2);
+};
+
+var scrollingTo = false;
+
+var centerPanel = function(panel, to_top) {
+ scrollingTo = true;
+
+ var wasCentered = false;
+
+ if (panel.hasClass('middle')) {
+ wasCentered = true;
+ } else {
+ $('div.panel.middle').removeClass('middle');
+ panel.addClass('middle');
+ }
+
+ var x = panel.position().left + panel.outerWidth()/2 - $(window).width()/2;
+ var y = 0;
+ var bottom = panel.position().top + panel.outerHeight();
+ var link = panel.find('a.current');
+
+ if (!to_top && link.length > 0 && !wasCentered)
+ y = link.position().top - $(window).height()/2;
+
+ if (y < 0) {
+ y = 0;
+ to_top = true;
+ }
+
+ // console.log([
+ // "position=",
+ // [panel.position().left, panel.position().top],
+ // " x/y=",
+ // [x, y],
+ // " offset=",
+ // [window.pageXOffset, window.pageYOffset]
+ // ]);
+
+ if (window.pageYOffset > bottom && !to_top && y == 0)
+ to_top = true;
+
+ var after = function(){
+ scrollingTo = false;
+ }
+
+ if (!navigator.userAgent.match(/WebKit.*Mobile/)) {
+ if (to_top || y > 0)
+ $.scrollTo({left:x, top:y}, 'fast', {queue:false, onAfter:after});
+ else
+ $.scrollTo(x, 'fast', {axis:'x', onAfter:after});
+ }
+};
+
+var findClosestPanel = function(){
+ var left = window.pageXOffset;
+ var closest = false;
+ var showPanel = null;
+
+ $('div.panel').each(function(){
+ var panel = $(this);
+ var pos = panel.position().left + panel.outerWidth()/2 - $(window).width()/2;
+ var diff = Math.abs(pos - left);
+
+ if (closest === false || diff < closest) {
+ closest = diff;
+ showPanel = panel;
+ } else
+ return false;
+ });
+
+ return showPanel;
+};
+
+$('ul.dumps_list li').live('mouseover mouseout', function(event){
+ if (event.type == 'mouseover') {
+ $(this).find('form.delete').show();
+ } else {
+ $(this).find('form.delete').hide();
+ }
+});
+
+$('a.enable_private').live('click', function(){
+ $.get('/beta', function(){
+ $.facebox("<h3 class='centered'>Thanks. We'll let you know when more private beta invites are available.</h3>");
+ });
+ return false;
+});
+
+$('a.facebox').live('click', function(){
+ $.facebox({ajax: this.href});
+ return false;
+});
+
+$('ul.nav li a:not(.popout)').live('click', function(){
+ var link = $(this);
+ var nav = $(this).parents('ul.nav:first');
+ nav.find('a.selected').removeClass('selected');
+ $(this).addClass('selected');
+
+ var panel = $(this).parents('div.panel:first');
+ panel.nextAll('div.panel').remove();
+ panel.find('> div.content').html('<center><img src="/demo/spinner.gif" class="spinner"></center>');
+
+ $.ajax({
+ url: this.href,
+ success: function(html){
+ var newPanel = $(html);
+ var hash = panel.attr('url') || '';
+ var link_text = $.trim(link.text());
+
+ panel.replaceWith(newPanel);
+
+ var curr;
+ if (link_text.match(/^group\s*by/))
+ curr = '\\\\CHANGE=' + escape($.trim($('select.group_key').val()));
+ else
+ curr = '\\\\' + link.attr('href').replace(/^\/dump\/.+?\//,'');
+
+ if (hash.indexOf(curr) == -1)
+ newPanel.attr('url', hash + curr);
+ else
+ newPanel.attr('url', hash);
+
+ newPanel.setupPanel();
+
+ updateBodyWidth();
+ centerPanel(newPanel, true);
+ },
+ cache: false
+ });
+
+ return false;
+});
+
+$('form#signup, form#login').live('submit', function(){
+ var form = $(this);
+ var submit = form.find('input[type="submit"]');
+
+ if (submit.attr('disabled'))
+ return false;
+ submit.attr('disabled', 'disabled');
+
+ form.ajaxSubmit({
+ success: function(responseText, statusText) {
+ window.location.reload(true);
+ },
+ error: function (obj) {
+ alert(obj.responseText);
+ submit.removeAttr('disabled');
+ }
+ });
+
+ return false;
+});
+
+$('div.panel .content a:not(.facebox)').live('click', function(){
+ var link = $(this);
+ var curPanel = $(this).parents('div.panel:first');
+
+ curPanel.find('a.current').removeClass('current');
+ link.addClass('current');
+
+ var panel = $('<div class="panel additional"><center><img src="/demo/spinner.gif" class="spinner"></center></div>');
+ curPanel.nextAll('div.panel').remove().end().after(panel);
+
+ $.ajax({
+ url: this.href,
+ success: function(html){
+ link.addClass('current');
+
+ var newPanel = $(html);
+ panel.replaceWith(newPanel);
+ newPanel.setupPanel();
+
+ updateBodyWidth();
+ centerPanel(newPanel, true);
+ },
+ cache: false
+ });
+
+ return false;
+});
+
+$('div.panel ul.nav li.group select.group_key').live('change', function(){
+ var select = $(this);
+ var link = select.parents('a:first');
+ link.attr('href', link.attr('href').replace(/group:[a-z]+/,'group:'+select.val()));
+ link.click();
+});
+
+$('div#menubar select.collection').live('change', function(){
+ var select = $(this);
+ window.location = '/dump/' + select.val();
+});
+
+$(function(){
+ $(window).keydown(function(e){
+ if (e.which == 37 || e.which == 39)
+ return false;
+ });
+
+ $(window).keyup(function(e){
+ if (e.which == 37) {
+ var panel = findClosestPanel();
+ if (!panel)
+ return false;
+
+ var obj = panel.prev('div.panel');
+ if (obj.length)
+ centerPanel(obj);
+ else
+ centerPanel(panel);
+ return false;
+
+ } else if (e.which == 39) {
+ var panel = findClosestPanel();
+ if (!panel)
+ return false;
+
+ var obj = panel.next('div.panel');
+ if (obj.length)
+ centerPanel(obj);
+ else
+ centerPanel(panel);
+ return false;
+ }
+ });
+
+ var spinner = new Image();
+ spinner.src = '/demo/spinner.gif';
+
+ REPLAY = unescape(window.location.hash).split('\\\\').slice(1);
+ if (REPLAY.length > 0) {
+ var old = REPLAY;
+ REPLAY = [];
+ for (key in old) {
+ REPLAY.push(old[key]);
+ }
+ }
+
+ var panel = $('div.panel')
+ panel.setupPanel();
+
+ var width = panel.outerWidth();
+ $('body').css('marginLeft', ($(window).width()/2 - width/2) + 'px');
+
+ $('pre.prettyprint').prettify();
+});
225 public/javascripts/application.js
@@ -0,0 +1,225 @@
+// Place your application-specific JavaScript functions and classes here
+// This file is automatically included by javascript_include_tag :defaults
+
+/*
+$.fn.setupPanel = function(){
+ var numPanels = $('div.panel').length;
+
+ return $(this).each(function(){
+ var panel = $(this);
+
+ if (numPanels > 1)
+ panel.addClass('additional');
+
+ });
+};
+
+