yodel 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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,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,6 @@
1
+ Dir.chdir(File.dirname(__FILE__)) do
2
+ require './mime_type_set'
3
+ require './mime_type'
4
+ require './yodel'
5
+ require './default_mime_set'
6
+ 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