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,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