trellis 0.0.1 → 0.0.2
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.
- data/History.txt +7 -0
- data/Manifest.txt +6 -181
- data/examples/crud_components/source/crud_components.rb +0 -1
- data/examples/examples.txt +32 -30
- data/examples/flickr/source/flickr.rb +0 -4
- data/examples/hangman/source/hangman.rb +1 -2
- data/examples/hilo/html/guess.xhtml +2 -2
- data/examples/hilo/source/hilo.rb +6 -6
- data/examples/routing/source/routing.rb +50 -0
- data/examples/stateful_counters/source/stateful_counters.rb +9 -4
- data/lib/trellis/{core_components.rb → component_library/core_components.rb} +48 -398
- data/lib/trellis/component_library/grid.rb +311 -0
- data/lib/trellis/{jquery.rb → component_library/jquery.rb} +0 -0
- data/lib/trellis/component_library/object_editor.rb +110 -0
- data/lib/trellis/trellis.rb +162 -52
- data/lib/trellis/utils.rb +9 -3
- data/lib/trellis/version.rb +1 -1
- data/tasks/rspec.rake +2 -1
- data/test/application_spec.rb +55 -30
- data/test/component_spec.rb +95 -81
- data/test/core_extensions_spec.rb +34 -2
- data/test/default_router_spec.rb +91 -0
- data/test/fixtures/application_spec_applications.rb +91 -3
- data/test/page_spec.rb +18 -8
- data/test/router_spec.rb +49 -49
- metadata +8 -185
- data/.git/COMMIT_EDITMSG +0 -1
- data/.git/HEAD +0 -1
- data/.git/config +0 -9
- data/.git/description +0 -1
- data/.git/hooks/applypatch-msg.sample +0 -15
- data/.git/hooks/commit-msg.sample +0 -24
- data/.git/hooks/post-commit.sample +0 -8
- data/.git/hooks/post-receive.sample +0 -15
- data/.git/hooks/post-update.sample +0 -8
- data/.git/hooks/pre-applypatch.sample +0 -14
- data/.git/hooks/pre-commit.sample +0 -18
- data/.git/hooks/pre-rebase.sample +0 -169
- data/.git/hooks/prepare-commit-msg.sample +0 -36
- data/.git/hooks/update.sample +0 -107
- data/.git/index +0 -0
- data/.git/info/exclude +0 -6
- data/.git/logs/HEAD +0 -1
- data/.git/logs/refs/heads/master +0 -1
- data/.git/logs/refs/remotes/origin/master +0 -1
- data/.git/objects/01/2368d4dd3162ed570e6c1423ecb07a6a35f8c0 +0 -0
- data/.git/objects/02/238c88c522b88e71529495460cc5c2c8abe110 +0 -0
- data/.git/objects/07/03d3b123189f1da72d3889d9340e681841c96d +0 -0
- data/.git/objects/0b/3935fdbf3ce8d37e6cb2a6ec94edaccb1c58dc +0 -0
- data/.git/objects/0f/ecd1ad35f280c874ce070b7a97a4c1f86ace54 +0 -0
- data/.git/objects/12/61911b063b7e6883c5e9db7ab4396183f6f2b5 +0 -0
- data/.git/objects/13/0b7e2d214ec198d07aeed5b462597670ce85ad +0 -0
- data/.git/objects/13/214d40f02b63f1233fd7881b1df9bb75d9c0a7 +0 -0
- data/.git/objects/14/7522bdc96af0b578935d81c1a7347b40573940 +0 -0
- data/.git/objects/16/a7fde706fe92b3548254d29722f5ca311ce63b +0 -2
- data/.git/objects/16/fe580663cc29d141bf416dff5bf21bf4ea42a3 +0 -0
- data/.git/objects/17/2d55806918bafb4e701515edb27147a4ef6498 +0 -0
- data/.git/objects/17/686ef1882ed6c3c07b781fab18739b1c5e48bd +0 -0
- data/.git/objects/17/824053c41a2cb523303e20cac42f66f9ca2134 +0 -0
- data/.git/objects/18/68448c5f88bc73b2fef6b8f3aa75b663013a3a +0 -1
- data/.git/objects/1a/6d9051dc4c7db10bdcf5e17c5cb717b072d30a +0 -0
- data/.git/objects/1a/8bdb445fb5668dcd1309cb9de5dedf846c822f +0 -0
- data/.git/objects/1b/6fa67b70358fdb82eabcd7ef3bc46d518d0426 +0 -0
- data/.git/objects/1c/f6510ed08a274fb3292d40557cebb3e7090767 +0 -0
- data/.git/objects/1d/e661ce11d5335e5dd7968ef5698e2eb164eb52 +0 -0
- data/.git/objects/1e/301ee672d6040e734af6d0a5491185129d7b67 +0 -0
- data/.git/objects/21/2201d46200f13d23d8199bd4a7ecc9165205b3 +0 -0
- data/.git/objects/22/f687370babdf38d224d2a9ff039707e8d0aac1 +0 -0
- data/.git/objects/24/202d2de95dc31f5f1802e629a16ad45b723267 +0 -0
- data/.git/objects/25/4c5b6b8d211d8b9f07310fd342fd7b078a81af +0 -0
- data/.git/objects/25/5832653fde9e9748855347fe7bce013aa0a6c7 +0 -0
- data/.git/objects/25/806945c89b4b5adb8c8676c308e4653361109c +0 -5
- data/.git/objects/26/cb8deaca28bf275769cbb71b88e53a44adb9ca +0 -0
- data/.git/objects/2e/1c13fb5f154f89cccd4c645540a2ec8ee0f57a +0 -0
- data/.git/objects/2f/437425b2c085bb529379e409d6b4bc7f9f9fba +0 -0
- data/.git/objects/2f/7cf4a25937b71f3de52e70c7a47cb2f7da07ea +0 -0
- data/.git/objects/31/31bdbbac6e6e7e24fd6ea84332461f67e6e5b6 +0 -0
- data/.git/objects/32/02e02de478263912b729283eabc7cdab8a8037 +0 -0
- data/.git/objects/32/839ed9baa8c800d9033655d1ee995499825fb5 +0 -0
- data/.git/objects/34/2e479c61796125b0e025b483401ec12f3c5824 +0 -2
- data/.git/objects/36/7acb017aebab3f627a5208c25885c21fa7fdbb +0 -0
- data/.git/objects/38/12910d2e2a8e6eeb79a399d20751675d3fbe77 +0 -0
- data/.git/objects/38/3fe1e1a07537db6e0f39c6005b7b4e40fa5ccd +0 -0
- data/.git/objects/39/5ef328bc94df9191f4d80a77503a5be9f1f673 +0 -0
- data/.git/objects/39/dc76dc096fdf737e9f4f7062b78cbf4ab9628f +0 -0
- data/.git/objects/3b/09eb196140b471556ed4c3987c0bc739733617 +0 -0
- data/.git/objects/3b/9f97f1bf55834832a92bab1ab900918dc49570 +0 -0
- data/.git/objects/40/5ffd3925b39dd905638ec24640dd8efa27cc1b +0 -0
- data/.git/objects/42/4a5f37c6fe3a7cac54b0f85688c1cce7da9cdf +0 -0
- data/.git/objects/44/9e23363d4e20da6a67be7ab78fc610b4e9852a +0 -0
- data/.git/objects/48/90bb5d241cecaa30b28148c920b833056c7508 +0 -0
- data/.git/objects/4c/558c43676410a2c0a7d5c7b94fca18923a9af1 +0 -0
- data/.git/objects/4f/bf572175dd5245104679ff8f676f6a34466c29 +0 -0
- data/.git/objects/53/8583edf00c9f656b32149a0e69e030371737c5 +0 -0
- data/.git/objects/5b/8a2c4bf7bb56ae199414096dbda21fd4b8cd63 +0 -0
- data/.git/objects/5c/9fe8ff998d56dc076657ef88ee9f784ec160f9 +0 -0
- data/.git/objects/5f/8441f9d3507587940f31af04dd9fd5911d2812 +0 -0
- data/.git/objects/61/4f2752550e63b793e31dcf98b5ea26c32eb0be +0 -0
- data/.git/objects/65/801b0fabdfd673861dbb2ac134d438ab2cc5e4 +0 -3
- data/.git/objects/66/2746dffaddbcd311f4ac070b5b9bddcd8894a7 +0 -0
- data/.git/objects/69/1ed3b65603a0d0dabfb66720e7163664be78f5 +0 -0
- data/.git/objects/69/3dc3b6037df8c1c8dfda916d649e7975481124 +0 -0
- data/.git/objects/69/67538427b3a371b33c01c72726ac7e67a04c39 +0 -0
- data/.git/objects/69/6cd6c3508e2adcc68829497884ff3d4b774dc2 +0 -0
- data/.git/objects/69/6ee93917040308ccd0bfc245d63c1cbeb23dae +0 -0
- data/.git/objects/6a/3ac317746a8a2cfc88f3580bbd2b03dc0d50b5 +0 -0
- data/.git/objects/6d/6ba435839a65b96cb3f7584a40dd3767a0990f +0 -0
- data/.git/objects/6f/32c190a651a82caeefeb55eb797fc5daf05426 +0 -0
- data/.git/objects/71/fecbce1bec36eaf4de655763d4bb7ceffc991d +0 -0
- data/.git/objects/72/01d78a9e90347fd2f7790f9e8665587791d853 +0 -0
- data/.git/objects/73/6c97d6a551363ba4a4956408ba228fcab7f91f +0 -0
- data/.git/objects/75/3264271e935e18f4eada49aa1818c6fb9e6abb +0 -0
- data/.git/objects/75/cc7d390b4a565495cbdc5a08d4479e7e8dbfeb +0 -0
- data/.git/objects/76/7c0c96f68b9ccec34ae538045a63bbe4f97d3e +0 -0
- data/.git/objects/7b/47fe9b1ab9f2692b9706054de706430089ecd4 +0 -0
- data/.git/objects/7c/73144ef6847d1bc2315aee0925c0e5cf5c09d1 +0 -0
- data/.git/objects/7d/9c177c155386606aa1024d5cb020a097eea64a +0 -0
- data/.git/objects/7d/e9a3296007c09ba9b7bc1802e1c5d9ad1d5dbb +0 -0
- data/.git/objects/7e/b73d408147c8d84881109189a9724292abcb8d +0 -0
- data/.git/objects/82/0e3bf5449a8f6b8eb637a653dcb2dc387b393c +0 -0
- data/.git/objects/83/a10ce96763cb30d0b81425e9372f89c52f4c9f +0 -0
- data/.git/objects/83/bdb801e78444d59225f83cf2f336f114cd23cf +0 -0
- data/.git/objects/85/24e583e822abc18b1ffdd0aa356c9c079b5548 +0 -0
- data/.git/objects/88/b46cc8de5237897d4ba5023d3efdc83fec4f7c +0 -0
- data/.git/objects/89/5eea66474f48a2cbf643ad0ef266fd26f9d2a3 +0 -0
- data/.git/objects/8a/539c5fc35d5b770d67241d2ccb6c6855df6a43 +0 -0
- data/.git/objects/8a/82cdbfb20cc58fdffe4e13b70b55977deb92c6 +0 -0
- data/.git/objects/8b/2b537442e4ac8719ffa71b3ae0c707133df06f +0 -0
- data/.git/objects/8c/2a45f0c81284a081f8d7980e9a54210a9dd52c +0 -0
- data/.git/objects/8e/10355b4ab312450031966107ef6a65b13833ee +0 -0
- data/.git/objects/90/f6c76767ae725f15fa6790dcb6afd1719fa0e2 +0 -0
- data/.git/objects/92/92b696aba9840c089241c68d4c6ca14be38a64 +0 -2
- data/.git/objects/93/e03faa6906892434fa9f2ad230c4ececc64055 +0 -0
- data/.git/objects/93/eca2e28d80fda45d327fdfc6c39ae690af41cd +0 -0
- data/.git/objects/95/92fe63bd5403654730cdec76cbf116ef83bbff +0 -0
- data/.git/objects/96/bc6dd471c1c188a2eb4308bcc3ca4e6c8faac8 +0 -0
- data/.git/objects/96/d855221a9ff4fba57d2b70680b57bd5115cfde +0 -0
- data/.git/objects/97/4ec92b2b9eb01a73fb57dc78d463cafdda69a7 +0 -0
- data/.git/objects/97/b7ec01ebe089832a0972d20b0a40d54456985c +0 -2
- data/.git/objects/97/fa6aaae4cfc7654feb39971e7aee5c41ed1679 +0 -1
- data/.git/objects/98/18351c6685a1df5d23686e6346d11d261cc6f0 +0 -0
- data/.git/objects/98/3d3b7e55cbbce2f2a52c6e6d830a5dca7b4d42 +0 -0
- data/.git/objects/a2/e551049a5388112c21e7f3a4a720bf720601fa +0 -0
- data/.git/objects/a3/7066bea4fae90d34761a3ebb3f8a348d94eba1 +0 -0
- data/.git/objects/a6/c99619145d1b9b75aa15ca04fbe4db0b2ee79b +0 -0
- data/.git/objects/aa/e2234d0a8c1485096c7a7de039eceb4e019e13 +0 -0
- data/.git/objects/b0/3b52f4269c9719e02ecd6caabd8aff5adf3aac +0 -0
- data/.git/objects/b1/50dbbd89f825302a9e82d8fdd66f238b8e6fc4 +0 -0
- data/.git/objects/b4/545fbe393aaa18f1c48de4d9f68f64ebf09ddc +0 -0
- data/.git/objects/b6/8691404e088692929d40b988b993f815d76756 +0 -0
- data/.git/objects/b7/daa3d844ef5f5cd917084e48edd82e67f8f476 +0 -0
- data/.git/objects/bf/4232281dba5a0f17e8b5c16b9cbd0f52274fa8 +0 -0
- data/.git/objects/bf/9b84d10f19188beffe5ee99e6f5af035ba77d3 +0 -2
- data/.git/objects/c0/06e664b306145de804f74ab27550d62a92f58a +0 -1
- data/.git/objects/c2/2806b16775f66453012d085d21fb8698521ea9 +0 -0
- data/.git/objects/c2/7f6559350f7adb19d43742b55b2f91d07b6550 +0 -0
- data/.git/objects/c3/833df21ed674a77374fd40ed931f900134d38b +0 -0
- data/.git/objects/c3/b9da021eb76abad0b17ad6c4be75595e41a7e6 +0 -0
- data/.git/objects/c5/ca6b7e3561ea88aba1712aabb984d1167787f1 +0 -0
- data/.git/objects/c6/39f65e1f43c75b7f57c5ab28623d295dfb2afd +0 -0
- data/.git/objects/c8/6f05fa5539f8e69e94850d1cd917919ff581be +0 -0
- data/.git/objects/cb/5e499c8dcc6f2901014a08e74f04b7a4bd6596 +0 -0
- data/.git/objects/cc/1289d25eeee6ba31b4e406372cb9abf969fc6f +0 -0
- data/.git/objects/cc/784e1b49111c42fa7eae10ad0e788186e672d1 +0 -0
- data/.git/objects/cc/bcae254dad515d7ef01a461980544e4483c41e +0 -0
- data/.git/objects/ce/bfadad3a7c17148998e04275e6d0d1cf1b2e1e +0 -0
- data/.git/objects/ce/f3335522abdc47a2c94be70455dcfaa9a51a27 +0 -0
- data/.git/objects/cf/6add7ea568d3d90d6a1f8afb0898b0119b14ff +0 -0
- data/.git/objects/d0/719abd196033fec0e4cbe2af68a26ca435389c +0 -0
- data/.git/objects/d1/ce63ff0eaec4dba419e5547f99195dfb9b5b20 +0 -0
- data/.git/objects/d2/c3feb3c74b892cca0049c0aced6ce786a784c8 +0 -0
- data/.git/objects/d7/8a91da13413419da71b539a3c6c7df4d8a2458 +0 -0
- data/.git/objects/d8/c7bed2b1357d54b6935536a8a51a3c73d535b4 +0 -0
- data/.git/objects/da/c3a1277ccb4806011e33eb32c5609cc45c46f2 +0 -0
- data/.git/objects/dc/3f920f4b937d0e2ac512f0bb31d477624c46d5 +0 -0
- data/.git/objects/dc/6c389abf5bd0d7b9be2a6bb6af90738a410e59 +0 -0
- data/.git/objects/df/07c423309f39c4d5570b0fad65dff23af654ae +0 -0
- data/.git/objects/e2/93a5c9e2257b9f3fcb8fa5d5c8147a2f07ba1b +0 -0
- data/.git/objects/e4/8464df56bf487e96e21ea99487330266dae3c9 +0 -0
- data/.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 +0 -0
- data/.git/objects/e8/0c13a1d1f62756fb9f1e4fb9f7512e5b8c5816 +0 -0
- data/.git/objects/e9/b8ade2125b2615ddc4c8043940c40ed36215c9 +0 -0
- data/.git/objects/ed/59a7599a81ef7ea7c230c3332345a3484febf8 +0 -0
- data/.git/objects/f0/d2a74226877e9a28af814d0d3727e4a3487439 +0 -0
- data/.git/objects/f0/f3ba7264f1d8e60eb93a907092af98fdc248e4 +0 -0
- data/.git/objects/f1/cdcc7ff2dc6298318523b2c4e425f515a58bf7 +0 -3
- data/.git/objects/f4/45c96f406f4d2f9839b3fe2fe8bc7f943ab469 +0 -0
- data/.git/objects/f7/eef7ce0a650d6f0c85dc4982f481da7bd44362 +0 -0
- data/.git/objects/f9/0abf96c5059ce1ba115f73bc9cf33e4663b3e1 +0 -0
- data/.git/objects/f9/cf7ae572eaebbd63f809f16a5ef14b9c679790 +0 -0
- data/.git/objects/fc/300df53fe2c9467210e2fd4c2f34e1c9c7c89a +0 -0
- data/.git/objects/fc/45546596ab8debc4c9f49a7abc7fc14a7bcc7f +0 -4
- data/.git/objects/fc/68d89167f69614eda17bd56dfccb4355d818e6 +0 -0
- data/.git/objects/fc/e4c02bb132e73b3cb957da182db7b75a47c242 +0 -0
- data/.git/objects/fe/be283da7f326344013f1fc847c12c8f29ab6ef +0 -0
- data/.git/refs/heads/master +0 -1
- data/.git/refs/remotes/origin/master +0 -1
- data/.gitignore +0 -5
- data/README.txt +0 -156
- data/examples/simplest/html/start.xhtml +0 -0
- data/website/index.html +0 -124
- data/website/index.txt +0 -119
- data/website/javascripts/rounded_corners_lite.inc.js +0 -285
- data/website/stylesheets/screen.css +0 -138
- data/website/template.html.erb +0 -48
data/lib/trellis/trellis.rb
CHANGED
|
@@ -46,12 +46,12 @@ module Trellis
|
|
|
46
46
|
# pages and it must define a home page or entry point into the application
|
|
47
47
|
class Application
|
|
48
48
|
include Logging
|
|
49
|
+
include Rack::Utils
|
|
49
50
|
|
|
50
51
|
# descendant application classes get a singleton class level instances for
|
|
51
52
|
# holding homepage, dependent pages, static resource routing paths
|
|
52
53
|
def self.inherited(child) #:nodoc:
|
|
53
|
-
child.
|
|
54
|
-
child.attr_array(:pages, :create_accessor => false)
|
|
54
|
+
child.class_attr_reader(:homepage)
|
|
55
55
|
child.attr_array(:static_routes)
|
|
56
56
|
child.meta_def(:logger) { Application.logger }
|
|
57
57
|
super
|
|
@@ -61,13 +61,7 @@ module Trellis
|
|
|
61
61
|
# the entry point is the URL pattern / where the application is mounted
|
|
62
62
|
def self.home(sym)
|
|
63
63
|
@homepage = sym
|
|
64
|
-
@pages << sym
|
|
65
64
|
end
|
|
66
|
-
|
|
67
|
-
# class method that takes a list of page symbols
|
|
68
|
-
def self.pages(*syms)
|
|
69
|
-
@pages = @pages | syms
|
|
70
|
-
end
|
|
71
65
|
|
|
72
66
|
# define url paths for static resources
|
|
73
67
|
def self.map_static(urls = [], root = File.expand_path("#{File.dirname($0)}/../html/"))
|
|
@@ -86,6 +80,7 @@ module Trellis
|
|
|
86
80
|
end
|
|
87
81
|
|
|
88
82
|
def configured_instance
|
|
83
|
+
# configure rack middleware
|
|
89
84
|
application = Rack::ShowStatus.new(self)
|
|
90
85
|
application = Rack::ShowExceptions.new(application)
|
|
91
86
|
application = Rack::CommonLogger.new(application, Application.logger)
|
|
@@ -97,38 +92,51 @@ module Trellis
|
|
|
97
92
|
end
|
|
98
93
|
application
|
|
99
94
|
end
|
|
95
|
+
|
|
96
|
+
# find the first page with a suitable router, if none is found use the default router
|
|
97
|
+
def find_router_for(request)
|
|
98
|
+
match = Page.subclasses.values.find { |page| page.router && page.router.matches?(request) }
|
|
99
|
+
match ? match.router : DefaultRouter.new(:application => self)
|
|
100
|
+
end
|
|
100
101
|
|
|
101
102
|
# implements the rack specification
|
|
102
103
|
def call(env)
|
|
103
104
|
response = Rack::Response.new
|
|
104
105
|
request = Rack::Request.new(env)
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
route = DefaultRouter.new(self).route(request)
|
|
108
|
-
|
|
106
|
+
|
|
109
107
|
Application.logger.debug "request received with url_root of #{request.script_name}"
|
|
108
|
+
|
|
109
|
+
session = env["rack.session"]
|
|
110
|
+
|
|
111
|
+
router = find_router_for(request)
|
|
112
|
+
route = router.route(request)
|
|
110
113
|
|
|
111
114
|
page = route.destination.new if route.destination
|
|
112
115
|
if page
|
|
113
116
|
page.class.url_root = request.script_name
|
|
117
|
+
page.path = request.path_info.sub(/^\//, '')
|
|
114
118
|
page.inject_dependent_pages
|
|
115
119
|
page.call_if_provided(:before_load)
|
|
116
120
|
page.load_page_session_information(session)
|
|
117
121
|
page.call_if_provided(:after_load)
|
|
118
122
|
page.params = request.params.keys_to_symbols
|
|
119
|
-
|
|
123
|
+
router.inject_parameters_into_page_instance(page, request)
|
|
124
|
+
result = route.event ? page.process_event(route.event, route.value, route.source, session) : page
|
|
120
125
|
|
|
121
126
|
# prepare the http response
|
|
122
|
-
if request.post? &&
|
|
123
|
-
# redirect after post
|
|
127
|
+
if (request.post? || route.event) && result.kind_of?(Trellis::Page)
|
|
128
|
+
# for action events of posts then use redirect after post pattern
|
|
129
|
+
# remove the events path and just return to the page
|
|
130
|
+
path = result.path ? result.path.gsub(/\/events\/.*/, '') : result.class.class_to_sym
|
|
124
131
|
response.status = 302
|
|
125
|
-
response.headers["Location"] = "#{request.script_name}/#{
|
|
132
|
+
response.headers["Location"] = "#{request.script_name}/#{path}"
|
|
126
133
|
else
|
|
127
|
-
|
|
134
|
+
# for render requests simply render the page
|
|
135
|
+
response.body = result.kind_of?(Trellis::Page) ? result.render : result
|
|
128
136
|
response.status = 200
|
|
129
137
|
end
|
|
130
138
|
else
|
|
131
|
-
|
|
139
|
+
response.status = 404
|
|
132
140
|
end
|
|
133
141
|
response.finish
|
|
134
142
|
end
|
|
@@ -140,7 +148,7 @@ module Trellis
|
|
|
140
148
|
class Route
|
|
141
149
|
attr_reader :destination, :event, :source, :value
|
|
142
150
|
|
|
143
|
-
def initialize(destination, event, source, value)
|
|
151
|
+
def initialize(destination, event = nil, source = nil, value = nil)
|
|
144
152
|
@destination, @event, @source, @value = destination, event, source, value
|
|
145
153
|
end
|
|
146
154
|
end
|
|
@@ -148,24 +156,111 @@ module Trellis
|
|
|
148
156
|
# -- Router --
|
|
149
157
|
# A Router returns a Route in response to an HTTP request
|
|
150
158
|
class Router
|
|
151
|
-
|
|
159
|
+
EVENT_REGEX = %r{^(?:.+)/events/(?:([^/\.]+)(?:\.([^/\.]+)?)?)(?:/(?:([^\.]+)?))?}
|
|
160
|
+
|
|
161
|
+
attr_reader :application, :pattern, :keys, :path, :page
|
|
152
162
|
|
|
153
|
-
def initialize(
|
|
154
|
-
@application = application
|
|
163
|
+
def initialize(options={})
|
|
164
|
+
@application = options[:application]
|
|
165
|
+
@path = options[:path]
|
|
166
|
+
@page = options[:page]
|
|
167
|
+
compile_path if @path
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def route(request = nil)
|
|
171
|
+
# get the event information if any
|
|
172
|
+
value, source, event = request.path_info.match(EVENT_REGEX).to_a.reverse if request
|
|
173
|
+
Route.new(@page, event, source, value)
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def matches?(request)
|
|
177
|
+
request.path_info.gsub(/\/events\/.*/, '').match(@pattern) != nil
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def inject_parameters_into_page_instance(page, request)
|
|
181
|
+
# extract parameters and named parameters from request
|
|
182
|
+
if @pattern && @page && match = @pattern.match(request.path_info.gsub(/\/events\/.*/, ''))
|
|
183
|
+
values = match.captures.to_a
|
|
184
|
+
params =
|
|
185
|
+
if @keys.any?
|
|
186
|
+
@keys.zip(values).inject({}) do |hash,(k,v)|
|
|
187
|
+
if k == 'splat'
|
|
188
|
+
(hash[k] ||= []) << v
|
|
189
|
+
else
|
|
190
|
+
hash[k] = v
|
|
191
|
+
end
|
|
192
|
+
hash
|
|
193
|
+
end
|
|
194
|
+
elsif values.any?
|
|
195
|
+
{'captures' => values}
|
|
196
|
+
else
|
|
197
|
+
{}
|
|
198
|
+
end
|
|
199
|
+
params.each_pair { |name, value| page.instance_variable_set("@#{name}".to_sym, value) }
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
private
|
|
204
|
+
|
|
205
|
+
# borrowed (stolen) from Sinatra!
|
|
206
|
+
def compile_path
|
|
207
|
+
@keys = []
|
|
208
|
+
if @path.respond_to? :to_str
|
|
209
|
+
special_chars = %w{. + ( )}
|
|
210
|
+
pattern =
|
|
211
|
+
@path.to_str.gsub(/((:\w+)|[\*#{special_chars.join}])/) do |match|
|
|
212
|
+
case match
|
|
213
|
+
when "*"
|
|
214
|
+
@keys << 'splat'
|
|
215
|
+
"(.*?)"
|
|
216
|
+
when *special_chars
|
|
217
|
+
Regexp.escape(match)
|
|
218
|
+
else
|
|
219
|
+
@keys << $2[1..-1]
|
|
220
|
+
"([^/?&#]+)"
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
@pattern = /^#{pattern}$/
|
|
224
|
+
elsif @path.respond_to?(:keys) && @path.respond_to?(:match)
|
|
225
|
+
@pattern = @path
|
|
226
|
+
@keys = @path.keys
|
|
227
|
+
elsif @path.respond_to? :match
|
|
228
|
+
@pattern = path
|
|
229
|
+
else
|
|
230
|
+
raise TypeError, @path
|
|
231
|
+
end
|
|
155
232
|
end
|
|
156
233
|
end
|
|
157
234
|
|
|
158
235
|
# -- DefaultRouter --
|
|
159
|
-
# The default routing scheme is in the form /page[.
|
|
236
|
+
# The default routing scheme is in the form /page[.event[_source]][/value][?query_params]
|
|
160
237
|
class DefaultRouter < Router
|
|
161
|
-
ROUTE_REGEX = %r{
|
|
162
|
-
|
|
163
|
-
def route(request)
|
|
164
|
-
value, source, event, destination = request.path_info.match(ROUTE_REGEX).to_a.reverse
|
|
238
|
+
ROUTE_REGEX = %r{^/([^/]+)(?:/(?:events/(?:([^/\.]+)(?:\.([^/\.]+)?)?)(?:/(?:([^\.]+)?))?)?)?}
|
|
239
|
+
|
|
240
|
+
def route(request)
|
|
241
|
+
value, source, event, destination = request.path_info.match(ROUTE_REGEX).to_a.reverse
|
|
165
242
|
destination = @application.class.homepage unless destination
|
|
166
243
|
page = Page.get_page(destination.to_sym)
|
|
167
|
-
|
|
168
|
-
Route.new(page, event, source, value)
|
|
244
|
+
|
|
245
|
+
Route.new(page, event, source, value)
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def matches?(request)
|
|
249
|
+
request.path_info.match(ROUTE_REGEX) != nil
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
def self.to_uri(options={})
|
|
253
|
+
url_root = options[:url_root]
|
|
254
|
+
page = options[:page]
|
|
255
|
+
event = options[:event]
|
|
256
|
+
source = options[:source]
|
|
257
|
+
value = options[:value]
|
|
258
|
+
destination = page.kind_of?(Trellis::Page) ? (page.path || page.class.class_to_sym) : page
|
|
259
|
+
url_root = page.kind_of?(Trellis::Page) && page.class.url_root ? "/#{page.class.url_root}" : '/' unless url_root
|
|
260
|
+
source = source ? ".#{source}" : ''
|
|
261
|
+
value = value ? "/#{value}" : ''
|
|
262
|
+
event_info = event ? "/events/#{event}#{source}#{value}" : ''
|
|
263
|
+
"#{url_root}#{destination}#{event_info}"
|
|
169
264
|
end
|
|
170
265
|
end
|
|
171
266
|
|
|
@@ -179,7 +274,7 @@ module Trellis
|
|
|
179
274
|
|
|
180
275
|
@@subclasses = Hash.new
|
|
181
276
|
|
|
182
|
-
attr_accessor :params, :logger
|
|
277
|
+
attr_accessor :params, :path, :logger
|
|
183
278
|
|
|
184
279
|
def self.inherited(child) #:nodoc:
|
|
185
280
|
@@subclasses[child.class_to_sym] = child
|
|
@@ -189,9 +284,10 @@ module Trellis
|
|
|
189
284
|
child.attr_array(:components)
|
|
190
285
|
child.attr_array(:stateful_components)
|
|
191
286
|
child.attr_array(:persistents)
|
|
192
|
-
child.
|
|
193
|
-
child.
|
|
194
|
-
child.
|
|
287
|
+
child.class_attr_accessor :url_root
|
|
288
|
+
child.class_attr_accessor :name
|
|
289
|
+
child.class_attr_accessor :router
|
|
290
|
+
child.class_attr_accessor :layout
|
|
195
291
|
child.meta_def(:add_stateful_component) { |tid,clazz| @stateful_components << [tid,clazz] }
|
|
196
292
|
|
|
197
293
|
locate_template child
|
|
@@ -232,15 +328,24 @@ module Trellis
|
|
|
232
328
|
def self.pages(*syms)
|
|
233
329
|
@pages = @pages | syms
|
|
234
330
|
end
|
|
331
|
+
|
|
332
|
+
def self.route(path)
|
|
333
|
+
router = Router.new(:path => path, :page => self)
|
|
334
|
+
self.instance_variable_set(:@router, router)
|
|
335
|
+
end
|
|
235
336
|
|
|
236
337
|
def self.persistent(*fields)
|
|
237
|
-
|
|
338
|
+
instance_attr_accessor fields
|
|
238
339
|
@persistents = @persistents | fields
|
|
239
340
|
end
|
|
240
341
|
|
|
241
342
|
def self.get_page(sym)
|
|
242
343
|
@@subclasses[sym]
|
|
243
344
|
end
|
|
345
|
+
|
|
346
|
+
def self.subclasses
|
|
347
|
+
@@subclasses
|
|
348
|
+
end
|
|
244
349
|
|
|
245
350
|
def initialize # TODO this is Ugly.... should no do it in initialize since it'll require super in child classes
|
|
246
351
|
self.class.stateful_components.each do |id_component|
|
|
@@ -313,7 +418,7 @@ module Trellis
|
|
|
313
418
|
|
|
314
419
|
def self.locate_template(clazz)
|
|
315
420
|
begin
|
|
316
|
-
if clazz.url_root.nil? || clazz.url_root.
|
|
421
|
+
if clazz.url_root.nil? || clazz.url_root.empty?
|
|
317
422
|
dir = "#{File.dirname($0)}/../html/"
|
|
318
423
|
else
|
|
319
424
|
dir = "#{File.dirname($0)}#{clazz.url_root}/html/".gsub("Rack: ", '')
|
|
@@ -439,6 +544,9 @@ module Trellis
|
|
|
439
544
|
@context.globals.send(sym, value)
|
|
440
545
|
end
|
|
441
546
|
end
|
|
547
|
+
|
|
548
|
+
#TODO add public page methods to the context
|
|
549
|
+
|
|
442
550
|
|
|
443
551
|
# add the page to the context too
|
|
444
552
|
@context.globals.page = page
|
|
@@ -477,8 +585,8 @@ module Trellis
|
|
|
477
585
|
# component registration
|
|
478
586
|
@@components[child.class_to_sym] = child
|
|
479
587
|
|
|
480
|
-
child.
|
|
481
|
-
child.
|
|
588
|
+
child.class_attr_accessor(:body)
|
|
589
|
+
child.class_attr_accessor(:cname)
|
|
482
590
|
child.cname = child.underscore_class_name
|
|
483
591
|
child.meta_def(:stateful?) { @stateful }
|
|
484
592
|
|
|
@@ -531,10 +639,6 @@ module Trellis
|
|
|
531
639
|
send("#{sym}=", default_value) if default_value
|
|
532
640
|
end
|
|
533
641
|
|
|
534
|
-
def self.dom_contribution(&block) #TODO names don't match!
|
|
535
|
-
@document_modifications << block
|
|
536
|
-
end
|
|
537
|
-
|
|
538
642
|
def self.add_style_links_to_page(page, attributes)
|
|
539
643
|
style_links.each do |href|
|
|
540
644
|
href = href.replace_ant_style_properties(attributes) if attributes
|
|
@@ -603,13 +707,17 @@ module Trellis
|
|
|
603
707
|
end
|
|
604
708
|
end
|
|
605
709
|
|
|
606
|
-
def self.page_contribution(sym, contribution, options=nil)
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
710
|
+
def self.page_contribution(sym, contribution=nil, options=nil, &block)
|
|
711
|
+
unless (sym == :dom && block_given?)
|
|
712
|
+
# add the contribution to the appropriate array of contributions
|
|
713
|
+
# scripts, class_scripts, styles, class_styles, script_links, style_links
|
|
714
|
+
scope = options[:scope] || :class if options
|
|
715
|
+
receiver = sym.to_s.plural
|
|
716
|
+
receiver = "class_#{receiver}" if scope == :class
|
|
717
|
+
instance_variable_get("@#{receiver}").send(:<<, contribution)
|
|
718
|
+
else
|
|
719
|
+
@document_modifications << block
|
|
720
|
+
end
|
|
613
721
|
end
|
|
614
722
|
|
|
615
723
|
def self.get_component(sym)
|
|
@@ -646,7 +754,7 @@ module Trellis
|
|
|
646
754
|
def save_component_session_information(page, instance_variable_name, session_data)
|
|
647
755
|
self.class.persistents.each do |field|
|
|
648
756
|
key = "#{page.class}_#{self.class}_#{instance_variable_name}_#{field}"
|
|
649
|
-
session_data[key] = instance_variable_get("@#{field}".to_sym)
|
|
757
|
+
session_data[key] = instance_variable_get("@#{field}".to_sym) if session_data
|
|
650
758
|
end
|
|
651
759
|
end
|
|
652
760
|
|
|
@@ -654,7 +762,7 @@ module Trellis
|
|
|
654
762
|
self.class.persistents.each do |field|
|
|
655
763
|
field_sym = "@#{field}".to_sym
|
|
656
764
|
current_value = instance_variable_get(field_sym)
|
|
657
|
-
new_value = session_data["#{page.class}_#{self.class}_#{instance_variable_name}_#{field}"]
|
|
765
|
+
new_value = session_data["#{page.class}_#{self.class}_#{instance_variable_name}_#{field}"] if session_data
|
|
658
766
|
if current_value != new_value && new_value != nil
|
|
659
767
|
instance_variable_set(field_sym, new_value)
|
|
660
768
|
end
|
|
@@ -685,7 +793,7 @@ module Trellis
|
|
|
685
793
|
# create pass-through methods for each event handler in the component (on_something methods)
|
|
686
794
|
self.public_instance_methods.each do |method_name|
|
|
687
795
|
if method_name.starts_with?('on_')
|
|
688
|
-
page.meta_def("#{method_name}_from_#{cname}#{id}") do |*args|
|
|
796
|
+
page.meta_def("#{method_name}_from_#{cname}_#{id}") do |*args|
|
|
689
797
|
result = page.instance_variable_get("@#{cname}_#{id}".to_sym).send(method_name.to_sym, *args)
|
|
690
798
|
# if the method returns a page, navigate to that page, otherwise navigate to the source page
|
|
691
799
|
(result && result.kind_of?(String)) ? result : page
|
|
@@ -697,5 +805,7 @@ module Trellis
|
|
|
697
805
|
end
|
|
698
806
|
|
|
699
807
|
# load trellis core components
|
|
700
|
-
require 'trellis/core_components'
|
|
808
|
+
require 'trellis/component_library/core_components'
|
|
809
|
+
require 'trellis/component_library/grid'
|
|
810
|
+
require 'trellis/component_library/object_editor'
|
|
701
811
|
end
|
data/lib/trellis/utils.rb
CHANGED
|
@@ -57,19 +57,25 @@ class Class #:nodoc:
|
|
|
57
57
|
downcase.split('/').last
|
|
58
58
|
end
|
|
59
59
|
|
|
60
|
-
def
|
|
60
|
+
def class_attr_accessor(*syms)
|
|
61
61
|
syms.flatten.each do |sym|
|
|
62
62
|
metaclass.instance_eval { attr_accessor(sym) }
|
|
63
63
|
end
|
|
64
64
|
end
|
|
65
|
+
|
|
66
|
+
def instance_attr_accessor(*syms)
|
|
67
|
+
syms.flatten.each do |sym|
|
|
68
|
+
instance_eval { attr_accessor(sym) }
|
|
69
|
+
end
|
|
70
|
+
end
|
|
65
71
|
|
|
66
|
-
def
|
|
72
|
+
def class_attr_reader(*syms)
|
|
67
73
|
syms.flatten.each do |sym|
|
|
68
74
|
metaclass.instance_eval { attr_reader(sym) }
|
|
69
75
|
end
|
|
70
76
|
end
|
|
71
77
|
|
|
72
|
-
def
|
|
78
|
+
def class_attr_writer(*syms)
|
|
73
79
|
syms.flatten.each do |sym|
|
|
74
80
|
metaclass.instance_eval { attr_writer(sym) }
|
|
75
81
|
end
|
data/lib/trellis/version.rb
CHANGED
data/tasks/rspec.rake
CHANGED
data/test/application_spec.rb
CHANGED
|
@@ -3,7 +3,7 @@ require File.dirname(__FILE__) + '/spec_helper.rb'
|
|
|
3
3
|
require "rack"
|
|
4
4
|
require_fixtures 'application_spec_applications'
|
|
5
5
|
|
|
6
|
-
describe Trellis::Application, "
|
|
6
|
+
describe Trellis::Application, " when declared" do
|
|
7
7
|
before do
|
|
8
8
|
@homepage = TestApp::MyApp.instance_eval { @homepage }
|
|
9
9
|
@pages = TestApp::MyApp.instance_eval { @pages }
|
|
@@ -14,19 +14,19 @@ describe Trellis::Application, " a Trellis application when declared" do
|
|
|
14
14
|
@homepage.should == :home
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
-
it "should contain the home page in its collection of pages" do
|
|
18
|
-
@pages.include?(:home).should be(true)
|
|
19
|
-
end
|
|
20
|
-
|
|
21
17
|
it "should contain any declared static routes" do
|
|
22
18
|
images_route = @static_routes.select { |item| item[:urls].include?('/images') }
|
|
23
|
-
images_route.should_not be_empty
|
|
19
|
+
images_route.should_not be_empty
|
|
24
20
|
style_route = @static_routes.select { |item| item[:urls].include?('/style') }
|
|
25
21
|
style_route.should_not be_empty
|
|
26
22
|
favicon_route = @static_routes.select { |item| item[:urls].include?('/favicon.ico') }
|
|
27
23
|
favicon_route.should_not be_empty
|
|
28
|
-
|
|
29
|
-
|
|
24
|
+
jquery_route = @static_routes.select { |item| item[:urls].include?('/jquery') && item[:root].include?('./js') }
|
|
25
|
+
jquery_route.should_not be_empty
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it "should include Rack::Utils" do
|
|
29
|
+
TestApp::MyApp.included_modules.should include(Rack::Utils)
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
end
|
|
@@ -47,33 +47,58 @@ describe Trellis::Application, " when requesting the root url with a GET" do
|
|
|
47
47
|
end
|
|
48
48
|
end
|
|
49
49
|
|
|
50
|
-
describe Trellis::Application, "
|
|
50
|
+
describe Trellis::Application, " requesting a route" do
|
|
51
51
|
before(:each) do
|
|
52
52
|
application = TestApp::MyApp.new
|
|
53
|
-
@request = Rack::MockRequest.new(application)
|
|
53
|
+
@request = Rack::MockRequest.new(application)
|
|
54
54
|
end
|
|
55
|
-
|
|
56
|
-
it "
|
|
57
|
-
response = @request.get("/
|
|
58
|
-
response.
|
|
55
|
+
|
|
56
|
+
it "should return a 404 (not found)" do
|
|
57
|
+
response = @request.get("/blowup")
|
|
58
|
+
response.status.should be(404)
|
|
59
59
|
end
|
|
60
|
-
|
|
61
|
-
it "should return
|
|
62
|
-
response = @request.get("/
|
|
63
|
-
response.body.should == "
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
it "should
|
|
67
|
-
|
|
68
|
-
|
|
60
|
+
|
|
61
|
+
it "should return the page contents of the first page matching the route" do
|
|
62
|
+
response = @request.get("/whoa")
|
|
63
|
+
response.body.should == "<html><body>whoa!</body></html>"
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it "should support a single named parameter" do
|
|
67
|
+
response_brian = @request.get("/hello/brian")
|
|
68
|
+
response_anne = @request.get("/hello/anne")
|
|
69
|
+
response_brian.body.should == "<html><body><h2>Hello</h2>brian</body></html>"
|
|
70
|
+
response_anne.body.should == '<html><body><h2>Hello</h2>anne</body></html>'
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it "should support multiple named parameters" do
|
|
74
|
+
response = @request.get('/report/2009/05/31')
|
|
75
|
+
response.body.should == "<html><body><h2>Report for</h2>05/31/2009</body></html>"
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it "should support optional parameters" do
|
|
79
|
+
response_all_params = @request.get('/foobar/hello/world')
|
|
80
|
+
response_one_param = @request.get('/foobar/hello')
|
|
81
|
+
response_no_param = @request.get('/foobar')
|
|
82
|
+
response_all_params.body.should == "<html><body>hello-world</body></html>"
|
|
83
|
+
response_one_param.body.should == "<html><body>hello-</body></html>"
|
|
84
|
+
response_no_param.body.should == "<html><body>-</body></html>"
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
it "should support a wildcard parameters" do
|
|
88
|
+
response = @request.get('/splat/goodbye/cruel/world')
|
|
89
|
+
response.body.should == '<html><body>goodbye/cruel/world</body></html>'
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
it "should supports mixing multiple splat" do
|
|
93
|
+
response = @request.get('/splats/bar/foo/bling/baz/boom')
|
|
94
|
+
response.body.should == '<html><body>barblingbaz/boom</body></html>'
|
|
95
|
+
|
|
96
|
+
no_route_response = @request.get('/splats/bar/foo/baz')
|
|
97
|
+
no_route_response.status.should be(404)
|
|
69
98
|
end
|
|
70
|
-
end
|
|
71
99
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
homepage.inject_dependent_pages
|
|
76
|
-
injected_page = homepage.instance_eval { @other }
|
|
77
|
-
injected_page.class.should == TestApp::Other
|
|
100
|
+
it "should supports mixing named and wildcard params" do
|
|
101
|
+
response = @request.get('/mixed/afoo/bar/baz')
|
|
102
|
+
response.body.should == '<html><body>bar/baz-afoo</body></html>'
|
|
78
103
|
end
|
|
79
104
|
end
|