danwrong-evil 0.1 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. data/CHANGELOG +1 -0
  2. data/LICENSE +22 -0
  3. data/Manifest +33 -7
  4. data/README.textile +20 -0
  5. data/Rakefile +21 -2
  6. data/assets/config.ru +10 -2
  7. data/assets/evil-lib.js +222 -0
  8. data/assets/evil.css +204 -10
  9. data/assets/evil.js +20 -0
  10. data/assets/logo.png +0 -0
  11. data/evil.gemspec +32 -19
  12. data/lib/evil/application.rb +85 -17
  13. data/lib/evil/extensions.rb +71 -0
  14. data/lib/evil/helpers.rb +62 -0
  15. data/lib/evil/models/config_pair.rb +7 -0
  16. data/lib/evil/models/template.rb +10 -9
  17. data/lib/evil/models.rb +0 -12
  18. data/lib/evil/open_id.rb +101 -0
  19. data/lib/evil/plugin/base.rb +56 -0
  20. data/lib/evil/plugin/configuration.rb +77 -0
  21. data/lib/evil/plugin/environment.rb +15 -0
  22. data/lib/evil/plugin/filesystem.rb +18 -0
  23. data/lib/evil/plugin/tag.rb +81 -0
  24. data/lib/evil/plugin.rb +21 -0
  25. data/lib/evil/setup/generator.rb +13 -1
  26. data/lib/evil/setup/migration.rb +4 -11
  27. data/lib/evil.rb +34 -1
  28. data/test/app/evil_test.rb +64 -0
  29. data/test/dev_env.rb +10 -0
  30. data/test/test_helper.rb +12 -0
  31. data/test/units/plugin/base_test.rb +47 -0
  32. data/test/units/plugin/tag_test.rb +108 -0
  33. data/views/_banner.haml +8 -0
  34. data/views/_errors.haml +9 -0
  35. data/views/index.haml +41 -0
  36. data/views/layout.haml +13 -0
  37. data/views/login.haml +5 -0
  38. data/views/plugins/_fields.haml +7 -0
  39. data/views/plugins/edit.haml +2 -0
  40. data/views/plugins/fields/_password.haml +3 -0
  41. data/views/plugins/fields/_text.haml +3 -0
  42. data/views/plugins/new.haml +6 -0
  43. data/views/templates/_fields.haml +23 -0
  44. data/views/templates/edit.haml +3 -0
  45. data/views/templates/new.haml +3 -0
  46. metadata +115 -13
  47. data/lib/evil/models/plugin.rb +0 -13
  48. data/test/harness/config.ru +0 -9
  49. data/test/harness/evil.db +0 -0
  50. data/test/harness/public/javascripts/evil.js +0 -0
  51. data/test/harness/public/stylesheets/evil.css +0 -24
  52. data/test/harness/tmp/restart.txt +0 -0
data/CHANGELOG CHANGED
@@ -1 +1,2 @@
1
+ v0.1.1 It's damn close to being useful!
1
2
  v0.1 Initial release
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2009 Dan Webb
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
data/Manifest CHANGED
@@ -1,22 +1,48 @@
1
1
  assets/config.ru
2
+ assets/evil-lib.js
2
3
  assets/evil.css
3
4
  assets/evil.js
5
+ assets/logo.png
4
6
  bin/evil
5
7
  CHANGELOG
8
+ evil.gemspec
6
9
  lib/evil/application.rb
10
+ lib/evil/extensions.rb
11
+ lib/evil/helpers.rb
7
12
  lib/evil/models/config_pair.rb
8
- lib/evil/models/plugin.rb
9
13
  lib/evil/models/template.rb
10
14
  lib/evil/models/whitelist.rb
11
15
  lib/evil/models.rb
16
+ lib/evil/open_id.rb
17
+ lib/evil/plugin/base.rb
18
+ lib/evil/plugin/configuration.rb
19
+ lib/evil/plugin/environment.rb
20
+ lib/evil/plugin/filesystem.rb
21
+ lib/evil/plugin/tag.rb
22
+ lib/evil/plugin.rb
12
23
  lib/evil/setup/db_tool.rb
13
24
  lib/evil/setup/generator.rb
14
25
  lib/evil/setup/migration.rb
15
26
  lib/evil.rb
16
- Rakefile
17
- test/harness/config.ru
18
- test/harness/evil.db
19
- test/harness/public/javascripts/evil.js
20
- test/harness/public/stylesheets/evil.css
21
- test/harness/tmp/restart.txt
27
+ LICENSE
22
28
  Manifest
29
+ Rakefile
30
+ README.textile
31
+ test/app/evil_test.rb
32
+ test/dev_env.rb
33
+ test/test_helper.rb
34
+ test/units/plugin/base_test.rb
35
+ test/units/plugin/tag_test.rb
36
+ views/_banner.haml
37
+ views/_errors.haml
38
+ views/index.haml
39
+ views/layout.haml
40
+ views/login.haml
41
+ views/plugins/_fields.haml
42
+ views/plugins/edit.haml
43
+ views/plugins/fields/_password.haml
44
+ views/plugins/fields/_text.haml
45
+ views/plugins/new.haml
46
+ views/templates/_fields.haml
47
+ views/templates/edit.haml
48
+ views/templates/new.haml
data/README.textile ADDED
@@ -0,0 +1,20 @@
1
+ h1. EVIL
2
+
3
+ Author: Dan Webb (dan@danwebb.net)
4
+
5
+ Evil is going to be a quick and easy tool for bringing together data from all over the web into a cunning little website. It's going to be easy to set up, easy to extend with plugins using any web API and be a good citzen in the Ruby web dev world by being a standard Rack app that you can deploy with Passenger, on Heroku or whatever you feel like. Hopefully it'll be mountable as part of a Rails or Merb app.
6
+
7
+ To create an Evil site:
8
+
9
+ <pre><code>sudo gem install evil
10
+
11
+ evil init {your_app_root}
12
+ evil whitelist {your_openid}</pre></code>
13
+
14
+ Then point Passenger at it, send it up to Heruko or run it on your grandad's ZX Spectrum then go to /admin and get going.
15
+
16
+ h2. Planned features
17
+
18
+ * Caching via Rack::Cache
19
+ * Plugin architecture that makes it easy to interact with web APIs (probably based on HTTParty)
20
+ * Simple OpenID whitelist for access to the admin interface
data/Rakefile CHANGED
@@ -1,11 +1,30 @@
1
1
  require 'echoe'
2
+ require 'fileutils'
2
3
 
3
4
  Echoe.new("evil") do |p|
4
5
  p.author = "Dan Webb"
5
6
  p.email = 'dan@danwebb.net'
6
7
  p.summary = "A simple framework for creating sites from 3rd party data and services"
7
8
  p.url = "http://www.evilmashups.com"
8
- p.runtime_dependencies = ["sinatra", "activerecord",
9
- "sqlite3-ruby", "clip", "haml"]
9
+ p.runtime_dependencies = ["sinatra >= 0.9.1.1", "activerecord >= 2.3.2",
10
+ "sqlite3-ruby >= 1.2.1", "clip >= 1.0.0",
11
+ "haml >= 2.0.9", "liquid >= 2.0.0",
12
+ "danwrong-liquid-inheritance >= 0.1.1", "rack-cache >= 0.4"]
13
+
14
+ p.development_dependencies = ['shoulda']
10
15
  p.retain_gemspec = true
16
+ p.ignore_pattern = ['test/example/**/*']
17
+ end
18
+
19
+ desc 'Move assets from text/example to assets'
20
+ task :sync_assets do
21
+ ['test/example/public/images/*', 'test/example/public/stylesheets/*',
22
+ 'test/example/public/javascripts/*'].each do |glob|
23
+ FileUtils.cp Dir[glob], 'assets/'
24
+ end
25
+ end
26
+
27
+ desc 'Open a irb session with Evil loaded in the context of test/example'
28
+ task :dev_console do
29
+ exec 'irb -r test/dev_env'
11
30
  end
data/assets/config.ru CHANGED
@@ -3,6 +3,14 @@ require 'evil'
3
3
 
4
4
  Evil.app_root = File.expand_path(File.dirname(__FILE__))
5
5
 
6
- require 'evil/application'
6
+ unless Evil.heroku?
7
+ require 'rack/cache'
8
+
9
+ use Rack::Cache,
10
+ :verbose => true,
11
+ :metastore => "file:#{Evil.app_root}/cache/meta",
12
+ :entitystore => "file:#{Evil.app_root}/cache/entity"
13
+ end
7
14
 
8
- run Sinatra::Application
15
+ use Rack::Session::Cookie, :secret => '__SECRET__', :key => '_evil'
16
+ run Evil::Application
@@ -0,0 +1,222 @@
1
+ (function($) {
2
+
3
+ var addMethods = function(source) {
4
+ var ancestor = this.superclass && this.superclass.prototype;
5
+ var properties = $.keys(source);
6
+
7
+ if (!$.keys({ toString: true }).length) properties.push("toString", "valueOf");
8
+
9
+ for (var i = 0, length = properties.length; i < length; i++) {
10
+ var property = properties[i], value = source[property];
11
+ if (ancestor && $.isFunction(value) && $.argumentNames(value)[0] == "$super") {
12
+
13
+ var method = value, value = $.extend($.wrap((function(m) {
14
+ return function() { return ancestor[m].apply(this, arguments) };
15
+ })(property), method), {
16
+ valueOf: function() { return method },
17
+ toString: function() { return method.toString() }
18
+ });
19
+ }
20
+ this.prototype[property] = value;
21
+ }
22
+
23
+ return this;
24
+ }
25
+
26
+ $.extend({
27
+ keys: function(obj) {
28
+ var keys = [];
29
+ for (var key in obj) keys.push(key);
30
+ return keys;
31
+ },
32
+
33
+ argumentNames: function(func) {
34
+ var names = func.toString().match(/^[\s\(]*function[^(]*\((.*?)\)/)[1].split(/, ?/);
35
+ return names.length == 1 && !names[0] ? [] : names;
36
+ },
37
+
38
+ bind: function(func, scope) {
39
+ return function() {
40
+ return func.apply(scope, $.makeArray(arguments));
41
+ }
42
+ },
43
+
44
+ wrap: function(func, wrapper) {
45
+ var __method = func;
46
+ return function() {
47
+ return wrapper.apply(this, [$.bind(__method, this)].concat($.makeArray(arguments)));
48
+ }
49
+ },
50
+
51
+ klass: function() {
52
+ var parent = null, properties = $.makeArray(arguments);
53
+ if ($.isFunction(properties[0])) parent = properties.shift();
54
+
55
+ var klass = function() {
56
+ this.initialize.apply(this, arguments);
57
+ };
58
+
59
+ klass.superclass = parent;
60
+ klass.subclasses = [];
61
+ klass.addMethods = addMethods;
62
+
63
+ if (parent) {
64
+ var subclass = function() { };
65
+ subclass.prototype = parent.prototype;
66
+ klass.prototype = new subclass;
67
+ parent.subclasses.push(klass);
68
+ }
69
+
70
+ for (var i = 0; i < properties.length; i++)
71
+ klass.addMethods(properties[i]);
72
+
73
+ if (!klass.prototype.initialize)
74
+ klass.prototype.initialize = function() {};
75
+
76
+ klass.prototype.constructor = klass;
77
+
78
+ return klass;
79
+ },
80
+ delegate: function(rules) {
81
+ return function(e) {
82
+ var target = $(e.target), parent = null;
83
+ for (var selector in rules) {
84
+ if (target.is(selector) || ((parent = target.parents(selector)) && parent.length > 0)) {
85
+ return rules[selector].apply(this, [parent || target].concat($.makeArray(arguments)));
86
+ }
87
+ parent = null;
88
+ }
89
+ }
90
+ }
91
+ });
92
+
93
+ var bindEvents = function(instance) {
94
+ for (var member in instance) {
95
+ if (member.match(/^on(.+)/) && typeof instance[member] == 'function') {
96
+ instance.element.bind(RegExp.$1, $.bind(instance[member], instance));
97
+ }
98
+ }
99
+ }
100
+
101
+ var behaviorWrapper = function(behavior) {
102
+ return $.klass(behavior, {
103
+ initialize: function($super, element, args) {
104
+ this.element = $(element);
105
+ if ($super) $super.apply(this, args);
106
+ }
107
+ });
108
+ }
109
+
110
+ var attachBehavior = function(el, behavior, args) {
111
+ var wrapper = behaviorWrapper(behavior);
112
+ instance = new wrapper(el, args);
113
+
114
+ bindEvents(instance);
115
+
116
+ if (!behavior.instances) behavior.instances = [];
117
+
118
+ behavior.instances.push(instance);
119
+
120
+ return instance;
121
+ };
122
+
123
+
124
+ $.fn.extend({
125
+ attach: function() {
126
+ var args = $.makeArray(arguments), behavior = args.shift();
127
+
128
+ if ($.livequery && this.selector) {
129
+ return this.livequery(function() {
130
+ attachBehavior(this, behavior, args);
131
+ });
132
+ } else {
133
+ return this.each(function() {
134
+ attachBehavior(this, behavior, args);
135
+ });
136
+ }
137
+ },
138
+ attachAndReturn: function() {
139
+ var args = $.makeArray(arguments), behavior = args.shift();
140
+
141
+ return $.map(this, function(el) {
142
+ return attachBehavior(el, behavior, args);
143
+ });
144
+ },
145
+ delegate: function(type, rules) {
146
+ return this.bind(type, $.delegate(rules));
147
+ },
148
+ attached: function(behavior) {
149
+ var instances = [];
150
+
151
+ if (!behavior.instances) return instances;
152
+
153
+ this.each(function(i, element) {
154
+ $.each(behavior.instances, function(i, instance) {
155
+ if (instance.element.get(0) == element) instances.push(instance);
156
+ });
157
+ });
158
+
159
+ return instances;
160
+ },
161
+ firstAttached: function(behavior) {
162
+ return this.attached(behavior)[0];
163
+ }
164
+ });
165
+
166
+ Remote = $.klass({
167
+ initialize: function(options) {
168
+ if (this.element.attr('nodeName') == 'FORM') this.element.attach(Remote.Form, options);
169
+ else this.element.attach(Remote.Link, options);
170
+ }
171
+ });
172
+
173
+ Remote.Base = $.klass({
174
+ initialize : function(options) {
175
+ this.options = $.extend({
176
+
177
+ }, options || {});
178
+ },
179
+ _makeRequest : function(options) {
180
+ $.ajax(options);
181
+ return false;
182
+ }
183
+ });
184
+
185
+ Remote.Link = $.klass(Remote.Base, {
186
+ onclick: function() {
187
+ var options = $.extend({ url: this.element.attr('href'), type: 'GET' }, this.options);
188
+ return this._makeRequest(options);
189
+ }
190
+ });
191
+
192
+ Remote.Form = $.klass(Remote.Base, {
193
+ onclick: function(e) {
194
+ var target = e.target;
195
+
196
+ if ($.inArray(target.nodeName.toLowerCase(), ['input', 'button']) >= 0 && target.type.match(/submit|image/))
197
+ this._submitButton = target;
198
+ },
199
+ onsubmit: function() {
200
+ var data = this.element.serializeArray();
201
+
202
+ if (this._submitButton) data.push({ name: this._submitButton.name, value: this._submitButton.value });
203
+
204
+ var options = $.extend({
205
+ url : this.element.attr('action'),
206
+ type : this.element.attr('method') || 'GET',
207
+ data : data
208
+ }, this.options);
209
+
210
+ this._makeRequest(options);
211
+
212
+ return false;
213
+ }
214
+ });
215
+
216
+ $.ajaxSetup({
217
+ beforeSend: function(xhr) {
218
+ xhr.setRequestHeader("Accept", "text/javascript, text/html, application/xml, text/xml, */*");
219
+ }
220
+ });
221
+
222
+ })(jQuery);
data/assets/evil.css CHANGED
@@ -1,24 +1,218 @@
1
1
  body {
2
2
  background: black;
3
3
  color: white;
4
- font-family: Monaco, Courier, fixed-width;
5
- text-align: center;
4
+ font-family: Verdana, Helvetica, Arial, sans-serif;
5
+ padding: 0;
6
+ margin: 0;
6
7
  }
7
8
 
8
- #page {
9
- width: 60%;
10
- margin: 3em auto;
11
- text-align: left;
12
- background: #333;
9
+ h1, h2, h3, h4, h5, p {
10
+ margin: 0;
11
+ font-size: 1em;
12
+ }
13
+
14
+ a {
15
+ color: red;
16
+ }
17
+
18
+ a img {
19
+ border: 0;
13
20
  }
14
21
 
15
22
  #banner {
16
- padding: 0.2em 1em;
23
+ background: #222;
24
+ padding: 1em 1.5em;
25
+ font-size: 70%;
26
+ border-bottom: 5px solid #333;
27
+ }
28
+
29
+ #banner #logo {
30
+ float: left;
31
+ }
32
+
33
+ #banner form {
34
+ text-align: right;
35
+ }
36
+
37
+ input.submit {
38
+ border: 1px solid;
39
+ border-color: #666 #000 #000 #666;
40
+ background: #444;
41
+ color: white;
42
+ padding: 0.1em 0.3em;
43
+ text-transform: uppercase;
44
+ font-size: 0.8em;
45
+ border-radius: 10px;
46
+ -moz-border-radius: 10px;
47
+ -webkit-border-radius: 10px;
48
+ }
49
+
50
+ #openid_url {
51
+ border-width: 1px;
52
+ padding: 0.2em 0.2em 0.2em 26px;
53
+ background: url(http://wiki.openid.net/f/openid-16x16.gif) no-repeat #FFF 0.3em;
54
+ width: 12em;
55
+ }
56
+
57
+ #login {
58
+ text-align: center;
59
+ }
60
+
61
+ #login img {
62
+ margin-right: 1em;
63
+ }
64
+
65
+ #login p {
66
+ position: absolute;
67
+ top: 50%;
68
+ left: 50%;
69
+ margin: -4em 0 0 -14em;
70
+ width: 26em;
71
+ background: #222;
72
+ padding: 1em;
73
+ border-radius: 30px;
74
+ -moz-border-radius: 30px;
75
+ -webkit-border-radius: 30px;
76
+ }
77
+
78
+ #login input {
79
+ font-size: 1.2em;
80
+ }
81
+
82
+ #login input.submit {
83
+ border: 1px solid;
84
+ border-color: #666 #000 #000 #666;
85
+ background: #444;
86
+ color: white;
87
+ padding: 0.7em;
88
+ text-transform: uppercase;
89
+ font-size: 0.8em;
90
+ border-radius: 20px;
91
+ -moz-border-radius: 20px;
92
+ -webkit-border-radius: 20px;
93
+ margin-left: 1em;
17
94
  }
18
95
 
19
96
  #content {
20
- margin: 0.5em;
21
- padding: 0.5em;
97
+ min-width: 600px;
98
+ padding: 2em 1.5em 8em 1.5em;
99
+ }
100
+
101
+ .wide #content {
102
+ min-width: 600px;
103
+ padding: 2em 0 8em 1.5em;
104
+ }
105
+
106
+ #overview table {
107
+ width: 100%;
108
+ font-size: 80%;
109
+ border-collapse: collapse;
110
+ margin: 0 1.5em 2em 0;
111
+ }
112
+
113
+ #overview table th {
114
+ text-align: left;
115
+ font-weight: normal;
116
+ }
117
+
118
+ #overview table tr.title {
119
+ background: #000;
120
+ border: none;
121
+ font-size: 1.4em;
122
+ }
123
+
124
+ #overview table tr.title th {
125
+ padding: 0 0 0.5em 0;
126
+ }
127
+
128
+ #overview table thead tr {
129
+ background: #666;
130
+ border: 1px solid #555;
131
+ border-width: 1px 0;
132
+ }
133
+
134
+ #overview table th, #overview table td {
135
+ padding: 0.5em 0.6em;
136
+ }
137
+
138
+ #overview table tr.alt {
139
+ background: #222;
140
+ }
141
+
142
+ #overview table td.tools {
143
+ text-align: right;
144
+ }
145
+
146
+ #overview td.tools a {
147
+ border: 1px solid;
148
+ border-color: #0040FF #002697 #002697 #0040FF;
149
+ background: #0036F1;
150
+ color: white;
151
+ padding: 0.1em 0.5em;
152
+ font-size: 0.8em;
153
+ border-radius: 10px;
154
+ -moz-border-radius: 10px;
155
+ -webkit-border-radius: 10px;
156
+ text-decoration: none;
157
+ }
158
+
159
+ #overview tfoot td {
160
+ text-align: right;
161
+ padding: 1em 0.6em;
162
+ }
163
+
164
+ #overview tfoot a {
165
+ border: 1px solid;
166
+ border-color: #5CDD24 #24570E #24570E #5CDD24;
167
+ background: #00B710;
168
+ color: white;
169
+ padding: 0.1em 0.5em;
170
+ font-size: 0.8em;
171
+ border-radius: 10px;
172
+ -moz-border-radius: 10px;
173
+ -webkit-border-radius: 10px;
174
+ text-decoration: none;
175
+ }
176
+
177
+ .wide #overview table {
178
+ width: 48%;
179
+ float: left;
180
+ margin: 0 2% 2em 0;
181
+ }
182
+
183
+ #content form {
184
+ width: 50%;
185
+ margin: 0 auto;
186
+ }
187
+
188
+ label {
189
+ display: block;
190
+ }
191
+
192
+ form p {
193
+ margin: 1em 0;
194
+ }
195
+
196
+ form input.text, form textarea {
197
+ width: 100%;
198
+ font-family: Verdana, Helvetica, Arial, sans-serif;
199
+ font-size: 1em;
200
+ }
201
+
202
+ form div.errors {
203
+ border: 1px solid red;
204
+ font-size: 0.8em;
205
+ font-style: italic;
206
+ padding: .8em;
207
+ color: red;
22
208
  background: #111;
209
+ border-radius: 10px;
210
+ -moz-border-radius: 10px;
211
+ -webkit-border-radius: 10px;
23
212
  }
24
213
 
214
+ textarea#template_source {
215
+ height: 500px;
216
+ font-family: Monaco, Courier, fixed;
217
+ font-size: 0.8em;
218
+ }
data/assets/evil.js CHANGED
@@ -0,0 +1,20 @@
1
+ jQuery(function($) {
2
+ var Sizer = $.klass({
3
+ initialize: function(threshold, className) {
4
+ this.threshold = threshold;
5
+ this.className = className;
6
+ this.body = $('body');
7
+ this._update();
8
+ },
9
+ onresize: function() {
10
+ this._update();
11
+ },
12
+ _update: function() {
13
+ if (this.body.width() > this.threshold) this.body.addClass(this.className);
14
+ else this.body.removeClass(this.className);
15
+ }
16
+ });
17
+
18
+
19
+ $(window).attach(Sizer, 1000, 'wide')
20
+ });
data/assets/logo.png ADDED
Binary file