yodel 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +9 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +63 -0
- data/LICENSE +1 -0
- data/README.rdoc +20 -0
- data/Rakefile +1 -0
- data/bin/yodel +4 -0
- data/lib/yodel.rb +22 -0
- data/lib/yodel/application/application.rb +44 -0
- data/lib/yodel/application/extension.rb +59 -0
- data/lib/yodel/application/request_handler.rb +48 -0
- data/lib/yodel/application/yodel.rb +25 -0
- data/lib/yodel/command/command.rb +94 -0
- data/lib/yodel/command/deploy.rb +67 -0
- data/lib/yodel/command/dns_server.rb +16 -0
- data/lib/yodel/command/installer.rb +229 -0
- data/lib/yodel/config/config.rb +30 -0
- data/lib/yodel/config/environment.rb +16 -0
- data/lib/yodel/config/yodel.rb +21 -0
- data/lib/yodel/exceptions/destroyed_record.rb +2 -0
- data/lib/yodel/exceptions/domain_not_found.rb +16 -0
- data/lib/yodel/exceptions/duplicate_layout.rb +2 -0
- data/lib/yodel/exceptions/exceptions.rb +3 -0
- data/lib/yodel/exceptions/inconsistent_lock_state.rb +2 -0
- data/lib/yodel/exceptions/invalid_field.rb +2 -0
- data/lib/yodel/exceptions/invalid_index.rb +2 -0
- data/lib/yodel/exceptions/invalid_mixin.rb +2 -0
- data/lib/yodel/exceptions/invalid_model_field.rb +2 -0
- data/lib/yodel/exceptions/layout_not_found.rb +2 -0
- data/lib/yodel/exceptions/mass_assignment.rb +2 -0
- data/lib/yodel/exceptions/missing_migration.rb +2 -0
- data/lib/yodel/exceptions/missing_root_directory.rb +15 -0
- data/lib/yodel/exceptions/unable_to_acquire_lock.rb +2 -0
- data/lib/yodel/exceptions/unauthorised.rb +2 -0
- data/lib/yodel/exceptions/unknown_field.rb +2 -0
- data/lib/yodel/middleware/development_server.rb +180 -0
- data/lib/yodel/middleware/error_pages.rb +72 -0
- data/lib/yodel/middleware/public_assets.rb +78 -0
- data/lib/yodel/middleware/request.rb +16 -0
- data/lib/yodel/middleware/site_detector.rb +22 -0
- data/lib/yodel/mime_types/default_mime_set.rb +28 -0
- data/lib/yodel/mime_types/mime_type.rb +68 -0
- data/lib/yodel/mime_types/mime_type_set.rb +41 -0
- data/lib/yodel/mime_types/mime_types.rb +6 -0
- data/lib/yodel/mime_types/yodel.rb +15 -0
- data/lib/yodel/models/api/api.rb +1 -0
- data/lib/yodel/models/api/api_call.rb +87 -0
- data/lib/yodel/models/core/associations/association.rb +37 -0
- data/lib/yodel/models/core/associations/associations.rb +22 -0
- data/lib/yodel/models/core/associations/counts/many_association.rb +18 -0
- data/lib/yodel/models/core/associations/counts/one_association.rb +22 -0
- data/lib/yodel/models/core/associations/embedded/embedded_association.rb +47 -0
- data/lib/yodel/models/core/associations/embedded/embedded_record_array.rb +12 -0
- data/lib/yodel/models/core/associations/embedded/many_embedded_association.rb +62 -0
- data/lib/yodel/models/core/associations/embedded/one_embedded_association.rb +49 -0
- data/lib/yodel/models/core/associations/query/many_query_association.rb +10 -0
- data/lib/yodel/models/core/associations/query/one_query_association.rb +10 -0
- data/lib/yodel/models/core/associations/query/query_association.rb +64 -0
- data/lib/yodel/models/core/associations/record_association.rb +38 -0
- data/lib/yodel/models/core/associations/store/many_store_association.rb +32 -0
- data/lib/yodel/models/core/associations/store/one_store_association.rb +14 -0
- data/lib/yodel/models/core/associations/store/store_association.rb +51 -0
- data/lib/yodel/models/core/attachments/attachment.rb +73 -0
- data/lib/yodel/models/core/attachments/image.rb +38 -0
- data/lib/yodel/models/core/core.rb +15 -0
- data/lib/yodel/models/core/fields/alias_field.rb +32 -0
- data/lib/yodel/models/core/fields/array_field.rb +64 -0
- data/lib/yodel/models/core/fields/attachment_field.rb +42 -0
- data/lib/yodel/models/core/fields/boolean_field.rb +28 -0
- data/lib/yodel/models/core/fields/change_sensitive_array.rb +96 -0
- data/lib/yodel/models/core/fields/change_sensitive_hash.rb +53 -0
- data/lib/yodel/models/core/fields/color_field.rb +4 -0
- data/lib/yodel/models/core/fields/date_field.rb +35 -0
- data/lib/yodel/models/core/fields/decimal_field.rb +19 -0
- data/lib/yodel/models/core/fields/email_field.rb +10 -0
- data/lib/yodel/models/core/fields/enum_field.rb +33 -0
- data/lib/yodel/models/core/fields/field.rb +154 -0
- data/lib/yodel/models/core/fields/fields.rb +29 -0
- data/lib/yodel/models/core/fields/fields_field.rb +31 -0
- data/lib/yodel/models/core/fields/filter_mixin.rb +9 -0
- data/lib/yodel/models/core/fields/filtered_string_field.rb +5 -0
- data/lib/yodel/models/core/fields/filtered_text_field.rb +5 -0
- data/lib/yodel/models/core/fields/function_field.rb +28 -0
- data/lib/yodel/models/core/fields/hash_field.rb +54 -0
- data/lib/yodel/models/core/fields/html_field.rb +15 -0
- data/lib/yodel/models/core/fields/image_field.rb +11 -0
- data/lib/yodel/models/core/fields/integer_field.rb +25 -0
- data/lib/yodel/models/core/fields/password_field.rb +21 -0
- data/lib/yodel/models/core/fields/self_field.rb +27 -0
- data/lib/yodel/models/core/fields/string_field.rb +15 -0
- data/lib/yodel/models/core/fields/tags_field.rb +7 -0
- data/lib/yodel/models/core/fields/text_field.rb +7 -0
- data/lib/yodel/models/core/fields/time_field.rb +36 -0
- data/lib/yodel/models/core/functions/function.rb +471 -0
- data/lib/yodel/models/core/functions/functions.rb +2 -0
- data/lib/yodel/models/core/functions/trigger.rb +14 -0
- data/lib/yodel/models/core/log/log.rb +33 -0
- data/lib/yodel/models/core/log/log_entry.rb +12 -0
- data/lib/yodel/models/core/model/abstract_model.rb +59 -0
- data/lib/yodel/models/core/model/model.rb +460 -0
- data/lib/yodel/models/core/model/mongo_model.rb +25 -0
- data/lib/yodel/models/core/model/site_model.rb +17 -0
- data/lib/yodel/models/core/mongo/mongo.rb +3 -0
- data/lib/yodel/models/core/mongo/primary_key_factory.rb +12 -0
- data/lib/yodel/models/core/mongo/query.rb +68 -0
- data/lib/yodel/models/core/mongo/record_index.rb +89 -0
- data/lib/yodel/models/core/record/abstract_record.rb +411 -0
- data/lib/yodel/models/core/record/embedded_record.rb +47 -0
- data/lib/yodel/models/core/record/mongo_record.rb +83 -0
- data/lib/yodel/models/core/record/record.rb +386 -0
- data/lib/yodel/models/core/record/section.rb +21 -0
- data/lib/yodel/models/core/record/site_record.rb +31 -0
- data/lib/yodel/models/core/site/migration.rb +52 -0
- data/lib/yodel/models/core/site/remote.rb +61 -0
- data/lib/yodel/models/core/site/site.rb +202 -0
- data/lib/yodel/models/core/validations/email_address_validation.rb +24 -0
- data/lib/yodel/models/core/validations/embedded_records_validation.rb +31 -0
- data/lib/yodel/models/core/validations/errors.rb +51 -0
- data/lib/yodel/models/core/validations/excluded_from_validation.rb +10 -0
- data/lib/yodel/models/core/validations/excludes_combinations_validation.rb +18 -0
- data/lib/yodel/models/core/validations/format_validation.rb +10 -0
- data/lib/yodel/models/core/validations/included_in_validation.rb +10 -0
- data/lib/yodel/models/core/validations/includes_combinations_validation.rb +14 -0
- data/lib/yodel/models/core/validations/length_validation.rb +28 -0
- data/lib/yodel/models/core/validations/password_confirmation_validation.rb +11 -0
- data/lib/yodel/models/core/validations/required_validation.rb +9 -0
- data/lib/yodel/models/core/validations/unique_validation.rb +9 -0
- data/lib/yodel/models/core/validations/validation.rb +39 -0
- data/lib/yodel/models/core/validations/validations.rb +15 -0
- data/lib/yodel/models/email/email.rb +79 -0
- data/lib/yodel/models/migrations/01_record_model.rb +29 -0
- data/lib/yodel/models/migrations/02_page_model.rb +45 -0
- data/lib/yodel/models/migrations/03_layout_model.rb +38 -0
- data/lib/yodel/models/migrations/04_group_model.rb +61 -0
- data/lib/yodel/models/migrations/05_user_model.rb +24 -0
- data/lib/yodel/models/migrations/06_snippet_model.rb +13 -0
- data/lib/yodel/models/migrations/07_search_page_model.rb +32 -0
- data/lib/yodel/models/migrations/08_default_site_options.rb +21 -0
- data/lib/yodel/models/migrations/09_security_page_models.rb +36 -0
- data/lib/yodel/models/migrations/10_record_proxy_page_model.rb +17 -0
- data/lib/yodel/models/migrations/11_email_model.rb +28 -0
- data/lib/yodel/models/migrations/12_api_call_model.rb +23 -0
- data/lib/yodel/models/migrations/13_redirect_page_model.rb +13 -0
- data/lib/yodel/models/migrations/14_menu_model.rb +20 -0
- data/lib/yodel/models/models.rb +8 -0
- data/lib/yodel/models/pages/form_builder.rb +379 -0
- data/lib/yodel/models/pages/html_decorator.rb +132 -0
- data/lib/yodel/models/pages/layout.rb +120 -0
- data/lib/yodel/models/pages/menu.rb +32 -0
- data/lib/yodel/models/pages/page.rb +378 -0
- data/lib/yodel/models/pages/pages.rb +7 -0
- data/lib/yodel/models/pages/record_proxy_page.rb +188 -0
- data/lib/yodel/models/pages/redirect_page.rb +11 -0
- data/lib/yodel/models/search/search.rb +1 -0
- data/lib/yodel/models/search/search_page.rb +58 -0
- data/lib/yodel/models/security/facebook_login_page.rb +55 -0
- data/lib/yodel/models/security/group.rb +10 -0
- data/lib/yodel/models/security/guests_group.rb +5 -0
- data/lib/yodel/models/security/login_page.rb +20 -0
- data/lib/yodel/models/security/logout_page.rb +13 -0
- data/lib/yodel/models/security/noone_group.rb +5 -0
- data/lib/yodel/models/security/owner_group.rb +8 -0
- data/lib/yodel/models/security/password.rb +5 -0
- data/lib/yodel/models/security/password_reset_page.rb +47 -0
- data/lib/yodel/models/security/security.rb +10 -0
- data/lib/yodel/models/security/user.rb +33 -0
- data/lib/yodel/public/core/css/core.css +257 -0
- data/lib/yodel/public/core/css/reset.css +48 -0
- data/lib/yodel/public/core/images/cross.png +0 -0
- data/lib/yodel/public/core/images/spinner.gif +0 -0
- data/lib/yodel/public/core/images/tick.png +0 -0
- data/lib/yodel/public/core/images/yodel.png +0 -0
- data/lib/yodel/public/core/js/jquery.min.js +18 -0
- data/lib/yodel/public/core/js/json2.js +480 -0
- data/lib/yodel/public/core/js/yodel_jquery.js +238 -0
- data/lib/yodel/request/authentication.rb +76 -0
- data/lib/yodel/request/flash.rb +28 -0
- data/lib/yodel/request/request.rb +4 -0
- data/lib/yodel/requires.rb +47 -0
- data/lib/yodel/task_queue/queue_daemon.rb +33 -0
- data/lib/yodel/task_queue/queue_worker.rb +32 -0
- data/lib/yodel/task_queue/stats_thread.rb +27 -0
- data/lib/yodel/task_queue/task.rb +62 -0
- data/lib/yodel/task_queue/task_queue.rb +40 -0
- data/lib/yodel/types/date.rb +5 -0
- data/lib/yodel/types/object_id.rb +11 -0
- data/lib/yodel/types/time.rb +5 -0
- data/lib/yodel/version.rb +3 -0
- data/system/Library/LaunchDaemons/com.yodelcms.dns.plist +26 -0
- data/system/Library/LaunchDaemons/com.yodelcms.server.plist +26 -0
- data/system/etc/resolver/yodel +2 -0
- data/system/usr/local/bin/yodel_command_runner +2 -0
- data/system/usr/local/etc/yodel/development_settings.rb +28 -0
- data/system/usr/local/etc/yodel/production_settings.rb +27 -0
- data/system/var/log/yodel.log +0 -0
- data/test/helper.rb +18 -0
- data/test/test_yodel.rb +4 -0
- data/yodel.gemspec +47 -0
- metadata +501 -0
@@ -0,0 +1,72 @@
|
|
1
|
+
class ErrorPages
|
2
|
+
def initialize(app)
|
3
|
+
@app = app
|
4
|
+
end
|
5
|
+
|
6
|
+
def call(env)
|
7
|
+
status, headers, response = @app.call(env)
|
8
|
+
if status.to_i >= 400 && status.to_i != 401
|
9
|
+
return render_error_page(status, response)
|
10
|
+
else
|
11
|
+
return [status, headers, response]
|
12
|
+
end
|
13
|
+
rescue
|
14
|
+
Yodel.config.logger.warn $!.to_s
|
15
|
+
if Yodel.env.production?
|
16
|
+
if $!.is_a?(DomainNotFound)
|
17
|
+
return render_error_page(404, ['No site has been set up at this address'])
|
18
|
+
else
|
19
|
+
return render_error_page(500, [])
|
20
|
+
end
|
21
|
+
else
|
22
|
+
if $!.respond_to?(:error) && $!.respond_to?(:description)
|
23
|
+
return render_error_page(404, $!.error, $!.description)
|
24
|
+
else
|
25
|
+
raise $!
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def render_error_page(error_code, response, description=nil)
|
31
|
+
template = Ember::Template.new(TEMPLATE)
|
32
|
+
if response.present? && response.respond_to?(:length) && response.length > 0
|
33
|
+
components = []
|
34
|
+
response.each {|component| components << component}
|
35
|
+
error = components.join
|
36
|
+
elsif error_code == 403
|
37
|
+
error = "Unauthorised"
|
38
|
+
else
|
39
|
+
error = "We're sorry, something went wrong."
|
40
|
+
end
|
41
|
+
if description.nil?
|
42
|
+
if error_code == 404
|
43
|
+
description = "You may have mistyped the address or the page may have moved."
|
44
|
+
elsif error_code == 403
|
45
|
+
description = "You must log in before performing this action."
|
46
|
+
else
|
47
|
+
description = "We've been notified about this issue and we'll take a look at it shortly."
|
48
|
+
end
|
49
|
+
end
|
50
|
+
[error_code, {'Content-Type' => 'text/html'}, [template.render(binding)]]
|
51
|
+
end
|
52
|
+
|
53
|
+
TEMPLATE = <<HTML
|
54
|
+
<!DOCTYPE html>
|
55
|
+
<html>
|
56
|
+
<head>
|
57
|
+
<title><%= error_code %> - <%= error %></title>
|
58
|
+
<link rel="stylesheet" href="/core/css/core.css" type="text/css">
|
59
|
+
</head>
|
60
|
+
<body>
|
61
|
+
<article id="modal">
|
62
|
+
<header>
|
63
|
+
<h1>yodel</h1>
|
64
|
+
<div id="lip"></div>
|
65
|
+
</header>
|
66
|
+
<h1><%= error %></h1>
|
67
|
+
<p><%= description %></p>
|
68
|
+
</article>
|
69
|
+
</body>
|
70
|
+
</html>
|
71
|
+
HTML
|
72
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'rack/utils'
|
2
|
+
require 'rack/mime'
|
3
|
+
|
4
|
+
# some code copied from Rack::File
|
5
|
+
class PublicAssets
|
6
|
+
SEPARATORS = Regexp.union(*[::File::SEPARATOR, ::File::ALT_SEPARATOR].compact)
|
7
|
+
|
8
|
+
def initialize(app)
|
9
|
+
@app = app
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(env)
|
13
|
+
parts = Rack::Utils.unescape(env["PATH_INFO"]).split(SEPARATORS)
|
14
|
+
return [403, {"Content-Type" => "text/plain"}, "Forbidden"] if parts.include? ".."
|
15
|
+
request = Rack::Request.new(env)
|
16
|
+
site = env['yodel.site']
|
17
|
+
|
18
|
+
unless site.nil?
|
19
|
+
public_directories = site.public_directories
|
20
|
+
else
|
21
|
+
public_directories = Yodel.config.public_directories
|
22
|
+
end
|
23
|
+
|
24
|
+
public_directories.each do |public_dir|
|
25
|
+
path = File.join(public_dir, *parts)
|
26
|
+
if File.file?(path) && File.readable?(path)
|
27
|
+
return dup.serve_file(path, env)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# raise any delayed exceptions. These are delayed till this middleware so
|
32
|
+
# the assets needed to render the error pages for them are available. Without
|
33
|
+
# this delay, a DomainNotFound exception raised in SiteDetector would prevent
|
34
|
+
# PublicAssest from serving the css or images used on the error page.
|
35
|
+
raise DomainNotFound.new(request.host, request.port) if site.nil?
|
36
|
+
raise MissingRootDirectory.new(site, request.port) if !File.directory?(site.root_directory)
|
37
|
+
@app.call(env)
|
38
|
+
end
|
39
|
+
|
40
|
+
def each
|
41
|
+
File.open(@path, 'rb') do |file|
|
42
|
+
file.seek(@range.begin)
|
43
|
+
remaining_len = @range.end - @range.begin + 1
|
44
|
+
while remaining_len > 0
|
45
|
+
part = file.read([8192, remaining_len].min)
|
46
|
+
break unless part
|
47
|
+
remaining_len -= part.length
|
48
|
+
yield part
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
protected
|
54
|
+
def serve_file(path, env)
|
55
|
+
response = [200, {
|
56
|
+
"Last-Modified" => File.mtime(path).httpdate,
|
57
|
+
"Content-Type" => Rack::Mime.mime_type(File.extname(path), 'text/plain')
|
58
|
+
}, self]
|
59
|
+
|
60
|
+
size = FileTest.size?(path) || Rack::Utils.bytesize(IO.read(path))
|
61
|
+
ranges = Rack::Utils.byte_ranges(env, size)
|
62
|
+
|
63
|
+
if ranges.nil? || ranges.length > 1
|
64
|
+
@range = 0..size-1
|
65
|
+
elsif ranges.empty?
|
66
|
+
return [416, {"Content-Type" => "text/plain"}, "Byte range unsatisfiable"]
|
67
|
+
else # partial content
|
68
|
+
@range = ranges[0]
|
69
|
+
response[0] = 206
|
70
|
+
response[1]["Content-Range"] = "bytes #{@range.begin}-#{@range.end}/#{size}"
|
71
|
+
size = @range.end - @range.begin + 1
|
72
|
+
end
|
73
|
+
|
74
|
+
response[1]["Content-Length"] = size.to_s
|
75
|
+
@path = path
|
76
|
+
response
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Rack
|
2
|
+
class Request
|
3
|
+
# almost verbatim copy of the 'url' method; just we
|
4
|
+
# don't append the full_path to the constructed url
|
5
|
+
def scheme_and_host
|
6
|
+
url = scheme + "://"
|
7
|
+
url << host
|
8
|
+
|
9
|
+
if scheme == "https" && port != 443 ||
|
10
|
+
scheme == "http" && port != 80
|
11
|
+
url << ":#{port}"
|
12
|
+
end
|
13
|
+
url
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class SiteDetector
|
2
|
+
def initialize(app)
|
3
|
+
@app = app
|
4
|
+
end
|
5
|
+
|
6
|
+
def call(env)
|
7
|
+
request = Rack::Request.new(env)
|
8
|
+
site = Site.where(domains: request.host).first
|
9
|
+
env['yodel.site'] = site
|
10
|
+
|
11
|
+
unless site.nil?
|
12
|
+
if Yodel.env.production?
|
13
|
+
raise MissingRootDirectory.new(site, request.port) if !File.directory?(site.root_directory)
|
14
|
+
env['rack.session.options'][:domain] = ".#{site.domains.first}"
|
15
|
+
end
|
16
|
+
else
|
17
|
+
raise DomainNotFound.new(request.host, request.port) if Yodel.env.production?
|
18
|
+
end
|
19
|
+
|
20
|
+
@app.call(env)
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
Yodel.mime_types do
|
2
|
+
mime_type :html do
|
3
|
+
extensions 'html', 'htm', 'shtml'
|
4
|
+
mime_types 'text/html'
|
5
|
+
auth_type :page
|
6
|
+
layout_processor :ember
|
7
|
+
end
|
8
|
+
|
9
|
+
mime_type :json do
|
10
|
+
extensions 'json'
|
11
|
+
mime_types 'application/json'
|
12
|
+
layout_processor :eval
|
13
|
+
auth_type :basic
|
14
|
+
processor do |data|
|
15
|
+
JSON.generate(data)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
mime_type :atom do
|
20
|
+
extensions 'atom'
|
21
|
+
mime_types 'application/atom+xml'
|
22
|
+
layout_processor :eval
|
23
|
+
auth_type :basic
|
24
|
+
builder do
|
25
|
+
Builder::XmlMarkup.new
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
class MimeType
|
2
|
+
attr_accessor :name, :extensions, :mime_types
|
3
|
+
def initialize(name)
|
4
|
+
@name = name
|
5
|
+
@extensions = []
|
6
|
+
@mime_types = []
|
7
|
+
@processor = nil
|
8
|
+
@builder = nil
|
9
|
+
@auth_type = :page
|
10
|
+
@layout_processor = :ember
|
11
|
+
end
|
12
|
+
|
13
|
+
def mime_types(*types)
|
14
|
+
@mime_types += types
|
15
|
+
end
|
16
|
+
|
17
|
+
def extensions(*exts)
|
18
|
+
@extensions += exts
|
19
|
+
end
|
20
|
+
|
21
|
+
def default_extension(ext=nil)
|
22
|
+
@extensions.first
|
23
|
+
end
|
24
|
+
|
25
|
+
def default_mime_type
|
26
|
+
@mime_types.first
|
27
|
+
end
|
28
|
+
|
29
|
+
def has_builder?
|
30
|
+
!@builder.nil?
|
31
|
+
end
|
32
|
+
|
33
|
+
def builder(&block)
|
34
|
+
@builder = block
|
35
|
+
end
|
36
|
+
|
37
|
+
def create_builder
|
38
|
+
@builder ? @builder.call : nil
|
39
|
+
end
|
40
|
+
|
41
|
+
def has_processor?
|
42
|
+
!@processor.nil?
|
43
|
+
end
|
44
|
+
|
45
|
+
def processor(&block)
|
46
|
+
@processor = block
|
47
|
+
end
|
48
|
+
|
49
|
+
def process(data)
|
50
|
+
@processor ? @processor.call(data) : data
|
51
|
+
end
|
52
|
+
|
53
|
+
def layout_processor(*processor)
|
54
|
+
if processor.empty?
|
55
|
+
@layout_processor
|
56
|
+
else
|
57
|
+
@layout_processor = processor.first
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def auth_type(*type)
|
62
|
+
if type.empty?
|
63
|
+
@auth_type
|
64
|
+
else
|
65
|
+
@auth_type = type.first
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
class MimeTypeSet
|
2
|
+
attr_accessor :types
|
3
|
+
def initialize
|
4
|
+
@default = nil # default mime type when no other can be matched
|
5
|
+
@types = {} # index by type name (:html)
|
6
|
+
@extensions = {} # index by type extensions (html, htm)
|
7
|
+
@mime_types = {} # index by mime types (text/html)
|
8
|
+
end
|
9
|
+
|
10
|
+
def each
|
11
|
+
@types.values.each do |type|
|
12
|
+
yield type
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def [](name)
|
17
|
+
@types[name]
|
18
|
+
end
|
19
|
+
|
20
|
+
def <<(type)
|
21
|
+
@default ||= type
|
22
|
+
@types[type.name] = type
|
23
|
+
type.extensions.each {|extension| @extensions[extension] = type}
|
24
|
+
type.mime_types.each {|mime_type| @mime_types[mime_type] = type}
|
25
|
+
end
|
26
|
+
|
27
|
+
def mime_type_for_request(format, accept)
|
28
|
+
# try to match by file extension first
|
29
|
+
return @extensions[format] if format && @extensions.has_key?(format)
|
30
|
+
|
31
|
+
# parse the accept string and try to match by mime type. The accept
|
32
|
+
# header looks like: application/xml,text/html;q=0.9
|
33
|
+
accept.to_s.split(',').each do |mime_type|
|
34
|
+
name = mime_type.split(';').first
|
35
|
+
return @mime_types[name] if @mime_types.has_key?(name)
|
36
|
+
end
|
37
|
+
|
38
|
+
# as a last resort, respond with the first mime type defined
|
39
|
+
@default
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Yodel
|
2
|
+
def self.mime_types(&block)
|
3
|
+
if block_given?
|
4
|
+
instance_eval &block
|
5
|
+
else
|
6
|
+
@mime_type_set ||= MimeTypeSet.new
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.mime_type(name, &block)
|
11
|
+
mime_type = MimeType.new(name)
|
12
|
+
mime_type.instance_eval &block
|
13
|
+
mime_types << mime_type
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require './api/api_call'
|
@@ -0,0 +1,87 @@
|
|
1
|
+
class APICall < Record
|
2
|
+
def call(data)
|
3
|
+
Task.add_task(:call_api, encode_data_hash(data), site)
|
4
|
+
end
|
5
|
+
|
6
|
+
def perform_call(data)
|
7
|
+
data = decode_data_hash(data)
|
8
|
+
mime = Yodel.mime_types[mime_type.to_sym]
|
9
|
+
headers = {'Content-Type' => mime.default_mime_type}
|
10
|
+
|
11
|
+
# FIXME: reloading should be done elsewhere, not a concern of APICall
|
12
|
+
#Layout.reload_layouts(site) if Yodel.env.development?
|
13
|
+
if body_layout && layout = site.layouts.where(name: body_layout, mime_type: mime_type).first
|
14
|
+
@data = data
|
15
|
+
set_content(body)
|
16
|
+
set_binding(binding)
|
17
|
+
payload = mime.process(layout.render(self))
|
18
|
+
else
|
19
|
+
payload = body
|
20
|
+
end
|
21
|
+
|
22
|
+
Net::HTTP.start(domain, port) do |http|
|
23
|
+
request = request_class.new(path, headers)
|
24
|
+
case authentication
|
25
|
+
when 'basic'
|
26
|
+
request.basic_auth username, password
|
27
|
+
when 'digest'
|
28
|
+
# FIXME: implement
|
29
|
+
end
|
30
|
+
response = http.request(request, payload)
|
31
|
+
Function.new(function).execute(binding)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
def content
|
37
|
+
@content
|
38
|
+
end
|
39
|
+
|
40
|
+
def set_content(content)
|
41
|
+
@content = content
|
42
|
+
end
|
43
|
+
|
44
|
+
def get_binding
|
45
|
+
@binding
|
46
|
+
end
|
47
|
+
|
48
|
+
def set_binding(binding)
|
49
|
+
@binding = binding
|
50
|
+
end
|
51
|
+
|
52
|
+
def data
|
53
|
+
@data
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
private
|
58
|
+
def encode_data_hash(data)
|
59
|
+
({_id: self.id}).tap do |encoded|
|
60
|
+
data.each do |key, value|
|
61
|
+
value = {_id: value.id} if value.is_a?(Record)
|
62
|
+
encoded[key] = value
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def decode_data_hash(data)
|
68
|
+
data.each do |key, value|
|
69
|
+
if value.is_a?(Hash) && value.key?('_id')
|
70
|
+
data[key] = site.records.find(value['_id'])
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def request_class
|
76
|
+
case self.http_method.downcase
|
77
|
+
when 'get'
|
78
|
+
Net::HTTP::Get
|
79
|
+
when 'post'
|
80
|
+
Net::HTTP::Post
|
81
|
+
when 'put'
|
82
|
+
Net::HTTP::Put
|
83
|
+
when 'delete'
|
84
|
+
Net::HTTP::Delete
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|