mirror of
https://github.com/TracksApp/tracks.git
synced 2026-01-07 09:48:50 +01:00
Updated to run under Rails 0.11
git-svn-id: http://www.rousette.org.uk/svn/tracks-repos/trunk@48 a4c988fc-2ded-0310-b66e-134b36920a42
This commit is contained in:
parent
dec033fb30
commit
f6eeb1d20d
3 changed files with 529 additions and 0 deletions
189
tracks/README
Normal file
189
tracks/README
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
== Welcome to Rails
|
||||
|
||||
Rails is a web-application and persistance framework that includes everything
|
||||
needed to create database-backed web-applications according to the
|
||||
Model-View-Control pattern of separation. This pattern splits the view (also
|
||||
called the presentation) into "dumb" templates that are primarily responsible
|
||||
for inserting pre-build 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 a 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/activerecord/README.html.
|
||||
|
||||
The controller and view is 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/actionpack/README.html.
|
||||
|
||||
|
||||
== Requirements
|
||||
|
||||
* Database and driver (MySQL, PostgreSQL, or SQLite)
|
||||
* Rake[http://rake.rubyforge.org] for running tests and the generating documentation
|
||||
|
||||
== Optionals
|
||||
|
||||
* Apache 1.3.x or 2.x or lighttpd 1.3.11+ (or any FastCGI-capable webserver with a
|
||||
mod_rewrite-like module)
|
||||
* FastCGI (or mod_ruby) for better performance on Apache
|
||||
|
||||
== Getting started
|
||||
|
||||
1. Run the WEBrick servlet: <tt>ruby script/server</tt>
|
||||
(run with --help for options)
|
||||
2. Go to http://localhost:3000/ and get "Congratulations, you've put Ruby on Rails!"
|
||||
3. Follow the guidelines on the "Congratulations, you've put Ruby on Rails!" screen
|
||||
|
||||
|
||||
== Example for Apache conf
|
||||
|
||||
<VirtualHost *:80>
|
||||
ServerName rails
|
||||
DocumentRoot /path/application/public/
|
||||
ErrorLog /path/application/log/server.log
|
||||
|
||||
<Directory /path/application/public/>
|
||||
Options ExecCGI FollowSymLinks
|
||||
AllowOverride all
|
||||
Allow from all
|
||||
Order allow,deny
|
||||
</Directory>
|
||||
</VirtualHost>
|
||||
|
||||
NOTE: Be sure that CGIs can be executed in that directory as well. So ExecCGI
|
||||
should be on and ".cgi" should respond. All requests from 127.0.0.1 goes
|
||||
through CGI, so no Apache restart is necessary for changes. All other requests
|
||||
goes through FCGI (or mod_ruby) that requires restart to show changes.
|
||||
|
||||
|
||||
== Example for lighttpd conf (with FastCGI)
|
||||
|
||||
server.port = 8080
|
||||
server.bind = "127.0.0.1"
|
||||
# server.event-handler = "freebsd-kqueue" # needed on OS X
|
||||
|
||||
server.modules = ( "mod_rewrite", "mod_fastcgi" )
|
||||
|
||||
url.rewrite = ( "^/$" => "index.html", "^([^.]+)$" => "$1.html" )
|
||||
server.error-handler-404 = "/dispatch.fcgi"
|
||||
|
||||
server.document-root = "/path/application/public"
|
||||
server.errorlog = "/path/application/log/server.log"
|
||||
|
||||
fastcgi.server = ( ".fcgi" =>
|
||||
( "localhost" =>
|
||||
(
|
||||
"min-procs" => 1,
|
||||
"max-procs" => 5,
|
||||
"socket" => "/tmp/application.fcgi.socket",
|
||||
"bin-path" => "/path/application/public/dispatch.fcgi",
|
||||
"bin-environment" => ( "RAILS_ENV" => "development" )
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
== Debugging Rails
|
||||
|
||||
Have "tail -f" commands running on both the server.log, production.log, and
|
||||
test.log files. 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.
|
||||
|
||||
|
||||
== Breakpoints
|
||||
|
||||
Breakpoint support is available through the script/breakpointer client. This
|
||||
means that you can break out of execution at any point in the code, investigate
|
||||
and change the model, AND then resume execution! Example:
|
||||
|
||||
class WeblogController < ActionController::Base
|
||||
def index
|
||||
@posts = Post.find_all
|
||||
breakpoint "Breaking out from the list"
|
||||
end
|
||||
end
|
||||
|
||||
So the controller will accept the action, run the first line, then present you
|
||||
with a IRB prompt in the breakpointer window. Here you can do things like:
|
||||
|
||||
Executing breakpoint "Breaking out from the list" at .../webrick_server.rb:16 in 'breakpoint'
|
||||
|
||||
>> @posts.inspect
|
||||
=> "[#<Post:0x14a6be8 @attributes={\"title\"=>nil, \"body\"=>nil, \"id\"=>\"1\"}>,
|
||||
#<Post:0x14a6620 @attributes={\"title\"=>\"Rails you know!\", \"body\"=>\"Only ten..\", \"id\"=>\"2\"}>]"
|
||||
>> @posts.first.title = "hello from a breakpoint"
|
||||
=> "hello from a breakpoint"
|
||||
|
||||
...and even better is that 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 press CTRL-D
|
||||
|
||||
|
||||
== Console
|
||||
|
||||
You can interact with the domain model by starting the console through script/console.
|
||||
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. Start the script without arguments to see the options.
|
||||
|
||||
|
||||
== Description of contents
|
||||
|
||||
app
|
||||
Holds all the code that's specific to this particular application.
|
||||
|
||||
app/controllers
|
||||
Holds controllers that should be named like weblog_controller.rb for
|
||||
automated URL mapping. All controllers should descend from
|
||||
ActionController::Base.
|
||||
|
||||
app/models
|
||||
Holds models that should be named like post.rb.
|
||||
Most models will descent from ActiveRecord::Base.
|
||||
|
||||
app/views
|
||||
Holds the template files for the view that should be named like
|
||||
weblog/index.rhtml for the WeblogController#index action. All views uses eRuby
|
||||
syntax. This directory can also be used to keep stylesheets, images, and so on
|
||||
that can be symlinked to public.
|
||||
|
||||
app/helpers
|
||||
Holds view helpers that should be named like weblog_helper.rb.
|
||||
|
||||
config
|
||||
Configuration files for the Rails environment, the routing map, the database, and other dependencies.
|
||||
|
||||
components
|
||||
Self-contained mini-applications that can bundle controllers, models, and views together.
|
||||
|
||||
lib
|
||||
Application specific libraries. Basically, any kind of custom code that doesn't
|
||||
belong controllers, models, or helpers. This directory is in the load path.
|
||||
|
||||
public
|
||||
The directory available for the web server. Contains sub-directories for images, stylesheets,
|
||||
and javascripts. Also contains the dispatchers and the default HTML files.
|
||||
|
||||
script
|
||||
Helper scripts for automation and generation.
|
||||
|
||||
test
|
||||
Unit and functional tests along with fixtures.
|
||||
|
||||
vendor
|
||||
External libraries that the application depend on. This directory is in the load path.
|
||||
336
tracks/public/javascripts/prototype.js
vendored
Normal file
336
tracks/public/javascripts/prototype.js
vendored
Normal file
|
|
@ -0,0 +1,336 @@
|
|||
/* Prototype: an object-oriented Javascript library, version 1.0.1
|
||||
* (c) 2005 Sam Stephenson <sam@conio.net>
|
||||
*
|
||||
* Prototype is freely distributable under the terms of an MIT-style license.
|
||||
* For details, see http://prototype.conio.net/
|
||||
*/
|
||||
|
||||
Prototype = {
|
||||
Version: '1.0.1'
|
||||
}
|
||||
|
||||
Class = {
|
||||
create: function() {
|
||||
return function() {
|
||||
this.initialize.apply(this, arguments);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Abstract = new Object();
|
||||
|
||||
Object.prototype.extend = function(object) {
|
||||
for (property in object) {
|
||||
this[property] = object[property];
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
Function.prototype.bind = function(object) {
|
||||
var method = this;
|
||||
return function() {
|
||||
method.apply(object, arguments);
|
||||
}
|
||||
}
|
||||
|
||||
Function.prototype.bindAsEventListener = function(object) {
|
||||
var method = this;
|
||||
return function(event) {
|
||||
method.call(object, event || window.event);
|
||||
}
|
||||
}
|
||||
|
||||
Try = {
|
||||
these: function() {
|
||||
var returnValue;
|
||||
|
||||
for (var i = 0; i < arguments.length; i++) {
|
||||
var lambda = arguments[i];
|
||||
try {
|
||||
returnValue = lambda();
|
||||
break;
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
}
|
||||
|
||||
Toggle = {
|
||||
display: function() {
|
||||
for (var i = 0; i < elements.length; i++) {
|
||||
var element = $(elements[i]);
|
||||
element.style.display =
|
||||
(element.style.display == 'none' ? '' : 'none');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
function $() {
|
||||
var elements = new Array();
|
||||
|
||||
for (var i = 0; i < arguments.length; i++) {
|
||||
var element = arguments[i];
|
||||
if (typeof element == 'string')
|
||||
element = document.getElementById(element);
|
||||
|
||||
if (arguments.length == 1)
|
||||
return element;
|
||||
|
||||
elements.push(element);
|
||||
}
|
||||
|
||||
return elements;
|
||||
}
|
||||
|
||||
function getElementsByClassName(className, element) {
|
||||
var children = (element || document).getElementsByTagName('*');
|
||||
var elements = new Array();
|
||||
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
var child = children[i];
|
||||
var classNames = child.className.split(' ');
|
||||
for (var j = 0; j < classNames.length; j++) {
|
||||
if (classNames[j] == className) {
|
||||
elements.push(child);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return elements;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
Ajax = {
|
||||
getTransport: function() {
|
||||
return Try.these(
|
||||
function() {return new ActiveXObject('Msxml2.XMLHTTP')},
|
||||
function() {return new ActiveXObject('Microsoft.XMLHTTP')},
|
||||
function() {return new XMLHttpRequest()}
|
||||
) || false;
|
||||
},
|
||||
|
||||
emptyFunction: function() {}
|
||||
}
|
||||
|
||||
Ajax.Base = function() {};
|
||||
Ajax.Base.prototype = {
|
||||
setOptions: function(options) {
|
||||
this.options = {
|
||||
method: 'post',
|
||||
asynchronous: true,
|
||||
parameters: ''
|
||||
}.extend(options || {});
|
||||
}
|
||||
}
|
||||
|
||||
Ajax.Request = Class.create();
|
||||
Ajax.Request.Events =
|
||||
['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
|
||||
|
||||
Ajax.Request.prototype = (new Ajax.Base()).extend({
|
||||
initialize: function(url, options) {
|
||||
this.transport = Ajax.getTransport();
|
||||
this.setOptions(options);
|
||||
|
||||
try {
|
||||
if (this.options.method == 'get')
|
||||
url += '?' + this.options.parameters + '&_=';
|
||||
|
||||
this.transport.open(this.options.method, url, true);
|
||||
|
||||
if (this.options.asynchronous) {
|
||||
this.transport.onreadystatechange = this.onStateChange.bind(this);
|
||||
setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10);
|
||||
}
|
||||
|
||||
if (this.options.method == 'post') {
|
||||
this.transport.setRequestHeader('Connection', 'close');
|
||||
this.transport.setRequestHeader('Content-type',
|
||||
'application/x-www-form-urlencoded');
|
||||
}
|
||||
|
||||
this.transport.send(this.options.method == 'post' ?
|
||||
this.options.parameters + '&_=' : null);
|
||||
|
||||
} catch (e) {
|
||||
}
|
||||
},
|
||||
|
||||
onStateChange: function() {
|
||||
var readyState = this.transport.readyState;
|
||||
if (readyState != 1)
|
||||
this.respondToReadyState(this.transport.readyState);
|
||||
},
|
||||
|
||||
respondToReadyState: function(readyState) {
|
||||
var event = Ajax.Request.Events[readyState];
|
||||
(this.options['on' + event] || Ajax.emptyFunction)(this.transport);
|
||||
}
|
||||
});
|
||||
|
||||
Ajax.Updater = Class.create();
|
||||
Ajax.Updater.prototype = (new Ajax.Base()).extend({
|
||||
initialize: function(container, url, options) {
|
||||
this.container = $(container);
|
||||
this.setOptions(options);
|
||||
|
||||
if (this.options.asynchronous) {
|
||||
this.onComplete = this.options.onComplete;
|
||||
this.options.onComplete = this.updateContent.bind(this);
|
||||
}
|
||||
|
||||
this.request = new Ajax.Request(url, this.options);
|
||||
|
||||
if (!this.options.asynchronous)
|
||||
this.updateContent();
|
||||
},
|
||||
|
||||
updateContent: function() {
|
||||
this.container.innerHTML = this.request.transport.responseText;
|
||||
if (this.onComplete) this.onComplete(this.request);
|
||||
}
|
||||
});
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
Field = {
|
||||
clear: function() {
|
||||
for (var i = 0; i < arguments.length; i++)
|
||||
$(arguments[i]).value = '';
|
||||
},
|
||||
|
||||
focus: function(element) {
|
||||
$(element).focus();
|
||||
},
|
||||
|
||||
present: function() {
|
||||
for (var i = 0; i < arguments.length; i++)
|
||||
if ($(arguments[i]).value == '') return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
Form = {
|
||||
serialize: function(form) {
|
||||
var elements = Form.getElements($(form));
|
||||
var queryComponents = new Array();
|
||||
|
||||
for (var i = 0; i < elements.length; i++) {
|
||||
var queryComponent = Form.Element.serialize(elements[i]);
|
||||
if (queryComponent)
|
||||
queryComponents.push(queryComponent);
|
||||
}
|
||||
|
||||
return queryComponents.join('&');
|
||||
},
|
||||
|
||||
getElements: function(form) {
|
||||
form = $(form);
|
||||
var elements = new Array();
|
||||
|
||||
for (tagName in Form.Element.Serializers) {
|
||||
var tagElements = form.getElementsByTagName(tagName);
|
||||
for (var j = 0; j < tagElements.length; j++)
|
||||
elements.push(tagElements[j]);
|
||||
}
|
||||
return elements;
|
||||
}
|
||||
}
|
||||
|
||||
Form.Element = {
|
||||
serialize: function(element) {
|
||||
element = $(element);
|
||||
var method = element.tagName.toLowerCase();
|
||||
var parameter = Form.Element.Serializers[method](element);
|
||||
|
||||
if (parameter)
|
||||
return encodeURIComponent(parameter[0]) + '=' +
|
||||
encodeURIComponent(parameter[1]);
|
||||
},
|
||||
|
||||
getValue: function(element) {
|
||||
element = $(element);
|
||||
var method = element.tagName.toLowerCase();
|
||||
var parameter = Form.Element.Serializers[method](element);
|
||||
|
||||
if (parameter)
|
||||
return parameter[1];
|
||||
}
|
||||
}
|
||||
|
||||
Form.Element.Serializers = {
|
||||
input: function(element) {
|
||||
switch (element.type.toLowerCase()) {
|
||||
case 'hidden':
|
||||
case 'text':
|
||||
return Form.Element.Serializers.textarea(element);
|
||||
case 'checkbox':
|
||||
case 'radio':
|
||||
return Form.Element.Serializers.inputSelector(element);
|
||||
}
|
||||
},
|
||||
|
||||
inputSelector: function(element) {
|
||||
if (element.checked)
|
||||
return [element.name, element.value];
|
||||
},
|
||||
|
||||
textarea: function(element) {
|
||||
return [element.name, element.value];
|
||||
},
|
||||
|
||||
select: function(element) {
|
||||
var index = element.selectedIndex;
|
||||
return [element.name, (index >= 0) ? element.options[index].value : ''];
|
||||
}
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
Abstract.TimedObserver = function() {}
|
||||
Abstract.TimedObserver.prototype = {
|
||||
initialize: function(element, frequency, callback) {
|
||||
this.frequency = frequency;
|
||||
this.element = $(element);
|
||||
this.callback = callback;
|
||||
|
||||
this.lastValue = this.getValue();
|
||||
this.registerCallback();
|
||||
},
|
||||
|
||||
registerCallback: function() {
|
||||
setTimeout(this.onTimerEvent.bind(this), this.frequency * 1000);
|
||||
},
|
||||
|
||||
onTimerEvent: function() {
|
||||
var value = this.getValue();
|
||||
if (this.lastValue != value) {
|
||||
this.callback(this.element, value);
|
||||
this.lastValue = value;
|
||||
}
|
||||
|
||||
this.registerCallback();
|
||||
}
|
||||
}
|
||||
|
||||
Form.Element.Observer = Class.create();
|
||||
Form.Element.Observer.prototype = (new Abstract.TimedObserver()).extend({
|
||||
getValue: function() {
|
||||
return Form.Element.getValue(this.element);
|
||||
}
|
||||
});
|
||||
|
||||
Form.Observer = Class.create();
|
||||
Form.Observer.prototype = (new Abstract.TimedObserver()).extend({
|
||||
getValue: function() {
|
||||
return Form.serialize(this.element);
|
||||
}
|
||||
});
|
||||
|
||||
4
tracks/script/runner
Executable file
4
tracks/script/runner
Executable file
|
|
@ -0,0 +1,4 @@
|
|||
#!/usr/local/bin/ruby
|
||||
|
||||
require File.dirname(__FILE__) + '/../config/environment'
|
||||
eval(ARGV.first)
|
||||
Loading…
Add table
Add a link
Reference in a new issue