orange-core 0.5.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. data/README.markdown +145 -0
  2. data/lib/orange-core.rb +8 -0
  3. data/lib/orange-core/application.rb +132 -0
  4. data/lib/orange-core/assets/css/exceptions.css +50 -0
  5. data/lib/orange-core/assets/js/exceptions.js +44 -0
  6. data/lib/orange-core/carton.rb +178 -0
  7. data/lib/orange-core/core.rb +266 -0
  8. data/lib/orange-core/magick.rb +270 -0
  9. data/lib/orange-core/middleware/base.rb +96 -0
  10. data/lib/orange-core/middleware/database.rb +45 -0
  11. data/lib/orange-core/middleware/four_oh_four.rb +45 -0
  12. data/lib/orange-core/middleware/globals.rb +17 -0
  13. data/lib/orange-core/middleware/loader.rb +13 -0
  14. data/lib/orange-core/middleware/rerouter.rb +53 -0
  15. data/lib/orange-core/middleware/restful_router.rb +99 -0
  16. data/lib/orange-core/middleware/route_context.rb +39 -0
  17. data/lib/orange-core/middleware/route_site.rb +51 -0
  18. data/lib/orange-core/middleware/show_exceptions.rb +80 -0
  19. data/lib/orange-core/middleware/static.rb +67 -0
  20. data/lib/orange-core/middleware/static_file.rb +32 -0
  21. data/lib/orange-core/middleware/template.rb +60 -0
  22. data/lib/orange-core/packet.rb +232 -0
  23. data/lib/orange-core/plugin.rb +172 -0
  24. data/lib/orange-core/resource.rb +96 -0
  25. data/lib/orange-core/resources/mapper.rb +36 -0
  26. data/lib/orange-core/resources/model_resource.rb +228 -0
  27. data/lib/orange-core/resources/not_found.rb +10 -0
  28. data/lib/orange-core/resources/page_parts.rb +68 -0
  29. data/lib/orange-core/resources/parser.rb +113 -0
  30. data/lib/orange-core/resources/routable_resource.rb +16 -0
  31. data/lib/orange-core/resources/scaffold.rb +106 -0
  32. data/lib/orange-core/stack.rb +226 -0
  33. data/lib/orange-core/templates/exceptions.haml +111 -0
  34. data/lib/orange-core/views/default_resource/create.haml +4 -0
  35. data/lib/orange-core/views/default_resource/edit.haml +9 -0
  36. data/lib/orange-core/views/default_resource/list.haml +10 -0
  37. data/lib/orange-core/views/default_resource/show.haml +4 -0
  38. data/lib/orange-core/views/default_resource/table_row.haml +7 -0
  39. data/lib/orange-core/views/not_found/404.haml +2 -0
  40. data/spec/orange-core/application_spec.rb +183 -0
  41. data/spec/orange-core/carton_spec.rb +136 -0
  42. data/spec/orange-core/core_spec.rb +248 -0
  43. data/spec/orange-core/magick_spec.rb +96 -0
  44. data/spec/orange-core/middleware/base_spec.rb +38 -0
  45. data/spec/orange-core/middleware/globals_spec.rb +3 -0
  46. data/spec/orange-core/middleware/rerouter_spec.rb +3 -0
  47. data/spec/orange-core/middleware/restful_router_spec.rb +3 -0
  48. data/spec/orange-core/middleware/route_context_spec.rb +3 -0
  49. data/spec/orange-core/middleware/route_site_spec.rb +3 -0
  50. data/spec/orange-core/middleware/show_exceptions_spec.rb +3 -0
  51. data/spec/orange-core/middleware/static_file_spec.rb +3 -0
  52. data/spec/orange-core/middleware/static_spec.rb +3 -0
  53. data/spec/orange-core/mock/mock_app.rb +16 -0
  54. data/spec/orange-core/mock/mock_carton.rb +43 -0
  55. data/spec/orange-core/mock/mock_core.rb +2 -0
  56. data/spec/orange-core/mock/mock_middleware.rb +25 -0
  57. data/spec/orange-core/mock/mock_mixins.rb +19 -0
  58. data/spec/orange-core/mock/mock_model_resource.rb +47 -0
  59. data/spec/orange-core/mock/mock_pulp.rb +24 -0
  60. data/spec/orange-core/mock/mock_resource.rb +26 -0
  61. data/spec/orange-core/mock/mock_router.rb +10 -0
  62. data/spec/orange-core/orange_spec.rb +19 -0
  63. data/spec/orange-core/packet_spec.rb +203 -0
  64. data/spec/orange-core/resource_spec.rb +96 -0
  65. data/spec/orange-core/resources/mapper_spec.rb +5 -0
  66. data/spec/orange-core/resources/model_resource_spec.rb +246 -0
  67. data/spec/orange-core/resources/parser_spec.rb +5 -0
  68. data/spec/orange-core/resources/routable_resource_spec.rb +5 -0
  69. data/spec/orange-core/spec_helper.rb +53 -0
  70. data/spec/orange-core/stack_spec.rb +232 -0
  71. data/spec/stats.rb +182 -0
  72. metadata +227 -0
data/README.markdown ADDED
@@ -0,0 +1,145 @@
1
+ Orange
2
+ ======
3
+
4
+ Orange is intended to be a middle ground between the simplicity of Sinatra
5
+ and the power of Rails. Orange is being developed by Orange Sparkle Ball, inc
6
+ for our own use.
7
+
8
+ orange-core represents the core dependencies for orange applications. If you want
9
+ a full featured Orange-based CMS, we're working on splitting that into the Sparkles
10
+ project.
11
+
12
+ **Note**: Orange is still in the alpha stage. Test coverage is lack-luster at best.
13
+ Tread carefully.
14
+
15
+ More Info
16
+ =========
17
+
18
+ Orange Philosophy
19
+ -----------------
20
+ The Orange application framework is intended to be a fully customizable CMS
21
+ capable of hosting sites while maintaining Sinatra-like ease of
22
+ programming. Some core ideas behind Orange:
23
+
24
+ * We believe in magic - as long as it's not the evil kind
25
+ * Put as much functionality into middleware as possible, so it can be easily reused
26
+ and remixed
27
+ * Give middleware a little more power so it's useful enough to handle more tasks
28
+
29
+
30
+ Should I Use Orange?
31
+ --------------------
32
+
33
+ "I want to create a quick RESTful web service that does one thing and does it well"
34
+
35
+ No. Use Sinatra for this kind of thing. It's perfect for creating quick web apps based on RESTful
36
+ ideals. Or perhaps use a Sinatra clone built on Orange, so you can incorporate orange plugins...
37
+ but such a clone doesn't exist (yet).
38
+
39
+ "I want to create a powerful web application that needs to be rock solid and use a
40
+ well-tested foundation"
41
+
42
+ No. This is where Ruby on Rails shines, it's a well supported, thoroughly tested framework
43
+ for building web applications that gives you everything you need for the lifecycle of your
44
+ application
45
+
46
+ "I want to deploy a website on Ruby that has some dynamic elements, maybe allowing me
47
+ to create parts of the page in a plugin if necessary."
48
+
49
+ Yes. This is what orange was designed for - we're building it to be able to quickly deploy
50
+ websites that can have a Ruby base without the heavy-weight Ruby on Rails backend, but without
51
+ feeling like you have to start from scratch like it feels in Sinatra.
52
+
53
+
54
+ Required Gems
55
+ -------------
56
+
57
+ To be updated... (this is full list for orange-core and orange-more, we're looking to minimize
58
+ orange-core dependencies).
59
+
60
+ * dm-core (+ do_[sqlite3|mysql|...] )
61
+ * dm-more
62
+ * dm-is-awesome_set
63
+ * rack
64
+ * haml
65
+ * rack-abstract-format (github)
66
+ * ruby-openid
67
+ * rack-openid
68
+ * openid_dm_store
69
+ * radius
70
+ * crack
71
+ * eventbright
72
+ * spreedly
73
+ * hominid
74
+ * mail
75
+ * tlsmail (If Ruby version <= 1.8.6)
76
+ * gattica
77
+
78
+ All dependencies should be loaded if you install the gem except for the datamapper
79
+ adapter relevant to your set up. If, for example, you want to use a mysql database,
80
+ you'll need to install do_mysql, and for an sqlite3 database, you'll need do_sqlite3
81
+
82
+
83
+ Also, you'll need a web server of some kind and need to set it up for rack.
84
+
85
+ **Testing**
86
+
87
+ If you want to test, you'll need the following gems:
88
+
89
+ * rspec
90
+ * rack-test
91
+
92
+ Yard is also helpful for generating API docs
93
+
94
+ The following are useful rake tasks for testing purposes:
95
+
96
+ * rake test => (same as rake spec)
97
+ * rake spec => runs rspec with color enabled and spec_helper included
98
+ * rake doc => runs yardoc (no, not really necessary)
99
+ * rake clean => clear out the temporary files not included in the repo
100
+ * rake rcov => runs rspec with rcov
101
+
102
+ For my own reference - jeweler rake task for deploying the new gem:
103
+
104
+ * rake version:bump:patch release
105
+
106
+ Programming Info
107
+ ================
108
+
109
+ The basics of using the orange framework...
110
+
111
+ Terminology
112
+ -----------
113
+
114
+ * **Application**: The last stop for the packet after traversing through the middleware stack.
115
+ * **Core**: This is the core orange object, accessible from all points of the orange
116
+ system. Usually the orange instance can be called by simply using the "orange" function
117
+ * **Mixins**: Extra functionality added directly to the core. Mixins are generally for only
118
+ a couple of extra methods, anything more should probably be created as a resource.
119
+ * **Packet**: This object represents a web request coming in to the orange system.
120
+ Each request is instantiated as a packet before it is sent through the middleware stack.
121
+ * **Pulp**: Mixin added to the packet object rather than the Core.
122
+ * **Resources**: Resources are extra functionality contained within an object, accessible
123
+ from the core.
124
+ * **Stack**: The bundled collection of Orange-enhanced middleware sitting on top of the
125
+ Orange application
126
+
127
+ Pulp and Mixins
128
+ ---------------
129
+ The ability to add pulp and mixins is incredibly handy because the packet and the core are
130
+ available from just about anywhere in the Orange framework. For instance, the haml parser
131
+ evaluates all local calls as if made to the packet, so adding pulp is essentially adding
132
+ functionality that is directly available to haml.
133
+
134
+
135
+ LICENSE:
136
+ =========
137
+ (The MIT License)
138
+
139
+ Copyright © 2009 Orange Sparkle Ball, inc
140
+
141
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ‘Software’), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
142
+
143
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
144
+
145
+ THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,8 @@
1
+ libdir = File.dirname(__FILE__)
2
+ $LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
3
+
4
+ require File.join(libdir, 'orange-core', 'magick.rb')
5
+ Dir.glob(File.join(libdir, 'orange-core', '*.rb')).each {|f| require f }
6
+ Dir.glob(File.join(libdir, 'orange-core', 'cartons', '*.rb')).each {|f| require f }
7
+ Dir.glob(File.join(libdir, 'orange-core', 'resources', '*.rb')).each {|f| require f }
8
+ Dir.glob(File.join(libdir, 'orange-core', 'middleware', '*.rb')).each {|f| require f }
@@ -0,0 +1,132 @@
1
+ module Orange
2
+ # Orange::Application is the main class used for building an
3
+ # Orange App. Typically you will not initialize it directly,
4
+ # but use the app method, which returns the entire Orange
5
+ # stack, with all middleware and the Orange::Application as
6
+ # the main receiver
7
+ #
8
+ # To override the stack generated by default, you can use
9
+ # the self.stack method, which will be used to create a new
10
+ # Orange::Stack
11
+ class Application
12
+ extend ClassInheritableAttributes
13
+ cattr_accessor :core, :stack_block
14
+ # Initialize will set the core, and additionally accept any
15
+ # other options to be added in to the opts array
16
+ # @param [Orange::Core] core the orange core instance that this application will use
17
+ # @param [Hash] *opts the optional arguments
18
+ def initialize(core = false, *opts, &block)
19
+ @core = core || self.class.core
20
+ @options ||= {}
21
+ @options = Orange::Options.new(*opts, &block).hash.with_defaults(self.class.opts)
22
+ @core.application(self) # Register self into core
23
+ init
24
+ end
25
+
26
+ # stack_init is ONLY called after the {#app}
27
+ # method is called, loading a stack
28
+ # (just in case the middleware stack added necessary functionality, etc)
29
+ def stack_init
30
+ end
31
+
32
+ # This method is called by initialize, subclasses should override this method
33
+ # for their own initialization needs.
34
+ #
35
+ # It will usually be better to use stack_init, which gives full access to
36
+ # the initialized stack
37
+ def init
38
+ end
39
+
40
+ # Set the orange core to be a new core
41
+ #
42
+ # Generally, the core should be set during initialization, rather
43
+ # than with this method.
44
+ # @param [Orange::Core] core the orange core instance
45
+ def set_core(core)
46
+ @core = core
47
+ end
48
+
49
+ # The standard call as required by rack. This will
50
+ # make an Orange::Packet object (if necessary) and
51
+ # then send it to the appropriate router for routing.
52
+ #
53
+ # If the :self_routing option is true (default) then
54
+ # the packet will be routed by the application if there
55
+ # is not already another class volunteering for that role.
56
+ # (Routers declare themselves in the orange
57
+ # env['route.router'] to be called by the application)
58
+ #
59
+ #
60
+ def call(env)
61
+ packet = Orange::Packet.new(@core, env)
62
+ # Set up this application as router if nothing else has
63
+ # assumed routing responsibility (for Sinatra DSL like routing)
64
+ self_routing = opts[:self_routing] || true
65
+ if (!packet['route.router'] && self_routing)
66
+ packet['route.router'] = self
67
+ end
68
+ packet.route
69
+ packet.finish
70
+ end
71
+
72
+ # Returns the core
73
+ # @return [Orange::Core] the core instance set for the application
74
+ def orange
75
+ @core
76
+ end
77
+
78
+ # The default route method for the application. Must be overridden in subclasses.
79
+ #
80
+ # This method will raise a RuntimeError if not overridden.
81
+ # The intent is for the application subclass to override this method
82
+ # and use it to handle packets not routed by Stack middleware.
83
+ def route(packet)
84
+ raise "default response from Orange::Application.route"
85
+ end
86
+
87
+ # Used to set optional values at class level. Will be merged into the options
88
+ # given at initialization time
89
+ def self.set(key, v = true)
90
+ @class_opts ||= {}
91
+ @class_opts[key] = v
92
+ end
93
+
94
+ # Gives access to class defined options.
95
+ def self.opts
96
+ @class_opts ||= {}
97
+ end
98
+
99
+ # Gives access to options for the application, both from the class and the
100
+ # instance level.
101
+ # @return [Hash] the options hash
102
+ def opts
103
+ @options
104
+ end
105
+
106
+ # Returns an instance of Orange::Stack to be run by Rack
107
+ #
108
+ # Usually, you'll call this in the rackup file: `run MyApplication.app`
109
+ def self.app(c = false)
110
+ if c
111
+ self.core = c
112
+ else
113
+ self.core ||= Orange::Core.new
114
+ end
115
+ return self.core.stack unless self.core.stack.blank?
116
+ if self.stack_block.instance_of?(Proc)
117
+ Orange::Stack.new self, self.core, &self.stack_block # turn saved proc into a block arg
118
+ else
119
+ Orange::Stack.new self, self.core
120
+ end
121
+ end
122
+
123
+ # Changes the stack that will be used when {#app}
124
+ # is called
125
+ #
126
+ # Each call to stack overrides the previous one.
127
+ def self.stack(core = false, &block)
128
+ self.core = core if core
129
+ self.stack_block = Proc.new # pulls in the block and makes it a proc
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,50 @@
1
+ html * { padding:0; margin:0; }
2
+ body * { padding:10px 20px; }
3
+ body * * { padding:0; }
4
+ body { font:small sans-serif; }
5
+ body>div { border-bottom:1px solid #ddd; }
6
+ h1 { font-weight:normal; }
7
+ h2 { margin-bottom:.8em; }
8
+ h2 span { font-size:80%; color:#666; font-weight:normal; }
9
+ h3 { margin:1em 0 .5em 0; }
10
+ h4 { margin:0 0 .5em 0; font-weight: normal; }
11
+ table {
12
+ border:1px solid #ccc; border-collapse: collapse; background:white; }
13
+ tbody td, tbody th { vertical-align:top; padding:2px 3px; }
14
+ thead th {
15
+ padding:1px 6px 1px 3px; background:#fefefe; text-align:left;
16
+ font-weight:normal; font-size:11px; border:1px solid #ddd; }
17
+ tbody th { text-align:right; color:#666; padding-right:.5em; }
18
+ table.vars { margin:5px 0 2px 40px; }
19
+ table.vars td, table.req td { font-family:monospace; }
20
+ table td.code { width:100%;}
21
+ table td.code div { overflow:hidden; }
22
+ table.source th { color:#666; }
23
+ table.source td {
24
+ font-family:monospace; white-space:pre; border-bottom:1px solid #eee; }
25
+ ul.traceback { list-style-type:none; }
26
+ ul.traceback li.frame { margin-bottom:1em; }
27
+ div.context { margin: 10px 0; }
28
+ div.context ol {
29
+ padding-left:30px; margin:0 10px; list-style-position: inside; }
30
+ div.context ol li {
31
+ font-family:monospace; white-space:pre; color:#666; cursor:pointer; }
32
+ div.context ol.context-line li { color:black; background-color:#ccc; }
33
+ div.context ol.context-line li span { float: right; }
34
+ div.commands { margin-left: 40px; }
35
+ div.commands a { color:black; text-decoration:none; }
36
+ #summary { background: #fda; }
37
+ #summary h2 { font-weight: normal; color: #666; }
38
+ #summary ul#quicklinks { list-style-type: none; margin-bottom: 2em; }
39
+ #summary ul#quicklinks li { float: left; padding: 0 1em; }
40
+ #summary ul#quicklinks>li+li { border-left: 1px #666 solid; }
41
+ #explanation { background:#eee; }
42
+ #template, #template-not-exist { background:#f6f6f6; }
43
+ #template-not-exist ul { margin: 0 0 0 20px; }
44
+ #traceback { background:#eee; }
45
+ #requestinfo { background:#f6f6f6; padding-left:120px; }
46
+ #summary table { border:none; background:transparent; }
47
+ #requestinfo h2, #requestinfo h3 { position:relative; margin-left:-100px; }
48
+ #requestinfo h3 { margin-bottom:-1em; }
49
+ .error { background: #ffc; }
50
+ .specific { color:#cc3300; font-weight:bold; }
@@ -0,0 +1,44 @@
1
+ function getElementsByClassName(oElm, strTagName, strClassName){
2
+ // Written by Jonathan Snook, http://www.snook.ca/jon;
3
+ // Add-ons by Robert Nyman, http://www.robertnyman.com
4
+ var arrElements = (strTagName == "*" && document.all)? document.all :
5
+ oElm.getElementsByTagName(strTagName);
6
+ var arrReturnElements = new Array();
7
+ strClassName = strClassName.replace(/\-/g, "\\-");
8
+ var oRegExp = new RegExp("(^|\\s)" + strClassName + "(\\s|$$)");
9
+ var oElement;
10
+ for(var i=0; i<arrElements.length; i++){
11
+ oElement = arrElements[i];
12
+ if(oRegExp.test(oElement.className)){
13
+ arrReturnElements.push(oElement);
14
+ }
15
+ }
16
+ return (arrReturnElements)
17
+ }
18
+ function hideAll(elems) {
19
+ for (var e = 0; e < elems.length; e++) {
20
+ elems[e].style.display = 'none';
21
+ }
22
+ }
23
+ window.onload = function() {
24
+ hideAll(getElementsByClassName(document, 'table', 'vars'));
25
+ hideAll(getElementsByClassName(document, 'ol', 'pre-context'));
26
+ hideAll(getElementsByClassName(document, 'ol', 'post-context'));
27
+ }
28
+ function toggle() {
29
+ for (var i = 0; i < arguments.length; i++) {
30
+ var e = document.getElementById(arguments[i]);
31
+ if (e) {
32
+ e.style.display = e.style.display == 'none' ? 'block' : 'none';
33
+ }
34
+ }
35
+ return false;
36
+ }
37
+ function varToggle(link, id) {
38
+ toggle('v' + id);
39
+ var s = link.getElementsByTagName('span')[0];
40
+ var uarr = String.fromCharCode(0x25b6);
41
+ var darr = String.fromCharCode(0x25bc);
42
+ s.innerHTML = s.innerHTML == uarr ? darr : uarr;
43
+ return false;
44
+ }
@@ -0,0 +1,178 @@
1
+ require 'dm-core'
2
+
3
+ module Orange
4
+ # Orange::Carton is the main model class for Orange. It's based on Datamapper.
5
+ # In addition to handling the database interactions, the carton keeps
6
+ # track of declared properties, which is used to create scaffolds.
7
+ #
8
+ # All subclasses should start by declaring the "id" attribute. All models
9
+ # are assumed to have an id attribute by most everything else, so it's
10
+ # a good idea to have one. Also, we use this to tie in initialization before
11
+ # using the other dsl-ish methods (since these other methods are class level
12
+ # and would happen before initialize ever gets called)
13
+ #
14
+ # Orange::Carton adds many shortcut methods for adding various datatypes
15
+ # to the model in a more declarative style (`id` vs `property :id, Serial`)
16
+ #
17
+ # For classes that don't need anything but scaffolding, there's the
18
+ # as_resource method, which automatically creates a scaffolding resource
19
+ # for the model.
20
+ #
21
+ # A model that doesn't need scaffolded at all could optionally forgo the carton
22
+ # class and just include DataMapper::Resource. All carton methods are to
23
+ # improve scaffolding capability.
24
+ class Carton
25
+ SCAFFOLD_OPTIONS = [:display_name, :levels] unless defined?(SCAFFOLD_OPTIONS)
26
+ extend ClassInheritableAttributes
27
+ cattr_accessor :scaffold_properties
28
+
29
+ # Declares a ModelResource subclass that scaffolds this carton
30
+ # The Subclass will have the name of the carton followed by "_Resource"
31
+ def self.as_resource
32
+ name = self.to_s
33
+ eval <<-HEREDOC
34
+ class ::#{name}_Resource < Orange::ModelResource
35
+ use #{name}
36
+ end
37
+ HEREDOC
38
+ end
39
+
40
+ # Include DataMapper types (required to be able to use Serial)
41
+ include DataMapper::Types
42
+
43
+ # Do setup of object and declare an id
44
+ def self.id
45
+ include DataMapper::Resource
46
+ property(:id, Serial)
47
+ self.scaffold_properties ||= []
48
+ self.init
49
+ end
50
+
51
+ # Stub init method
52
+ def self.init
53
+ end
54
+
55
+ # Return properties that should be shown for a given context
56
+ def self.form_props(context = :live)
57
+ self.scaffold_properties.select{|p| p[:levels].include?(context) }
58
+ end
59
+
60
+ # Helper to wrap properties into admin level
61
+ def self.admin(&block)
62
+ @levels = [:admin, :orange]
63
+ instance_eval(&block)
64
+ @levels = false
65
+ end
66
+
67
+ # Helper to wrap properties into orange level
68
+ def self.orange(&block)
69
+ @levels = [:orange]
70
+ instance_eval(&block)
71
+ @levels = false
72
+ end
73
+
74
+ # Helper to wrap properties into front level
75
+ def self.front(&block)
76
+ @levels = [:live, :admin, :orange]
77
+ instance_eval(&block)
78
+ @levels = false
79
+ end
80
+
81
+ def self.add_scaffold(name, type, dm_type, opts)
82
+ self.scaffold_properties << {:name => name, :type => type, :levels => @levels}.merge(opts) if @levels || opts.has_key?(:levels)
83
+ opts = opts.delete_if{|k,v| SCAFFOLD_OPTIONS.include?(k)} # DataMapper doesn't like arbitrary opts
84
+ self.property(name, dm_type, opts)
85
+ end
86
+
87
+ # Define a helper for title type database stuff
88
+ # Show in a context if wrapped in one of the helpers
89
+ def self.title(name, opts = {})
90
+ add_scaffold(name, :title, String, opts)
91
+ end
92
+
93
+ # Define a helper for title type database stuff
94
+ # Show in a context if wrapped in one of the helpers
95
+ def self.datetime(name, opts = {})
96
+ add_scaffold(name, :datetime, DateTime, opts)
97
+ end
98
+
99
+ # Define a helper for title type database stuff
100
+ # Show in a context if wrapped in one of the helpers
101
+ def self.date(name, opts = {})
102
+ add_scaffold(name, :date, Date, opts)
103
+ end
104
+
105
+ # Define a helper for title type database stuff
106
+ # Show in a context if wrapped in one of the helpers
107
+ def self.time(name, opts = {})
108
+ add_scaffold(name, :time, Time, opts)
109
+ end
110
+
111
+ # Define a helper for fulltext type database stuff
112
+ # Show in a context if wrapped in one of the helpers
113
+ def self.fulltext(name, opts = {})
114
+ add_scaffold(name, :fulltext, Text, opts)
115
+ end
116
+
117
+ # Define a helper for boolean type database stuff
118
+ # Show in a context if wrapped in one of the helpers
119
+ def self.boolean(name, opts = {})
120
+ add_scaffold(name, :boolean, Boolean, opts)
121
+ end
122
+
123
+ # Define a helper for input type="text" type database stuff
124
+ # Show in a context if wrapped in one of the helpers
125
+ def self.text(name, opts = {})
126
+ add_scaffold(name, :text, String, opts)
127
+ end
128
+
129
+ # Define a helper for type database stuff
130
+ # Show in a context if wrapped in one of the helpers
131
+ def self.expose(name, opts = {})
132
+ self.scaffold_properties << {:name => name, :type => :text, :levels => @levels, :opts => opts} if @levels
133
+ end
134
+
135
+ # Define a helper for input type="text" type database stuff
136
+ # Show in a context if wrapped in one of the helpers
137
+ def self.string(name, opts = {})
138
+ self.text(name, opts)
139
+ end
140
+
141
+ # Override DataMapper to include context sensitivity (as set by helpers)
142
+ def self.scaffold_property(name, type, opts = {})
143
+ my_type = type.to_s.downcase.to_sym
144
+ add_scaffold(name, my_type, type, opts)
145
+ end
146
+
147
+
148
+ # For more generic cases, use same syntax as DataMapper does.
149
+ # The difference is that this will make it an admin property.
150
+ def self.admin_property(name, type, opts = {})
151
+ my_type = type.to_s.downcase.to_sym
152
+ opts[:levels] = [:admin, :orange]
153
+ add_scaffold(name, my_type, type, opts)
154
+ end
155
+
156
+ # For more generic cases, use same syntax as DataMapper does.
157
+ # The difference is that this will make it a front property.
158
+ def self.front_property(name, type, opts = {})
159
+ my_type = type.to_s.downcase.to_sym
160
+ opts[:levels] = [:live, :admin, :orange]
161
+ add_scaffold(name, my_type, type, opts)
162
+ end
163
+
164
+ # For more generic cases, use same syntax as DataMapper does.
165
+ # The difference is that this will make it an orange property.
166
+ def self.orange_property(name, type, opts = {})
167
+ my_type = type.to_s.downcase.to_sym
168
+ opts[:levels] = [:orange]
169
+ add_scaffold(name, my_type, type, opts)
170
+ end
171
+
172
+ def to_s
173
+ <<-DOC
174
+ {"class": "#{self.class.to_s}", "id": "#{self.id}"}
175
+ DOC
176
+ end
177
+ end
178
+ end