pakyow-core 0.7.2 → 0.8rc1
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/pakyow-core/bin/pakyow +7 -6
- data/pakyow-core/lib/commands/USAGE +2 -1
- data/pakyow-core/lib/commands/console.rb +1 -1
- data/pakyow-core/lib/commands/server.rb +1 -1
- data/pakyow-core/lib/core/application.rb +129 -404
- data/pakyow-core/lib/core/base.rb +16 -4
- data/pakyow-core/lib/core/configuration/app.rb +6 -2
- data/pakyow-core/lib/core/configuration/server.rb +6 -1
- data/pakyow-core/lib/core/fn_context.rb +5 -0
- data/pakyow-core/lib/core/helpers.rb +16 -0
- data/pakyow-core/lib/core/loader.rb +1 -2
- data/pakyow-core/lib/core/log.rb +13 -9
- data/pakyow-core/lib/core/middleware/logger.rb +37 -0
- data/pakyow-core/lib/core/middleware/not_found.rb +40 -0
- data/pakyow-core/lib/core/middleware/presenter.rb +25 -0
- data/pakyow-core/lib/core/middleware/reloader.rb +14 -0
- data/pakyow-core/lib/core/middleware/router.rb +33 -0
- data/pakyow-core/lib/core/middleware/setup.rb +15 -0
- data/pakyow-core/lib/core/middleware/static.rb +27 -0
- data/pakyow-core/lib/core/presenter_base.rb +4 -0
- data/pakyow-core/lib/core/request.rb +43 -15
- data/pakyow-core/lib/core/response.rb +6 -0
- data/pakyow-core/lib/core/route_lookup.rb +37 -0
- data/pakyow-core/lib/core/route_set.rb +260 -0
- data/pakyow-core/lib/core/route_template.rb +77 -0
- data/pakyow-core/lib/core/route_template_defaults.rb +29 -0
- data/pakyow-core/lib/core/router.rb +156 -0
- data/pakyow-core/lib/generators/pakyow/app/app_generator.rb +12 -2
- data/pakyow-core/lib/generators/pakyow/app/templates/app.rb +12 -0
- data/pakyow-core/lib/generators/pakyow/app/templates/config.ru +1 -1
- data/pakyow-core/lib/generators/pakyow/app/templates/rakefile +1 -1
- data/pakyow-core/lib/generators/pakyow/app/templates/{app/views → views}/main.html +0 -0
- data/pakyow-core/lib/generators/pakyow/app/templates/{app/views → views}/pakyow.html +1 -1
- data/pakyow-core/lib/utils/string.rb +11 -10
- metadata +61 -71
- data/pakyow-core/lib/core/logger.rb +0 -33
- data/pakyow-core/lib/core/reloader.rb +0 -12
- data/pakyow-core/lib/core/route_store.rb +0 -220
- data/pakyow-core/lib/core/static.rb +0 -25
- data/pakyow-core/lib/generators/pakyow/app/templates/app/lib/application_controller.rb +0 -6
- data/pakyow-core/lib/generators/pakyow/app/templates/config/application.rb +0 -18
- data/pakyow-core/lib/generators/pakyow/app/templates/logs/requests.log +0 -0
@@ -0,0 +1,260 @@
|
|
1
|
+
module Pakyow
|
2
|
+
class RouteSet
|
3
|
+
def initialize
|
4
|
+
@routes = {:get => [], :post => [], :put => [], :delete => []}
|
5
|
+
|
6
|
+
@routes_by_name = {}
|
7
|
+
@grouped_routes_by_name = {}
|
8
|
+
|
9
|
+
@fns = {}
|
10
|
+
@groups = {}
|
11
|
+
|
12
|
+
@templates = {}
|
13
|
+
|
14
|
+
@handlers = []
|
15
|
+
|
16
|
+
@scope = {:name => nil, :path => '/', :hooks => {:before => [], :after => []}}
|
17
|
+
end
|
18
|
+
|
19
|
+
# Creates or retreives a named route function. When retrieving,
|
20
|
+
#
|
21
|
+
def fn(name, &block)
|
22
|
+
@fns[name] = block and return if block
|
23
|
+
|
24
|
+
#TODO rewrite to not return array
|
25
|
+
[@fns[name]]
|
26
|
+
end
|
27
|
+
|
28
|
+
def default(*args, &block)
|
29
|
+
self.register_route(:get, '/', *args, &block)
|
30
|
+
end
|
31
|
+
|
32
|
+
def get(*args, &block)
|
33
|
+
self.register_route(:get, *args, &block)
|
34
|
+
end
|
35
|
+
|
36
|
+
def put(*args, &block)
|
37
|
+
self.register_route(:put, *args, &block)
|
38
|
+
end
|
39
|
+
|
40
|
+
def post(*args, &block)
|
41
|
+
self.register_route(:post, *args, &block)
|
42
|
+
end
|
43
|
+
|
44
|
+
def delete(*args, &block)
|
45
|
+
self.register_route(:delete, *args, &block)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Returns a lambda that routes request to a controller/action.
|
49
|
+
#TODO move to RouteHelpers
|
50
|
+
def call(controller, action)
|
51
|
+
lambda {
|
52
|
+
controller = Object.const_get(controller)
|
53
|
+
action ||= Configuration::Base.app.default_action
|
54
|
+
|
55
|
+
instance = controller.new
|
56
|
+
request.controller = instance
|
57
|
+
request.action = action
|
58
|
+
|
59
|
+
instance.send(action)
|
60
|
+
}
|
61
|
+
end
|
62
|
+
|
63
|
+
# Creates a handler.
|
64
|
+
#
|
65
|
+
def handler(name, *args, &block)
|
66
|
+
code, fn = args
|
67
|
+
|
68
|
+
fn = code and code = nil if code.is_a?(Proc)
|
69
|
+
fn = block if block_given?
|
70
|
+
|
71
|
+
@handlers << [name, code, [fn]]
|
72
|
+
end
|
73
|
+
|
74
|
+
def group(name, *args, &block)
|
75
|
+
# deep clone existing hooks to reset at the close of this group
|
76
|
+
original_hooks = Marshal.load(Marshal.dump(@scope[:hooks]))
|
77
|
+
|
78
|
+
@scope[:hooks] = self.merge_hooks(@scope[:hooks], args[0]) if @scope[:hooks] && args[0]
|
79
|
+
|
80
|
+
@scope[:name] = name
|
81
|
+
@groups[name] = []
|
82
|
+
@grouped_routes_by_name[name] = {}
|
83
|
+
|
84
|
+
self.instance_exec(&block)
|
85
|
+
@scope[:name] = nil
|
86
|
+
|
87
|
+
@scope[:hooks] = original_hooks
|
88
|
+
end
|
89
|
+
|
90
|
+
def namespace(path, *args, &block)
|
91
|
+
name, hooks = args
|
92
|
+
hooks = name if name.is_a?(Hash)
|
93
|
+
|
94
|
+
original_path = @scope[:path]
|
95
|
+
@scope[:path] = File.join(@scope[:path], path)
|
96
|
+
|
97
|
+
self.group(name, hooks || {}, &block)
|
98
|
+
@scope[:path] = original_path
|
99
|
+
end
|
100
|
+
|
101
|
+
def template(name, &block)
|
102
|
+
@templates[name] = block
|
103
|
+
end
|
104
|
+
|
105
|
+
def expand(t_name, g_name, path = nil, data = nil, &block)
|
106
|
+
data = path and path = nil if path.is_a?(Hash)
|
107
|
+
|
108
|
+
# evaluate block in context of some class that implements
|
109
|
+
# method_missing to store map of functions
|
110
|
+
# (e.g. index, show)
|
111
|
+
t = RouteTemplate.new(block, g_name, path, self)
|
112
|
+
|
113
|
+
# evaluate template in same context, where func looks up funcs
|
114
|
+
# from map and extends get (and others) to add proper names
|
115
|
+
t.expand(@templates[t_name], data)
|
116
|
+
end
|
117
|
+
|
118
|
+
# Returns a route tuple:
|
119
|
+
# [regex, vars, name, fns, path]
|
120
|
+
#
|
121
|
+
def match(path, method)
|
122
|
+
path = StringUtils.normalize_path(path)
|
123
|
+
|
124
|
+
@routes[method.to_sym].each{|r|
|
125
|
+
case r[0]
|
126
|
+
when Regexp
|
127
|
+
if data = r[0].match(path)
|
128
|
+
return r, data
|
129
|
+
end
|
130
|
+
when String
|
131
|
+
if r[0] == path
|
132
|
+
return r, nil
|
133
|
+
end
|
134
|
+
end
|
135
|
+
}
|
136
|
+
|
137
|
+
nil
|
138
|
+
end
|
139
|
+
|
140
|
+
def handle(name_or_code)
|
141
|
+
@handlers.each{ |h|
|
142
|
+
return h if h[0] == name_or_code || h[1] == name_or_code
|
143
|
+
}
|
144
|
+
|
145
|
+
nil
|
146
|
+
end
|
147
|
+
|
148
|
+
# Name based route lookup
|
149
|
+
def route(name, group = nil)
|
150
|
+
return @routes_by_name[name] unless group
|
151
|
+
|
152
|
+
if grouped_routes = @grouped_routes_by_name[group]
|
153
|
+
grouped_routes[name]
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
protected
|
158
|
+
|
159
|
+
def merge_hooks(h1, h2)
|
160
|
+
# normalize
|
161
|
+
h1[:before] ||= []
|
162
|
+
h1[:after] ||= []
|
163
|
+
h1[:around] ||= []
|
164
|
+
h2[:before] ||= []
|
165
|
+
h2[:after] ||= []
|
166
|
+
h2[:around] ||= []
|
167
|
+
|
168
|
+
# merge
|
169
|
+
h1[:before].concat(h2[:before])
|
170
|
+
h1[:after].concat(h2[:after])
|
171
|
+
h1[:around].concat(h2[:around])
|
172
|
+
h1
|
173
|
+
end
|
174
|
+
|
175
|
+
def register_route(method, path, *args, &block)
|
176
|
+
name, fns, hooks = self.parse_route_args(args)
|
177
|
+
|
178
|
+
fns ||= []
|
179
|
+
# add passed block to fns
|
180
|
+
fns << block if block_given?
|
181
|
+
|
182
|
+
hooks = self.merge_hooks(hooks || {}, @scope[:hooks])
|
183
|
+
|
184
|
+
# build the final list of fns
|
185
|
+
fns = self.build_fns(fns, hooks)
|
186
|
+
|
187
|
+
# prepend scope path if we're in a scope
|
188
|
+
path = File.join(@scope[:path], path)
|
189
|
+
path = StringUtils.normalize_path(path)
|
190
|
+
|
191
|
+
# get regex and vars for path
|
192
|
+
regex, vars = self.build_route_matcher(path)
|
193
|
+
|
194
|
+
# create the route tuple
|
195
|
+
route = [regex, vars, name, fns, path]
|
196
|
+
|
197
|
+
@routes[method] << route
|
198
|
+
@routes_by_name[name] = route
|
199
|
+
|
200
|
+
# store group references if we're in a scope
|
201
|
+
return unless group = @scope[:name]
|
202
|
+
@groups[group] << route
|
203
|
+
@grouped_routes_by_name[group][name] = route
|
204
|
+
end
|
205
|
+
|
206
|
+
def parse_route_args(args)
|
207
|
+
ret = []
|
208
|
+
args.each { |arg|
|
209
|
+
if arg.is_a?(Hash) # we have hooks
|
210
|
+
ret[2] = arg
|
211
|
+
elsif arg.is_a?(Array) # we have fns
|
212
|
+
ret[1] = arg
|
213
|
+
elsif arg.is_a?(Proc) # we have a fn
|
214
|
+
ret[1] = [arg]
|
215
|
+
elsif !arg.nil? # we have a name
|
216
|
+
ret[0] = arg
|
217
|
+
end
|
218
|
+
}
|
219
|
+
ret
|
220
|
+
end
|
221
|
+
|
222
|
+
def build_fns(main_fns, hooks)
|
223
|
+
fns = []
|
224
|
+
fns.concat(hooks[:around]) if hooks && hooks[:around]
|
225
|
+
fns.concat(hooks[:before]) if hooks && hooks[:before]
|
226
|
+
fns.concat(main_fns) if main_fns
|
227
|
+
fns.concat(hooks[:after]) if hooks && hooks[:after]
|
228
|
+
fns.concat(hooks[:around]) if hooks && hooks[:around]
|
229
|
+
fns
|
230
|
+
end
|
231
|
+
|
232
|
+
def build_route_matcher(path)
|
233
|
+
return path, [] if path.is_a?(Regexp)
|
234
|
+
|
235
|
+
# check for vars
|
236
|
+
return path, [] unless path[0,1] == ':' || path.index('/:')
|
237
|
+
|
238
|
+
# we have vars
|
239
|
+
vars = []
|
240
|
+
position_counter = 1
|
241
|
+
regex_route = path
|
242
|
+
route_segments = path.split('/')
|
243
|
+
route_segments.each_with_index { |segment, i|
|
244
|
+
if segment.include?(':')
|
245
|
+
vars << { :position => position_counter, :var => segment.gsub(':', '').to_sym }
|
246
|
+
if i == route_segments.length-1 then
|
247
|
+
regex_route = regex_route.sub(segment, '((\w|[-.~:@!$\'\(\)\*\+,;])*)')
|
248
|
+
position_counter += 2
|
249
|
+
else
|
250
|
+
regex_route = regex_route.sub(segment, '((\w|[-.~:@!$\'\(\)\*\+,;])*)')
|
251
|
+
position_counter += 2
|
252
|
+
end
|
253
|
+
end
|
254
|
+
}
|
255
|
+
reg = Regexp.new("^#{regex_route}$")
|
256
|
+
return reg, vars
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
@@ -0,0 +1,77 @@
|
|
1
|
+
#TODO rename router to set and .func to .fn
|
2
|
+
|
3
|
+
module Pakyow
|
4
|
+
class RouteTemplate
|
5
|
+
attr_accessor :path
|
6
|
+
|
7
|
+
def initialize(block, g_name, path, router)
|
8
|
+
@fns = {}
|
9
|
+
@g_name = g_name
|
10
|
+
@path = path
|
11
|
+
@router = router
|
12
|
+
|
13
|
+
self.instance_exec(&block)
|
14
|
+
end
|
15
|
+
|
16
|
+
def action(method, *args, &block)
|
17
|
+
fns = block_given? ? [block] : args[0]
|
18
|
+
@fns[method] = fns
|
19
|
+
end
|
20
|
+
|
21
|
+
def expand(template, data)
|
22
|
+
@expanding = true
|
23
|
+
|
24
|
+
t = self
|
25
|
+
if @path
|
26
|
+
@router.namespace(@path, @g_name) {
|
27
|
+
t.instance_exec(data, &template)
|
28
|
+
}
|
29
|
+
else
|
30
|
+
@router.group(@g_name) {
|
31
|
+
t.instance_exec(data, &template)
|
32
|
+
}
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def fn(name)
|
37
|
+
@expanding ? @fns[name] : @router.func(name)
|
38
|
+
end
|
39
|
+
|
40
|
+
def call(controller, action)
|
41
|
+
@router.call(controller, action)
|
42
|
+
end
|
43
|
+
|
44
|
+
def get(*args, &block)
|
45
|
+
@router.get(*args, &block)
|
46
|
+
end
|
47
|
+
|
48
|
+
def put(*args, &block)
|
49
|
+
@router.put(*args, &block)
|
50
|
+
end
|
51
|
+
|
52
|
+
def post(*args, &block)
|
53
|
+
@router.post(*args, &block)
|
54
|
+
end
|
55
|
+
|
56
|
+
def delete(*args, &block)
|
57
|
+
@router.delete(*args, &block)
|
58
|
+
end
|
59
|
+
|
60
|
+
#TODO best name?
|
61
|
+
def map_actions(controller, actions)
|
62
|
+
actions.each { |a|
|
63
|
+
self.action(a, self.call(controller, a))
|
64
|
+
}
|
65
|
+
end
|
66
|
+
|
67
|
+
#TODO best name?
|
68
|
+
def map_restful_actions(controller)
|
69
|
+
self.map_actions(controller, self.restful_actions)
|
70
|
+
end
|
71
|
+
|
72
|
+
def restful_actions
|
73
|
+
[:index, :show, :new, :create, :edit, :update, :delete]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Pakyow
|
2
|
+
class RouteTemplateDefaults
|
3
|
+
def self.register
|
4
|
+
Pakyow::Router.instance.set(:default) {
|
5
|
+
template(:restful) {
|
6
|
+
get '/', :index, fn(:index)
|
7
|
+
|
8
|
+
# special case for show (view path is overridden)
|
9
|
+
if show_fns = fn(:show)
|
10
|
+
show_fns = [show_fns] unless show_fns.is_a?(Array)
|
11
|
+
get '/:id', :show, show_fns.unshift(
|
12
|
+
lambda {
|
13
|
+
presenter.view_path = File.join(self.path, 'show') if Configuration::Base.app.presenter
|
14
|
+
}
|
15
|
+
)
|
16
|
+
end
|
17
|
+
|
18
|
+
get '/new', :new, fn(:new)
|
19
|
+
post '/', :create, fn(:create)
|
20
|
+
|
21
|
+
get '/:id/edit', :edit, fn(:edit)
|
22
|
+
put '/:id', :update, fn(:update)
|
23
|
+
|
24
|
+
delete '/:id', :delete, fn(:delete)
|
25
|
+
}
|
26
|
+
}
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,156 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
module Pakyow
|
4
|
+
# A singleton that manages route sets.
|
5
|
+
#
|
6
|
+
class Router
|
7
|
+
include Singleton
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
end
|
11
|
+
|
12
|
+
def reset
|
13
|
+
@sets = {}
|
14
|
+
RouteTemplateDefaults.register
|
15
|
+
self
|
16
|
+
end
|
17
|
+
|
18
|
+
# Creates a new set (or appends to a set if it exists).
|
19
|
+
#
|
20
|
+
def set(name, &block)
|
21
|
+
@sets[name] ||= RouteSet.new
|
22
|
+
@sets[name].instance_exec(&block)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Iterates through route sets and returns the first matching route.
|
26
|
+
#
|
27
|
+
def route(name, group = nil)
|
28
|
+
@sets.each { |set|
|
29
|
+
if r = set[1].route(name, group)
|
30
|
+
return r
|
31
|
+
end
|
32
|
+
}
|
33
|
+
|
34
|
+
nil
|
35
|
+
end
|
36
|
+
|
37
|
+
# Performs the initial routing for a request.
|
38
|
+
#
|
39
|
+
def route!(request)
|
40
|
+
self.trampoline(self.match(request))
|
41
|
+
end
|
42
|
+
|
43
|
+
# Reroutes a request.
|
44
|
+
#
|
45
|
+
def reroute!(request)
|
46
|
+
throw :fns, self.match(request)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Finds and invokes a handler by name or by status code.
|
50
|
+
#
|
51
|
+
def handle!(name_or_code, from_logic = false)
|
52
|
+
@sets.each { |set|
|
53
|
+
if h = set[1].handle(name_or_code)
|
54
|
+
Pakyow.app.response.status = h[0]
|
55
|
+
from_logic ? throw(:fns, h[2]) : self.trampoline(h[2])
|
56
|
+
break
|
57
|
+
end
|
58
|
+
}
|
59
|
+
end
|
60
|
+
|
61
|
+
def routed?
|
62
|
+
@routed
|
63
|
+
end
|
64
|
+
|
65
|
+
# Looks up and populates a path with data
|
66
|
+
#
|
67
|
+
def path(name, data = nil)
|
68
|
+
RouteLookup.new.path(name, data)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Looks up a route grouping
|
72
|
+
#
|
73
|
+
def group(name)
|
74
|
+
RouteLookup.new.group(name)
|
75
|
+
end
|
76
|
+
|
77
|
+
protected
|
78
|
+
|
79
|
+
# Calls a list of route functions in order (each in a separate context).
|
80
|
+
#
|
81
|
+
def call_fns(fns)
|
82
|
+
fns.each {|fn| self.context.instance_exec(&fn)}
|
83
|
+
end
|
84
|
+
|
85
|
+
# Creates a context in which to evaluate a route function.
|
86
|
+
#
|
87
|
+
def context
|
88
|
+
FnContext.new
|
89
|
+
end
|
90
|
+
|
91
|
+
# Finds the first matching route for the request path/method and
|
92
|
+
# returns the list of route functions for that route.
|
93
|
+
#
|
94
|
+
def match(request)
|
95
|
+
path = StringUtils.normalize_path(request.working_path)
|
96
|
+
method = request.working_method
|
97
|
+
|
98
|
+
@routed = false
|
99
|
+
|
100
|
+
match, data = nil
|
101
|
+
@sets.each { |set|
|
102
|
+
match, data = set[1].match(path, method)
|
103
|
+
break if match
|
104
|
+
}
|
105
|
+
|
106
|
+
fns = []
|
107
|
+
if match
|
108
|
+
fns = match[3]
|
109
|
+
|
110
|
+
# handle route params
|
111
|
+
#TODO where to do this?
|
112
|
+
request.params.merge!(HashUtils.strhash(self.data_from_path(path, data, match[1])))
|
113
|
+
|
114
|
+
#TODO where to do this?
|
115
|
+
request.route_path = match[4]
|
116
|
+
end
|
117
|
+
|
118
|
+
fns
|
119
|
+
end
|
120
|
+
|
121
|
+
# Calls route functions and catches new functions as
|
122
|
+
# they're thrown (e.g. by reroute).
|
123
|
+
#
|
124
|
+
def trampoline(fns)
|
125
|
+
until fns.empty?
|
126
|
+
fns = catch(:fns) {
|
127
|
+
self.call_fns(fns)
|
128
|
+
|
129
|
+
# Getting here means that call() returned normally (not via a throw)
|
130
|
+
:fall_through
|
131
|
+
} # end :fns catch block
|
132
|
+
|
133
|
+
# If reroute! or invoke_handler! was called in the block, block will have a new value (nil or block).
|
134
|
+
# If neither was called, block will be :fall_through
|
135
|
+
|
136
|
+
@routed = case fns
|
137
|
+
when [] then false
|
138
|
+
when :fall_through then fns = [] and true
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
# Extracts the data from a path.
|
144
|
+
#
|
145
|
+
def data_from_path(path, matches, vars)
|
146
|
+
data = {}
|
147
|
+
return data unless matches
|
148
|
+
|
149
|
+
vars.each {|v|
|
150
|
+
data[v[:var]] = matches[v[:position]]
|
151
|
+
}
|
152
|
+
|
153
|
+
data
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
@@ -5,7 +5,8 @@ module Pakyow
|
|
5
5
|
class AppGenerator
|
6
6
|
class << self
|
7
7
|
def start
|
8
|
-
|
8
|
+
case ARGV.first
|
9
|
+
when '--help', '-h', nil
|
9
10
|
puts File.open(File.join(CORE_PATH, 'commands/USAGE-NEW')).read
|
10
11
|
else
|
11
12
|
generator = self.new
|
@@ -19,7 +20,16 @@ module Pakyow
|
|
19
20
|
end
|
20
21
|
|
21
22
|
def build(dest)
|
22
|
-
|
23
|
+
if !File.directory?(dest) || (Dir.entries(dest) - ['.', '..']).empty?
|
24
|
+
FileUtils.cp_r(@src, dest)
|
25
|
+
else
|
26
|
+
ARGV.clear
|
27
|
+
print "The folder '#{dest}' is in use. Would you like to populate it anyway? [Yn] "
|
28
|
+
|
29
|
+
if gets.chomp! == 'Y'
|
30
|
+
FileUtils.cp_r(@src, dest)
|
31
|
+
end
|
32
|
+
end
|
23
33
|
end
|
24
34
|
end
|
25
35
|
end
|
@@ -1,2 +1,2 @@
|
|
1
|
-
require File.expand_path('
|
1
|
+
require File.expand_path('application', __FILE__)
|
2
2
|
PakyowApplication::Application.stage(ENV['ENV'])
|
File without changes
|
@@ -3,16 +3,6 @@ module Pakyow
|
|
3
3
|
# Utility methods for strings.
|
4
4
|
class StringUtils
|
5
5
|
|
6
|
-
# Creates an underscored, lowercase version of a string.
|
7
|
-
# This was borrowed from another library, probably ActiveSupport.
|
8
|
-
def self.underscore(string)
|
9
|
-
string.gsub(/::/, '/').
|
10
|
-
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
11
|
-
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
12
|
-
tr("-", "_").
|
13
|
-
downcase
|
14
|
-
end
|
15
|
-
|
16
6
|
# split . seperated string at the last .
|
17
7
|
def self.split_at_last_dot(s)
|
18
8
|
split_index = s.rindex('.')
|
@@ -32,6 +22,17 @@ module Pakyow
|
|
32
22
|
return ret
|
33
23
|
end
|
34
24
|
|
25
|
+
def self.parse_path_from_caller(caller)
|
26
|
+
caller.match(/^(.+)(:?:\d+(:?:in `.+')?$)/)[1]
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.normalize_path(path)
|
30
|
+
return path if path.is_a?(Regexp)
|
31
|
+
|
32
|
+
path = path[1, path.length - 1] if path[0, 1] == '/'
|
33
|
+
path = path[0, path.length - 1] if path[path.length - 1, 1] == '/'
|
34
|
+
path
|
35
|
+
end
|
35
36
|
|
36
37
|
end
|
37
38
|
end
|