punchbox 1.0.1
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.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +164 -0
- data/Rakefile +33 -0
- data/app/assets/javascripts/punchbox.es6 +78 -0
- data/app/assets/javascripts/punchbox.js +101 -0
- data/lib/punchbox.rb +3 -0
- data/lib/punchbox/attribute_helper.rb +6 -0
- data/lib/punchbox/engine.rb +11 -0
- data/lib/punchbox/version.rb +3 -0
- data/lib/tasks/punchbox_tasks.rake +4 -0
- metadata +83 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 663d8f696f1b9b5f4c317b27e15325efd2e7be6b
|
4
|
+
data.tar.gz: c79a9152bb2c34821b3be762a1c40c798d0d6a3c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1f23c149e2b9996bdbd215de7a6094dc9487fc87be5ffb3ff9c69b39d3a9cdfbf628cf2f8c1bae66adbe58a6b8eb6c75ab99b35cca7d5e8c6bbde66d9281d335
|
7
|
+
data.tar.gz: 2cc7537e9b24cc05864f62b43afb0364ab80764281416ffc84d8ad39e29ec309cc3267fc4b3f1041acc9dd2f6dbd34b862a9cca2fc09d4865ae27299ccfbace4
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2017 Kieran Eglin
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,164 @@
|
|
1
|
+
# Punchbox 👊
|
2
|
+
|
3
|
+
Punchbox is a dead-simple way to add page-specific JavaScript to your Rails project.
|
4
|
+
|
5
|
+
The goal for this project is to have a focused scope (page-specific JS and nothing else), be as lightweight as possible, have no dependencies, and to run on <sup><sup>almost</sup></sup> any browser that runs JS.
|
6
|
+
|
7
|
+
Punchbox's syntax is a spiritual successor to [Paloma](https://github.com/kbparagua/paloma) and is otherwise inspired by a project I've contributed to in the past, [seed_tray](https://github.com/LoamStudios/seed_tray).
|
8
|
+
|
9
|
+
## Compatibility
|
10
|
+
|
11
|
+
Punchbox should work on Rails 4+ and really any Ruby version that's supported by your version of Rails. There's really nothing fancy happening Ruby-side.
|
12
|
+
|
13
|
+
The JavaScript is pure without any dependencies (no jQuery! 🎉). It should work down to IE9, thereby working on 98% of browsers.
|
14
|
+
|
15
|
+
Punchbox works with or without Turbolinks.
|
16
|
+
|
17
|
+
The JavaScript is only ~840 *bytes* minified and gzipped.
|
18
|
+
|
19
|
+
## Installation
|
20
|
+
|
21
|
+
You know the drill. Add this line to your application's Gemfile:
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
gem 'punchbox'
|
25
|
+
```
|
26
|
+
|
27
|
+
And then execute:
|
28
|
+
```bash
|
29
|
+
$ bundle
|
30
|
+
```
|
31
|
+
|
32
|
+
Or install it yourself as:
|
33
|
+
```bash
|
34
|
+
$ gem install punchbox
|
35
|
+
```
|
36
|
+
|
37
|
+
Once you've done that, require Punchbox before tree in your main JavaScript file.
|
38
|
+
|
39
|
+
```javascript
|
40
|
+
// ...
|
41
|
+
//= require punchbox
|
42
|
+
//= require_tree .
|
43
|
+
```
|
44
|
+
|
45
|
+
Finally, include the `punchbox_attributes` hooks in your main `<body>` tag.
|
46
|
+
|
47
|
+
```html
|
48
|
+
<body <%= punchbox_attributes %>>
|
49
|
+
<%= yield %>
|
50
|
+
</body>
|
51
|
+
```
|
52
|
+
## Usage
|
53
|
+
|
54
|
+
Punchbox's syntax is very similar to that of Paloma's. To run JS on a certain page, you call Punchbox like so:
|
55
|
+
|
56
|
+
```javascript
|
57
|
+
Punchbox.on(<controller: string>, <callable>);
|
58
|
+
```
|
59
|
+
|
60
|
+
**Important**
|
61
|
+
|
62
|
+
Punchbox automatically runs your code after your document is ready, negating the need for `$(document).ready` and the like. If this is a problem, please make an issue and I'll consider changing this behaviour.
|
63
|
+
|
64
|
+
### Controller
|
65
|
+
|
66
|
+
`controller` should be a string or a function that returns a string. It should be the exact name of the controller (plus namespace) that you want your code to run on. It should be ClassCase (PascalCase) only.
|
67
|
+
|
68
|
+
For example, if your controller is `PostsController`, you'd enter 'Posts'. If your controller is `AdminPanelsController`, you'd enter `AdminPanels`.
|
69
|
+
|
70
|
+
#### Namespacing
|
71
|
+
|
72
|
+
Namespacing is accomplished via forward slashes.
|
73
|
+
|
74
|
+
If your controller is `AdminPanel::Settings::UserManagersController`, you'd enter `AdminPanel/Settings/UserManagers`.
|
75
|
+
|
76
|
+
### Callable (read this part)
|
77
|
+
|
78
|
+
`callable` can be a function, class, or object. Classes are the preferred convention, but of course you can use whatever is compatible with your current workflow.
|
79
|
+
|
80
|
+
#### Function names
|
81
|
+
|
82
|
+
Functions should be named after the actions on which they should run. A function `index` would run when the `index` action is invoked.
|
83
|
+
|
84
|
+
The exception is `controller`. Punchbox treats functions named `controller` in a special way and runs them on every action in a given Rails controller.
|
85
|
+
|
86
|
+
#### Examples
|
87
|
+
|
88
|
+
In all examples I'll be targeting the `Posts` controller. *Remember that the name of your `callable` or your JS filepath don't have anything to do with Punchbox's functionality. However, I'll be naming the callables after the controllers to keep up with the preferred convention.*
|
89
|
+
|
90
|
+
**ES2015+ (class syntax)**
|
91
|
+
|
92
|
+
```javascript
|
93
|
+
class Posts {
|
94
|
+
controller() {
|
95
|
+
console.log('Hello from every action on a controller!');
|
96
|
+
}
|
97
|
+
|
98
|
+
index() {
|
99
|
+
console.log('Hello from just the index action!');
|
100
|
+
}
|
101
|
+
|
102
|
+
// ...
|
103
|
+
}
|
104
|
+
|
105
|
+
Punchbox.on('Posts', Posts); // Notice that you don't instantiate the class
|
106
|
+
```
|
107
|
+
|
108
|
+
**Function syntax**
|
109
|
+
|
110
|
+
```javascript
|
111
|
+
function Posts() {
|
112
|
+
// ...
|
113
|
+
};
|
114
|
+
|
115
|
+
Posts.prototype.controller = function() {
|
116
|
+
console.log('Hello from every action on a controller!');
|
117
|
+
};
|
118
|
+
|
119
|
+
Posts.prototype.index = function() {
|
120
|
+
console.log('Hello from just the index action!');
|
121
|
+
};
|
122
|
+
|
123
|
+
Punchbox.on('Posts', Posts); // Notice that you don't instantiate the function
|
124
|
+
```
|
125
|
+
|
126
|
+
**Object syntax**
|
127
|
+
|
128
|
+
```javascript
|
129
|
+
var Posts = {
|
130
|
+
controller: function() {
|
131
|
+
console.log('Hello from every action on a controller!');
|
132
|
+
},
|
133
|
+
|
134
|
+
index: function() {
|
135
|
+
console.log('Hello from just the index action!');
|
136
|
+
}
|
137
|
+
}
|
138
|
+
|
139
|
+
Punchbox.on('Posts', Posts);
|
140
|
+
```
|
141
|
+
|
142
|
+
**CoffeeScript**
|
143
|
+
|
144
|
+
```coffeescript
|
145
|
+
class Posts
|
146
|
+
controller: ->
|
147
|
+
console.log 'Hello from every action on a controller!'
|
148
|
+
|
149
|
+
index: ->
|
150
|
+
console.log 'Hello from just the index action!'
|
151
|
+
|
152
|
+
Punchbox.on('Posts', Posts) # Notice that you don't instantiate the class
|
153
|
+
```
|
154
|
+
|
155
|
+
## Contributing
|
156
|
+
|
157
|
+
If you want to contribute, don't hesitate to create an issue or PR! Since this project is in it's infancy, your input could help shape the project as a whole!
|
158
|
+
|
159
|
+
Please note that the magic happens inside `app/assets/javascripts/punchbox.es6`. `punchbox.js` is then created via Babel before deployment. If you make a PR, please only edit `punchbox.es6`.
|
160
|
+
|
161
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/kieraneglin/punchbox/. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.
|
162
|
+
|
163
|
+
## License
|
164
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'Punchbox'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.md')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
require 'bundler/gem_tasks'
|
23
|
+
|
24
|
+
require 'rake/testtask'
|
25
|
+
|
26
|
+
Rake::TestTask.new(:test) do |t|
|
27
|
+
t.libs << 'test'
|
28
|
+
t.pattern = 'test/**/*_test.rb'
|
29
|
+
t.verbose = false
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
task default: :test
|
@@ -0,0 +1,78 @@
|
|
1
|
+
class Punchbox {
|
2
|
+
static on(controller, classOrObject) {
|
3
|
+
let punchbox = new Punchbox();
|
4
|
+
punchbox.instantiatable = classOrObject;
|
5
|
+
|
6
|
+
punchbox._onPageLoad(() => {
|
7
|
+
punchbox._assignAttributes();
|
8
|
+
if (controller === punchbox._snakeToPascal(punchbox.controller)) {
|
9
|
+
punchbox._run();
|
10
|
+
}
|
11
|
+
});
|
12
|
+
}
|
13
|
+
|
14
|
+
_assignAttributes() {
|
15
|
+
let bodyTag = document.body;
|
16
|
+
|
17
|
+
this.controller = bodyTag.getAttribute('data-punchbox-controller');
|
18
|
+
this.action = bodyTag.getAttribute('data-punchbox-action');
|
19
|
+
}
|
20
|
+
|
21
|
+
_callIfExists(functionName) {
|
22
|
+
let instance = this.instantiatable;
|
23
|
+
|
24
|
+
if (typeof instance[functionName] === 'function') {
|
25
|
+
instance[functionName]();
|
26
|
+
|
27
|
+
if(functionName === 'controller') {
|
28
|
+
document.dispatchEvent(new Event(`punchbox:${this.controller}:run`));
|
29
|
+
} else {
|
30
|
+
document.dispatchEvent(new Event(`punchbox:${this.controller}:${this.action}:run`));
|
31
|
+
}
|
32
|
+
}
|
33
|
+
}
|
34
|
+
|
35
|
+
_instantiate() {
|
36
|
+
let classOrObject = this.instantiatable;
|
37
|
+
|
38
|
+
if (typeof classOrObject === 'function') {
|
39
|
+
return new classOrObject();
|
40
|
+
} else if (typeof classOrObject === 'object') {
|
41
|
+
return classOrObject;
|
42
|
+
}
|
43
|
+
}
|
44
|
+
|
45
|
+
_onPageLoad(callback) {
|
46
|
+
if (window.Turbolinks) {
|
47
|
+
let loadEvent = Turbolinks.EVENTS ? 'page:change' : 'turbolinks:load';
|
48
|
+
|
49
|
+
document.addEventListener(loadEvent, () => {
|
50
|
+
callback();
|
51
|
+
});
|
52
|
+
} else {
|
53
|
+
window.onload = () => {
|
54
|
+
callback();
|
55
|
+
};
|
56
|
+
}
|
57
|
+
}
|
58
|
+
|
59
|
+
_run() {
|
60
|
+
// It's like 4am. Please excuse my naming
|
61
|
+
this.instantiatable = this._instantiate();
|
62
|
+
this._callIfExists('controller');
|
63
|
+
this._callIfExists(this.action);
|
64
|
+
}
|
65
|
+
|
66
|
+
_snakeToPascal(string) {
|
67
|
+
return string.split('_').map((str) => {
|
68
|
+
return this._upperFirst(
|
69
|
+
str.split('/')
|
70
|
+
.map(this._upperFirst)
|
71
|
+
.join('/'));
|
72
|
+
}).join('');
|
73
|
+
}
|
74
|
+
|
75
|
+
_upperFirst(string) {
|
76
|
+
return string.slice(0, 1).toUpperCase() + string.slice(1, string.length);
|
77
|
+
}
|
78
|
+
}
|
@@ -0,0 +1,101 @@
|
|
1
|
+
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
|
2
|
+
|
3
|
+
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
|
4
|
+
|
5
|
+
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
6
|
+
|
7
|
+
var Punchbox = function () {
|
8
|
+
function Punchbox() {
|
9
|
+
_classCallCheck(this, Punchbox);
|
10
|
+
}
|
11
|
+
|
12
|
+
_createClass(Punchbox, [{
|
13
|
+
key: '_assignAttributes',
|
14
|
+
value: function _assignAttributes() {
|
15
|
+
var bodyTag = document.body;
|
16
|
+
|
17
|
+
this.controller = bodyTag.getAttribute('data-punchbox-controller');
|
18
|
+
this.action = bodyTag.getAttribute('data-punchbox-action');
|
19
|
+
}
|
20
|
+
}, {
|
21
|
+
key: '_callIfExists',
|
22
|
+
value: function _callIfExists(functionName) {
|
23
|
+
var instance = this.instantiatable;
|
24
|
+
|
25
|
+
if (typeof instance[functionName] === 'function') {
|
26
|
+
instance[functionName]();
|
27
|
+
|
28
|
+
if (functionName === 'controller') {
|
29
|
+
document.dispatchEvent(new Event('punchbox:' + this.controller + ':run'));
|
30
|
+
} else {
|
31
|
+
document.dispatchEvent(new Event('punchbox:' + this.controller + ':' + this.action + ':run'));
|
32
|
+
}
|
33
|
+
}
|
34
|
+
}
|
35
|
+
}, {
|
36
|
+
key: '_instantiate',
|
37
|
+
value: function _instantiate() {
|
38
|
+
var classOrObject = this.instantiatable;
|
39
|
+
|
40
|
+
if (typeof classOrObject === 'function') {
|
41
|
+
return new classOrObject();
|
42
|
+
} else if ((typeof classOrObject === 'undefined' ? 'undefined' : _typeof(classOrObject)) === 'object') {
|
43
|
+
return classOrObject;
|
44
|
+
}
|
45
|
+
}
|
46
|
+
}, {
|
47
|
+
key: '_onPageLoad',
|
48
|
+
value: function _onPageLoad(callback) {
|
49
|
+
if (window.Turbolinks) {
|
50
|
+
var loadEvent = Turbolinks.EVENTS ? 'page:change' : 'turbolinks:load';
|
51
|
+
|
52
|
+
document.addEventListener(loadEvent, function () {
|
53
|
+
callback();
|
54
|
+
});
|
55
|
+
} else {
|
56
|
+
window.onload = function () {
|
57
|
+
callback();
|
58
|
+
};
|
59
|
+
}
|
60
|
+
}
|
61
|
+
}, {
|
62
|
+
key: '_run',
|
63
|
+
value: function _run() {
|
64
|
+
// It's like 4am. Please excuse my naming
|
65
|
+
this.instantiatable = this._instantiate();
|
66
|
+
this._callIfExists('controller');
|
67
|
+
this._callIfExists(this.action);
|
68
|
+
}
|
69
|
+
}, {
|
70
|
+
key: '_snakeToPascal',
|
71
|
+
value: function _snakeToPascal(string) {
|
72
|
+
var _this = this;
|
73
|
+
|
74
|
+
return string.split('_').map(function (str) {
|
75
|
+
return _this._upperFirst(str.split('/').map(_this._upperFirst).join('/'));
|
76
|
+
}).join('');
|
77
|
+
}
|
78
|
+
}, {
|
79
|
+
key: '_upperFirst',
|
80
|
+
value: function _upperFirst(string) {
|
81
|
+
return string.slice(0, 1).toUpperCase() + string.slice(1, string.length);
|
82
|
+
}
|
83
|
+
}], [{
|
84
|
+
key: 'on',
|
85
|
+
value: function on(controller, classOrObject) {
|
86
|
+
var _this2 = this;
|
87
|
+
|
88
|
+
var punchbox = new Punchbox();
|
89
|
+
punchbox.instantiatable = classOrObject;
|
90
|
+
|
91
|
+
punchbox._onPageLoad(function () {
|
92
|
+
punchbox._assignAttributes();
|
93
|
+
if (controller === punchbox._snakeToPascal(punchbox.controller)) {
|
94
|
+
punchbox._run();
|
95
|
+
}
|
96
|
+
});
|
97
|
+
}
|
98
|
+
}]);
|
99
|
+
|
100
|
+
return Punchbox;
|
101
|
+
}();
|
data/lib/punchbox.rb
ADDED
metadata
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: punchbox
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Kieran Eglin
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-05-24 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 4.0.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 4.0.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: sqlite3
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description: Defining page-specific Javascript shouldn't be hard. That's what Punchbox
|
42
|
+
is looking to fix
|
43
|
+
email:
|
44
|
+
- kieran.eglin@gmail.com
|
45
|
+
executables: []
|
46
|
+
extensions: []
|
47
|
+
extra_rdoc_files: []
|
48
|
+
files:
|
49
|
+
- MIT-LICENSE
|
50
|
+
- README.md
|
51
|
+
- Rakefile
|
52
|
+
- app/assets/javascripts/punchbox.es6
|
53
|
+
- app/assets/javascripts/punchbox.js
|
54
|
+
- lib/punchbox.rb
|
55
|
+
- lib/punchbox/attribute_helper.rb
|
56
|
+
- lib/punchbox/engine.rb
|
57
|
+
- lib/punchbox/version.rb
|
58
|
+
- lib/tasks/punchbox_tasks.rake
|
59
|
+
homepage: https://github.com/kieraneglin/punchbox
|
60
|
+
licenses:
|
61
|
+
- MIT
|
62
|
+
metadata: {}
|
63
|
+
post_install_message:
|
64
|
+
rdoc_options: []
|
65
|
+
require_paths:
|
66
|
+
- lib
|
67
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: 1.9.3
|
72
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
requirements: []
|
78
|
+
rubyforge_project:
|
79
|
+
rubygems_version: 2.6.11
|
80
|
+
signing_key:
|
81
|
+
specification_version: 4
|
82
|
+
summary: Dead simple page-specific Javascript for Rails
|
83
|
+
test_files: []
|