apotomo 1.0.0.beta1 → 1.0.0.beta2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. data/Gemfile +1 -1
  2. data/README.rdoc +144 -90
  3. data/Rakefile +2 -1
  4. data/lib/apotomo.rb +1 -3
  5. data/lib/apotomo/rails/controller_methods.rb +16 -2
  6. data/lib/apotomo/request_processor.rb +1 -8
  7. data/lib/apotomo/version.rb +1 -1
  8. data/lib/apotomo/widget.rb +4 -2
  9. data/lib/apotomo/widget_shortcuts.rb +18 -5
  10. data/test/dummy/Rakefile +7 -0
  11. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  12. data/test/dummy/config.ru +4 -0
  13. data/test/dummy/config/database.yml +22 -0
  14. data/test/dummy/config/locales/en.yml +5 -0
  15. data/{rails/init.rb → test/dummy/db/test.sqlite3} +0 -0
  16. data/test/dummy/public/404.html +26 -0
  17. data/test/dummy/public/422.html +26 -0
  18. data/test/dummy/public/500.html +26 -0
  19. data/test/dummy/public/favicon.ico +0 -0
  20. data/test/dummy/public/javascripts/application.js +2 -0
  21. data/test/dummy/public/javascripts/controls.js +965 -0
  22. data/test/dummy/public/javascripts/dragdrop.js +974 -0
  23. data/test/dummy/public/javascripts/effects.js +1123 -0
  24. data/test/dummy/public/javascripts/prototype.js +6001 -0
  25. data/test/dummy/public/javascripts/rails.js +175 -0
  26. data/test/dummy/script/rails +6 -0
  27. data/test/fixtures/mouse/content.html.erb +1 -0
  28. data/test/fixtures/mouse/eating.html.erb +1 -0
  29. data/test/fixtures/mouse/educate.html.erb +1 -0
  30. data/test/fixtures/mouse/feed.html.erb +1 -0
  31. data/test/fixtures/mouse/make_me_squeak.html.erb +1 -0
  32. data/test/fixtures/mouse/posing.html.erb +1 -0
  33. data/test/fixtures/mouse/snuggle.html.erb +1 -0
  34. data/test/rails/controller_methods_test.rb +11 -4
  35. data/test/unit/request_processor_test.rb +1 -8
  36. data/test/unit/widget_shortcuts_test.rb +25 -1
  37. data/test/unit/widget_test.rb +10 -0
  38. metadata +51 -11
  39. data/Gemfile.lock +0 -98
  40. data/README +0 -141
  41. data/test/dummy/tmp/app/cells/mouse_widget.rb +0 -11
  42. data/test/dummy/tmp/test/widgets/mouse_widget_test.rb +0 -15
@@ -0,0 +1,175 @@
1
+ (function() {
2
+ // Technique from Juriy Zaytsev
3
+ // http://thinkweb2.com/projects/prototype/detecting-event-support-without-browser-sniffing/
4
+ function isEventSupported(eventName) {
5
+ var el = document.createElement('div');
6
+ eventName = 'on' + eventName;
7
+ var isSupported = (eventName in el);
8
+ if (!isSupported) {
9
+ el.setAttribute(eventName, 'return;');
10
+ isSupported = typeof el[eventName] == 'function';
11
+ }
12
+ el = null;
13
+ return isSupported;
14
+ }
15
+
16
+ function isForm(element) {
17
+ return Object.isElement(element) && element.nodeName.toUpperCase() == 'FORM'
18
+ }
19
+
20
+ function isInput(element) {
21
+ if (Object.isElement(element)) {
22
+ var name = element.nodeName.toUpperCase()
23
+ return name == 'INPUT' || name == 'SELECT' || name == 'TEXTAREA'
24
+ }
25
+ else return false
26
+ }
27
+
28
+ var submitBubbles = isEventSupported('submit'),
29
+ changeBubbles = isEventSupported('change')
30
+
31
+ if (!submitBubbles || !changeBubbles) {
32
+ // augment the Event.Handler class to observe custom events when needed
33
+ Event.Handler.prototype.initialize = Event.Handler.prototype.initialize.wrap(
34
+ function(init, element, eventName, selector, callback) {
35
+ init(element, eventName, selector, callback)
36
+ // is the handler being attached to an element that doesn't support this event?
37
+ if ( (!submitBubbles && this.eventName == 'submit' && !isForm(this.element)) ||
38
+ (!changeBubbles && this.eventName == 'change' && !isInput(this.element)) ) {
39
+ // "submit" => "emulated:submit"
40
+ this.eventName = 'emulated:' + this.eventName
41
+ }
42
+ }
43
+ )
44
+ }
45
+
46
+ if (!submitBubbles) {
47
+ // discover forms on the page by observing focus events which always bubble
48
+ document.on('focusin', 'form', function(focusEvent, form) {
49
+ // special handler for the real "submit" event (one-time operation)
50
+ if (!form.retrieve('emulated:submit')) {
51
+ form.on('submit', function(submitEvent) {
52
+ var emulated = form.fire('emulated:submit', submitEvent, true)
53
+ // if custom event received preventDefault, cancel the real one too
54
+ if (emulated.returnValue === false) submitEvent.preventDefault()
55
+ })
56
+ form.store('emulated:submit', true)
57
+ }
58
+ })
59
+ }
60
+
61
+ if (!changeBubbles) {
62
+ // discover form inputs on the page
63
+ document.on('focusin', 'input, select, texarea', function(focusEvent, input) {
64
+ // special handler for real "change" events
65
+ if (!input.retrieve('emulated:change')) {
66
+ input.on('change', function(changeEvent) {
67
+ input.fire('emulated:change', changeEvent, true)
68
+ })
69
+ input.store('emulated:change', true)
70
+ }
71
+ })
72
+ }
73
+
74
+ function handleRemote(element) {
75
+ var method, url, params;
76
+
77
+ var event = element.fire("ajax:before");
78
+ if (event.stopped) return false;
79
+
80
+ if (element.tagName.toLowerCase() === 'form') {
81
+ method = element.readAttribute('method') || 'post';
82
+ url = element.readAttribute('action');
83
+ params = element.serialize();
84
+ } else {
85
+ method = element.readAttribute('data-method') || 'get';
86
+ url = element.readAttribute('href');
87
+ params = {};
88
+ }
89
+
90
+ new Ajax.Request(url, {
91
+ method: method,
92
+ parameters: params,
93
+ evalScripts: true,
94
+
95
+ onComplete: function(request) { element.fire("ajax:complete", request); },
96
+ onSuccess: function(request) { element.fire("ajax:success", request); },
97
+ onFailure: function(request) { element.fire("ajax:failure", request); }
98
+ });
99
+
100
+ element.fire("ajax:after");
101
+ }
102
+
103
+ function handleMethod(element) {
104
+ var method = element.readAttribute('data-method'),
105
+ url = element.readAttribute('href'),
106
+ csrf_param = $$('meta[name=csrf-param]')[0],
107
+ csrf_token = $$('meta[name=csrf-token]')[0];
108
+
109
+ var form = new Element('form', { method: "POST", action: url, style: "display: none;" });
110
+ element.parentNode.insert(form);
111
+
112
+ if (method !== 'post') {
113
+ var field = new Element('input', { type: 'hidden', name: '_method', value: method });
114
+ form.insert(field);
115
+ }
116
+
117
+ if (csrf_param) {
118
+ var param = csrf_param.readAttribute('content'),
119
+ token = csrf_token.readAttribute('content'),
120
+ field = new Element('input', { type: 'hidden', name: param, value: token });
121
+ form.insert(field);
122
+ }
123
+
124
+ form.submit();
125
+ }
126
+
127
+
128
+ document.on("click", "*[data-confirm]", function(event, element) {
129
+ var message = element.readAttribute('data-confirm');
130
+ if (!confirm(message)) event.stop();
131
+ });
132
+
133
+ document.on("click", "a[data-remote]", function(event, element) {
134
+ if (event.stopped) return;
135
+ handleRemote(element);
136
+ event.stop();
137
+ });
138
+
139
+ document.on("click", "a[data-method]", function(event, element) {
140
+ if (event.stopped) return;
141
+ handleMethod(element);
142
+ event.stop();
143
+ });
144
+
145
+ document.on("submit", function(event) {
146
+ var element = event.findElement(),
147
+ message = element.readAttribute('data-confirm');
148
+ if (message && !confirm(message)) {
149
+ event.stop();
150
+ return false;
151
+ }
152
+
153
+ var inputs = element.select("input[type=submit][data-disable-with]");
154
+ inputs.each(function(input) {
155
+ input.disabled = true;
156
+ input.writeAttribute('data-original-value', input.value);
157
+ input.value = input.readAttribute('data-disable-with');
158
+ });
159
+
160
+ var element = event.findElement("form[data-remote]");
161
+ if (element) {
162
+ handleRemote(element);
163
+ event.stop();
164
+ }
165
+ });
166
+
167
+ document.on("ajax:after", "form", function(event, element) {
168
+ var inputs = element.select("input[type=submit][disabled=true][data-disable-with]");
169
+ inputs.each(function(input) {
170
+ input.value = input.readAttribute('data-original-value');
171
+ input.removeAttribute('data-original-value');
172
+ input.disabled = false;
173
+ });
174
+ });
175
+ })();
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
3
+
4
+ APP_PATH = File.expand_path('../../config/application', __FILE__)
5
+ require File.expand_path('../../config/boot', __FILE__)
6
+ require 'rails/commands'
@@ -0,0 +1 @@
1
+ <div id="<%= @name %>"><%= widget_content %></div>
@@ -0,0 +1 @@
1
+ <div id="<%= @name %>">burp!</div>
@@ -0,0 +1 @@
1
+ If you see <%= @who %> do <%= @what %>!
@@ -0,0 +1 @@
1
+ <%= rendered_children.collect{|v| v.last}.join("\n")
@@ -0,0 +1 @@
1
+ <a><%= widget_id %></a>
@@ -0,0 +1 @@
1
+ <%= rendered_children.collect{|e| e.last}.join("") %>
@@ -0,0 +1 @@
1
+ <div id="<%= @name %>"><snuggle><%= rendered_children.collect{|e| e.last}.join("").html_safe %></snuggle></div>
@@ -22,6 +22,10 @@ class ControllerMethodsTest < ActionController::TestCase
22
22
  end
23
23
  end
24
24
 
25
+ should "respond to parent_controller" do
26
+ assert_equal @controller, @controller.send(:parent_controller)
27
+ end
28
+
25
29
  context "invoking #has_widgets" do
26
30
  setup do
27
31
  @controller.class.has_widgets do |root|
@@ -58,13 +62,16 @@ class ControllerMethodsTest < ActionController::TestCase
58
62
  assert @sub_controller.apotomo_root['berry']
59
63
  end
60
64
 
61
- should "be aliased to has_widgets" do
65
+ should "be executed in controller context" do
66
+ @controller.instance_eval do
67
+ def roomies; ['mice', 'cows']; end
68
+ end
69
+
62
70
  @controller.class.has_widgets do |root|
63
- root << widget(:mouse_cell, 'kid')
71
+ root << widget(:mouse_cell, 'kid', :display, :roomies => roomies)
64
72
  end
65
73
 
66
- assert @controller.apotomo_root['mum']
67
- assert @controller.apotomo_root['kid']
74
+ assert_equal ['mice', 'cows'], @controller.apotomo_root['kid'].opts[:roomies]
68
75
  end
69
76
  end
70
77
 
@@ -37,20 +37,13 @@ class RequestProcessorTest < ActiveSupport::TestCase
37
37
  assert_equal @root.size, 1
38
38
  end
39
39
 
40
- should "allow has_widgets blocks with root parameter, only" do
40
+ should "allow has_widgets blocks with root parameter" do
41
41
  @processor.send(:attach_stateless_blocks_for, [Proc.new{ |root|
42
42
  root.add widget('mouse_cell', 'mouse')
43
43
  }], @root, parent_controller)
44
44
 
45
45
  assert_equal 'mouse', @processor.root['mouse'].name
46
46
  end
47
-
48
- should "allow has_widgets blocks with both root and controller parameter" do
49
- @processor.send(:attach_stateless_blocks_for, [Proc.new{ |root,controller|
50
- root.add widget('mouse_cell', 'mouse')
51
- }], @root, parent_controller)
52
- assert_equal 'mouse', @processor.root['mouse'].name
53
- end
54
47
  end
55
48
 
56
49
  context "option processing at construction time" do
@@ -25,7 +25,7 @@ class WidgetShortcutsTest < Test::Unit::TestCase
25
25
  context "#widget" do
26
26
  context "with all arguments" do
27
27
  setup do
28
- @mum = widget(:mum_widget, 'mum', :eating)
28
+ @mum = widget(:mum_widget, 'mum', :eating, :color => 'grey', :type => :hungry)
29
29
  end
30
30
 
31
31
  should "create a MumWidget instance" do
@@ -33,6 +33,30 @@ class WidgetShortcutsTest < Test::Unit::TestCase
33
33
  assert_equal :eating, @mum.instance_variable_get(:@start_state)
34
34
  assert_equal 'mum', @mum.name
35
35
  end
36
+
37
+ should "accept options" do
38
+ assert_equal({:color => "grey", :type => :hungry}, @mum.opts)
39
+ end
40
+ end
41
+
42
+ context "with 3 arguments and no start_state" do
43
+ should "set a default start_state" do
44
+ @mum = widget(:mum_widget, 'mum', :color => 'grey', :type => :hungry)
45
+ assert_kind_of MumWidget, @mum
46
+ assert_equal :display, @mum.instance_variable_get(:@start_state)
47
+ assert_equal 'mum', @mum.name
48
+ assert_equal({:color => "grey", :type => :hungry}, @mum.opts)
49
+ end
50
+ end
51
+
52
+ context "with 3 arguments and no options" do
53
+ should "not set options" do
54
+ @mum = widget(:mum_widget, 'mum', :squeak)
55
+ assert_kind_of MumWidget, @mum
56
+ assert_equal :squeak, @mum.instance_variable_get(:@start_state)
57
+ assert_equal 'mum', @mum.name
58
+ assert_equal({}, @mum.opts)
59
+ end
36
60
  end
37
61
 
38
62
  context "with id only" do
@@ -128,5 +128,15 @@ class WidgetTest < ActiveSupport::TestCase
128
128
  should "alias #widget_id to #name" do
129
129
  assert_equal @mum.name, @mum.widget_id
130
130
  end
131
+
132
+ should "provide #param" do
133
+ @controller.params = HashWithIndifferentAccess.new('type' => 'Wireless mouse', :brand => "Logitech")
134
+ @mum = widget(:mouse_cell, 'mum', :display, :color => 'grey', :type => 'shrew')
135
+ assert_equal nil, @mum.param(:whatever)
136
+ assert_equal 'grey', @mum.param(:color)
137
+ assert_equal 'grey', @mum.param('color')
138
+ assert_equal 'shrew', @mum.param(:type)
139
+ assert_equal 'Logitech', @mum.param(:brand)
140
+ end
131
141
  end
132
142
  end
metadata CHANGED
@@ -6,8 +6,8 @@ version: !ruby/object:Gem::Version
6
6
  - 1
7
7
  - 0
8
8
  - 0
9
- - beta1
10
- version: 1.0.0.beta1
9
+ - beta2
10
+ version: 1.0.0.beta2
11
11
  platform: ruby
12
12
  authors:
13
13
  - Nick Sutterer
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-10-13 00:00:00 +02:00
18
+ date: 2010-10-16 00:00:00 +02:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -85,13 +85,10 @@ executables: []
85
85
  extensions: []
86
86
 
87
87
  extra_rdoc_files:
88
- - README
89
88
  - README.rdoc
90
89
  - TODO
91
90
  files:
92
91
  - Gemfile
93
- - Gemfile.lock
94
- - README
95
92
  - README.rdoc
96
93
  - Rakefile
97
94
  - TODO
@@ -123,7 +120,13 @@ files:
123
120
  - lib/generators/apotomo/templates/widget.rb
124
121
  - lib/generators/apotomo/templates/widget_test.rb
125
122
  - lib/generators/apotomo/widget_generator.rb
126
- - rails/init.rb
123
+ - test/fixtures/mouse/feed.html.erb
124
+ - test/fixtures/mouse/eating.html.erb
125
+ - test/fixtures/mouse/make_me_squeak.html.erb
126
+ - test/fixtures/mouse/posing.html.erb
127
+ - test/fixtures/mouse/educate.html.erb
128
+ - test/fixtures/mouse/snuggle.html.erb
129
+ - test/fixtures/mouse/content.html.erb
127
130
  - test/fixtures/application_widget_tree.rb
128
131
  - test/rails/view_methods_test.rb
129
132
  - test/rails/controller_methods_test.rb
@@ -138,15 +141,30 @@ files:
138
141
  - test/dummy/config/initializers/secret_token.rb
139
142
  - test/dummy/config/initializers/inflections.rb
140
143
  - test/dummy/config/initializers/backtrace_silencers.rb
144
+ - test/dummy/config/locales/en.yml
141
145
  - test/dummy/config/routes.rb
142
146
  - test/dummy/config/boot.rb
143
147
  - test/dummy/config/environment.rb
144
148
  - test/dummy/config/environments/production.rb
145
149
  - test/dummy/config/environments/test.rb
146
150
  - test/dummy/config/environments/development.rb
147
- - test/dummy/tmp/test/widgets/mouse_widget_test.rb
148
- - test/dummy/tmp/app/cells/mouse_widget.rb
151
+ - test/dummy/config/database.yml
152
+ - test/dummy/script/rails
153
+ - test/dummy/config.ru
154
+ - test/dummy/db/test.sqlite3
155
+ - test/dummy/Rakefile
156
+ - test/dummy/public/422.html
157
+ - test/dummy/public/favicon.ico
158
+ - test/dummy/public/500.html
159
+ - test/dummy/public/404.html
160
+ - test/dummy/public/javascripts/controls.js
161
+ - test/dummy/public/javascripts/application.js
162
+ - test/dummy/public/javascripts/rails.js
163
+ - test/dummy/public/javascripts/dragdrop.js
164
+ - test/dummy/public/javascripts/prototype.js
165
+ - test/dummy/public/javascripts/effects.js
149
166
  - test/dummy/app/controllers/application_controller.rb
167
+ - test/dummy/app/views/layouts/application.html.erb
150
168
  - test/dummy/app/helpers/application_helper.rb
151
169
  - test/unit/test_widget_shortcuts.rb
152
170
  - test/unit/event_handler_test.rb
@@ -203,6 +221,13 @@ signing_key:
203
221
  specification_version: 3
204
222
  summary: Web components for Rails.
205
223
  test_files:
224
+ - test/fixtures/mouse/feed.html.erb
225
+ - test/fixtures/mouse/eating.html.erb
226
+ - test/fixtures/mouse/make_me_squeak.html.erb
227
+ - test/fixtures/mouse/posing.html.erb
228
+ - test/fixtures/mouse/educate.html.erb
229
+ - test/fixtures/mouse/snuggle.html.erb
230
+ - test/fixtures/mouse/content.html.erb
206
231
  - test/fixtures/application_widget_tree.rb
207
232
  - test/rails/view_methods_test.rb
208
233
  - test/rails/controller_methods_test.rb
@@ -217,15 +242,30 @@ test_files:
217
242
  - test/dummy/config/initializers/secret_token.rb
218
243
  - test/dummy/config/initializers/inflections.rb
219
244
  - test/dummy/config/initializers/backtrace_silencers.rb
245
+ - test/dummy/config/locales/en.yml
220
246
  - test/dummy/config/routes.rb
221
247
  - test/dummy/config/boot.rb
222
248
  - test/dummy/config/environment.rb
223
249
  - test/dummy/config/environments/production.rb
224
250
  - test/dummy/config/environments/test.rb
225
251
  - test/dummy/config/environments/development.rb
226
- - test/dummy/tmp/test/widgets/mouse_widget_test.rb
227
- - test/dummy/tmp/app/cells/mouse_widget.rb
252
+ - test/dummy/config/database.yml
253
+ - test/dummy/script/rails
254
+ - test/dummy/config.ru
255
+ - test/dummy/db/test.sqlite3
256
+ - test/dummy/Rakefile
257
+ - test/dummy/public/422.html
258
+ - test/dummy/public/favicon.ico
259
+ - test/dummy/public/500.html
260
+ - test/dummy/public/404.html
261
+ - test/dummy/public/javascripts/controls.js
262
+ - test/dummy/public/javascripts/application.js
263
+ - test/dummy/public/javascripts/rails.js
264
+ - test/dummy/public/javascripts/dragdrop.js
265
+ - test/dummy/public/javascripts/prototype.js
266
+ - test/dummy/public/javascripts/effects.js
228
267
  - test/dummy/app/controllers/application_controller.rb
268
+ - test/dummy/app/views/layouts/application.html.erb
229
269
  - test/dummy/app/helpers/application_helper.rb
230
270
  - test/unit/test_widget_shortcuts.rb
231
271
  - test/unit/event_handler_test.rb
@@ -1,98 +0,0 @@
1
- PATH
2
- remote: /home/nick/projects/cells
3
- specs:
4
- cells (3.4.2)
5
-
6
- GEM
7
- remote: http://rubygems.org/
8
- specs:
9
- abstract (1.0.0)
10
- actionmailer (3.0.0)
11
- actionpack (= 3.0.0)
12
- mail (~> 2.2.5)
13
- actionpack (3.0.0)
14
- activemodel (= 3.0.0)
15
- activesupport (= 3.0.0)
16
- builder (~> 2.1.2)
17
- erubis (~> 2.6.6)
18
- i18n (~> 0.4.1)
19
- rack (~> 1.2.1)
20
- rack-mount (~> 0.6.12)
21
- rack-test (~> 0.5.4)
22
- tzinfo (~> 0.3.23)
23
- activemodel (3.0.0)
24
- activesupport (= 3.0.0)
25
- builder (~> 2.1.2)
26
- i18n (~> 0.4.1)
27
- activerecord (3.0.0)
28
- activemodel (= 3.0.0)
29
- activesupport (= 3.0.0)
30
- arel (~> 1.0.0)
31
- tzinfo (~> 0.3.23)
32
- activeresource (3.0.0)
33
- activemodel (= 3.0.0)
34
- activesupport (= 3.0.0)
35
- activesupport (3.0.0)
36
- arel (1.0.1)
37
- activesupport (~> 3.0.0)
38
- builder (2.1.2)
39
- erubis (2.6.6)
40
- abstract (>= 1.0.0)
41
- gemcutter (0.6.1)
42
- git (1.2.5)
43
- hooks (0.1.2)
44
- i18n (0.4.1)
45
- jeweler (1.4.0)
46
- gemcutter (>= 0.1.0)
47
- git (>= 1.2.5)
48
- rubyforge (>= 2.0.0)
49
- json_pure (1.4.6)
50
- mail (2.2.6.1)
51
- activesupport (>= 2.3.6)
52
- mime-types
53
- treetop (>= 1.4.5)
54
- mime-types (1.16)
55
- mocha (0.9.8)
56
- rake
57
- onfire (0.1.0)
58
- polyglot (0.3.1)
59
- rack (1.2.1)
60
- rack-mount (0.6.13)
61
- rack (>= 1.0.0)
62
- rack-test (0.5.6)
63
- rack (>= 1.0)
64
- rails (3.0.0)
65
- actionmailer (= 3.0.0)
66
- actionpack (= 3.0.0)
67
- activerecord (= 3.0.0)
68
- activeresource (= 3.0.0)
69
- activesupport (= 3.0.0)
70
- bundler (~> 1.0.0)
71
- railties (= 3.0.0)
72
- railties (3.0.0)
73
- actionpack (= 3.0.0)
74
- activesupport (= 3.0.0)
75
- rake (>= 0.8.4)
76
- thor (~> 0.14.0)
77
- rake (0.8.7)
78
- rubyforge (2.0.4)
79
- json_pure (>= 1.1.7)
80
- shoulda (2.11.3)
81
- sqlite3-ruby (1.2.5)
82
- thor (0.14.3)
83
- treetop (1.4.8)
84
- polyglot (>= 0.3.1)
85
- tzinfo (0.3.23)
86
-
87
- PLATFORMS
88
- ruby
89
-
90
- DEPENDENCIES
91
- cells!
92
- hooks (~> 0.1.2)
93
- jeweler
94
- mocha
95
- onfire
96
- rails (~> 3.0.0)
97
- shoulda
98
- sqlite3-ruby (= 1.2.5)