yodel 0.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.
Files changed (200) hide show
  1. data/.document +5 -0
  2. data/.gitignore +9 -0
  3. data/Gemfile +2 -0
  4. data/Gemfile.lock +63 -0
  5. data/LICENSE +1 -0
  6. data/README.rdoc +20 -0
  7. data/Rakefile +1 -0
  8. data/bin/yodel +4 -0
  9. data/lib/yodel.rb +22 -0
  10. data/lib/yodel/application/application.rb +44 -0
  11. data/lib/yodel/application/extension.rb +59 -0
  12. data/lib/yodel/application/request_handler.rb +48 -0
  13. data/lib/yodel/application/yodel.rb +25 -0
  14. data/lib/yodel/command/command.rb +94 -0
  15. data/lib/yodel/command/deploy.rb +67 -0
  16. data/lib/yodel/command/dns_server.rb +16 -0
  17. data/lib/yodel/command/installer.rb +229 -0
  18. data/lib/yodel/config/config.rb +30 -0
  19. data/lib/yodel/config/environment.rb +16 -0
  20. data/lib/yodel/config/yodel.rb +21 -0
  21. data/lib/yodel/exceptions/destroyed_record.rb +2 -0
  22. data/lib/yodel/exceptions/domain_not_found.rb +16 -0
  23. data/lib/yodel/exceptions/duplicate_layout.rb +2 -0
  24. data/lib/yodel/exceptions/exceptions.rb +3 -0
  25. data/lib/yodel/exceptions/inconsistent_lock_state.rb +2 -0
  26. data/lib/yodel/exceptions/invalid_field.rb +2 -0
  27. data/lib/yodel/exceptions/invalid_index.rb +2 -0
  28. data/lib/yodel/exceptions/invalid_mixin.rb +2 -0
  29. data/lib/yodel/exceptions/invalid_model_field.rb +2 -0
  30. data/lib/yodel/exceptions/layout_not_found.rb +2 -0
  31. data/lib/yodel/exceptions/mass_assignment.rb +2 -0
  32. data/lib/yodel/exceptions/missing_migration.rb +2 -0
  33. data/lib/yodel/exceptions/missing_root_directory.rb +15 -0
  34. data/lib/yodel/exceptions/unable_to_acquire_lock.rb +2 -0
  35. data/lib/yodel/exceptions/unauthorised.rb +2 -0
  36. data/lib/yodel/exceptions/unknown_field.rb +2 -0
  37. data/lib/yodel/middleware/development_server.rb +180 -0
  38. data/lib/yodel/middleware/error_pages.rb +72 -0
  39. data/lib/yodel/middleware/public_assets.rb +78 -0
  40. data/lib/yodel/middleware/request.rb +16 -0
  41. data/lib/yodel/middleware/site_detector.rb +22 -0
  42. data/lib/yodel/mime_types/default_mime_set.rb +28 -0
  43. data/lib/yodel/mime_types/mime_type.rb +68 -0
  44. data/lib/yodel/mime_types/mime_type_set.rb +41 -0
  45. data/lib/yodel/mime_types/mime_types.rb +6 -0
  46. data/lib/yodel/mime_types/yodel.rb +15 -0
  47. data/lib/yodel/models/api/api.rb +1 -0
  48. data/lib/yodel/models/api/api_call.rb +87 -0
  49. data/lib/yodel/models/core/associations/association.rb +37 -0
  50. data/lib/yodel/models/core/associations/associations.rb +22 -0
  51. data/lib/yodel/models/core/associations/counts/many_association.rb +18 -0
  52. data/lib/yodel/models/core/associations/counts/one_association.rb +22 -0
  53. data/lib/yodel/models/core/associations/embedded/embedded_association.rb +47 -0
  54. data/lib/yodel/models/core/associations/embedded/embedded_record_array.rb +12 -0
  55. data/lib/yodel/models/core/associations/embedded/many_embedded_association.rb +62 -0
  56. data/lib/yodel/models/core/associations/embedded/one_embedded_association.rb +49 -0
  57. data/lib/yodel/models/core/associations/query/many_query_association.rb +10 -0
  58. data/lib/yodel/models/core/associations/query/one_query_association.rb +10 -0
  59. data/lib/yodel/models/core/associations/query/query_association.rb +64 -0
  60. data/lib/yodel/models/core/associations/record_association.rb +38 -0
  61. data/lib/yodel/models/core/associations/store/many_store_association.rb +32 -0
  62. data/lib/yodel/models/core/associations/store/one_store_association.rb +14 -0
  63. data/lib/yodel/models/core/associations/store/store_association.rb +51 -0
  64. data/lib/yodel/models/core/attachments/attachment.rb +73 -0
  65. data/lib/yodel/models/core/attachments/image.rb +38 -0
  66. data/lib/yodel/models/core/core.rb +15 -0
  67. data/lib/yodel/models/core/fields/alias_field.rb +32 -0
  68. data/lib/yodel/models/core/fields/array_field.rb +64 -0
  69. data/lib/yodel/models/core/fields/attachment_field.rb +42 -0
  70. data/lib/yodel/models/core/fields/boolean_field.rb +28 -0
  71. data/lib/yodel/models/core/fields/change_sensitive_array.rb +96 -0
  72. data/lib/yodel/models/core/fields/change_sensitive_hash.rb +53 -0
  73. data/lib/yodel/models/core/fields/color_field.rb +4 -0
  74. data/lib/yodel/models/core/fields/date_field.rb +35 -0
  75. data/lib/yodel/models/core/fields/decimal_field.rb +19 -0
  76. data/lib/yodel/models/core/fields/email_field.rb +10 -0
  77. data/lib/yodel/models/core/fields/enum_field.rb +33 -0
  78. data/lib/yodel/models/core/fields/field.rb +154 -0
  79. data/lib/yodel/models/core/fields/fields.rb +29 -0
  80. data/lib/yodel/models/core/fields/fields_field.rb +31 -0
  81. data/lib/yodel/models/core/fields/filter_mixin.rb +9 -0
  82. data/lib/yodel/models/core/fields/filtered_string_field.rb +5 -0
  83. data/lib/yodel/models/core/fields/filtered_text_field.rb +5 -0
  84. data/lib/yodel/models/core/fields/function_field.rb +28 -0
  85. data/lib/yodel/models/core/fields/hash_field.rb +54 -0
  86. data/lib/yodel/models/core/fields/html_field.rb +15 -0
  87. data/lib/yodel/models/core/fields/image_field.rb +11 -0
  88. data/lib/yodel/models/core/fields/integer_field.rb +25 -0
  89. data/lib/yodel/models/core/fields/password_field.rb +21 -0
  90. data/lib/yodel/models/core/fields/self_field.rb +27 -0
  91. data/lib/yodel/models/core/fields/string_field.rb +15 -0
  92. data/lib/yodel/models/core/fields/tags_field.rb +7 -0
  93. data/lib/yodel/models/core/fields/text_field.rb +7 -0
  94. data/lib/yodel/models/core/fields/time_field.rb +36 -0
  95. data/lib/yodel/models/core/functions/function.rb +471 -0
  96. data/lib/yodel/models/core/functions/functions.rb +2 -0
  97. data/lib/yodel/models/core/functions/trigger.rb +14 -0
  98. data/lib/yodel/models/core/log/log.rb +33 -0
  99. data/lib/yodel/models/core/log/log_entry.rb +12 -0
  100. data/lib/yodel/models/core/model/abstract_model.rb +59 -0
  101. data/lib/yodel/models/core/model/model.rb +460 -0
  102. data/lib/yodel/models/core/model/mongo_model.rb +25 -0
  103. data/lib/yodel/models/core/model/site_model.rb +17 -0
  104. data/lib/yodel/models/core/mongo/mongo.rb +3 -0
  105. data/lib/yodel/models/core/mongo/primary_key_factory.rb +12 -0
  106. data/lib/yodel/models/core/mongo/query.rb +68 -0
  107. data/lib/yodel/models/core/mongo/record_index.rb +89 -0
  108. data/lib/yodel/models/core/record/abstract_record.rb +411 -0
  109. data/lib/yodel/models/core/record/embedded_record.rb +47 -0
  110. data/lib/yodel/models/core/record/mongo_record.rb +83 -0
  111. data/lib/yodel/models/core/record/record.rb +386 -0
  112. data/lib/yodel/models/core/record/section.rb +21 -0
  113. data/lib/yodel/models/core/record/site_record.rb +31 -0
  114. data/lib/yodel/models/core/site/migration.rb +52 -0
  115. data/lib/yodel/models/core/site/remote.rb +61 -0
  116. data/lib/yodel/models/core/site/site.rb +202 -0
  117. data/lib/yodel/models/core/validations/email_address_validation.rb +24 -0
  118. data/lib/yodel/models/core/validations/embedded_records_validation.rb +31 -0
  119. data/lib/yodel/models/core/validations/errors.rb +51 -0
  120. data/lib/yodel/models/core/validations/excluded_from_validation.rb +10 -0
  121. data/lib/yodel/models/core/validations/excludes_combinations_validation.rb +18 -0
  122. data/lib/yodel/models/core/validations/format_validation.rb +10 -0
  123. data/lib/yodel/models/core/validations/included_in_validation.rb +10 -0
  124. data/lib/yodel/models/core/validations/includes_combinations_validation.rb +14 -0
  125. data/lib/yodel/models/core/validations/length_validation.rb +28 -0
  126. data/lib/yodel/models/core/validations/password_confirmation_validation.rb +11 -0
  127. data/lib/yodel/models/core/validations/required_validation.rb +9 -0
  128. data/lib/yodel/models/core/validations/unique_validation.rb +9 -0
  129. data/lib/yodel/models/core/validations/validation.rb +39 -0
  130. data/lib/yodel/models/core/validations/validations.rb +15 -0
  131. data/lib/yodel/models/email/email.rb +79 -0
  132. data/lib/yodel/models/migrations/01_record_model.rb +29 -0
  133. data/lib/yodel/models/migrations/02_page_model.rb +45 -0
  134. data/lib/yodel/models/migrations/03_layout_model.rb +38 -0
  135. data/lib/yodel/models/migrations/04_group_model.rb +61 -0
  136. data/lib/yodel/models/migrations/05_user_model.rb +24 -0
  137. data/lib/yodel/models/migrations/06_snippet_model.rb +13 -0
  138. data/lib/yodel/models/migrations/07_search_page_model.rb +32 -0
  139. data/lib/yodel/models/migrations/08_default_site_options.rb +21 -0
  140. data/lib/yodel/models/migrations/09_security_page_models.rb +36 -0
  141. data/lib/yodel/models/migrations/10_record_proxy_page_model.rb +17 -0
  142. data/lib/yodel/models/migrations/11_email_model.rb +28 -0
  143. data/lib/yodel/models/migrations/12_api_call_model.rb +23 -0
  144. data/lib/yodel/models/migrations/13_redirect_page_model.rb +13 -0
  145. data/lib/yodel/models/migrations/14_menu_model.rb +20 -0
  146. data/lib/yodel/models/models.rb +8 -0
  147. data/lib/yodel/models/pages/form_builder.rb +379 -0
  148. data/lib/yodel/models/pages/html_decorator.rb +132 -0
  149. data/lib/yodel/models/pages/layout.rb +120 -0
  150. data/lib/yodel/models/pages/menu.rb +32 -0
  151. data/lib/yodel/models/pages/page.rb +378 -0
  152. data/lib/yodel/models/pages/pages.rb +7 -0
  153. data/lib/yodel/models/pages/record_proxy_page.rb +188 -0
  154. data/lib/yodel/models/pages/redirect_page.rb +11 -0
  155. data/lib/yodel/models/search/search.rb +1 -0
  156. data/lib/yodel/models/search/search_page.rb +58 -0
  157. data/lib/yodel/models/security/facebook_login_page.rb +55 -0
  158. data/lib/yodel/models/security/group.rb +10 -0
  159. data/lib/yodel/models/security/guests_group.rb +5 -0
  160. data/lib/yodel/models/security/login_page.rb +20 -0
  161. data/lib/yodel/models/security/logout_page.rb +13 -0
  162. data/lib/yodel/models/security/noone_group.rb +5 -0
  163. data/lib/yodel/models/security/owner_group.rb +8 -0
  164. data/lib/yodel/models/security/password.rb +5 -0
  165. data/lib/yodel/models/security/password_reset_page.rb +47 -0
  166. data/lib/yodel/models/security/security.rb +10 -0
  167. data/lib/yodel/models/security/user.rb +33 -0
  168. data/lib/yodel/public/core/css/core.css +257 -0
  169. data/lib/yodel/public/core/css/reset.css +48 -0
  170. data/lib/yodel/public/core/images/cross.png +0 -0
  171. data/lib/yodel/public/core/images/spinner.gif +0 -0
  172. data/lib/yodel/public/core/images/tick.png +0 -0
  173. data/lib/yodel/public/core/images/yodel.png +0 -0
  174. data/lib/yodel/public/core/js/jquery.min.js +18 -0
  175. data/lib/yodel/public/core/js/json2.js +480 -0
  176. data/lib/yodel/public/core/js/yodel_jquery.js +238 -0
  177. data/lib/yodel/request/authentication.rb +76 -0
  178. data/lib/yodel/request/flash.rb +28 -0
  179. data/lib/yodel/request/request.rb +4 -0
  180. data/lib/yodel/requires.rb +47 -0
  181. data/lib/yodel/task_queue/queue_daemon.rb +33 -0
  182. data/lib/yodel/task_queue/queue_worker.rb +32 -0
  183. data/lib/yodel/task_queue/stats_thread.rb +27 -0
  184. data/lib/yodel/task_queue/task.rb +62 -0
  185. data/lib/yodel/task_queue/task_queue.rb +40 -0
  186. data/lib/yodel/types/date.rb +5 -0
  187. data/lib/yodel/types/object_id.rb +11 -0
  188. data/lib/yodel/types/time.rb +5 -0
  189. data/lib/yodel/version.rb +3 -0
  190. data/system/Library/LaunchDaemons/com.yodelcms.dns.plist +26 -0
  191. data/system/Library/LaunchDaemons/com.yodelcms.server.plist +26 -0
  192. data/system/etc/resolver/yodel +2 -0
  193. data/system/usr/local/bin/yodel_command_runner +2 -0
  194. data/system/usr/local/etc/yodel/development_settings.rb +28 -0
  195. data/system/usr/local/etc/yodel/production_settings.rb +27 -0
  196. data/system/var/log/yodel.log +0 -0
  197. data/test/helper.rb +18 -0
  198. data/test/test_yodel.rb +4 -0
  199. data/yodel.gemspec +47 -0
  200. metadata +501 -0
@@ -0,0 +1,238 @@
1
+ var Yodel = {};
2
+
3
+ // Security
4
+ Yodel.Security = {
5
+ login: function(credentials, path) {
6
+ var action = (path ? path : '/login');
7
+ var form = jQuery('<form action="' + action + '" metod="post"/>');
8
+ jQuery.each(credentials, function(key, value) {
9
+ form.append(jQuery('<input type="hidden" name="' + key + '" value="' + value + '"/>'));
10
+ });
11
+ form.submit();
12
+ },
13
+
14
+ remote_login: function(credentials, fns, path) {
15
+ path = path ? path : '/login.json';
16
+ var request = jQuery.post(path, credentials, 'json');
17
+ request.success(function(data, textStatus, jqXHR) {
18
+ if(fns.success)
19
+ fns.success(data);
20
+ });
21
+ request.error(function(jqXHR, textStatus, errorThrown) {
22
+ if(fns.failure) {
23
+ var json = {};
24
+ try {
25
+ json = jQuery.parseJSON(jqXHR.responseText);
26
+ } catch(err) {}
27
+ fns.failure(json);
28
+ }
29
+ });
30
+ },
31
+
32
+ Facebook: {
33
+ Window: {
34
+ width: 1000,
35
+ height: 480,
36
+ title: 'Login'
37
+ },
38
+ redirectPath: '/facebook',
39
+ redirectURI: null,
40
+ clientID: '',
41
+ scope: null
42
+ },
43
+
44
+ facebook_login: function() {
45
+ var Options = Yodel.Security.Facebook;
46
+ var url = 'https://www.facebook.com/dialog/oauth?client_id=' + Options.clientID;
47
+
48
+ if(Options.redirectURI) {
49
+ url += '&redirect_uri=' + Options.redirectURI;
50
+ } else {
51
+ url += '&redirect_uri=' + window.location.protocol + '//' + window.location.host + Options.redirectPath;
52
+ }
53
+
54
+ if(Options.scope) {
55
+ url += '&scope=' + Options.scope;
56
+ }
57
+
58
+ var windowOptionsString = 'width=' + Options.Window.width + ',height=' + Options.Window.height;
59
+ window.open(url, Options.Window.title, windowOptionsString);
60
+ }
61
+ }
62
+
63
+ jQuery('*[data-oauth-login=facebook]').live('click', function(event, element) {
64
+ Yodel.Security.facebook_login();
65
+ event.preventDefault();
66
+ });
67
+
68
+
69
+ // Record interaction
70
+ Yodel.Records = {
71
+ update: function(path, fields) {
72
+ if((path.length <= 5) || (path.substr(-5,5) != '.json'))
73
+ path = path + '.json';
74
+ jQuery.post(path, {_method: 'put', record: JSON.stringify(fields)});
75
+ }
76
+ };
77
+
78
+ jQuery('.yodel-remote-action').live('click', function(event, element) {
79
+ var action = element.attr('data-action');
80
+ if(action)
81
+ eval(action);
82
+ });
83
+
84
+
85
+ // Remote form submission
86
+ Yodel.Forms = {
87
+ clearStatusStates: function(form) {
88
+ jQuery(form).find('span[data-handles]').each(function(index, statusData) {
89
+ var status = jQuery(statusData).children().first();
90
+ if(status) {
91
+ status.html('');
92
+ status.removeClass('new');
93
+ status.removeClass('valid');
94
+ status.removeClass('invalid');
95
+ }
96
+ });
97
+ },
98
+
99
+ setState: function(fieldName, form, state, message) {
100
+ // FIXME: assumes all fields have a status element as well
101
+ // check the form contains a field element for this message
102
+ var field = jQuery(form).find('*[data-field=' + fieldName + ']').first();
103
+ if(!field[0]) {
104
+ console.log("Form does not contain a field for: " + fieldName);
105
+ return;
106
+ }
107
+
108
+ var statusData = jQuery(form).find('span[data-handles*=' + fieldName + ']').first();
109
+ var status = statusData.children().first();
110
+
111
+ // submission of a form never transitions a field to the new state
112
+ status.removeClass('new');
113
+
114
+ if(state == 'valid') {
115
+ if(!status.hasClass('invalid')) {
116
+ status.removeClass('invalid');
117
+ status.addClass('valid');
118
+ }
119
+ field.removeClass('invalid');
120
+ field.addClass('valid');
121
+ } else {
122
+ status.addClass('invalid');
123
+ status.removeClass('valid');
124
+ field.addClass('invalid');
125
+ field.removeClass('valid');
126
+ }
127
+
128
+ // either use the supplied message, or override it with
129
+ // a static message provided in the layout
130
+ var staticMessage = statusData.attr('data-' + state + '-text');
131
+ if(staticMessage)
132
+ message = staticMessage;
133
+ else
134
+ message = message || '';
135
+
136
+ // set data-errors on the field
137
+ field.attr('data-errors', message);
138
+
139
+ // insert or replace the status element's text. Statuses may
140
+ // correspond to more than one field, hence concatenating msgs.
141
+ if(message)
142
+ if(status.html() == '')
143
+ status.html(message);
144
+ else
145
+ status.html(status.html() + ', ' + message);
146
+ },
147
+
148
+ handleFailure: function(activityElement, form, data) {
149
+ var failureFunction = Yodel.Forms.callbackFunction(form, 'failure');
150
+ Yodel.Forms.hideActivityElement(activityElement);
151
+ if(failureFunction)
152
+ failureFunction(data);
153
+ },
154
+
155
+ callbackFunction: function(form, name) {
156
+ var functionName = form.attr('data-' + name + '-function');
157
+ if(functionName) {
158
+ var functionReference = eval(functionName);
159
+ if(functionReference)
160
+ return functionReference;
161
+ }
162
+ return null;
163
+ },
164
+
165
+ hideActivityElement: function(activityElement) {
166
+ if(activityElement)
167
+ activityElement.css('visibility', 'hidden');
168
+ },
169
+
170
+ submit: function(event, element) {
171
+ var form = jQuery(element);
172
+
173
+ // show the activity indicator if present
174
+ var activityElement = form.find('.yodel-form-activity')
175
+ activityElement.css('visibility', 'visible');
176
+
177
+ // create an iframe to handle the submission if necessary. iframes
178
+ // are used instead of ajax calls so file uploads can be supported
179
+ var iframeID = 'form_' + form.attr('id') + '_iframe';
180
+ var iframe = jQuery('#' + iframeID);
181
+ if(iframe.length == 0) {
182
+ form.after('<iframe id="' + iframeID + '" name="' + iframeID + '" style="display:none"></iframe>');
183
+ form.attr('target', iframeID);
184
+ iframe = jQuery('#' + iframeID);
185
+
186
+ // FIXME: handle timeouts, failed requests
187
+ iframe.load(function() {
188
+ var iframeBody = iframe.contents().find('body');
189
+ var jsonText = iframeBody.find(':contains("{")').html();
190
+ var json = jQuery.parseJSON(jsonText);
191
+ setTimeout(function () { iframeBody.html(''); }, 1);
192
+
193
+ if(json.success) {
194
+ Yodel.Forms.hideActivityElement(activityElement);
195
+ var successFunction = Yodel.Forms.callbackFunction(form, 'success');
196
+ var record = json.record;
197
+
198
+ if(successFunction)
199
+ successFunction(record);
200
+
201
+ } else {
202
+ Yodel.Forms.hideActivityElement(activityElement);
203
+ var errorsFunction = Yodel.Forms.callbackFunction(form, 'errors');
204
+ var errors = json.errors;
205
+
206
+ if(errorsFunction) {
207
+ errorsFunction(errors);
208
+ } else {
209
+ Yodel.Forms.clearStatusStates(form);
210
+ form.find('*[data-field]').each(function(index, element) {
211
+ var fieldName = jQuery(element).attr('data-field');
212
+ if(json.errors[fieldName])
213
+ Yodel.Forms.setState(fieldName, form, 'invalid', json.errors[fieldName]);
214
+ else
215
+ Yodel.Forms.setState(fieldName, form, 'valid');
216
+ });
217
+ }
218
+ }
219
+ });
220
+ }
221
+ }
222
+ }
223
+
224
+ if(jQuery.browser.msie) {
225
+ jQuery('form').live('focusin', function(focusEvent) {
226
+ var form = focusEvent.target;
227
+ if(jQuery(form).attr('data-remote') == 'true' && !form.builtSubmitEvent) {
228
+ jQuery(form).submit(function(submitEvent) {
229
+ Yodel.Forms.submit(submitEvent, submitEvent.target);
230
+ });
231
+ form.builtSubmitEvent = true;
232
+ }
233
+ });
234
+ } else {
235
+ jQuery('form[data-remote=true]').live('submit', function(event) {
236
+ Yodel.Forms.submit(event, event.target);
237
+ });
238
+ }
@@ -0,0 +1,76 @@
1
+ # some of the basic auth code taken from an inspired by rack/auth/basic
2
+ module Authentication
3
+ AUTHORIZATION_KEYS = ['HTTP_AUTHORIZATION', 'X-HTTP_AUTHORIZATION', 'X_HTTP_AUTHORIZATION']
4
+
5
+ def logged_in?(auth_type=nil)
6
+ !current_user(auth_type).nil?
7
+ end
8
+
9
+ def current_user(auth_type=nil)
10
+ unless defined?(@current_user)
11
+ @current_user = nil
12
+ auth_type ||= mime_type.auth_type
13
+
14
+ if auth_type == :page || session['current_user_id']
15
+ @current_user = site.users.find(session['current_user_id'])
16
+ session.delete('current_user_id') if @current_user.nil?
17
+
18
+ elsif auth_type == :basic
19
+ unless authorization_key.nil? || !basic?
20
+ user = site.users.first(username: credentials.first)
21
+ @current_user = user if user.try(:passwords_match?, credentials.last)
22
+ end
23
+ end
24
+ end
25
+ @current_user
26
+ end
27
+
28
+ def prompt_login(auth_type=nil)
29
+ auth_type ||= mime_type.auth_type
30
+ case auth_type
31
+ when :page
32
+ session[:redirect_to_after_login] = self.path
33
+ response.redirect site.login_pages.first.path
34
+ when :basic
35
+ response['Content-Type'] = 'text/plain'
36
+ response['WWW-Authenticate'] = "Basic realm=\"#{title}\""
37
+ response.status = 401
38
+ response.body = []
39
+ end
40
+ end
41
+
42
+ def login(credentials)
43
+ password = credentials.delete('password')
44
+ user = site.users.first(credentials)
45
+ if user && user.passwords_match?(password)
46
+ store_authenticated_user(user)
47
+ end
48
+ !@current_user.nil?
49
+ end
50
+
51
+ def logout
52
+ session.delete('current_user_id')
53
+ end
54
+
55
+ def store_authenticated_user(user)
56
+ session['current_user_id'] = user.id
57
+ @current_user = user
58
+ end
59
+
60
+ private
61
+ def authorization_key
62
+ @authorization_key ||= AUTHORIZATION_KEYS.find {|key| env.has_key?(key)}
63
+ end
64
+
65
+ def basic?
66
+ parts.first.downcase == 'basic'
67
+ end
68
+
69
+ def credentials
70
+ @credentials ||= parts.last.unpack("m*").first.split(/:/, 2)
71
+ end
72
+
73
+ def parts
74
+ @parts ||= env[authorization_key].split(' ', 2)
75
+ end
76
+ end
@@ -0,0 +1,28 @@
1
+ class Flash
2
+ def initialize(session)
3
+ @session = session
4
+ @last_request = @session['flash'] || {}
5
+ @this_request = {}
6
+ end
7
+
8
+ def finalize
9
+ @session['flash'] = @this_request
10
+ end
11
+
12
+ def [](key)
13
+ @this_request[key] || @last_request[key]
14
+ end
15
+
16
+ def now(key, value)
17
+ @last_request[key] = value
18
+ end
19
+
20
+ def []=(key, value)
21
+ @this_request[key] = value
22
+ end
23
+
24
+ def delete(key)
25
+ @this_request.delete(key)
26
+ @last_request.delete(key)
27
+ end
28
+ end
@@ -0,0 +1,4 @@
1
+ Dir.chdir(File.dirname(__FILE__)) do
2
+ require './authentication'
3
+ require './flash'
4
+ end
@@ -0,0 +1,47 @@
1
+ require 'logger'
2
+ require 'forwardable'
3
+ require 'bigdecimal'
4
+ require 'date'
5
+ require 'ostruct'
6
+ require 'net/http'
7
+ require 'open-uri'
8
+ require 'fileutils'
9
+ require 'uri'
10
+
11
+ # load bundled gems
12
+ require 'rubygems'
13
+ require 'bundler'
14
+ require 'rack'
15
+ require 'plucky' # requires mongo
16
+ require 'ember'
17
+ require 'mail'
18
+ require 'hpricot'
19
+ require 'json'
20
+ require 'rack/contrib'
21
+ require 'rubydns'
22
+ require 'git'
23
+ require 'highline'
24
+ require 'mini_magick'
25
+ require 'linguistics'
26
+
27
+ # manually load active support extensions
28
+ require 'active_support/core_ext/object/blank'
29
+ require 'active_support/core_ext/object/try'
30
+ require 'active_support/inflector'
31
+ require 'active_support/core_ext/hash/keys'
32
+ require 'active_support/core_ext/array/conversions'
33
+ require 'active_support/core_ext/time/calculations'
34
+
35
+ # for consistency, default to syck for YAML dumps/loads
36
+ # psych and syck have incompatible nil representations
37
+ YAML::ENGINE.yamler = 'syck'
38
+
39
+ # config and environment are loaded separately
40
+ # from yodel, so a configuration can be created
41
+ # before loading the server or console
42
+ require File.join(File.dirname(__FILE__), 'config', 'config')
43
+ if $settings
44
+ require $settings
45
+ else
46
+ require '/usr/local/etc/yodel/settings.rb'
47
+ end
@@ -0,0 +1,33 @@
1
+ module QueueDaemon
2
+ IMMEDIATE_WORKERS = 1
3
+ DELAYED_WORKERS = 1
4
+
5
+ def self.run
6
+ immediate_queue = TaskQueue.new(true)
7
+ delayed_queue = TaskQueue.new(false)
8
+
9
+ immediate_workers = []
10
+ delayed_workers = []
11
+
12
+ puts "Starting Workers..."
13
+ stats_thread = StatsThread.new
14
+ IMMEDIATE_WORKERS.times do
15
+ immediate_workers << QueueWorker.new(immediate_queue, stats_thread)
16
+ end
17
+
18
+ DELAYED_WORKERS.times do
19
+ delayed_workers << QueueWorker.new(delayed_queue, stats_thread)
20
+ end
21
+
22
+ Signal.trap('INT') do
23
+ puts("Shutting down...")
24
+ stats_thread.stop
25
+ immediate_workers.each(&:stop)
26
+ delayed_workers.each(&:stop)
27
+ end
28
+
29
+ immediate_workers.each {|worker| worker.join}
30
+ delayed_workers.each {|worker| worker.join}
31
+ puts "Shutdown complete"
32
+ end
33
+ end
@@ -0,0 +1,32 @@
1
+ class QueueWorker
2
+ PAUSE_DURATION = 1
3
+ def initialize(queue, stats_thread)
4
+ @stats_thread = stats_thread
5
+ @queue = queue
6
+ run
7
+ end
8
+
9
+ def run
10
+ @running = true
11
+ @thread = Thread.new do
12
+ while @running
13
+ task = @queue.pop
14
+ sleep(PAUSE_DURATION) and next if task.nil?
15
+ task.execute
16
+ @stats_thread.processed_task
17
+ end
18
+ end
19
+ end
20
+
21
+ def stop
22
+ @running = false
23
+ end
24
+
25
+ def kill
26
+ @thread.kill
27
+ end
28
+
29
+ def join
30
+ @thread.join
31
+ end
32
+ end