merb 0.3.1 → 0.3.3
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/README +1 -1
- data/Rakefile +19 -9
- data/TODO +2 -15
- data/examples/skeleton.tar +0 -0
- data/examples/skeleton/dist/conf/router.rb +2 -2
- data/lib/merb.rb +9 -11
- data/lib/merb/core_ext.rb +0 -1
- data/lib/merb/core_ext/merb_hash.rb +70 -1
- data/lib/merb/core_ext/merb_symbol.rb +1 -7
- data/lib/merb/merb_controller.rb +13 -9
- data/lib/merb/merb_dispatcher.rb +1 -1
- data/lib/merb/merb_mailer.rb +7 -5
- data/lib/merb/merb_request.rb +8 -4
- data/lib/merb/merb_router.rb +238 -150
- data/lib/merb/merb_server.rb +26 -3
- data/lib/merb/merb_view_context.rb +20 -4
- data/lib/merb/mixins/controller_mixin.rb +24 -13
- data/lib/merb/mixins/form_control_mixin.rb +5 -0
- data/lib/merb/mixins/render_mixin.rb +24 -11
- data/lib/merb/mixins/view_context_mixin.rb +1 -1
- data/lib/merb/template/haml.rb +35 -29
- data/lib/merb/template/markaby.rb +8 -1
- data/lib/tasks/merb.rake +8 -0
- metadata +29 -2
data/README
CHANGED
data/Rakefile
CHANGED
@@ -15,7 +15,7 @@ include FileUtils
|
|
15
15
|
|
16
16
|
|
17
17
|
NAME = "merb"
|
18
|
-
VERS = "0.3.
|
18
|
+
VERS = "0.3.3"
|
19
19
|
CLEAN.include ['**/.*.sw?', '*.gem', '.config']
|
20
20
|
|
21
21
|
setup_clean [ "pkg", "lib/*.bundle", "*.gem",
|
@@ -29,13 +29,6 @@ task :merb => [:clean, :rdoc, :package]
|
|
29
29
|
task :doc => [:rdoc]
|
30
30
|
|
31
31
|
|
32
|
-
#Rake::RDocTask.new do |rdoc|
|
33
|
-
# rdoc.rdoc_dir = 'doc/rdoc'
|
34
|
-
# rdoc.options += RDOC_OPTS
|
35
|
-
# rdoc.main = "README"
|
36
|
-
# rdoc.title = "Merb Documentation"
|
37
|
-
# rdoc.rdoc_files.add ['README', 'LICENSE', 'TODO', 'lib/**/*.rb']
|
38
|
-
#end
|
39
32
|
Rake::RDocTask.new do |rdoc|
|
40
33
|
files =['README', 'LICENSE', 'TODO', 'lib/**/*.rb']
|
41
34
|
rdoc.rdoc_files.add(files)
|
@@ -66,6 +59,9 @@ spec = Gem::Specification.new do |s|
|
|
66
59
|
s.add_dependency('mongrel')
|
67
60
|
s.add_dependency('erubis')
|
68
61
|
s.add_dependency('json')
|
62
|
+
s.add_dependency('mime-types')
|
63
|
+
s.add_dependency('xml-simple')
|
64
|
+
s.add_dependency('archive-tar-minitar')
|
69
65
|
s.required_ruby_version = '>= 1.8.4'
|
70
66
|
|
71
67
|
s.files = %w(LICENSE README Rakefile TODO) + Dir.glob("{bin,test,lib,examples}/**/*")
|
@@ -132,7 +128,7 @@ end
|
|
132
128
|
|
133
129
|
desc 'Run all tests, specs and finish with rcov'
|
134
130
|
task :aok do
|
135
|
-
sh %{rake specs}
|
131
|
+
sh %{rake specs;rake rcov}
|
136
132
|
end
|
137
133
|
|
138
134
|
desc "Run all specs"
|
@@ -144,11 +140,25 @@ end
|
|
144
140
|
|
145
141
|
desc "RCov"
|
146
142
|
Spec::Rake::SpecTask.new('rcov') do |t|
|
143
|
+
t.spec_opts = ["--format", "specdoc"]
|
147
144
|
t.spec_files = FileList['specs/**/*_spec.rb']
|
148
145
|
t.libs = ['lib', 'server/lib' ]
|
149
146
|
t.rcov = true
|
150
147
|
end
|
151
148
|
|
149
|
+
STATS_DIRECTORIES = [
|
150
|
+
%w(Code lib/),
|
151
|
+
%w(Unit\ tests specs),
|
152
|
+
].collect { |name, dir| [ name, "./#{dir}" ] }.select { |name, dir| File.directory?(dir) }
|
153
|
+
|
154
|
+
desc "Report code statistics (KLOCs, etc) from the application"
|
155
|
+
task :stats do
|
156
|
+
require __DIR__ + '/tools/code_statistics'
|
157
|
+
#require 'extra/stats'
|
158
|
+
verbose = true
|
159
|
+
CodeStatistics.new(*STATS_DIRECTORIES).to_s
|
160
|
+
end
|
161
|
+
|
152
162
|
##############################################################################
|
153
163
|
# SVN
|
154
164
|
##############################################################################
|
data/TODO
CHANGED
@@ -1,16 +1,3 @@
|
|
1
1
|
[TODO]
|
2
|
-
*
|
3
|
-
*
|
4
|
-
* Helpers
|
5
|
-
* config file **DONE**
|
6
|
-
* session
|
7
|
-
** AR ** DONE **
|
8
|
-
** DRb
|
9
|
-
* Rails integration
|
10
|
-
* Text backend?
|
11
|
-
* plugins support(rails plugins too?)
|
12
|
-
* Layouts **DONE**
|
13
|
-
* auto template_dir **DONE**
|
14
|
-
* before/after filters **DONE**
|
15
|
-
* merbjs **DONE**
|
16
|
-
* testing
|
2
|
+
* more specs for Template engines and Rendering
|
3
|
+
*
|
data/examples/skeleton.tar
CHANGED
Binary file
|
@@ -10,7 +10,7 @@
|
|
10
10
|
|
11
11
|
|
12
12
|
puts "Compiling routes.."
|
13
|
-
Merb::
|
13
|
+
Merb::Router.prepare do |r|
|
14
14
|
# restfull routes
|
15
15
|
# r.resources :posts
|
16
16
|
|
@@ -19,4 +19,4 @@ Merb::RouteMatcher.prepare do |r|
|
|
19
19
|
|
20
20
|
# change this for your home page to be avaiable at /
|
21
21
|
#r.add '/', :controller => 'whatever', :action =>'index'
|
22
|
-
end
|
22
|
+
end
|
data/lib/merb.rb
CHANGED
@@ -11,16 +11,11 @@ end
|
|
11
11
|
require 'fileutils'
|
12
12
|
require 'erubis'
|
13
13
|
require 'logger'
|
14
|
-
|
15
|
-
begin
|
16
|
-
require 'fjson'
|
17
|
-
rescue LoadError
|
18
|
-
require 'json'
|
19
|
-
end
|
14
|
+
require 'json'
|
20
15
|
|
21
16
|
|
22
17
|
module Merb
|
23
|
-
VERSION='0.3.
|
18
|
+
VERSION='0.3.3' unless defined?VERSION
|
24
19
|
class Server
|
25
20
|
class << self
|
26
21
|
def config
|
@@ -56,6 +51,7 @@ MERB_FRAMEWORK_ROOT = __DIR__
|
|
56
51
|
MERB_ROOT = Merb::Server.merb_root || Dir.pwd
|
57
52
|
DIST_ROOT = Merb::Server.dist_root || Dir.pwd+'/dist'
|
58
53
|
MERB_ENV = Merb::Server.config[:environment]
|
54
|
+
MERB_VIEW_ROOT = MERB_ROOT / "dist/app/views"
|
59
55
|
|
60
56
|
logpath = $TESTING ? "/tmp/merb_test.log" : "#{MERB_ROOT}/log/merb.#{Merb::Server.port}.log"
|
61
57
|
MERB_LOGGER = Logger.new(logpath)
|
@@ -78,10 +74,12 @@ MERB_LOGGER.level = case (Merb::Server.log_level.downcase rescue '')
|
|
78
74
|
end
|
79
75
|
|
80
76
|
module Mongrel::Const
|
81
|
-
HTTP_COOKIE
|
82
|
-
QUERY_STRING
|
83
|
-
APPLICATION_JSON
|
84
|
-
TEXT_JSON
|
77
|
+
HTTP_COOKIE = 'HTTP_COOKIE'.freeze
|
78
|
+
QUERY_STRING = 'QUERY_STRING'.freeze
|
79
|
+
APPLICATION_JSON = 'application/json'.freeze
|
80
|
+
TEXT_JSON = 'text/x-json'.freeze
|
81
|
+
APPLICATION_XML = 'application/xml'.freeze
|
82
|
+
TEXT_XML = 'text/xml'.freeze
|
85
83
|
UPCASE_CONTENT_TYPE = 'CONTENT_TYPE'.freeze
|
86
84
|
end
|
87
85
|
|
data/lib/merb/core_ext.rb
CHANGED
@@ -1,8 +1,77 @@
|
|
1
1
|
class Hash
|
2
|
+
|
3
|
+
class << self
|
4
|
+
def from_xml(xml)
|
5
|
+
# TODO: Refactor this into something much cleaner that doesn't rely on XmlSimple
|
6
|
+
undasherize_keys(typecast_xml_value(XmlSimple.xml_in(xml,
|
7
|
+
'forcearray' => false,
|
8
|
+
'forcecontent' => true,
|
9
|
+
'keeproot' => true,
|
10
|
+
'contentkey' => '__content__')
|
11
|
+
))
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
def typecast_xml_value(value)
|
16
|
+
case value.class.to_s
|
17
|
+
when "Hash"
|
18
|
+
if value.has_key?("__content__")
|
19
|
+
content = translate_xml_entities(value["__content__"])
|
20
|
+
case value["type"]
|
21
|
+
when "integer" then content.to_i
|
22
|
+
when "boolean" then content.strip == "true"
|
23
|
+
when "datetime" then ::Time.parse(content).utc
|
24
|
+
when "date" then ::Date.parse(content)
|
25
|
+
else content
|
26
|
+
end
|
27
|
+
else
|
28
|
+
(value.blank? || value['type'] || value['nil'] == 'true') ? nil : value.inject({}) do |h,(k,v)|
|
29
|
+
h[k] = typecast_xml_value(v)
|
30
|
+
h
|
31
|
+
end
|
32
|
+
end
|
33
|
+
when "Array"
|
34
|
+
value.map! { |i| typecast_xml_value(i) }
|
35
|
+
case value.length
|
36
|
+
when 0 then nil
|
37
|
+
when 1 then value.first
|
38
|
+
else value
|
39
|
+
end
|
40
|
+
when "String"
|
41
|
+
value
|
42
|
+
else
|
43
|
+
raise "can't typecast #{value.inspect}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def translate_xml_entities(value)
|
48
|
+
value.gsub(/</, "<").
|
49
|
+
gsub(/>/, ">").
|
50
|
+
gsub(/"/, '"').
|
51
|
+
gsub(/'/, "'").
|
52
|
+
gsub(/&/, "&")
|
53
|
+
end
|
54
|
+
|
55
|
+
def undasherize_keys(params)
|
56
|
+
case params.class.to_s
|
57
|
+
when "Hash"
|
58
|
+
params.inject({}) do |h,(k,v)|
|
59
|
+
h[k.to_s.tr("-", "_")] = undasherize_keys(v)
|
60
|
+
h
|
61
|
+
end
|
62
|
+
when "Array"
|
63
|
+
params.map { |v| undasherize_keys(v) }
|
64
|
+
else
|
65
|
+
params
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
|
2
71
|
def with_indifferent_access
|
3
72
|
MerbHash.new(self)
|
4
73
|
end
|
5
|
-
def to_params
|
74
|
+
def to_params
|
6
75
|
result = ''
|
7
76
|
stack = []
|
8
77
|
|
data/lib/merb/merb_controller.rb
CHANGED
@@ -21,8 +21,8 @@ module Merb
|
|
21
21
|
self._layout = :application
|
22
22
|
self._session_id_key = :_session_id
|
23
23
|
self._template_extensions = { }
|
24
|
-
self._template_root = File.expand_path(
|
25
|
-
self._layout_root = File.expand_path(
|
24
|
+
self._template_root = File.expand_path(MERB_VIEW_ROOT)
|
25
|
+
self._layout_root = File.expand_path(MERB_VIEW_ROOT / "layout")
|
26
26
|
|
27
27
|
include Merb::ControllerMixin
|
28
28
|
include Merb::RenderMixin
|
@@ -51,6 +51,8 @@ module Merb
|
|
51
51
|
json = JSON.parse(request.read || "") || {}
|
52
52
|
json = MerbHash.new(json) if json.is_a? Hash
|
53
53
|
querystring.update(json)
|
54
|
+
elsif [Mongrel::Const::APPLICATION_XML, Mongrel::Const::TEXT_XML].include?(@env[Mongrel::Const::UPCASE_CONTENT_TYPE])
|
55
|
+
querystring.update(Hash.from_xml(request.read).with_indifferent_access)
|
54
56
|
else
|
55
57
|
querystring.update(query_parse(request.read))
|
56
58
|
end
|
@@ -64,7 +66,7 @@ module Merb
|
|
64
66
|
if @params.key?(:_method) && allow.include?(@method)
|
65
67
|
@method = @params.delete(:_method).downcase.intern
|
66
68
|
end
|
67
|
-
@request = Request.new(@env, @method)
|
69
|
+
@request = Request.new(@env, @method, request)
|
68
70
|
|
69
71
|
MERB_LOGGER.info("Params: #{params.inspect}\nCookies: #{cookies.inspect}")
|
70
72
|
end
|
@@ -178,12 +180,14 @@ module Merb
|
|
178
180
|
else
|
179
181
|
ok = true
|
180
182
|
end
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
183
|
+
if ok
|
184
|
+
case filter
|
185
|
+
when Symbol, String
|
186
|
+
send(filter)
|
187
|
+
when Proc
|
188
|
+
filter.call(self)
|
189
|
+
end
|
190
|
+
end
|
187
191
|
end
|
188
192
|
return :filter_chain_completed
|
189
193
|
end
|
data/lib/merb/merb_dispatcher.rb
CHANGED
@@ -49,7 +49,7 @@ module Merb
|
|
49
49
|
def route_path(path)
|
50
50
|
path = path.sub(/\/+/, '/').sub(/\?.*$/, '')
|
51
51
|
path = path[0..-2] if (path[-1] == ?/) && path.size > 1
|
52
|
-
Merb::
|
52
|
+
Merb::Router.match(path)
|
53
53
|
end
|
54
54
|
|
55
55
|
# take a controller class name string and reload or require
|
data/lib/merb/merb_mailer.rb
CHANGED
@@ -45,11 +45,7 @@ module Merb
|
|
45
45
|
end
|
46
46
|
end
|
47
47
|
=begin
|
48
|
-
|
49
|
-
:from => 'bar@foo.com',
|
50
|
-
:subject => 'Welcome to whatever!',
|
51
|
-
:body => partial(:sometemplate)
|
52
|
-
m.deliver!
|
48
|
+
|
53
49
|
|
54
50
|
Merb::Mailer.config = {
|
55
51
|
:host=>'smtp.yourserver.com',
|
@@ -58,4 +54,10 @@ Merb::Mailer.config = {
|
|
58
54
|
:pass=>'pass',
|
59
55
|
:auth=>:plain # :plain, :login, or :cram_md5, default :plain
|
60
56
|
}
|
57
|
+
m = Merb::Mailer.new :to => 'foo@bar.com',
|
58
|
+
:from => 'bar@foo.com',
|
59
|
+
:subject => 'Welcome to whatever!',
|
60
|
+
:body => partial(:sometemplate)
|
61
|
+
m.deliver!
|
62
|
+
|
61
63
|
=end
|
data/lib/merb/merb_request.rb
CHANGED
@@ -2,11 +2,17 @@ module Merb
|
|
2
2
|
|
3
3
|
class Request
|
4
4
|
attr_accessor :env
|
5
|
-
def initialize(env, method)
|
5
|
+
def initialize(env, method, request)
|
6
6
|
@env = env
|
7
7
|
@method = method
|
8
|
+
@request = request
|
8
9
|
end
|
9
10
|
|
11
|
+
def raw_post
|
12
|
+
@request.rewind
|
13
|
+
@request.read
|
14
|
+
end
|
15
|
+
|
10
16
|
# returns true if the request is an ajax request.
|
11
17
|
def xml_http_request?
|
12
18
|
not /XMLHttpRequest/i.match(@env['HTTP_X_REQUESTED_WITH']).nil?
|
@@ -148,9 +154,7 @@ module Merb
|
|
148
154
|
|
149
155
|
# create predicate methods for querying the REQUEST_METHOD
|
150
156
|
[:get, :post, :put, :delete, :head].each do |m|
|
151
|
-
|
152
|
-
def #{m}?; method == :#{m}; end
|
153
|
-
}
|
157
|
+
define_method("#{m}?") { method == m.to_sym }
|
154
158
|
end
|
155
159
|
|
156
160
|
end
|
data/lib/merb/merb_router.rb
CHANGED
@@ -1,97 +1,248 @@
|
|
1
1
|
module Merb
|
2
2
|
|
3
|
-
|
4
|
-
# You can define placeholder parts of the url with the :symbol notation.
|
5
|
-
# so r.add '/foo/:bar/baz/:id', :class => 'Bar', :method => 'foo'
|
6
|
-
# will match against a request to /foo/123/baz/456. It will then
|
7
|
-
# use the class Bar as your merb controller and call the foo method on it.
|
8
|
-
# the foo method will recieve a hash with {:bar => '123', :id => '456'}
|
9
|
-
# as the content. So the :placeholders sections of your routes become
|
10
|
-
# a hash of arguments to your controller methods. This route maps
|
11
|
-
# the default /controller/action and /controller/action/id urls:
|
12
|
-
# r.add '/:class/:method/:id'
|
13
|
-
class RouteMatcher
|
3
|
+
class Router
|
14
4
|
|
15
|
-
|
16
|
-
@@section_regexp = /(?::([a-z*_]+))/.freeze
|
5
|
+
SECTION_REGEXP = /(?::([a-z*_]+))/.freeze
|
17
6
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
7
|
+
class << self
|
8
|
+
|
9
|
+
def prepare(&block)
|
10
|
+
@@matcher = RouteMatcher.new
|
11
|
+
@@generator = RouteGenerator.new
|
12
|
+
|
13
|
+
yield self
|
14
|
+
|
15
|
+
@@matcher.compile_router
|
16
|
+
end
|
17
|
+
|
18
|
+
def add(*route)
|
19
|
+
@@matcher.add(*route)
|
20
|
+
end
|
21
|
+
|
22
|
+
def matcher
|
23
|
+
@@matcher
|
24
|
+
end
|
25
|
+
|
26
|
+
def generator
|
27
|
+
@@generator
|
28
|
+
end
|
29
|
+
|
30
|
+
def match(path)
|
31
|
+
@@matcher.route_request(path)
|
32
|
+
end
|
33
|
+
|
34
|
+
def generate(method, options = {})
|
35
|
+
@@generator.generate(method, options)
|
36
|
+
end
|
37
|
+
|
38
|
+
def resources(res, opts={})
|
39
|
+
opts[:prefix] ||= ""
|
40
|
+
if block_given?
|
41
|
+
procs = []
|
42
|
+
yield Resource.new(res, procs, opts)
|
43
|
+
procs.reverse.each &:call
|
44
|
+
else
|
45
|
+
generate_resources_routes(res,opts)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def resource(res, opts={})
|
50
|
+
opts[:prefix] ||= ""
|
51
|
+
if block_given?
|
52
|
+
procs = []
|
53
|
+
yield Resource.new(res, procs, opts)
|
54
|
+
procs.reverse.each &:call
|
55
|
+
else
|
56
|
+
generate_singleton_routes(res,opts)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def default_routes
|
61
|
+
@@matcher.default_routes
|
62
|
+
end
|
63
|
+
|
64
|
+
def compiled_statement
|
65
|
+
@@matcher.compiled_statement
|
66
|
+
end
|
67
|
+
|
68
|
+
def compiled_regexen
|
69
|
+
@@matcher.compiled_regexen
|
70
|
+
end
|
71
|
+
|
72
|
+
def generate_resources_routes(res, opts)
|
73
|
+
[@@matcher,@@generator].each { |r| r.generate_resources_routes(res, opts) }
|
74
|
+
end
|
75
|
+
|
76
|
+
def generate_singleton_routes(res, opts)
|
77
|
+
[@@matcher,@@generator].each { |r| r.generate_singleton_routes(res, opts) }
|
78
|
+
end
|
27
79
|
|
28
|
-
#
|
29
|
-
# as the body of the route_request method.
|
30
|
-
def self.compiled_statement
|
31
|
-
@@compiled_statement
|
32
|
-
end
|
33
|
-
|
34
|
-
def self.compiled_regexen
|
35
|
-
@@compiled_regexen
|
36
|
-
end
|
80
|
+
end # class << self
|
37
81
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
82
|
+
class RouteMatcher
|
83
|
+
|
84
|
+
def initialize
|
85
|
+
@routes = Array.new
|
86
|
+
@compiled_statement = String.new
|
87
|
+
@compiled_regexen = Array.new
|
88
|
+
end
|
89
|
+
|
90
|
+
# the final compiled lambda that gets used
|
91
|
+
# as the body of the route_request method.
|
92
|
+
def compiled_statement
|
93
|
+
@compiled_statement
|
94
|
+
end
|
95
|
+
|
96
|
+
def compiled_regexen
|
97
|
+
@compiled_regexen
|
98
|
+
end
|
99
|
+
|
100
|
+
# add a route to be compiled
|
101
|
+
def add(*route)
|
102
|
+
@routes << [route[0], (route[1] || {})]
|
103
|
+
end
|
104
|
+
|
105
|
+
# build up a string that defines a lambda
|
106
|
+
# that does a case statement on the PATH_INFO
|
107
|
+
# against each of the compiled routes in turn.
|
108
|
+
# first route that matches wins.
|
109
|
+
def compile_router
|
110
|
+
router_lambda = @routes.inject("lambda{|path| \n sections={}\n case path\n") { |m,r|
|
111
|
+
m << compile(r)
|
112
|
+
} << " else\n return {:controller=>'Noroutefound', :action=>'noroute'}\n end\n}"
|
113
|
+
@compiled_statement = router_lambda
|
114
|
+
meta_def(:route_request, &eval(router_lambda))
|
115
|
+
end
|
116
|
+
|
117
|
+
# compile each individual route into a when /.../
|
118
|
+
# component of the case statement. Takes /:sections
|
119
|
+
# of the route def that start with : and turns them
|
120
|
+
# into placeholders for whatever urls match against
|
121
|
+
# the route in question.
|
122
|
+
def compile(route)
|
123
|
+
raise ArgumentError unless String === route[0]
|
124
|
+
code, count = '', 0
|
125
|
+
while route[0] =~ Router::SECTION_REGEXP
|
126
|
+
route[0] = route[0].dup
|
127
|
+
name = $1
|
128
|
+
(name =~ /(\*+)(\w+)/) ? (flag = true; name = $2) : (flag = false)
|
129
|
+
count += 1
|
130
|
+
if flag
|
131
|
+
route[0].sub!(Router::SECTION_REGEXP, "([^,?]+)")
|
132
|
+
else
|
133
|
+
route[0].sub!(Router::SECTION_REGEXP, "([^\/,?]+)")
|
134
|
+
end
|
135
|
+
code << " sections[:#{name}] = $#{count}\n"
|
136
|
+
end
|
137
|
+
@compiled_regexen << Regexp.new(route[0])
|
138
|
+
index = @compiled_regexen.size - 1
|
139
|
+
condition = " when @compiled_regexen[#{index}] "
|
140
|
+
statement = "#{condition}\n#{code}"
|
141
|
+
statement << " return #{route[1].inspect}.update(sections)\n"
|
142
|
+
statement
|
143
|
+
end
|
144
|
+
|
145
|
+
def generate_resources_routes(res,opt)
|
146
|
+
with_options :controller => res.to_s, :rest => true do |r|
|
147
|
+
r.add "#{opt[:prefix]}/#{res}/:id[;/]edit", :allowed => {:get => 'edit'}
|
148
|
+
r.add "#{opt[:prefix]}/#{res}/new[;/]:action", :allowed => {:get => 'new', :post => 'new', :put => 'new', :delete => 'new'}
|
149
|
+
r.add "#{opt[:prefix]}/#{res}/new" , :allowed => {:get => 'new'}
|
150
|
+
if mem = opt[:member]
|
151
|
+
mem.keys.sort_by{|x| "#{x}"}.each {|action|
|
152
|
+
allowed = mem[action].injecting({}) {|h, verb| h[verb] = "#{action}"}
|
153
|
+
r.add "#{opt[:prefix]}/#{res}/:id[;/]+#{action}", :allowed => allowed
|
154
|
+
}
|
155
|
+
end
|
156
|
+
if coll = opt[:collection]
|
157
|
+
coll.keys.sort_by{|x| "#{x}"}.each {|action|
|
158
|
+
allowed = coll[action].injecting({}) {|h, verb| h[verb] = "#{action}"}
|
159
|
+
r.add "#{opt[:prefix]}/#{res}[;/]#{action}", :allowed => allowed
|
160
|
+
}
|
161
|
+
end
|
162
|
+
r.add "#{opt[:prefix]}/#{res}/:id\\.:format", :allowed => {:get => 'show', :put => 'update', :delete => 'destroy'}
|
163
|
+
r.add "#{opt[:prefix]}/#{res}\\.:format", :allowed => {:get => 'index', :post => 'create'}
|
164
|
+
r.add "#{opt[:prefix]}/#{res}/:id", :allowed => {:get => 'show', :put => 'update', :delete => 'destroy'}
|
165
|
+
r.add "#{opt[:prefix]}/#{res}/?", :allowed => {:get => 'index', :post => 'create'}
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def generate_singleton_routes(res,opt)
|
170
|
+
with_options :controller => res.to_s, :rest => true do |r|
|
171
|
+
r.add "#{opt[:prefix]}/#{res}[;/]edit", :allowed => {:get => 'edit'}
|
172
|
+
r.add "#{opt[:prefix]}/#{res}\\.:format", :allowed => {:get => 'show'}
|
173
|
+
r.add "#{opt[:prefix]}/#{res}/new" , :allowed => {:get => 'new'}
|
174
|
+
r.add "#{opt[:prefix]}/#{res}/?", :allowed => {:get => 'show', :post => 'create', :put => 'update', :delete => 'destroy'}
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def default_routes
|
179
|
+
add "/:controller/:action/:id\\.:format"
|
180
|
+
add "/:controller/:action/:id"
|
181
|
+
add "/:controller/:action\\.:format"
|
182
|
+
add "/:controller/:action"
|
183
|
+
add "/:controller\\.:format", :action => 'index'
|
184
|
+
add "/:controller", :action => 'index'
|
185
|
+
end
|
45
186
|
end
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
187
|
+
|
188
|
+
class RouteGenerator
|
189
|
+
|
190
|
+
attr_accessor :paths
|
191
|
+
|
192
|
+
def initialize
|
193
|
+
@paths = {}
|
194
|
+
end
|
195
|
+
|
196
|
+
def add(name, path)
|
197
|
+
name = name.intern unless Symbol === name
|
198
|
+
@paths[name] = path
|
199
|
+
end
|
200
|
+
|
201
|
+
def generate(name, options = {})
|
202
|
+
path = @paths[name]
|
203
|
+
while path =~ Router::SECTION_REGEXP
|
204
|
+
path.sub!(Router::SECTION_REGEXP, options[$~[1].intern].to_s)
|
205
|
+
end
|
206
|
+
if f = options[:format]
|
207
|
+
"#{path}.#{f}"
|
208
|
+
else
|
209
|
+
path
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
def generate_singleton_routes(res,opt)
|
214
|
+
add "edit_#{res}", "#{opt[:prefix]}/#{res}/edit"
|
215
|
+
add "new_#{res}","#{opt[:prefix]}/#{res}/new"
|
216
|
+
add res, "#{opt[:prefix]}/#{res}"
|
217
|
+
end
|
218
|
+
|
219
|
+
def generate_resources_routes(res,opt)
|
220
|
+
res_singular = res.to_s.singularize
|
221
|
+
add res, "#{opt[:prefix]}/#{res}"
|
222
|
+
add res_singular, "#{opt[:prefix]}/#{res}/:id"
|
223
|
+
add "new_#{res_singular}", "#{opt[:prefix]}/#{res}/new"
|
224
|
+
add "custom_new_#{res_singular}", "#{opt[:prefix]}/#{res}/new/:action"
|
225
|
+
add "edit_#{res_singular}", "#{opt[:prefix]}/#{res}/:id/edit"
|
226
|
+
if mem = opt[:member]
|
227
|
+
mem.keys.sort_by{|x| "#{x}"}.each {|action|
|
228
|
+
add "#{action}_#{res_singular}", "#{opt[:prefix]}/#{res}/:id/#{action}"
|
229
|
+
}
|
230
|
+
end
|
231
|
+
if coll = opt[:collection]
|
232
|
+
coll.keys.sort_by{|x| "#{x}"}.each {|action|
|
233
|
+
add "#{action}_#{res_singular}", "#{opt[:prefix]}/#{res}/#{action}"
|
234
|
+
}
|
76
235
|
end
|
77
|
-
|
78
|
-
end
|
79
|
-
@@compiled_regexen << Regexp.new(route[0])
|
80
|
-
index = @@compiled_regexen.size - 1
|
81
|
-
condition = " when @@compiled_regexen[#{index}] "
|
82
|
-
statement = "#{condition}\n#{code}"
|
83
|
-
statement << " return #{route[1].inspect}.update(sections)\n"
|
84
|
-
statement
|
236
|
+
end
|
85
237
|
end
|
86
|
-
|
238
|
+
|
87
239
|
class Resource
|
88
|
-
# TODO : come up with naming convention and generate helper
|
89
|
-
# methods for route generation. Route generation
|
90
|
-
# framework needed but needs to be simple and light
|
91
240
|
|
92
241
|
def initialize(resource, procs=[], opts={})
|
93
242
|
@resource, @procs, @opts = resource, procs, opts
|
94
|
-
@procs << proc
|
243
|
+
@procs << proc do
|
244
|
+
[::Merb::Router.matcher, ::Merb::Router.generator].each {|r| r.generate_resources_routes(@resource, @opts) }
|
245
|
+
end
|
95
246
|
end
|
96
247
|
|
97
248
|
def resources(res, opts={})
|
@@ -101,7 +252,9 @@ module Merb
|
|
101
252
|
if block_given?
|
102
253
|
yield self.class.new(res, @procs, opts)
|
103
254
|
else
|
104
|
-
@procs << proc
|
255
|
+
@procs << proc do
|
256
|
+
[::Merb::Router.matcher, ::Merb::Router.generator].each {|r| r.generate_resources_routes(res, opts) }
|
257
|
+
end
|
105
258
|
end
|
106
259
|
end
|
107
260
|
|
@@ -112,76 +265,11 @@ module Merb
|
|
112
265
|
if block_given?
|
113
266
|
yield self.class.new(res, @procs, opts)
|
114
267
|
else
|
115
|
-
@procs << proc
|
116
|
-
|
117
|
-
|
118
|
-
end
|
119
|
-
|
120
|
-
# add a resource to be compiled for rest style dispatch
|
121
|
-
def self.resources(res, opts={})
|
122
|
-
opts[:prefix] ||= ""
|
123
|
-
if block_given?
|
124
|
-
procs = []
|
125
|
-
yield Resource.new(res, procs, opts)
|
126
|
-
procs.reverse.each &:call
|
127
|
-
else
|
128
|
-
generate_resources_routes(res,opts)
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
# add a resource to be compiled for rest style dispatch
|
133
|
-
def self.resource(res, opts={})
|
134
|
-
opts[:prefix] ||= ""
|
135
|
-
if block_given?
|
136
|
-
procs = []
|
137
|
-
yield Resource.new(res, procs, opts)
|
138
|
-
procs.reverse.each &:call
|
139
|
-
else
|
140
|
-
generate_singleton_routes(res,opts)
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
def self.generate_resources_routes(res,opt)
|
145
|
-
with_options :controller => res.to_s, :rest => true do |r|
|
146
|
-
r.add "#{opt[:prefix]}/#{res}/:id[;/]+edit", :allowed => {:get => 'edit'}
|
147
|
-
r.add "#{opt[:prefix]}/#{res}/new[;/]+:action", :allowed => {:get => 'new', :post => 'new', :put => 'new', :delete => 'new'}
|
148
|
-
r.add "#{opt[:prefix]}/#{res}/new" , :allowed => {:get => 'new'}
|
149
|
-
if mem = opt[:member]
|
150
|
-
mem.keys.sort_by{|x| "#{x}"}.each {|action|
|
151
|
-
allowed = mem[action].injecting({}) {|h, verb| h[verb] = "#{action}"}
|
152
|
-
r.add "#{opt[:prefix]}/#{res}/:id[;/]+#{action}", :allowed => allowed
|
153
|
-
}
|
268
|
+
@procs << proc do
|
269
|
+
[::Merb::Router.matcher, ::Merb::Router.generator].each { |r| r.generate_singleton_routes(res, opts) }
|
270
|
+
end
|
154
271
|
end
|
155
|
-
if coll = opt[:collection]
|
156
|
-
coll.keys.sort_by{|x| "#{x}"}.each {|action|
|
157
|
-
allowed = coll[action].injecting({}) {|h, verb| h[verb] = "#{action}"}
|
158
|
-
r.add "#{opt[:prefix]}/#{res}[;/]+#{action}", :allowed => allowed
|
159
|
-
}
|
160
|
-
end
|
161
|
-
r.add "#{opt[:prefix]}/#{res}/:id\\.:format", :allowed => {:get => 'show', :put => 'update', :delete => 'destroy'}
|
162
|
-
r.add "#{opt[:prefix]}/#{res}\\.:format", :allowed => {:get => 'index', :post => 'create'}
|
163
|
-
r.add "#{opt[:prefix]}/#{res}/:id", :allowed => {:get => 'show', :put => 'update', :delete => 'destroy'}
|
164
|
-
r.add "#{opt[:prefix]}/#{res}/?", :allowed => {:get => 'index', :post => 'create'}
|
165
272
|
end
|
166
273
|
end
|
167
|
-
|
168
|
-
def self.generate_singleton_routes(res,opt)
|
169
|
-
with_options :controller => res.to_s, :rest => true do |r|
|
170
|
-
r.add "#{opt[:prefix]}/#{res}[;/]+edit", :allowed => {:get => 'edit'}
|
171
|
-
r.add "#{opt[:prefix]}/#{res}\\.:format", :allowed => {:get => 'show'}
|
172
|
-
r.add "#{opt[:prefix]}/#{res}/new" , :allowed => {:get => 'new'}
|
173
|
-
r.add "#{opt[:prefix]}/#{res}/?", :allowed => {:get => 'show', :post => 'create', :put => 'update', :delete => 'destroy'}
|
174
|
-
end
|
175
|
-
end
|
176
|
-
|
177
|
-
def self.default_routes
|
178
|
-
add "/:controller/:action/:id\\.:format"
|
179
|
-
add "/:controller/:action/:id"
|
180
|
-
add "/:controller/:action\\.:format"
|
181
|
-
add "/:controller/:action"
|
182
|
-
add "/:controller\\.:format", :action => 'index'
|
183
|
-
add "/:controller", :action => 'index'
|
184
|
-
end
|
185
274
|
end
|
186
|
-
|
187
275
|
end
|
data/lib/merb/merb_server.rb
CHANGED
@@ -91,6 +91,10 @@ module Merb
|
|
91
91
|
options[:environment] = env
|
92
92
|
end
|
93
93
|
|
94
|
+
opts.on("-r", "--script-runner ['RUBY CODE'| FULL_SCRIPT_PATH]", "Command-line option to run scripts and/or code in the merb app") do |stuff_to_run|
|
95
|
+
options[:runner] = stuff_to_run
|
96
|
+
end
|
97
|
+
|
94
98
|
opts.on("-g", "--generate-app PATH", "Generate a fresh merb app at PATH") do |path|
|
95
99
|
options[:generate] = path || Dir.pwd
|
96
100
|
end
|
@@ -102,8 +106,8 @@ module Merb
|
|
102
106
|
opts.on("-M", "--merb-config FILENAME", "This flag is for explicitly declaring the merb app's config file") do |config|
|
103
107
|
options[:merb_config] = config
|
104
108
|
end
|
105
|
-
|
106
|
-
opts.on("-X", "--mutex on/off", "This flag is for
|
109
|
+
|
110
|
+
opts.on("-X", "--mutex on/off", "This flag is for turning the mutex lock on and off.") do |mutex|
|
107
111
|
if mutex == 'off'
|
108
112
|
options[:use_mutex] = false
|
109
113
|
else
|
@@ -191,6 +195,15 @@ module Merb
|
|
191
195
|
|
192
196
|
if @@merb_opts[:console]
|
193
197
|
initialize_merb
|
198
|
+
Object.class_eval do
|
199
|
+
def show_routes
|
200
|
+
Merb::Router.generator.paths.each {|p| puts p.inspect}
|
201
|
+
nil
|
202
|
+
end
|
203
|
+
def url(path, o={})
|
204
|
+
Merb::Router.generator.generate(path,o)
|
205
|
+
end
|
206
|
+
end
|
194
207
|
ARGV.clear # Avoid passing args to IRB
|
195
208
|
require 'irb'
|
196
209
|
require 'irb/completion'
|
@@ -203,6 +216,17 @@ module Merb
|
|
203
216
|
IRB.start
|
204
217
|
exit!
|
205
218
|
end
|
219
|
+
|
220
|
+
if @@merb_opts[:runner]
|
221
|
+
initialize_merb
|
222
|
+
code_or_file = @@merb_opts[:runner]
|
223
|
+
if File.exists?(code_or_file)
|
224
|
+
eval(File.read(code_or_file))
|
225
|
+
else
|
226
|
+
eval(code_or_file)
|
227
|
+
end
|
228
|
+
exit!
|
229
|
+
end
|
206
230
|
|
207
231
|
if @@merb_opts[:start_drb]
|
208
232
|
puts "Starting merb drb server on port: #{@@merb_opts[:drb_server_port]}"
|
@@ -220,7 +244,6 @@ module Merb
|
|
220
244
|
delete_pidfiles(@@merb_opts[:port])
|
221
245
|
start(@@merb_opts[:port])
|
222
246
|
else
|
223
|
-
initialize_merb
|
224
247
|
trap('TERM') { exit }
|
225
248
|
mongrel_start(@@merb_opts[:port])
|
226
249
|
end
|
@@ -3,9 +3,22 @@ require File.dirname(__FILE__)+'/mixins/view_context_mixin'
|
|
3
3
|
require File.dirname(__FILE__)+'/mixins/form_control_mixin'
|
4
4
|
|
5
5
|
module Merb
|
6
|
-
PROTECTED_IVARS = %w[@
|
7
|
-
|
8
|
-
|
6
|
+
PROTECTED_IVARS = %w[@_new_cookie
|
7
|
+
@method
|
8
|
+
@env
|
9
|
+
@body
|
10
|
+
@_fingerprint_before
|
11
|
+
@pager
|
12
|
+
@session
|
13
|
+
@headers
|
14
|
+
@page
|
15
|
+
@cookies
|
16
|
+
@request
|
17
|
+
@status
|
18
|
+
@_view_context_cache
|
19
|
+
@response
|
20
|
+
@params]
|
21
|
+
|
9
22
|
module GlobalHelper
|
10
23
|
end
|
11
24
|
# the ViewContext is really
|
@@ -19,6 +32,7 @@ module Merb
|
|
19
32
|
include Merb::GlobalHelper
|
20
33
|
|
21
34
|
def initialize(controller)
|
35
|
+
@_merb_partial_locals = {}
|
22
36
|
@controller = controller
|
23
37
|
(@controller.instance_variables - PROTECTED_IVARS).each do |ivar|
|
24
38
|
self.instance_variable_set(ivar, @controller.instance_variable_get(ivar))
|
@@ -43,7 +57,7 @@ module Merb
|
|
43
57
|
alias_method :old_respond_to?, :respond_to?
|
44
58
|
|
45
59
|
def respond_to?(sym, include_private=false)
|
46
|
-
old_respond_to?(sym, include_private) || @controller.respond_to?(sym, include_private)
|
60
|
+
old_respond_to?(sym, include_private) || @controller.respond_to?(sym, include_private) || @_merb_partial_locals.key?(sym)
|
47
61
|
end
|
48
62
|
|
49
63
|
# catch any method calls that the controller responds to
|
@@ -51,6 +65,8 @@ module Merb
|
|
51
65
|
def method_missing(sym, *args, &blk)
|
52
66
|
if @controller.respond_to? sym
|
53
67
|
@controller.send(sym, *args, &blk)
|
68
|
+
elsif @_merb_partial_locals.key? sym
|
69
|
+
@_merb_partial_locals[sym]
|
54
70
|
else
|
55
71
|
super
|
56
72
|
end
|
@@ -18,39 +18,46 @@ module Merb
|
|
18
18
|
status = input.read(boundary_size)
|
19
19
|
raise EOFError, "bad content body" unless status == boundary + EOL
|
20
20
|
rx = /(?:#{EOL})?#{Regexp.quote(boundary,'n')}(#{EOL}|--)/
|
21
|
-
|
22
21
|
loop {
|
23
22
|
head = nil
|
24
23
|
body = ''
|
25
24
|
filename = content_type = name = nil
|
26
|
-
|
25
|
+
read_size = 0
|
27
26
|
until head && buf =~ rx
|
28
|
-
|
27
|
+
i = buf.index("\r\n\r\n")
|
28
|
+
if( i == nil && read_size == 0 && content_length == 0 )
|
29
|
+
content_length = -1
|
30
|
+
break
|
31
|
+
end
|
32
|
+
if !head && i
|
29
33
|
head = buf.slice!(0, i+2) # First \r\n
|
30
34
|
buf.slice!(0, 2) # Second \r\n
|
31
|
-
|
32
35
|
filename = head[FILENAME_REGEX, 1]
|
33
36
|
content_type = head[CONTENT_TYPE_REGEX, 1]
|
34
37
|
name = head[NAME_REGEX, 1]
|
35
38
|
|
36
|
-
if filename && !filename.empty?
|
39
|
+
if filename && !filename.empty?
|
37
40
|
body = Tempfile.new(:Merb)
|
38
41
|
body.binmode if defined? body.binmode
|
39
42
|
end
|
40
43
|
next
|
41
44
|
end
|
42
|
-
|
45
|
+
|
46
|
+
|
43
47
|
# Save the read body part.
|
44
48
|
if head && (boundary_size+4 < buf.size)
|
45
49
|
body << buf.slice!(0, buf.size - (boundary_size+4))
|
46
50
|
end
|
47
51
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
+
read_size = bufsize < content_length ? bufsize : content_length
|
53
|
+
if( read_size > 0 )
|
54
|
+
c = input.read(read_size)
|
55
|
+
raise EOFError, "bad content body" if c.nil? || c.empty?
|
56
|
+
buf << c
|
57
|
+
content_length -= c.size
|
58
|
+
end
|
52
59
|
end
|
53
|
-
|
60
|
+
|
54
61
|
# Save the rest.
|
55
62
|
if i = buf.index(rx)
|
56
63
|
body << buf.slice!(0, i)
|
@@ -58,7 +65,7 @@ module Merb
|
|
58
65
|
|
59
66
|
content_length = -1 if $1 == "--"
|
60
67
|
end
|
61
|
-
|
68
|
+
|
62
69
|
if filename && !filename.empty?
|
63
70
|
body.rewind
|
64
71
|
data = {
|
@@ -92,6 +99,10 @@ module Merb
|
|
92
99
|
parms
|
93
100
|
end
|
94
101
|
|
102
|
+
def url(path, o={})
|
103
|
+
::Merb::Router.generator.generate(path,o)
|
104
|
+
end
|
105
|
+
|
95
106
|
# parses a query string or the payload of a POST
|
96
107
|
# request into the params hash. So for example:
|
97
108
|
# /foo?bar=nik&post[title]=heya&post[body]=whatever
|
@@ -246,4 +257,4 @@ module Merb
|
|
246
257
|
end
|
247
258
|
|
248
259
|
end
|
249
|
-
end
|
260
|
+
end
|
@@ -27,6 +27,9 @@ module Merb
|
|
27
27
|
# render :nothing => 200
|
28
28
|
# renders nothing with a status of 200
|
29
29
|
#
|
30
|
+
# render :template => 'shared/message'
|
31
|
+
# renders views/shared/message
|
32
|
+
#
|
30
33
|
# render :js => "$('some-div').toggle();"
|
31
34
|
# if the right hand side of :js => is a string then the proper
|
32
35
|
# javascript headers will be set and the string will be returned
|
@@ -57,6 +60,13 @@ module Merb
|
|
57
60
|
when partial = opts[:partial]
|
58
61
|
template = find_partial(partial, opts)
|
59
62
|
opts[:layout] = :none
|
63
|
+
|
64
|
+
# Add an instance variable that can be used to create the locals in the
|
65
|
+
# partial
|
66
|
+
if opts[:locals]
|
67
|
+
@_merb_partial_locals = opts[:locals]
|
68
|
+
end
|
69
|
+
opts[:clean_context] = true
|
60
70
|
when js = opts[:js]
|
61
71
|
headers['Content-Type'] = "text/javascript"
|
62
72
|
opts[:layout] = :none
|
@@ -71,14 +81,16 @@ module Merb
|
|
71
81
|
headers['Content-Type'] = 'application/xml'
|
72
82
|
headers['Encoding'] = 'UTF-8'
|
73
83
|
return xml
|
74
|
-
|
84
|
+
when template = opts[:template]
|
85
|
+
template = find_template(:template => template)
|
86
|
+
else
|
75
87
|
template = find_template(:action => action)
|
76
88
|
end
|
77
89
|
|
78
90
|
engine = engine_for(template)
|
79
91
|
options = {
|
80
92
|
:file => template,
|
81
|
-
:view_context => (opts[:clean_context] ? clean_view_context :
|
93
|
+
:view_context => (opts[:clean_context] ? clean_view_context : cached_view_context),
|
82
94
|
:opts => opts
|
83
95
|
}
|
84
96
|
content = engine.transform(options)
|
@@ -92,7 +104,7 @@ module Merb
|
|
92
104
|
# this returns a ViewContext object populated with all
|
93
105
|
# the instance variables in your controller. This is used
|
94
106
|
# as the view context object for the Erubis templates.
|
95
|
-
def
|
107
|
+
def cached_view_context
|
96
108
|
@_view_context_cache ||= ViewContext.new(self)
|
97
109
|
end
|
98
110
|
|
@@ -129,8 +141,8 @@ module Merb
|
|
129
141
|
# you can render partials in other view directories. So
|
130
142
|
# if you create a views/shared directory then you can call
|
131
143
|
# partials that live there like partial('shared/foo')
|
132
|
-
def partial(template)
|
133
|
-
render :partial => template
|
144
|
+
def partial(template, locals={})
|
145
|
+
render :partial => template, :locals => locals
|
134
146
|
end
|
135
147
|
|
136
148
|
private
|
@@ -146,11 +158,11 @@ module Merb
|
|
146
158
|
end
|
147
159
|
end
|
148
160
|
|
149
|
-
|
161
|
+
cached_view_context.instance_variable_set('@_layout_content', content)
|
150
162
|
engine = engine_for(layout_choice)
|
151
163
|
options = {
|
152
164
|
:file => layout_choice,
|
153
|
-
:view_context =>
|
165
|
+
:view_context => cached_view_context,
|
154
166
|
:opts => opts
|
155
167
|
}
|
156
168
|
engine.transform(options)
|
@@ -158,14 +170,15 @@ module Merb
|
|
158
170
|
|
159
171
|
# OPTIMIZE : combine find_template and find_partial ?
|
160
172
|
def find_template(opts={})
|
161
|
-
if
|
162
|
-
path =
|
163
|
-
|
173
|
+
if template = opts[:template]
|
174
|
+
path = _template_root / template
|
175
|
+
elsif action = opts[:action]
|
176
|
+
path = _template_root / self.class.name.snake_case / action
|
164
177
|
elsif _layout = opts[:layout]
|
165
178
|
path = _layout_root / _layout
|
166
179
|
else
|
167
180
|
raise "called find_template without an :action or :layout"
|
168
|
-
end
|
181
|
+
end
|
169
182
|
extensions = [_template_extensions.keys].flatten.uniq
|
170
183
|
glob = "#{path}.{#{opts[:ext] || extensions.join(',')}}"
|
171
184
|
Dir[glob].first
|
@@ -32,7 +32,7 @@ module Merb
|
|
32
32
|
%{<img src="#{opts.delete(:path) + img}" #{opts.map{|k,v| "#{k}=\"#{v}\""}.join(' ')} />}
|
33
33
|
end
|
34
34
|
|
35
|
-
# calls .to_json on data.
|
35
|
+
# calls .to_json on data.
|
36
36
|
# so it can be faster than escape_js
|
37
37
|
def js(data)
|
38
38
|
if data.respond_to? :to_json
|
data/lib/merb/template/haml.rb
CHANGED
@@ -27,9 +27,6 @@ module Merb
|
|
27
27
|
|
28
28
|
class << self
|
29
29
|
|
30
|
-
class_inheritable_accessor :haml_options
|
31
|
-
self.haml_options = { :locals => {} }
|
32
|
-
|
33
30
|
@@hamls ||= {}
|
34
31
|
@@mtimes ||= {}
|
35
32
|
|
@@ -39,16 +36,14 @@ module Merb
|
|
39
36
|
|
40
37
|
def transform(options = {})
|
41
38
|
opts, file, view_context = options.values_at(:opts, :file, :view_context)
|
42
|
-
|
39
|
+
|
43
40
|
begin
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
end
|
51
|
-
|
41
|
+
locals_code = build_locals(opts[:locals])
|
42
|
+
opts[:locals] = {}
|
43
|
+
|
44
|
+
template = load_template(file)
|
45
|
+
|
46
|
+
haml = ::Haml::Engine.new("#{locals_code}#{template}", opts)
|
52
47
|
haml.to_html(view_context)
|
53
48
|
rescue
|
54
49
|
# ::Haml::Engine often inserts a bogus "(haml):#{line_number}" entry in the backtrace.
|
@@ -59,26 +54,37 @@ module Merb
|
|
59
54
|
end
|
60
55
|
|
61
56
|
private
|
57
|
+
def load_template(file)
|
58
|
+
template = ""
|
59
|
+
if @@hamls[file] && !cache_template?(file)
|
60
|
+
template = @@hamls[file]
|
61
|
+
else
|
62
|
+
template = IO.read(file)
|
63
|
+
if cache_template?(file)
|
64
|
+
@@hamls[file], @@mtimes[file] = template, Time.now
|
65
|
+
end
|
66
|
+
return template
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def build_locals(locals)
|
71
|
+
locals_code = ""
|
72
|
+
if locals
|
73
|
+
locals_code = locals.keys.inject("") do |code, key|
|
74
|
+
code << "- #{key} = @_merb_partial_locals[:#{key}]\n"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
62
78
|
|
63
|
-
|
64
|
-
|
65
|
-
return @@hamls[path]
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
def set_precompiled(path, precompiled)
|
70
|
-
@@hamls[path], @@mtimes[path] = precompiled, Time.now
|
71
|
-
end
|
72
|
-
|
73
|
-
def cache_template?(path)
|
74
|
-
return false unless ::Merb::Server.config[:cache_templates]
|
75
|
-
return true unless @@hamls[path]
|
76
|
-
@@mtimes[path] < File.mtime(path) ||
|
77
|
-
(File.symlink?(path) && (@@mtimes[path] < File.lstat(path).mtime))
|
78
|
-
end
|
79
|
+
def cache_template?(path)
|
80
|
+
return false unless ::Merb::Server.config[:cache_templates]
|
81
|
+
return true unless @@hamls[path]
|
82
|
+
@@mtimes[path] < File.mtime(path) ||
|
83
|
+
(File.symlink?(path) && (@@mtimes[path] < File.lstat(path).mtime))
|
84
|
+
end
|
79
85
|
|
80
86
|
end
|
81
87
|
|
82
88
|
end
|
83
89
|
end
|
84
|
-
end
|
90
|
+
end
|
@@ -33,8 +33,15 @@ module Merb
|
|
33
33
|
# mab is just ruby, there's no two phase compile and run
|
34
34
|
def transform(options = {})
|
35
35
|
opts, file, view_context = options.values_at(:opts, :file, :view_context)
|
36
|
+
|
37
|
+
if opts[:locals]
|
38
|
+
locals = opts[:locals].keys.inject("") do |code, key|
|
39
|
+
code << "#{key} = @_merb_partial_locals[:#{key}]\n"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
36
43
|
mab = ::Markaby::Builder.new({}, view_context) {
|
37
|
-
instance_eval(File.read(file))
|
44
|
+
instance_eval("#{locals}#{File.read(file)}")
|
38
45
|
}
|
39
46
|
mab.to_s
|
40
47
|
end
|
data/lib/tasks/merb.rake
CHANGED
@@ -21,4 +21,12 @@ namespace :merb do
|
|
21
21
|
puts " - #{MERB_ROOT / 'dist/framework'} (recursive) "
|
22
22
|
puts " - #{MERB_ROOT / 'script/merb'}"
|
23
23
|
end
|
24
|
+
|
25
|
+
desc "freeze the merb framework from svn"
|
26
|
+
task :freeze_from_svn do
|
27
|
+
puts "Freezing Merb Framework from svn"
|
28
|
+
FileUtils.rm_rf MERB_ROOT / 'dist/framework'
|
29
|
+
system "svn co http://svn.devjavu.com/merb/trunk/lib #{MERB_ROOT / 'dist/framework'}"
|
30
|
+
end
|
31
|
+
|
24
32
|
end
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.2
|
|
3
3
|
specification_version: 1
|
4
4
|
name: merb
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.3.
|
7
|
-
date: 2007-
|
6
|
+
version: 0.3.3
|
7
|
+
date: 2007-05-31 00:00:00 -07:00
|
8
8
|
summary: Merb == Mongrel + Erb. Pocket rocket web framework.
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -175,3 +175,30 @@ dependencies:
|
|
175
175
|
- !ruby/object:Gem::Version
|
176
176
|
version: 0.0.0
|
177
177
|
version:
|
178
|
+
- !ruby/object:Gem::Dependency
|
179
|
+
name: mime-types
|
180
|
+
version_requirement:
|
181
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
182
|
+
requirements:
|
183
|
+
- - ">"
|
184
|
+
- !ruby/object:Gem::Version
|
185
|
+
version: 0.0.0
|
186
|
+
version:
|
187
|
+
- !ruby/object:Gem::Dependency
|
188
|
+
name: xml-simple
|
189
|
+
version_requirement:
|
190
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
191
|
+
requirements:
|
192
|
+
- - ">"
|
193
|
+
- !ruby/object:Gem::Version
|
194
|
+
version: 0.0.0
|
195
|
+
version:
|
196
|
+
- !ruby/object:Gem::Dependency
|
197
|
+
name: archive-tar-minitar
|
198
|
+
version_requirement:
|
199
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
200
|
+
requirements:
|
201
|
+
- - ">"
|
202
|
+
- !ruby/object:Gem::Version
|
203
|
+
version: 0.0.0
|
204
|
+
version:
|