edifice 0.0.5 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. data/.DS_Store +0 -0
  2. data/lib/edifice/form_model.rb +28 -0
  3. data/lib/edifice/helper.rb +16 -0
  4. data/lib/edifice/railtie.rb +10 -0
  5. data/lib/edifice/responder.rb +13 -0
  6. data/lib/edifice/version.rb +1 -1
  7. data/lib/edifice.rb +12 -2
  8. data/lib/public/javascripts/edifice/ajax_form.js +114 -174
  9. data/lib/public/javascripts/edifice/form.js +105 -0
  10. data/lib/tasks/edifice.rake +6 -0
  11. data/test/rails3/.gitignore +4 -0
  12. data/test/rails3/Gemfile +33 -0
  13. data/test/rails3/Gemfile.lock +75 -0
  14. data/test/rails3/README +256 -0
  15. data/test/rails3/Rakefile +7 -0
  16. data/test/rails3/app/controllers/application_controller.rb +3 -0
  17. data/test/rails3/app/controllers/posts_controller.rb +15 -0
  18. data/test/rails3/app/helpers/application_helper.rb +2 -0
  19. data/test/rails3/app/models/post.rb +10 -0
  20. data/test/rails3/app/views/layouts/application.html.erb +14 -0
  21. data/test/rails3/app/views/posts/new.html.erb +17 -0
  22. data/test/rails3/config/application.rb +50 -0
  23. data/test/rails3/config/boot.rb +6 -0
  24. data/test/rails3/config/database.yml +22 -0
  25. data/test/rails3/config/environment.rb +5 -0
  26. data/test/rails3/config/environments/development.rb +26 -0
  27. data/test/rails3/config/environments/production.rb +49 -0
  28. data/test/rails3/config/environments/test.rb +35 -0
  29. data/test/rails3/config/initializers/backtrace_silencers.rb +7 -0
  30. data/test/rails3/config/initializers/inflections.rb +10 -0
  31. data/test/rails3/config/initializers/mime_types.rb +5 -0
  32. data/test/rails3/config/initializers/secret_token.rb +7 -0
  33. data/test/rails3/config/initializers/session_store.rb +8 -0
  34. data/test/rails3/config/locales/en.yml +5 -0
  35. data/test/rails3/config/routes.rb +4 -0
  36. data/test/rails3/config.ru +4 -0
  37. data/test/rails3/db/seeds.rb +7 -0
  38. data/test/rails3/doc/README_FOR_APP +2 -0
  39. data/test/rails3/lib/tasks/.gitkeep +0 -0
  40. data/test/rails3/public/404.html +26 -0
  41. data/test/rails3/public/422.html +26 -0
  42. data/test/rails3/public/500.html +26 -0
  43. data/test/rails3/public/favicon.ico +0 -0
  44. data/test/rails3/public/images/rails.png +0 -0
  45. data/test/rails3/public/javascripts/.gitkeep +0 -0
  46. data/test/rails3/public/javascripts/application.js +0 -0
  47. data/test/rails3/public/javascripts/jquery-1.5.1.js +8316 -0
  48. data/test/rails3/public/robots.txt +5 -0
  49. data/test/rails3/public/stylesheets/.gitkeep +0 -0
  50. data/test/rails3/public/stylesheets/form.css +18 -0
  51. data/test/rails3/script/rails +6 -0
  52. data/test/rails3/vendor/plugins/.gitkeep +0 -0
  53. metadata +96 -7
  54. data/lib/edifice_helper.rb +0 -14
data/.DS_Store ADDED
Binary file
@@ -0,0 +1,28 @@
1
+ # kind of the evolution of the FormStruct
2
+
3
+ module Edifice
4
+ class FormModel
5
+ include ActiveModel::Validations
6
+ include ActiveModel::Conversion
7
+ extend ActiveModel::Naming
8
+
9
+ def initialize(attributes = {})
10
+ attributes.each { |n, v| send("#{n}=", v) if respond_to?("#{n}=") }
11
+ end
12
+
13
+ # default implementation, override as necessary
14
+ def save
15
+ valid?
16
+ end
17
+
18
+ def self.create(attributes = {})
19
+ form = new(attributes)
20
+ form.save
21
+ form
22
+ end
23
+
24
+ def persisted?
25
+ false
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,16 @@
1
+ module Edifice
2
+ module Helper
3
+ # put this in your layout somewhere
4
+ def edifice_meta_tags
5
+ %(<meta name='edifice-view_path' content='#{view_path_normalized}'/>
6
+ <meta name='edifice-view_name' content='#{view_name_normalized}'/>
7
+ <meta name='edifice-layout' content='#{layout_name}'/>).html_safe
8
+ end
9
+
10
+ # the default classes that get added to the body element when a view renders
11
+ # the c_ in front of view_path is for historical reasons
12
+ def edifice_body_classes
13
+ %(c_#{view_path} v_#{view_name} l_#{layout_name}").html_safe
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,10 @@
1
+ require 'edifice'
2
+ require 'rails'
3
+
4
+ module Edifice
5
+ class Railtie < Rails::Railtie
6
+ rake_tasks do
7
+ load "tasks/edifice.rake"
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,13 @@
1
+ # Basically a standard responder, but if
2
+ module Edifice
3
+ class Responder < ActionController::Responder
4
+ protected
5
+ def to_html
6
+ if controller.request.xhr? && !get? and has_errors? && default_action
7
+ render :action => default_action, :status => :unprocessable_entity, :layout => nil
8
+ else
9
+ super
10
+ end
11
+ end
12
+ end
13
+ end
@@ -1,3 +1,3 @@
1
1
  module Edifice
2
- VERSION = "0.0.5"
2
+ VERSION = "0.5.0"
3
3
  end
data/lib/edifice.rb CHANGED
@@ -1,6 +1,10 @@
1
- require 'edifice_helper'
1
+ require 'edifice/helper'
2
+ require 'edifice/form_model'
3
+ require 'edifice/responder'
2
4
 
3
5
  module Edifice
6
+ require 'edifice/railtie' if defined?(Rails)
7
+
4
8
  def self.install_js_files
5
9
  install_dir = ::Rails.application.paths.public.javascripts.first
6
10
  edifice_js_dir = File.join(File.dirname(__FILE__), 'public', 'javascripts', 'edifice')
@@ -17,6 +21,12 @@ module Edifice
17
21
  controller.after_filter(:write_edifice_headers)
18
22
  controller.alias_method_chain(:_render_template, :edifice)
19
23
  end
24
+
25
+ controller.class_eval do
26
+ def self.responder
27
+ Edifice::Responder
28
+ end
29
+ end
20
30
  end
21
31
 
22
32
  def _render_template_with_edifice(options)
@@ -57,4 +67,4 @@ end
57
67
 
58
68
  ActionController::Base.send :include, Edifice::Controller
59
69
  # ActionMailer::Base.send :include, Edifice::Controller
60
- ActionView::Base.send :include, EdificeHelper
70
+ ActionView::Base.send :include, Edifice::Helper
@@ -1,187 +1,127 @@
1
- /*
2
- Turn a standard form into an 'ajax' form.
1
+ (function($) {
2
+ var defaults = {
3
+ status_element: this, // element to set to .submitting/.success/.error as we submit
4
+ // a hash of input selector -> validation functions/names
5
+ validators: {},
6
+ // type of data to pass around (and thus what's seen by e.g. success)
7
+ dataType: 'html'
8
+ };
9
+
10
+ // EVENTS that fire during the course of the life of the submission:
11
+ // submit, invalid, success | error (+ user_error | server_error), complete
12
+ //
13
+ // -> success,errors + complete are all passed the data you would expect from
14
+ // jQuery.ajax
15
+
16
+ $.edifice_widgets.ajax_form = function() { return this.each(ajax_form); }
17
+
18
+ function ajax_form() {
19
+ var $form = $(this).edifice_form();
20
+ $form.settings = $form.read_options(defaults);
21
+ $.extend($form, methods);
3
22
 
4
- -- Relies on the action that the form points to to return a 422 with a new,
5
- 'errored' form if there is a (server-side) validation problem.
6
- Otherwise calls the various callbacks..
23
+ $form.initialize();
24
+ }
25
+ var methods = {
26
+ initialize: function() {
27
+ this.prepare_validators();
28
+ this.prepare_submit();
29
+ },
30
+
31
+ prepare_validators: function() {
32
+ var $form = this;
7
33
 
8
- Extra arguments:
9
- - status_sel: the selector of an element to set the classes 'submitting/success/error'
10
- on depending on the state of the form. Defaults to the form itself.
11
- - validators: a hash of input selectors to validation functions (or names)
12
- for client-side validation.
13
- */
14
-
15
- (function($){
16
- $.edifice_widgets.ajax_form = function() {
17
- return this.each(function(){
18
- var settings = $(this).read_options({
19
- status_sel: this, // element to set to .submitting/.success/.error as we submit
20
- //default callbacks
21
- error: function(x, t, e, form){
22
- if (x.status >= 400 && x.status < 500) { // a CLIENT ERROR
23
- //validation failed replace form with new one
24
- var $new_form = $(x.responseText);
25
- $(form).replaceWith($new_form);
26
- // -- not sure this is the best way to pass extra settings through, but it seems to work.
27
- $new_form.create_widget('ajax_form', settings);
28
- } else if (x.status >= 500 && x.status < 600) { // a SERVER ERROR
29
- // replace the body proxy with the result (i.e replace the entire page)
30
- $('#body_proxy').before($(x.responseText)).remove();
31
- } else {
32
- alert('Form failed. Please try again.');
33
- }
34
- },
35
- success: function(d, s, form){},
36
- complete: function(x, t, form){},
37
- submit: function(e){}, // return false to cancel submit
38
- validators: {}
39
- });
34
+ // setup validators from settings
35
+ for (var selector in $form.settings.validators) {
36
+ $form.set_validator($(selector), $form.settings.validators[selector]);
37
+ }
38
+
39
+ // setup validators from html
40
+ $form.fields().filter('[data-widget-validator]').each(function() {
41
+ $form.set_validator($(this), $(this).attr('data-widget-validator'));
42
+ });
43
+
44
+ // listen to validator
45
+ this.fields().live('change.ajax_form focusout.ajax_form', function() {
46
+ $form.validate($(this));
47
+ });
48
+ },
49
+
50
+ prepare_submit: function() {
51
+ var $form = this;
52
+ this.submit(function(event) {
53
+ event.preventDefault();
40
54
 
41
- // prepare the validators that are references
42
- for (var selector in settings.validators) {
43
- var validator = settings.validators[selector];
44
- if (typeof(validator) === 'string') {
45
- validator = settings.validators[selector] = $.edifice_widgets.ajax_form.validators[validator];
46
- }
47
- $(selector).bind('change.ajax_form', {validator: validator}, function(event) {
48
- validate($(this), event.data.validator);
49
- });
55
+ // do pre-submit validations
56
+ if (!$form.valid()) {
57
+ $form.trigger('invalid');
58
+ $form.error_fields().eq(0).focus(); // focus the first error
59
+ return false; // we are done.
50
60
  }
51
61
 
62
+ $form.submits().attr('disabled', true); // disable submit buttons
52
63
 
53
- function set_status_class(name) {
54
- $.each(['form_submitting', 'form_success', 'form_error'], function(i, old_name) {
55
- $(settings.status_sel).removeClass(old_name);
56
- });
57
- $(settings.status_sel).addClass(name);
58
- };
64
+ // TODO - set status class
59
65
 
60
- $(this).bind("submit.ajax_form", function(ev){
61
- var form = this;
62
-
63
- ev.preventDefault();
64
-
65
- // do client-side validations
66
- var valid = true, $first_error;
67
- for (var selector in settings.validators) {
68
- if (!validate($(selector), settings.validators[selector])) {
69
- if (valid) { valid = false; $first_error = $(selector); }
70
- }
71
- }
72
- if (!valid) { $first_error.focus(); return false; }
73
-
74
- // fire a custom event allowing anyone to cancel the submission
75
- var event = $.Event('submitting.ajax_form');
76
- event.cancel = false;
77
- event.submitEvent = ev;
78
- $(form).trigger(event);
79
-
80
- if (event.cancel)
81
- return false;
66
+ // send up the form and process the results
67
+ $.ajax({
68
+ url: $form.attr('action'), type: $form.attr('method'),
69
+ dataType: $form.settings.dataType,
70
+ data: $.param($form.serializeArray()),
71
+ cache: false,
72
+ error: function (x, t, e) { $form.error(x, t, e); },
73
+ success: function (data, status) {
74
+ $form.trigger('success', data, status);
75
+ },
76
+ complete: function (request, text_status) {
77
+ $form.trigger('complete', request, text_status);
82
78
 
83
- // also run the user defined submit function
84
- if (settings.submit(ev) === false) return false;
85
-
86
- // disable the first submit button on the form
87
- $(form).find('input[type=submit]:first').attr('disabled', true);
88
-
89
- // set the status
90
- set_status_class('form_submitting');
91
-
92
- // send up the form and process the results
93
- $.ajax({
94
- cache: false,
95
- data: $.param($(form).serializeArray()),
96
- type: form.method,
97
- url: form.action,
98
- error: function (x, t, e) {
99
- settings.error(x, t, e, form);
100
- set_status_class('form_error');
101
- },
102
- success: function (d, s) {
103
- settings.success(d, s, form);
104
- set_status_class('form_success');
105
- },
106
- complete: function (x, t) {
107
- // enable the first submit button on the form
108
- $(form).find('input[type=submit]:first').attr('disabled', false);
109
-
110
- settings.complete(x, t, form);
111
- }
112
- });
113
-
114
- return false;
79
+ $form.submits().removeAttr('disabled');
80
+ }
115
81
  });
116
- });
117
- };
118
-
119
- // call this on a div that represents a form element.
120
- // sets the right classes and enables disables actual form elements
121
- $.fn.form_element_state = function(command) {
122
- switch (command) {
123
- case 'disabled':
124
- this.addClass('disabled')
125
- .find('input, select').attr('disabled', true);
126
- break;
127
- case 'enabled':
128
- this.removeClass('disabled')
129
- .find('input, select').removeAttr('disabled');
130
- break;
131
- }
132
- return this;
133
- };
134
-
135
- function clearError(id) {
136
- // run over the input and the label pointing to it
137
- $('.fieldWithErrors').find('> #' + id + ', > label[for=' + id + ']').unwrap();
138
- $('label[for=' + id + ']').next('.formError').remove();
139
- };
140
-
141
- function addError(id, error) {
142
- // right now this is causing focus issues and we don't need it, so I won't waste time
143
- // fixing it.... but it could be used for different form layouts...
144
- // $('#' + id + ', label[for=' + id + ']').wrap('<div class="fieldWithErrors"></div>');
145
- $('label[for=' + id + ']').after('<div class="formError">' + error + '</div>');
146
- };
147
-
148
- // calls a validator, and takes care of setting the errors string
149
- function validate($input, validator) {
150
- var id = $input.attr('id');
151
- // clear existing validations
152
- clearError(id);
153
-
154
- var error = validator($input);
155
- if (error === true) {
156
- return true;
157
- } else {
158
- addError(id, error);
82
+
159
83
  return false;
160
- }
161
- }
84
+ });
85
+ },
162
86
 
163
- // validators expect a form element and return true or an error message
164
- $.edifice_widgets.ajax_form.validators = {
165
- not_empty: function($input) {
166
- return $input.val() ? true : 'must not be empty';
167
- }
168
- };
169
-
170
- // standardize .focus() for input tags. IE doesn't place the focus at the
171
- // end of the input box, this fixes it
172
- $.fn.inputFocus = function() {
173
- return this.each(function(){
174
- if ($.browser.msie) {
175
- if (this.createTextRange) {
176
- var FieldRange = this.createTextRange();
177
- FieldRange.moveStart('character', this.value.length);
178
- FieldRange.collapse();
179
- FieldRange.select();
180
- }
87
+ error: function(request, text_status, error) {
88
+ this.trigger('error', request, status, error);
89
+
90
+ // handle the different possible errors that we can see
91
+ if (request.status >= 400 && request.status < 500) {
92
+ // CLIENT ERROR -- server-side validation failed.
93
+ this.trigger('client_error', request, status, error);
94
+
95
+ // if data is html, we replace this content of the form with the content
96
+ // of the form that we've just received back
97
+ if (this.settings.dataType === 'html') {
98
+ // wrap in a div incase there are a bunch of floating elements, pull the form out
99
+ var $new_form = $('<div>').append(request.responseText).find('#' + this.attr('id'));
100
+
101
+ this.html($new_form.html());
102
+ this.prepare_validators();
103
+
104
+ } else if (this.settings.dataType === 'json') {
105
+ // we will be receiving an error object back, we can pass it straight into form.js
106
+ this.set_errors($.parseJSON(request.responseText));
181
107
  } else {
182
- this.focus();
108
+ throw "Don't know how to handle dataType " + this.settings.dataType;
183
109
  }
184
- });
185
- };
186
- }(jQuery));
187
-
110
+ } else if (x.status >= 500 && x.status < 600) {
111
+ // a SERVER ERROR -- something unrecoverable happened on the server
112
+ this.trigger('server_error', request, status, error);
113
+
114
+ // we aren't going to do anything here.
115
+ // FIXME: we should probably have a way to set this behaviour at the application level.
116
+ // for instance, you probably just want to redirect to somewhere, or show a dialog,
117
+ // or popup something or.....?
118
+ } else {
119
+ // some other kind of error. Revisit
120
+ alert('Form failed. Please try again.');
121
+ }
122
+
123
+
124
+ },
125
+ }
126
+
127
+ }(jQuery));
@@ -0,0 +1,105 @@
1
+ (function($) {
2
+ $.edifice = $.edifice || {};
3
+
4
+ // add methods to a form element so it can be easily manipulated
5
+ //
6
+ // add client side validation
7
+ // understands:
8
+ // - rails form structures
9
+ // - rails form errors in JSON
10
+ // - etc
11
+ $.fn.edifice_form = function() {
12
+ $.extend(this, methods);
13
+ return this;
14
+ };
15
+
16
+ // VALIDATORS:
17
+ // validators expect a form element and return true or an error message
18
+ $.fn.edifice_form.validators = {
19
+ not_empty: function($input) {
20
+ return $input.val() ? true : 'must not be empty';
21
+ }
22
+ };
23
+
24
+
25
+ // in these 'this' is the $form
26
+ var methods = {
27
+ fields: function() {
28
+ return this.find('input, textarea, select');
29
+ },
30
+
31
+ error_fields: function() {
32
+ var $form = this;
33
+ return this.fields().filter(function() {
34
+ return $form.has_error($(this));
35
+ });
36
+ },
37
+
38
+ submits: function() {
39
+ return this.find('input[type=submit], button[type=submit]');
40
+ },
41
+
42
+ valid: function() {
43
+ var $form = this, valid = true;
44
+
45
+ this.fields().each(function() {
46
+ if (!$form.validate($(this))) { valid = false; }
47
+ });
48
+
49
+ return valid;
50
+ },
51
+
52
+ // 'prepare' a validator --> validator returns true or an error string
53
+ set_validator: function($field, validator) {
54
+ if (typeof validator === 'string') {
55
+ validator = $.fn.edifice_form.validators[validator] || validator;
56
+ if (typeof validator !== 'function') { throw "Validator not defined: '" + validator + "'"; }
57
+ };
58
+ $field.data('validator', validator);
59
+ },
60
+
61
+ // validate a single field
62
+ validate: function($field) {
63
+ var validator;
64
+ if (validator = $field.data('validator')) {
65
+ var error = validator($field), valid = error === true;
66
+ this.clear_error($field);
67
+ if (!valid) {
68
+ this.add_error($field, error);
69
+ }
70
+
71
+ return valid;
72
+ } else {
73
+ return true;
74
+ }
75
+ },
76
+
77
+ has_error: function($field) {
78
+ return $field.parent('.field_with_errors').length > 0;
79
+ },
80
+
81
+ clear_error: function($field) {
82
+ var id = $field.attr('id');
83
+ if (this.has_error($field)) { $field.unwrap() }
84
+
85
+ this.find('.field_with_errors label[for=' + id + ']')
86
+ .unwrap()
87
+ .next('.formError').remove();
88
+ },
89
+
90
+ add_error: function($field, error) {
91
+ var id = $field.attr('id');
92
+ $field.wrap('<div class="field_with_errors">');
93
+ this.find('label[for=' + id + ']')
94
+ .wrap('<div class="field_with_errors">')
95
+ .after('<div class="formError">' + error + '</div>');
96
+ },
97
+
98
+ set_errors: function(errors) {
99
+ for (var name in errors) {
100
+ this.add_error(this.fields().filter('[name*=' + name + ']'), errors[name][0]);
101
+ }
102
+ }
103
+ };
104
+
105
+ }(jQuery));
@@ -0,0 +1,6 @@
1
+ namespace :edifice do
2
+ desc "Installs the edifice JS files into public/javascripts/edifice"
3
+ task :install do
4
+ Edifice.install_js_files
5
+ end
6
+ end
@@ -0,0 +1,4 @@
1
+ .bundle
2
+ db/*.sqlite3
3
+ log/*.log
4
+ tmp/
@@ -0,0 +1,33 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gem 'rails', '3.0.7'
4
+
5
+ # Bundle edge Rails instead:
6
+ # gem 'rails', :git => 'git://github.com/rails/rails.git'
7
+
8
+ gem 'sqlite3'
9
+
10
+ gem 'dynamic_form'
11
+
12
+ # Use unicorn as the web server
13
+ # gem 'unicorn'
14
+
15
+ # Deploy with Capistrano
16
+ # gem 'capistrano'
17
+
18
+ # To use debugger (ruby-debug for Ruby 1.8.7+, ruby-debug19 for Ruby 1.9.2+)
19
+ # gem 'ruby-debug'
20
+ # gem 'ruby-debug19', :require => 'ruby-debug'
21
+
22
+ # Bundle the extra gems:
23
+ # gem 'bj'
24
+ # gem 'nokogiri'
25
+ # gem 'sqlite3-ruby', :require => 'sqlite3'
26
+ # gem 'aws-s3', :require => 'aws/s3'
27
+
28
+ # Bundle gems for the local environment. Make sure to
29
+ # put test-only gems in this group so their generators
30
+ # and rake tasks are available in development mode:
31
+ # group :development, :test do
32
+ # gem 'webrat'
33
+ # end
@@ -0,0 +1,75 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ abstract (1.0.0)
5
+ actionmailer (3.0.7)
6
+ actionpack (= 3.0.7)
7
+ mail (~> 2.2.15)
8
+ actionpack (3.0.7)
9
+ activemodel (= 3.0.7)
10
+ activesupport (= 3.0.7)
11
+ builder (~> 2.1.2)
12
+ erubis (~> 2.6.6)
13
+ i18n (~> 0.5.0)
14
+ rack (~> 1.2.1)
15
+ rack-mount (~> 0.6.14)
16
+ rack-test (~> 0.5.7)
17
+ tzinfo (~> 0.3.23)
18
+ activemodel (3.0.7)
19
+ activesupport (= 3.0.7)
20
+ builder (~> 2.1.2)
21
+ i18n (~> 0.5.0)
22
+ activerecord (3.0.7)
23
+ activemodel (= 3.0.7)
24
+ activesupport (= 3.0.7)
25
+ arel (~> 2.0.2)
26
+ tzinfo (~> 0.3.23)
27
+ activeresource (3.0.7)
28
+ activemodel (= 3.0.7)
29
+ activesupport (= 3.0.7)
30
+ activesupport (3.0.7)
31
+ arel (2.0.10)
32
+ builder (2.1.2)
33
+ dynamic_form (1.1.4)
34
+ erubis (2.6.6)
35
+ abstract (>= 1.0.0)
36
+ i18n (0.5.0)
37
+ mail (2.2.19)
38
+ activesupport (>= 2.3.6)
39
+ i18n (>= 0.4.0)
40
+ mime-types (~> 1.16)
41
+ treetop (~> 1.4.8)
42
+ mime-types (1.16)
43
+ polyglot (0.3.1)
44
+ rack (1.2.3)
45
+ rack-mount (0.6.14)
46
+ rack (>= 1.0.0)
47
+ rack-test (0.5.7)
48
+ rack (>= 1.0)
49
+ rails (3.0.7)
50
+ actionmailer (= 3.0.7)
51
+ actionpack (= 3.0.7)
52
+ activerecord (= 3.0.7)
53
+ activeresource (= 3.0.7)
54
+ activesupport (= 3.0.7)
55
+ bundler (~> 1.0)
56
+ railties (= 3.0.7)
57
+ railties (3.0.7)
58
+ actionpack (= 3.0.7)
59
+ activesupport (= 3.0.7)
60
+ rake (>= 0.8.7)
61
+ thor (~> 0.14.4)
62
+ rake (0.9.2)
63
+ sqlite3 (1.3.3)
64
+ thor (0.14.6)
65
+ treetop (1.4.9)
66
+ polyglot (>= 0.3.1)
67
+ tzinfo (0.3.27)
68
+
69
+ PLATFORMS
70
+ ruby
71
+
72
+ DEPENDENCIES
73
+ dynamic_form
74
+ rails (= 3.0.7)
75
+ sqlite3