merb 0.3.4 → 0.3.7
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 +206 -197
- data/Rakefile +12 -21
- data/bin/merb +1 -1
- data/examples/skeleton/Rakefile +6 -20
- data/examples/skeleton/dist/app/mailers/layout/application.erb +1 -0
- data/examples/skeleton/dist/conf/database.yml +23 -0
- data/examples/skeleton/dist/conf/environments/development.rb +1 -0
- data/examples/skeleton/dist/conf/environments/production.rb +1 -0
- data/examples/skeleton/dist/conf/environments/test.rb +1 -0
- data/examples/skeleton/dist/conf/merb.yml +32 -28
- data/examples/skeleton/dist/conf/merb_init.rb +16 -13
- data/examples/skeleton/dist/conf/router.rb +9 -9
- data/examples/skeleton/dist/schema/migrations/001_add_sessions_table.rb +2 -2
- data/lib/merb.rb +23 -18
- data/lib/merb/caching/fragment_cache.rb +3 -7
- data/lib/merb/caching/store/memcache.rb +20 -0
- data/lib/merb/core_ext/merb_array.rb +0 -0
- data/lib/merb/core_ext/merb_class.rb +44 -4
- data/lib/merb/core_ext/merb_enumerable.rb +43 -1
- data/lib/merb/core_ext/merb_hash.rb +200 -122
- data/lib/merb/core_ext/merb_kernel.rb +2 -0
- data/lib/merb/core_ext/merb_module.rb +41 -0
- data/lib/merb/core_ext/merb_numeric.rb +57 -5
- data/lib/merb/core_ext/merb_object.rb +172 -6
- data/lib/merb/generators/merb_app/merb_app.rb +15 -9
- data/lib/merb/merb_abstract_controller.rb +193 -0
- data/lib/merb/merb_constants.rb +26 -1
- data/lib/merb/merb_controller.rb +143 -234
- data/lib/merb/merb_dispatcher.rb +28 -20
- data/lib/merb/merb_drb_server.rb +2 -3
- data/lib/merb/merb_exceptions.rb +194 -49
- data/lib/merb/merb_handler.rb +34 -26
- data/lib/merb/merb_mail_controller.rb +200 -0
- data/lib/merb/merb_mailer.rb +33 -13
- data/lib/merb/merb_part_controller.rb +42 -0
- data/lib/merb/merb_plugins.rb +293 -0
- data/lib/merb/merb_request.rb +6 -4
- data/lib/merb/merb_router.rb +99 -65
- data/lib/merb/merb_server.rb +65 -21
- data/lib/merb/merb_upload_handler.rb +2 -1
- data/lib/merb/merb_view_context.rb +36 -15
- data/lib/merb/mixins/basic_authentication_mixin.rb +5 -5
- data/lib/merb/mixins/controller_mixin.rb +67 -28
- data/lib/merb/mixins/erubis_capture_mixin.rb +1 -8
- data/lib/merb/mixins/form_control_mixin.rb +280 -42
- data/lib/merb/mixins/render_mixin.rb +127 -45
- data/lib/merb/mixins/responder_mixin.rb +5 -7
- data/lib/merb/mixins/view_context_mixin.rb +260 -94
- data/lib/merb/session.rb +23 -0
- data/lib/merb/session/merb_ar_session.rb +28 -16
- data/lib/merb/session/merb_mem_cache_session.rb +108 -0
- data/lib/merb/session/merb_memory_session.rb +65 -20
- data/lib/merb/template/erubis.rb +22 -13
- data/lib/merb/template/haml.rb +5 -16
- data/lib/merb/template/markaby.rb +5 -3
- data/lib/merb/template/xml_builder.rb +17 -5
- data/lib/merb/test/merb_fake_request.rb +63 -0
- data/lib/merb/test/merb_multipart.rb +58 -0
- data/lib/tasks/db.rake +2 -0
- data/lib/tasks/merb.rake +20 -8
- metadata +24 -25
- data/examples/skeleton.tar +0 -0
@@ -1,7 +1,49 @@
|
|
1
1
|
module Enumerable
|
2
|
+
|
3
|
+
# Abstract the common pattern of injecting a hash into a block to accumulate
|
4
|
+
# and return the injected hash.
|
5
|
+
#
|
6
|
+
# Both of these are equivalent
|
7
|
+
# [1,2,3].inject({}){|m,i| m[i] = i; m }
|
8
|
+
# [1,2,3].injecting({}){|m,i| m[i] = i }
|
9
|
+
# => {1=>1, 2=>2, 3=>3}
|
10
|
+
#
|
11
|
+
# The main difference is with injecting you do not have to end the block
|
12
|
+
# with ;m to return the accumulated hash m. In this sense it is very much
|
13
|
+
# like Object#returning
|
2
14
|
def injecting(s)
|
3
15
|
inject(s) do |k, i|
|
4
16
|
yield(k, i); k
|
5
17
|
end
|
6
18
|
end
|
7
|
-
|
19
|
+
|
20
|
+
# Look for any of an array of things inside another array (or any Enumerable).
|
21
|
+
#
|
22
|
+
# ['louie', 'bert'].include_any?('louie', 'chicken')
|
23
|
+
# => true
|
24
|
+
def include_any?(*args)
|
25
|
+
args.any? {|arg| self.include?(arg) }
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
#
|
30
|
+
# Returns a hash, which keys are evaluated result from the
|
31
|
+
# block, and values are arrays of elements in <i>enum</i>
|
32
|
+
# corresponding to the key.
|
33
|
+
#
|
34
|
+
# (1..6).group_by {|i| i%3} #=> {0=>[3, 6], 1=>[1, 4], 2=>[2, 5]}
|
35
|
+
#
|
36
|
+
# This is included in Ruby 1.9
|
37
|
+
# http://www.ruby-doc.org/core-1.9/classes/Enumerable.html#M002672
|
38
|
+
#
|
39
|
+
# Implementation from Ruby on Rails:
|
40
|
+
# trunk/activesupport/lib/active_support/core_ext/enumerable.rb
|
41
|
+
# [rev 5334]
|
42
|
+
#
|
43
|
+
def group_by
|
44
|
+
inject({}) do |groups, element|
|
45
|
+
(groups[yield(element)] ||= []) << element
|
46
|
+
groups
|
47
|
+
end
|
48
|
+
end if RUBY_VERSION < '1.9'
|
49
|
+
end
|
@@ -1,76 +1,73 @@
|
|
1
|
+
require 'hpricot'
|
2
|
+
|
1
3
|
class Hash
|
2
4
|
|
3
5
|
class << self
|
4
|
-
|
5
|
-
#
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
6
|
+
# Converts valid XML into a Ruby Hash structure.
|
7
|
+
# <tt>xml</tt>:: A string representation of valid XML
|
8
|
+
#
|
9
|
+
# == Typecasting
|
10
|
+
# Typecasting is performed on elements that have a "<tt>type</tt>" attribute of
|
11
|
+
# <tt>integer</tt>::
|
12
|
+
# <tt>boolean</tt>:: anything other than "true" evaluates to false
|
13
|
+
# <tt>datetime</tt>:: Returns a Time object. See +Time+ documentation for valid Time strings
|
14
|
+
# <tt>date</tt>:: Returns a Date object. See +Date+ documentation for valid Date strings
|
15
|
+
#
|
16
|
+
# Keys are automatically converted to +snake_case+
|
17
|
+
#
|
18
|
+
# == Caveats
|
19
|
+
# * Mixed content tags are assumed to be text and any xml tags are kept as a String
|
20
|
+
# * Any attributes other than type on a node containing a text node will be discarded
|
21
|
+
#
|
22
|
+
# == Examples
|
23
|
+
#
|
24
|
+
# ===Standard
|
25
|
+
# <user gender='m'>
|
26
|
+
# <age type='integer'>35</age>
|
27
|
+
# <name>Home Simpson</name>
|
28
|
+
# <dob type='date'>1988-01-01</dob>
|
29
|
+
# <joined-at type='datetime'>2000-04-28 23:01</joined-at>
|
30
|
+
# <is-cool type='boolean'>true</is-cool>
|
31
|
+
# </user>
|
32
|
+
#
|
33
|
+
# evaluates to
|
34
|
+
#
|
35
|
+
# { "user" =>
|
36
|
+
# { "gender" => "m",
|
37
|
+
# "age" => 35,
|
38
|
+
# "name" => "Home Simpson",
|
39
|
+
# "dob" => DateObject( 1998-01-01 ),
|
40
|
+
# "joined_at" => TimeObject( 2000-04-28 23:01),
|
41
|
+
# "is_cool" => true
|
42
|
+
# }
|
43
|
+
# }
|
44
|
+
#
|
45
|
+
# === Mixed Content
|
46
|
+
# <story>
|
47
|
+
# A Quick <em>brown</em> Fox
|
48
|
+
# </story>
|
49
|
+
#
|
50
|
+
# evaluates to
|
51
|
+
# { "story" => "A Quick <em>brown</em> Fox" }
|
52
|
+
#
|
53
|
+
# === Attributes other than type on a node containing text
|
54
|
+
# <story is-good='fasle'>
|
55
|
+
# A Quick <em>brown</em> Fox
|
56
|
+
# </story>
|
57
|
+
#
|
58
|
+
# evaluates to
|
59
|
+
# { "story" => "A Quick <em>brown</em> Fox" }
|
60
|
+
#
|
61
|
+
# <bicep unit='inches' type='integer'>60</bicep>
|
62
|
+
#
|
63
|
+
# evaluates with a typecast to an integer. But ignores the unit attribute
|
64
|
+
# { "bicep" => 60 }
|
65
|
+
|
66
|
+
def from_xml( xml )
|
67
|
+
ToHashParser.from_xml(xml)
|
45
68
|
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
69
|
end
|
69
70
|
|
70
|
-
|
71
|
-
def with_indifferent_access
|
72
|
-
MerbHash.new(self)
|
73
|
-
end
|
74
71
|
def to_params
|
75
72
|
result = ''
|
76
73
|
stack = []
|
@@ -94,88 +91,169 @@ class Hash
|
|
94
91
|
# lets through the keys in the argument
|
95
92
|
# >> {:one => 1, :two => 2, :three => 3}.pass(:one)
|
96
93
|
# => {:one=>1}
|
97
|
-
def pass(*
|
98
|
-
self.reject { |k,v| !
|
94
|
+
def pass(*allowed)
|
95
|
+
self.reject { |k,v| ! allowed.include?(k) }
|
99
96
|
end
|
100
97
|
alias only pass
|
101
98
|
|
102
99
|
# blocks the keys in the arguments
|
103
100
|
# >> {:one => 1, :two => 2, :three => 3}.block(:one)
|
104
101
|
# => {:two=>2, :three=>3}
|
105
|
-
def block(*
|
106
|
-
self.reject { |k,v|
|
102
|
+
def block(*rejected)
|
103
|
+
self.reject { |k,v| rejected.include?(k) }
|
107
104
|
end
|
108
105
|
alias except block
|
109
|
-
end
|
110
106
|
|
111
|
-
|
112
|
-
|
113
|
-
def
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
107
|
+
|
108
|
+
# Destructively convert all keys to symbols recursively.
|
109
|
+
def symbolize_keys!
|
110
|
+
keys.each do |key|
|
111
|
+
unless key.is_a?(Symbol)
|
112
|
+
self[key.to_sym] = self[key]
|
113
|
+
delete(key)
|
114
|
+
end
|
115
|
+
if Hash === (sub = self[key.to_sym])
|
116
|
+
sub.symbolize_keys!
|
117
|
+
end
|
119
118
|
end
|
119
|
+
self
|
120
120
|
end
|
121
|
-
|
122
|
-
def default(key)
|
123
|
-
self[key.to_s] if key.is_a?(Symbol)
|
124
|
-
end
|
125
|
-
|
126
|
-
alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
|
127
|
-
alias_method :regular_update, :update unless method_defined?(:regular_update)
|
128
|
-
alias_method :u, :regular_update
|
129
121
|
|
130
|
-
def
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
def update(other_hash)
|
135
|
-
other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) }
|
122
|
+
def environmentize_keys!
|
123
|
+
self.each do |key, value|
|
124
|
+
self[key.to_s.upcase] = delete(key)
|
125
|
+
end
|
136
126
|
self
|
137
127
|
end
|
128
|
+
|
129
|
+
def method_missing(m,*a)
|
130
|
+
m.to_s =~ /=$/ ? self[$`]=a[0] : a==[] ? self[m] : raise(NoMethodError,"#{m}")
|
131
|
+
end
|
132
|
+
|
133
|
+
def respond_to?(method, include_private=false)
|
134
|
+
return true if keys.include?(method)
|
135
|
+
super(method, include_private)
|
136
|
+
end
|
138
137
|
|
139
|
-
|
138
|
+
end
|
139
|
+
|
140
|
+
require 'rexml/parsers/streamparser'
|
141
|
+
require 'rexml/parsers/baseparser'
|
142
|
+
require 'rexml/light/node'
|
140
143
|
|
141
|
-
|
142
|
-
|
144
|
+
# This is a slighly modified version of the XMLUtilityNode from
|
145
|
+
# http://merb.devjavu.com/projects/merb/ticket/95 (has.sox@gmail.com)
|
146
|
+
# It's mainly just adding vowels, as I ht cd wth n vwls :)
|
147
|
+
# This represents the hard part of the work, all I did was change the underlying
|
148
|
+
# parser
|
149
|
+
class REXMLUtilityNode # :nodoc:
|
150
|
+
attr_accessor :name, :attributes, :children
|
151
|
+
|
152
|
+
def initialize(name, attributes = {})
|
153
|
+
@name = name.tr("-", "_")
|
154
|
+
@attributes = undasherize_keys(attributes)
|
155
|
+
@children = []
|
156
|
+
@text = false
|
143
157
|
end
|
158
|
+
|
159
|
+
def add_node(node)
|
160
|
+
@text = true if node.is_a? String
|
161
|
+
@children << node
|
162
|
+
end
|
163
|
+
|
164
|
+
def to_hash
|
165
|
+
if @text
|
166
|
+
return { name => typecast_value( translate_xml_entities( inner_html ) ) }
|
167
|
+
else
|
168
|
+
#change repeating groups into an array
|
169
|
+
# group by the first key of each element of the array to find repeating groups
|
170
|
+
groups = @children.group_by{ |c| c.name }
|
144
171
|
|
145
|
-
|
146
|
-
|
147
|
-
|
172
|
+
hash = {}
|
173
|
+
groups.each do |key, values|
|
174
|
+
if values.size == 1
|
175
|
+
hash.merge!( values.first )
|
176
|
+
else
|
177
|
+
hash.merge!( key => values.map{ |element| element.to_hash[key] } )
|
178
|
+
end
|
179
|
+
end
|
148
180
|
|
149
|
-
|
150
|
-
|
181
|
+
# merge the arrays, including attributes
|
182
|
+
hash.merge!( attributes ) unless attributes.empty?
|
183
|
+
return { name => hash }
|
184
|
+
end
|
151
185
|
end
|
152
186
|
|
153
|
-
def
|
154
|
-
|
187
|
+
def to_s
|
188
|
+
self.to_html
|
155
189
|
end
|
156
190
|
|
157
|
-
|
158
|
-
|
191
|
+
|
192
|
+
def typecast_value(value)
|
193
|
+
return value unless attributes["type"]
|
194
|
+
|
195
|
+
case attributes["type"]
|
196
|
+
when "integer" then value.to_i
|
197
|
+
when "boolean" then value.strip == "true"
|
198
|
+
when "datetime" then ::Time.parse(value).utc
|
199
|
+
when "date" then ::Date.parse(value)
|
200
|
+
else value
|
201
|
+
end
|
159
202
|
end
|
160
|
-
|
161
|
-
def
|
162
|
-
|
203
|
+
|
204
|
+
def translate_xml_entities(value)
|
205
|
+
value.gsub(/</, "<").
|
206
|
+
gsub(/>/, ">").
|
207
|
+
gsub(/"/, '"').
|
208
|
+
gsub(/'/, "'").
|
209
|
+
gsub(/&/, "&")
|
163
210
|
end
|
164
211
|
|
165
|
-
|
166
|
-
|
212
|
+
def undasherize_keys(params)
|
213
|
+
params.keys.each do |key, vvalue|
|
214
|
+
params[key.tr("-", "_")] = params.delete(key)
|
215
|
+
end
|
216
|
+
params
|
167
217
|
end
|
168
218
|
|
169
|
-
|
170
|
-
|
171
|
-
m.to_s =~ /=$/?self[$`]=a[0]:a==[]?self[m]:raise(NoMethodError,"#{m}")
|
219
|
+
def inner_html
|
220
|
+
@children.join
|
172
221
|
end
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
222
|
+
|
223
|
+
def to_html
|
224
|
+
"<#{name}#{attributes_to_xml}>#{inner_html}</#{name}>"
|
225
|
+
end
|
226
|
+
|
227
|
+
def attributes_to_xml
|
228
|
+
attributes.keys.map do |key|
|
229
|
+
"#{key}='#{attributes[key]}'"
|
230
|
+
end.join
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
class ToHashParser # :nodoc:
|
235
|
+
def self.from_xml(xml)
|
236
|
+
stack = []
|
237
|
+
parser = REXML::Parsers::BaseParser.new(xml)
|
238
|
+
|
239
|
+
while true
|
240
|
+
event = parser.pull
|
241
|
+
case event[0]
|
242
|
+
when :end_document
|
243
|
+
break
|
244
|
+
when :end_doctype, :start_doctype
|
245
|
+
# do nothing
|
246
|
+
when :start_element
|
247
|
+
stack.push REXMLUtilityNode.new(event[1], event[2])
|
248
|
+
when :end_element
|
249
|
+
if stack.size > 1
|
250
|
+
temp = stack.pop
|
251
|
+
stack.last.add_node(temp)
|
252
|
+
end
|
253
|
+
when :text
|
254
|
+
stack.last.add_node(event[1]) unless event[1].strip.length == 0
|
255
|
+
end
|
256
|
+
end
|
257
|
+
stack.pop.to_hash
|
258
|
+
end
|
181
259
|
end
|
@@ -1,5 +1,26 @@
|
|
1
1
|
class Module
|
2
2
|
|
3
|
+
# alias_method_chain :foo, :bar produces the following pattern:
|
4
|
+
#
|
5
|
+
# alias_method :foo_without_bar, :foo
|
6
|
+
# alias_method :foo, :foo_with_bar
|
7
|
+
#
|
8
|
+
# You will then need to write the foo_with_bar method, which will
|
9
|
+
# be able to reference foo_without_bar:
|
10
|
+
#
|
11
|
+
# def foo_with_bar
|
12
|
+
# foo_without_bar.map {|x| x + 1 }
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# Method punctuation (foo? and foo!) will be retained:
|
16
|
+
#
|
17
|
+
# alias_method_chain :foo?, :bar will produce:
|
18
|
+
# foo_without_bar?
|
19
|
+
# foo_with_bar?
|
20
|
+
#
|
21
|
+
# alias_method_chain :foo!, :bar will produce:
|
22
|
+
# foo_without_bar!
|
23
|
+
# foo_with_bar!
|
3
24
|
def alias_method_chain(target, feature)
|
4
25
|
# Strip out punctuation on predicates or bang methods since
|
5
26
|
# e.g. target?_without_feature is not a valid method name.
|
@@ -9,6 +30,26 @@ class Module
|
|
9
30
|
alias_method target, "#{aliased_target}_with_#{feature}#{punctuation}"
|
10
31
|
end
|
11
32
|
|
33
|
+
# defines a series of instance variables to be initialized when
|
34
|
+
# the class is initialized.
|
35
|
+
#
|
36
|
+
# For instance, you could create the class Foo:
|
37
|
+
#
|
38
|
+
# class Foo
|
39
|
+
# attr_initialize :bar, :baz
|
40
|
+
# attr_accessor :bar, :baz
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# and initialize an instance of the class as follows:
|
44
|
+
#
|
45
|
+
# Foo.new(1, 2)
|
46
|
+
#
|
47
|
+
# which will create a new Foo object with #bar equal to 1 and #baz
|
48
|
+
# equal to 2.
|
49
|
+
#
|
50
|
+
# Passing a different number of arguments to <tt>new</tt> than you
|
51
|
+
# passed to attr_initialize will result in an ArgumentError being
|
52
|
+
# thrown.
|
12
53
|
def attr_initialize(*attrs)
|
13
54
|
define_method(:initialize) do |*passed|
|
14
55
|
raise ArgumentError, "Wrong number of arguments" \
|