kenji 1.1.2 → 1.2.0
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.
- checksums.yaml +4 -4
- data/.hound.yml +2 -0
- data/.rubocop.yml +261 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +2 -2
- data/README.md +22 -14
- data/Rakefile +2 -2
- data/bin/kenji +24 -21
- data/kenji.gemspec +2 -2
- data/lib/kenji.rb +51 -42
- data/lib/kenji/app.rb +4 -2
- data/lib/kenji/controller.rb +42 -21
- data/lib/kenji/string-extensions.rb +25 -0
- data/lib/kenji/version.rb +1 -2
- data/spec/1/controllers/main.rb +0 -4
- data/spec/7/controllers/main.rb +8 -0
- data/spec/kenji_spec.rb +52 -15
- metadata +7 -4
- data/inited/__APP_NAME__ +0 -52
- data/lib/kenji/string_extensions.rb +0 -16
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c5fdf8390ae9d6d9eec304907eeb51dfac87e44e
|
|
4
|
+
data.tar.gz: 6d7e9930d844f336ae34af1d0bcf54073ce99a48
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 13094758c7771669041badcffff388ca387cb547bb4965c5010dd176e82cf66e9b6e50d78baa891aa0206a26044bf3e2e6ed08238544dcf58b2a6c917e2ed415
|
|
7
|
+
data.tar.gz: c2e5133ae4a52328ce095843f6e7f78757035e5f46136aaf685909c3a273822b6052f314144c6179afd4540d86f9fc7b1ef6b250dc1e5a42ef791c301419f1e8
|
data/.hound.yml
ADDED
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
# Ignore Rubocop on some files...
|
|
2
|
+
|
|
3
|
+
AllCops:
|
|
4
|
+
Exclude:
|
|
5
|
+
- 'spec/**/*'
|
|
6
|
+
|
|
7
|
+
# Enabling of disabled-by-default checks:
|
|
8
|
+
|
|
9
|
+
Style/CollectionMethods:
|
|
10
|
+
Enabled: true
|
|
11
|
+
|
|
12
|
+
Style/Encoding:
|
|
13
|
+
Enabled: true
|
|
14
|
+
|
|
15
|
+
######
|
|
16
|
+
|
|
17
|
+
# Align the elements of a hash literal if they span more than one line.
|
|
18
|
+
Style/AlignHash:
|
|
19
|
+
EnforcedHashRocketStyle: table
|
|
20
|
+
EnforcedColonStyle: table
|
|
21
|
+
|
|
22
|
+
# Align with the style guide.
|
|
23
|
+
Style/CollectionMethods:
|
|
24
|
+
# Mapping from undesired method to desired_method
|
|
25
|
+
# e.g. to use `detect` over `find`:
|
|
26
|
+
#
|
|
27
|
+
# CollectionMethods:
|
|
28
|
+
# PreferredMethods:
|
|
29
|
+
# find: detect
|
|
30
|
+
PreferredMethods:
|
|
31
|
+
collect: 'map'
|
|
32
|
+
collect!: 'map!'
|
|
33
|
+
inject: 'reduce'
|
|
34
|
+
detect: 'find'
|
|
35
|
+
find_all: 'select'
|
|
36
|
+
|
|
37
|
+
# Multi-line method chaining should be done with leading dots.
|
|
38
|
+
Style/DotPosition:
|
|
39
|
+
EnforcedStyle: leading
|
|
40
|
+
SupportedStyles:
|
|
41
|
+
- leading
|
|
42
|
+
- trailing
|
|
43
|
+
|
|
44
|
+
# Use empty lines between defs.
|
|
45
|
+
Style/EmptyLineBetweenDefs:
|
|
46
|
+
# If true, this parameter means that single line method definitions don't
|
|
47
|
+
# need an empty line between them.
|
|
48
|
+
AllowAdjacentOneLineDefs: false
|
|
49
|
+
|
|
50
|
+
Style/EmptyLinesAroundClassBody:
|
|
51
|
+
Enabled: false
|
|
52
|
+
|
|
53
|
+
Style/EmptyLinesAroundModuleBody:
|
|
54
|
+
Enabled: false
|
|
55
|
+
|
|
56
|
+
# Checks whether the source file has a utf-8 encoding comment or not
|
|
57
|
+
# AutoCorrectEncodingComment must match the regex
|
|
58
|
+
# /#.*coding\s?[:=]\s?(?:UTF|utf)-8/
|
|
59
|
+
Style/Encoding:
|
|
60
|
+
Enabled: false
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
# Checks use of for or each in multiline loops.
|
|
64
|
+
Style/For:
|
|
65
|
+
EnforcedStyle: each
|
|
66
|
+
|
|
67
|
+
# Built-in global variables are allowed by default.
|
|
68
|
+
Style/GlobalVars:
|
|
69
|
+
AllowedVariables: []
|
|
70
|
+
|
|
71
|
+
# `MinBodyLength` defines the number of lines of the a body of an if / unless
|
|
72
|
+
# needs to have to trigger this cop
|
|
73
|
+
Style/GuardClause:
|
|
74
|
+
MinBodyLength: 1
|
|
75
|
+
|
|
76
|
+
# Checks the indentation of the first key in a hash literal.
|
|
77
|
+
Style/IndentHash:
|
|
78
|
+
EnforcedStyle: special_inside_parentheses
|
|
79
|
+
|
|
80
|
+
Style/LambdaCall:
|
|
81
|
+
EnforcedStyle: call
|
|
82
|
+
SupportedStyles:
|
|
83
|
+
- call
|
|
84
|
+
- braces
|
|
85
|
+
|
|
86
|
+
Style/NonNilCheck:
|
|
87
|
+
IncludeSemanticChanges: true
|
|
88
|
+
|
|
89
|
+
Style/MethodDefParentheses:
|
|
90
|
+
EnforcedStyle: require_parentheses
|
|
91
|
+
|
|
92
|
+
Style/NumericLiterals:
|
|
93
|
+
MinDigits: 5
|
|
94
|
+
|
|
95
|
+
# Allow safe assignment in conditions.
|
|
96
|
+
Style/ParenthesesAroundCondition:
|
|
97
|
+
AllowSafeAssignment: true
|
|
98
|
+
|
|
99
|
+
Style/PercentLiteralDelimiters:
|
|
100
|
+
PreferredDelimiters:
|
|
101
|
+
'%': ()
|
|
102
|
+
'%i': ()
|
|
103
|
+
'%q': ()
|
|
104
|
+
'%Q': ()
|
|
105
|
+
'%r': '{}'
|
|
106
|
+
'%s': ()
|
|
107
|
+
'%w': ()
|
|
108
|
+
'%W': ()
|
|
109
|
+
'%x': ()
|
|
110
|
+
|
|
111
|
+
Style/Semicolon:
|
|
112
|
+
# Allow ; to separate several expressions on the same line.
|
|
113
|
+
AllowAsExpressionSeparator: true
|
|
114
|
+
|
|
115
|
+
Style/SignalException:
|
|
116
|
+
EnforcedStyle: only_raise
|
|
117
|
+
|
|
118
|
+
Style/SingleLineMethods:
|
|
119
|
+
AllowIfMethodIsEmpty: true
|
|
120
|
+
|
|
121
|
+
Style/StringLiterals:
|
|
122
|
+
EnforcedStyle: single_quotes
|
|
123
|
+
|
|
124
|
+
Style/StringLiteralsInInterpolation:
|
|
125
|
+
EnforcedStyle: single_quotes
|
|
126
|
+
|
|
127
|
+
Style/SpaceAroundBlockParameters:
|
|
128
|
+
EnforcedStyleInsidePipes: no_space
|
|
129
|
+
|
|
130
|
+
Style/SpaceAroundEqualsInParameterDefault:
|
|
131
|
+
EnforcedStyle: space
|
|
132
|
+
|
|
133
|
+
Style/SpaceAroundOperators:
|
|
134
|
+
MultiSpaceAllowedForOperators:
|
|
135
|
+
- '='
|
|
136
|
+
- '=>'
|
|
137
|
+
|
|
138
|
+
Style/SpaceBeforeBlockBraces:
|
|
139
|
+
EnforcedStyle: space
|
|
140
|
+
|
|
141
|
+
Style/SpaceInsideBlockBraces:
|
|
142
|
+
EnforcedStyle: space
|
|
143
|
+
# Valid values are: space, no_space
|
|
144
|
+
EnforcedStyleForEmptyBraces: no_space
|
|
145
|
+
# Space between { and |. Overrides EnforcedStyle if there is a conflict.
|
|
146
|
+
SpaceBeforeBlockParameters: false
|
|
147
|
+
|
|
148
|
+
Style/SpaceInsideHashLiteralBraces:
|
|
149
|
+
EnforcedStyle: no_space
|
|
150
|
+
EnforcedStyleForEmptyBraces: no_space
|
|
151
|
+
|
|
152
|
+
Style/SymbolProc:
|
|
153
|
+
# A list of method names to be ignored by the check.
|
|
154
|
+
# The names should be fairly unique, otherwise you'll end up ignoring lots of code.
|
|
155
|
+
IgnoredMethods:
|
|
156
|
+
- respond_to
|
|
157
|
+
|
|
158
|
+
Style/TrailingBlankLines:
|
|
159
|
+
EnforcedStyle: final_newline
|
|
160
|
+
|
|
161
|
+
Style/TrailingComma:
|
|
162
|
+
# If EnforcedStyleForMultiline is comma, the cop requires a comma after the
|
|
163
|
+
# last item of a list, but only for lists where each item is on its own line.
|
|
164
|
+
# If EnforcedStyleForMultiline is consistent_comma, the cop requires a comma
|
|
165
|
+
# after the last item of a list, for all lists.
|
|
166
|
+
EnforcedStyleForMultiline: comma
|
|
167
|
+
SupportedStyles:
|
|
168
|
+
- comma
|
|
169
|
+
- consistent_comma
|
|
170
|
+
- no_comma
|
|
171
|
+
|
|
172
|
+
# TrivialAccessors doesn't require exact name matches and doesn't allow
|
|
173
|
+
# predicated methods by default.
|
|
174
|
+
Style/TrivialAccessors:
|
|
175
|
+
ExactNameMatch: false
|
|
176
|
+
AllowPredicates: false
|
|
177
|
+
# Allows trivial writers that don't end in an equal sign. e.g.
|
|
178
|
+
#
|
|
179
|
+
# def on_exception(action)
|
|
180
|
+
# @on_exception=action
|
|
181
|
+
# end
|
|
182
|
+
# on_exception :restart
|
|
183
|
+
#
|
|
184
|
+
# Commonly used in DSLs
|
|
185
|
+
AllowDSLWriters: false
|
|
186
|
+
IgnoreClassMethods: false
|
|
187
|
+
Whitelist:
|
|
188
|
+
- to_ary
|
|
189
|
+
- to_a
|
|
190
|
+
- to_c
|
|
191
|
+
- to_enum
|
|
192
|
+
- to_h
|
|
193
|
+
- to_hash
|
|
194
|
+
- to_i
|
|
195
|
+
- to_int
|
|
196
|
+
- to_io
|
|
197
|
+
- to_open
|
|
198
|
+
- to_path
|
|
199
|
+
- to_proc
|
|
200
|
+
- to_r
|
|
201
|
+
- to_regexp
|
|
202
|
+
- to_str
|
|
203
|
+
- to_s
|
|
204
|
+
- to_sym
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
Style/WhileUntilModifier:
|
|
208
|
+
MaxLineLength: 80
|
|
209
|
+
|
|
210
|
+
Style/WordArray:
|
|
211
|
+
MinSize: 0
|
|
212
|
+
# The regular expression WordRegex decides what is considered a word.
|
|
213
|
+
WordRegex: !ruby/regexp '/\A[\p{Word}]+\z/'
|
|
214
|
+
|
|
215
|
+
##################### Metrics ##################################
|
|
216
|
+
|
|
217
|
+
Metrics/AbcSize:
|
|
218
|
+
# The ABC size is a calculated magnitude, so this number can be a Fixnum or
|
|
219
|
+
# a Float.
|
|
220
|
+
Max: 20
|
|
221
|
+
|
|
222
|
+
Metrics/BlockNesting:
|
|
223
|
+
Max: 3
|
|
224
|
+
|
|
225
|
+
Metrics/ClassLength:
|
|
226
|
+
CountComments: false # count full line comments?
|
|
227
|
+
Max: 300
|
|
228
|
+
|
|
229
|
+
# Avoid complex methods.
|
|
230
|
+
Metrics/CyclomaticComplexity:
|
|
231
|
+
Max: 6
|
|
232
|
+
|
|
233
|
+
Metrics/LineLength:
|
|
234
|
+
Max: 80
|
|
235
|
+
# To make it possible to copy or click on URIs in the code, we allow lines
|
|
236
|
+
# contaning a URI to be longer than Max.
|
|
237
|
+
AllowURI: true
|
|
238
|
+
URISchemes:
|
|
239
|
+
- http
|
|
240
|
+
- https
|
|
241
|
+
|
|
242
|
+
Metrics/MethodLength:
|
|
243
|
+
CountComments: false # count full line comments?
|
|
244
|
+
Max: 30
|
|
245
|
+
|
|
246
|
+
Metrics/ParameterLists:
|
|
247
|
+
Max: 5
|
|
248
|
+
CountKeywordArgs: true
|
|
249
|
+
|
|
250
|
+
Metrics/PerceivedComplexity:
|
|
251
|
+
Max: 7
|
|
252
|
+
|
|
253
|
+
##################### Lint ##################################
|
|
254
|
+
|
|
255
|
+
# Allow safe assignment in conditions.
|
|
256
|
+
Lint/AssignmentInCondition:
|
|
257
|
+
AllowSafeAssignment: true
|
|
258
|
+
|
|
259
|
+
# Align ends correctly.
|
|
260
|
+
Lint/EndAlignment:
|
|
261
|
+
AlignWith: keyword
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -39,9 +39,9 @@ look like this, in `controller/hello.rb`:
|
|
|
39
39
|
|
|
40
40
|
```ruby
|
|
41
41
|
class HelloController < Kenji::Controller
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
get '/world' do
|
|
43
|
+
{hello: :world}
|
|
44
|
+
end
|
|
45
45
|
end
|
|
46
46
|
```
|
|
47
47
|
|
|
@@ -50,19 +50,19 @@ A more representative example might be:
|
|
|
50
50
|
```ruby
|
|
51
51
|
class UserController < Kenji::Controller
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
# ...
|
|
54
54
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
get '/:id/friends' do |id|
|
|
56
|
+
# list friends for id
|
|
57
|
+
end
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
post '/:id/friend/:id' do |id, friend_id|
|
|
60
|
+
# add connection from user id to friend_id
|
|
61
|
+
end
|
|
62
62
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
delete '/:id/friend/:id' do |id, friend_id|
|
|
64
|
+
# delete connection from user id to friend_id
|
|
65
|
+
end
|
|
66
66
|
end
|
|
67
67
|
```
|
|
68
68
|
|
|
@@ -100,6 +100,14 @@ And already, your app is ready to go:
|
|
|
100
100
|
|
|
101
101
|
## Changelog
|
|
102
102
|
|
|
103
|
+
#### 1.2.0
|
|
104
|
+
|
|
105
|
+
- A new `exception_in_body` option (defaults to false) defines whether options
|
|
106
|
+
are returned to the HTTP response instead of the default “Something went
|
|
107
|
+
wrong...”
|
|
108
|
+
- `kenji init` no longer generates a useless binary.
|
|
109
|
+
- Internally, lots of style and best practices refactors.
|
|
110
|
+
|
|
103
111
|
#### 1.1.2
|
|
104
112
|
|
|
105
113
|
- The `respond_raw` method allows responding with raw data, instead of the
|
|
@@ -150,7 +158,7 @@ And already, your app is ready to go:
|
|
|
150
158
|
|
|
151
159
|
- `before` command.
|
|
152
160
|
- specs
|
|
153
|
-
- passing
|
|
161
|
+
- passing
|
|
154
162
|
|
|
155
163
|
## Still to do
|
|
156
164
|
|
data/Rakefile
CHANGED
data/bin/kenji
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
|
|
3
|
-
|
|
4
3
|
unless ARGV.first == 'init'
|
|
5
4
|
puts 'Usage: kenji init [project-name]'
|
|
6
5
|
Process.exit
|
|
@@ -17,28 +16,32 @@ path = File.realpath(path)
|
|
|
17
16
|
init_source = File.realpath(File.dirname(File.dirname(__FILE__))) << '/inited'
|
|
18
17
|
|
|
19
18
|
for_earch_item = lambda do |item, base|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
f.close
|
|
19
|
+
next if item == '.' || item == '..'
|
|
20
|
+
item_to_create = item.gsub('__APP_NAME__', app_name)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
if File.directory?("#{init_source}/#{base}/#{item}")
|
|
24
|
+
unless File.directory?("#{path}/#{base}/#{item}")
|
|
25
|
+
Dir.mkdir("#{path}/#{base}/#{item_to_create}")
|
|
26
|
+
end
|
|
27
|
+
puts "Initializing directory #{base}/#{item_to_create}"
|
|
28
|
+
Dir.foreach("#{init_source}/#{base}/#{item}") do |i|
|
|
29
|
+
for_earch_item.call(i, "#{base}/#{item}")
|
|
30
|
+
end
|
|
31
|
+
else
|
|
32
|
+
puts "Generating content for file #{base}/#{item_to_create}"
|
|
33
|
+
data = ''
|
|
34
|
+
f = File.open("#{init_source}/#{base}/#{item}", 'r')
|
|
35
|
+
while (line = f.gets)
|
|
36
|
+
data += (line.gsub('__APP_NAME__', app_name))
|
|
39
37
|
end
|
|
38
|
+
f.close
|
|
39
|
+
f = File.open("#{path}/#{base}/#{item_to_create}", 'w')
|
|
40
|
+
f.puts data
|
|
41
|
+
f.close
|
|
42
|
+
end
|
|
40
43
|
end
|
|
41
44
|
|
|
42
|
-
Dir.foreach(init_source) {
|
|
45
|
+
Dir.foreach(init_source) {|i| for_earch_item.call(i, '.') }
|
|
43
46
|
|
|
44
47
|
system("cd #{path}; bundle install")
|
data/kenji.gemspec
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
$LOAD_PATH.push(File.expand_path('../lib', __FILE__))
|
|
2
2
|
require 'kenji/version'
|
|
3
3
|
|
|
4
4
|
Gem::Specification.new do |s|
|
|
@@ -19,6 +19,6 @@ Gem::Specification.new do |s|
|
|
|
19
19
|
|
|
20
20
|
s.files = `git ls-files`.split("\n")
|
|
21
21
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
|
22
|
-
s.executables = `git ls-files -- bin/*`.split("\n").map{
|
|
22
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map {|f| File.basename(f) }
|
|
23
23
|
s.require_paths = ['lib']
|
|
24
24
|
end
|
data/lib/kenji.rb
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
require 'json'
|
|
2
2
|
require 'kenji/controller'
|
|
3
3
|
require 'kenji/app'
|
|
4
|
-
require 'kenji/
|
|
4
|
+
require 'kenji/string-extensions'
|
|
5
5
|
require 'rack'
|
|
6
6
|
|
|
7
|
+
using Kenji::StringExtensions
|
|
8
|
+
|
|
7
9
|
module Kenji
|
|
8
10
|
class Kenji
|
|
9
11
|
|
|
@@ -28,7 +30,7 @@ module Kenji
|
|
|
28
30
|
# `options` is an options hash that accepts the following keys:
|
|
29
31
|
#
|
|
30
32
|
# :directory => String (path)
|
|
31
|
-
#
|
|
33
|
+
#
|
|
32
34
|
# this is the preferred way of setting the root directory, when
|
|
33
35
|
# necessary. you should either set a root directory (which defaults to
|
|
34
36
|
# the current working directory), or set a root_controller. both are
|
|
@@ -46,6 +48,10 @@ module Kenji
|
|
|
46
48
|
# when true, Kenji will catch exceptions, print them in stderr, and and
|
|
47
49
|
# return a standard 500 error
|
|
48
50
|
#
|
|
51
|
+
# :exception_in_body => true | false
|
|
52
|
+
#
|
|
53
|
+
# if :catch_exceptions => true, return exception message in response
|
|
54
|
+
#
|
|
49
55
|
# :stderr => IO
|
|
50
56
|
#
|
|
51
57
|
# an IO stread, this is where Kenji logging goes by default. defaults
|
|
@@ -58,23 +64,24 @@ module Kenji
|
|
|
58
64
|
options ||= {}
|
|
59
65
|
|
|
60
66
|
@headers = {
|
|
61
|
-
'Content-Type' => 'application/json'
|
|
67
|
+
'Content-Type' => 'application/json',
|
|
62
68
|
}
|
|
63
69
|
@status = 200
|
|
64
70
|
@env = env
|
|
65
71
|
|
|
66
72
|
# deal with legacy root argument behavior
|
|
67
73
|
options[:directory] = File.expand_path(root) if root
|
|
68
|
-
|
|
74
|
+
|
|
69
75
|
@options = {
|
|
70
|
-
catch_exceptions:
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
76
|
+
catch_exceptions: true,
|
|
77
|
+
exception_in_body: false,
|
|
78
|
+
root_controller: nil,
|
|
79
|
+
directory: File.expand_path(Dir.getwd),
|
|
80
|
+
stderr: $stderr,
|
|
74
81
|
}.merge(options)
|
|
75
82
|
|
|
76
83
|
@stderr = @options[:stderr]
|
|
77
|
-
@root
|
|
84
|
+
@root = @options[:directory] + '/'
|
|
78
85
|
end
|
|
79
86
|
|
|
80
87
|
# This method does all the work!
|
|
@@ -98,16 +105,16 @@ module Kenji
|
|
|
98
105
|
|
|
99
106
|
if @options[:root_controller]
|
|
100
107
|
controller = controller_instance(@options[:root_controller])
|
|
101
|
-
subpath
|
|
102
|
-
out
|
|
103
|
-
success
|
|
108
|
+
subpath = segments.join('/')
|
|
109
|
+
out = controller.call(method, subpath).to_json
|
|
110
|
+
success = true
|
|
104
111
|
else
|
|
105
112
|
acc = ''; out = '', success = false
|
|
106
|
-
while head = segments.shift
|
|
113
|
+
while (head = segments.shift)
|
|
107
114
|
acc = "#{acc}/#{head}"
|
|
108
|
-
# if we have a valid controller
|
|
109
|
-
if controller = controller_for(acc)
|
|
110
|
-
subpath = '/'+segments.join('/')
|
|
115
|
+
# if we have a valid controller
|
|
116
|
+
if (controller = controller_for(acc))
|
|
117
|
+
subpath = '/' + segments.join('/')
|
|
111
118
|
out = controller.call(method, subpath).to_json
|
|
112
119
|
success = true
|
|
113
120
|
break
|
|
@@ -119,27 +126,22 @@ module Kenji
|
|
|
119
126
|
|
|
120
127
|
[@status, @headers, [out]]
|
|
121
128
|
end
|
|
122
|
-
rescue
|
|
129
|
+
rescue => e
|
|
123
130
|
raise e unless @options[:catch_exceptions]
|
|
124
131
|
# log exceptions
|
|
125
|
-
@stderr.puts
|
|
132
|
+
@stderr.puts(e.inspect)
|
|
126
133
|
e.backtrace.each {|b| @stderr.puts " #{b}" }
|
|
127
|
-
response_500
|
|
134
|
+
response_500(e)
|
|
128
135
|
end
|
|
129
136
|
|
|
130
|
-
|
|
131
|
-
|
|
132
137
|
# Methods for users!
|
|
133
138
|
|
|
134
|
-
|
|
135
139
|
# Sets one or multiple headers, as named arametres. eg.
|
|
136
|
-
#
|
|
140
|
+
#
|
|
137
141
|
# kenji.header 'Content-Type' => 'hello/world'
|
|
138
142
|
#
|
|
139
|
-
def header(hash={})
|
|
140
|
-
hash
|
|
141
|
-
@headers[key] = value
|
|
142
|
-
end
|
|
143
|
+
def header(hash = {})
|
|
144
|
+
@header.merge!(hash)
|
|
143
145
|
end
|
|
144
146
|
|
|
145
147
|
# Returns the response headers
|
|
@@ -161,16 +163,17 @@ module Kenji
|
|
|
161
163
|
end if raw
|
|
162
164
|
{} # default return value
|
|
163
165
|
end
|
|
164
|
-
|
|
166
|
+
|
|
165
167
|
# Respond to the request
|
|
166
168
|
#
|
|
167
|
-
def respond(code, message, hash={})
|
|
169
|
+
def respond(code, message, hash = {})
|
|
168
170
|
@status = code
|
|
169
|
-
response = {
|
|
170
|
-
status:
|
|
171
|
+
response = { # default structure.
|
|
172
|
+
status: code,
|
|
171
173
|
message: message,
|
|
172
174
|
}.merge(hash)
|
|
173
|
-
throw(:KenjiRespondControlFlowInterrupt,
|
|
175
|
+
throw(:KenjiRespondControlFlowInterrupt,
|
|
176
|
+
[@status, @headers, [response.to_json]])
|
|
174
177
|
end
|
|
175
178
|
|
|
176
179
|
# Respond with raw bytes
|
|
@@ -180,7 +183,7 @@ module Kenji
|
|
|
180
183
|
def respond_raw(status = 200, data)
|
|
181
184
|
@status = status
|
|
182
185
|
body = data.is_a?(IO) ? data : [data]
|
|
183
|
-
throw(:KenjiRespondControlFlowInterrupt, [@status, @headers,
|
|
186
|
+
throw(:KenjiRespondControlFlowInterrupt, [@status, @headers, body])
|
|
184
187
|
end
|
|
185
188
|
|
|
186
189
|
|
|
@@ -188,8 +191,15 @@ module Kenji
|
|
|
188
191
|
private
|
|
189
192
|
|
|
190
193
|
# 500 error
|
|
191
|
-
def response_500
|
|
192
|
-
|
|
194
|
+
def response_500(exception = nil)
|
|
195
|
+
message =
|
|
196
|
+
if exception.nil? || !@options[:exception_in_body]
|
|
197
|
+
'Something went wrong...'
|
|
198
|
+
else
|
|
199
|
+
exception.to_s
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
[500, @headers, [{status: 500, message: message}.to_json]]
|
|
193
203
|
end
|
|
194
204
|
|
|
195
205
|
# 404 error
|
|
@@ -203,10 +213,11 @@ module Kenji
|
|
|
203
213
|
def controller_for(subpath)
|
|
204
214
|
subpath = '/_' if subpath == '/'
|
|
205
215
|
path = "#{@root}controllers#{subpath}.rb"
|
|
206
|
-
return nil unless File.
|
|
216
|
+
return nil unless File.exist?(path)
|
|
207
217
|
require path
|
|
208
218
|
controller_name = subpath.split('/').last.sub(/^_/, 'Root')
|
|
209
|
-
controller_class =
|
|
219
|
+
controller_class =
|
|
220
|
+
Object.const_get(controller_name.to_s.to_camelcase + 'Controller')
|
|
210
221
|
controller_instance(controller_class)
|
|
211
222
|
end
|
|
212
223
|
|
|
@@ -215,8 +226,8 @@ module Kenji
|
|
|
215
226
|
#
|
|
216
227
|
def controller_instance(controller_class)
|
|
217
228
|
# ensure protocol compliance
|
|
218
|
-
unless
|
|
219
|
-
|
|
229
|
+
unless controller_class.method_defined?(:call) \
|
|
230
|
+
&& controller_class.instance_method(:call).arity == 2
|
|
220
231
|
return
|
|
221
232
|
end
|
|
222
233
|
controller = controller_class.new
|
|
@@ -224,8 +235,6 @@ module Kenji
|
|
|
224
235
|
return controller if controller
|
|
225
236
|
nil # default return value
|
|
226
237
|
end
|
|
227
|
-
|
|
238
|
+
|
|
228
239
|
end
|
|
229
|
-
|
|
230
240
|
end
|
|
231
|
-
|
data/lib/kenji/app.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
module Kenji
|
|
3
|
-
|
|
3
|
+
|
|
4
4
|
# Kenji::App is a simple wrapper class that helps avoid the awkward wrapping
|
|
5
5
|
# in a lambda typically necessary for using Kenji as a Rack app. Instead,
|
|
6
6
|
# simply do the following:
|
|
@@ -14,12 +14,14 @@ module Kenji
|
|
|
14
14
|
#
|
|
15
15
|
class App
|
|
16
16
|
|
|
17
|
-
def initialize(opts={})
|
|
17
|
+
def initialize(opts = {})
|
|
18
18
|
@opts = opts
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def call(env)
|
|
22
22
|
Kenji.new(env, @opts).call
|
|
23
23
|
end
|
|
24
|
+
|
|
24
25
|
end
|
|
26
|
+
|
|
25
27
|
end
|
data/lib/kenji/controller.rb
CHANGED
|
@@ -6,7 +6,7 @@ module Kenji
|
|
|
6
6
|
attr_accessor :kenji
|
|
7
7
|
|
|
8
8
|
# Routes below will accept routes in the format, eg.:
|
|
9
|
-
#
|
|
9
|
+
#
|
|
10
10
|
# /hello/:id/children
|
|
11
11
|
#
|
|
12
12
|
# Can contain any number of :id, but must be in their own url segment.
|
|
@@ -56,20 +56,27 @@ module Kenji
|
|
|
56
56
|
# segment or variable segment, and the leaf @action being the block
|
|
57
57
|
#
|
|
58
58
|
def self.route(*methods, path, &block)
|
|
59
|
+
|
|
59
60
|
# bind the block to self as an instance method, so its context is correct
|
|
60
61
|
define_method(:_tmp_route_action, &block)
|
|
61
62
|
block = instance_method(:_tmp_route_action)
|
|
62
63
|
remove_method(:_tmp_route_action)
|
|
64
|
+
|
|
63
65
|
# store the block for each method
|
|
64
66
|
methods.each do |method|
|
|
67
|
+
|
|
65
68
|
node = ((@routes ||= {})[method] ||= {})
|
|
66
69
|
segments = path.split('/')
|
|
67
|
-
|
|
68
|
-
segments.
|
|
69
|
-
|
|
70
|
+
# discard leading /'s empty segment
|
|
71
|
+
segments = segments.drop(1) if segments.first == ''
|
|
72
|
+
# lazily create tree
|
|
73
|
+
segments.each do |segment|
|
|
74
|
+
# discard :variable name
|
|
75
|
+
segment = ':' if segment =~ /^:/
|
|
70
76
|
node = (node[segment.to_sym] ||= {})
|
|
71
77
|
end
|
|
72
|
-
|
|
78
|
+
# store block as leaf in @action
|
|
79
|
+
node[:@action] = block
|
|
73
80
|
end
|
|
74
81
|
nil # void method
|
|
75
82
|
end
|
|
@@ -84,7 +91,8 @@ module Kenji
|
|
|
84
91
|
def self.pass(path, controller)
|
|
85
92
|
node = (@passes ||= {})
|
|
86
93
|
segments = path.split('/')
|
|
87
|
-
|
|
94
|
+
# discard leading /'s empty segment
|
|
95
|
+
segments = segments.drop(1) if segments.first == ''
|
|
88
96
|
segments.each do |segment|
|
|
89
97
|
node = (node[segment.to_sym] ||= {})
|
|
90
98
|
break if segment == '*'
|
|
@@ -92,7 +100,6 @@ module Kenji
|
|
|
92
100
|
node[:@controller] = controller
|
|
93
101
|
end
|
|
94
102
|
|
|
95
|
-
|
|
96
103
|
# This lets you define before blocks.
|
|
97
104
|
#
|
|
98
105
|
# class MyController < Kenji::Controller
|
|
@@ -108,7 +115,6 @@ module Kenji
|
|
|
108
115
|
(@befores ||= []) << block
|
|
109
116
|
end
|
|
110
117
|
|
|
111
|
-
|
|
112
118
|
# Most likely only used by Kenji itself.
|
|
113
119
|
# Override to implement your own routing, if you'd like.
|
|
114
120
|
#
|
|
@@ -117,7 +123,8 @@ module Kenji
|
|
|
117
123
|
self.class.befores.each {|b| b.bind(self).call }
|
|
118
124
|
|
|
119
125
|
segments = path.split('/')
|
|
120
|
-
|
|
126
|
+
# discard leading /'s empty segment
|
|
127
|
+
segments = segments.drop(1) if segments.first == ''
|
|
121
128
|
|
|
122
129
|
# check for passes
|
|
123
130
|
node = self.class.passes
|
|
@@ -138,19 +145,25 @@ module Kenji
|
|
|
138
145
|
node = self.class.routes[method] || {}
|
|
139
146
|
variables = []
|
|
140
147
|
searching = true
|
|
141
|
-
|
|
148
|
+
# traverse tree to find
|
|
149
|
+
segments.each do |segment|
|
|
142
150
|
if searching && node[segment.to_sym]
|
|
143
|
-
|
|
151
|
+
# attempt to move down to the plain text segment
|
|
152
|
+
node = node[segment.to_sym]
|
|
144
153
|
elsif searching && node[:':']
|
|
145
|
-
|
|
146
|
-
|
|
154
|
+
# attempt to find a variable segment
|
|
155
|
+
node = node[:':']
|
|
156
|
+
# either we've found a variable, or the `unless` below will trigger
|
|
157
|
+
variables << segment
|
|
147
158
|
else
|
|
148
|
-
|
|
159
|
+
# route failed to match variable or segment node so attempt fallback
|
|
160
|
+
return attempt_fallback(path)
|
|
149
161
|
end
|
|
150
162
|
end
|
|
151
|
-
|
|
163
|
+
# the block is stored in the @action leaf
|
|
164
|
+
if node && (action = node[:@action])
|
|
152
165
|
return action.bind(self).call(*variables)
|
|
153
|
-
else
|
|
166
|
+
else # or, fallback if necessary store the block for each method
|
|
154
167
|
return attempt_fallback(path)
|
|
155
168
|
end
|
|
156
169
|
end
|
|
@@ -175,13 +188,17 @@ module Kenji
|
|
|
175
188
|
end
|
|
176
189
|
|
|
177
190
|
private
|
|
191
|
+
|
|
178
192
|
# Accessor for @routes
|
|
193
|
+
|
|
179
194
|
def self.routes
|
|
180
195
|
@routes || {}
|
|
181
196
|
end
|
|
197
|
+
|
|
182
198
|
def self.passes
|
|
183
199
|
@passes || {}
|
|
184
200
|
end
|
|
201
|
+
|
|
185
202
|
def self.befores
|
|
186
203
|
@befores || []
|
|
187
204
|
end
|
|
@@ -190,24 +207,28 @@ module Kenji
|
|
|
190
207
|
variables = {}
|
|
191
208
|
match = false
|
|
192
209
|
|
|
193
|
-
while e = segments.shift
|
|
210
|
+
while (e = segments.shift)
|
|
194
211
|
# return false unless node
|
|
195
212
|
key = node.keys.first
|
|
196
|
-
if match = key.to_s.match(/^\:(\w+)/)
|
|
213
|
+
if (match = key.to_s.match(/^\:(\w+)/))
|
|
197
214
|
node = node[key.to_sym]
|
|
198
215
|
variables[match[1].to_sym] = e
|
|
199
216
|
match = true
|
|
200
217
|
else
|
|
201
218
|
# if there is no match it should not pass
|
|
202
|
-
break unless match = node.
|
|
219
|
+
break unless (match = node.key?(e.to_sym))
|
|
203
220
|
node = node[e.to_sym]
|
|
204
221
|
end
|
|
205
222
|
|
|
206
223
|
break if node[:@controller]
|
|
207
224
|
end
|
|
208
225
|
|
|
209
|
-
{
|
|
226
|
+
{
|
|
227
|
+
match: match,
|
|
228
|
+
variables: variables,
|
|
229
|
+
controller: node[:@controller],
|
|
230
|
+
remaining_segments: segments,
|
|
231
|
+
}
|
|
210
232
|
end
|
|
211
233
|
end
|
|
212
234
|
end
|
|
213
|
-
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Kenji string extensions
|
|
2
|
+
|
|
3
|
+
module Kenji
|
|
4
|
+
module StringExtensions
|
|
5
|
+
|
|
6
|
+
refine String do
|
|
7
|
+
|
|
8
|
+
def to_underscore!
|
|
9
|
+
gsub!(/(.)([A-Z])/, '\1_\2').downcase!
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def to_underscore
|
|
13
|
+
clone.to_underscore!
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def to_camelcase!
|
|
17
|
+
replace(split('_').each(&:capitalize!).join(''))
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def to_camelcase
|
|
21
|
+
clone.to_camelcase!
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
data/lib/kenji/version.rb
CHANGED
data/spec/1/controllers/main.rb
CHANGED
data/spec/kenji_spec.rb
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
require 'rack'
|
|
3
2
|
require 'rack/test'
|
|
4
3
|
require 'rspec'
|
|
@@ -6,7 +5,6 @@ require 'rspec/mocks'
|
|
|
6
5
|
|
|
7
6
|
require 'kenji'
|
|
8
7
|
|
|
9
|
-
|
|
10
8
|
# NOTE: these tests make use of the controllers defined in test/controllers.
|
|
11
9
|
|
|
12
10
|
def app_for(path, opts={})
|
|
@@ -23,7 +21,6 @@ describe Kenji::Kenji, 'expected responses' do
|
|
|
23
21
|
context '1' do
|
|
24
22
|
def app; app_for('1'); end
|
|
25
23
|
|
|
26
|
-
|
|
27
24
|
it 'should return 404 for unknown routes (no controller)' do
|
|
28
25
|
get '/sdlkjhb'
|
|
29
26
|
expected_response = {status: 404, message: 'Not found!'}.to_json
|
|
@@ -38,13 +35,6 @@ describe Kenji::Kenji, 'expected responses' do
|
|
|
38
35
|
last_response.status.should == 404
|
|
39
36
|
end
|
|
40
37
|
|
|
41
|
-
it 'should return 500 for exceptions' do
|
|
42
|
-
get '/main/crasher'
|
|
43
|
-
expected_response = {status: 500, message: 'Something went wrong...'}.to_json
|
|
44
|
-
last_response.body.should == expected_response
|
|
45
|
-
last_response.status.should == 500
|
|
46
|
-
end
|
|
47
|
-
|
|
48
38
|
it 'should route a GET call to a defined get call' do
|
|
49
39
|
get '/main/hello'
|
|
50
40
|
expected_response = {status: 200, hello: :world}.to_json
|
|
@@ -52,7 +42,6 @@ describe Kenji::Kenji, 'expected responses' do
|
|
|
52
42
|
end
|
|
53
43
|
|
|
54
44
|
[:post, :put, :delete, :patch].each do |method|
|
|
55
|
-
|
|
56
45
|
it "should route a #{method.to_s.upcase} to a defined #{method.to_s} call" do
|
|
57
46
|
send(method, '/main')
|
|
58
47
|
expected_response = {status: 1337}.to_json
|
|
@@ -87,7 +76,6 @@ describe Kenji::Kenji, 'expected responses' do
|
|
|
87
76
|
last_response.body.should == expected_response
|
|
88
77
|
last_response.status.should == 123
|
|
89
78
|
end
|
|
90
|
-
|
|
91
79
|
end
|
|
92
80
|
|
|
93
81
|
context '2' do
|
|
@@ -204,9 +192,58 @@ describe Kenji::Kenji, 'expected responses' do
|
|
|
204
192
|
last_response.status.should == 500
|
|
205
193
|
end
|
|
206
194
|
end
|
|
207
|
-
|
|
208
|
-
|
|
195
|
+
|
|
196
|
+
context '7' do
|
|
197
|
+
context 'catch_exceptions is false' do
|
|
198
|
+
def app
|
|
199
|
+
app_for('7',
|
|
200
|
+
catch_exceptions: false)
|
|
201
|
+
|
|
202
|
+
it 'should raise' do
|
|
203
|
+
-> { get '/main/crasher' }.must_raise
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
context 'catch_exceptions is true' do
|
|
209
|
+
context :exception_in_body do
|
|
210
|
+
context 'exception_in_body is false' do
|
|
211
|
+
def app
|
|
212
|
+
app_for('7',
|
|
213
|
+
catch_exceptions: true,
|
|
214
|
+
exception_in_body: false)
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
it 'should return 500 for exceptions' do
|
|
218
|
+
get '/main/crasher'
|
|
219
|
+
last_response
|
|
220
|
+
.body
|
|
221
|
+
.should == {status: 500,
|
|
222
|
+
message: 'Something went wrong...'}.to_json
|
|
223
|
+
last_response.status.should == 500
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
context 'exception_in_body is true' do
|
|
228
|
+
def app
|
|
229
|
+
app_for('7',
|
|
230
|
+
catch_exceptions: true,
|
|
231
|
+
exception_in_body: true)
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
it 'should return 500 for exceptions' do
|
|
235
|
+
get '/main/crasher'
|
|
236
|
+
last_response
|
|
237
|
+
.body
|
|
238
|
+
.should == {status: 500,
|
|
239
|
+
message: 'kaboom!'}.to_json
|
|
240
|
+
last_response.status.should == 500
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
|
|
209
247
|
# TODO: Write unit tests for Kenji::App
|
|
210
248
|
# TODO: Write unit tests for new root directory behavior.
|
|
211
|
-
|
|
212
249
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: kenji
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Kenneth Ballenegger
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2015-04-02 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: json
|
|
@@ -89,6 +89,8 @@ extensions: []
|
|
|
89
89
|
extra_rdoc_files: []
|
|
90
90
|
files:
|
|
91
91
|
- ".gitignore"
|
|
92
|
+
- ".hound.yml"
|
|
93
|
+
- ".rubocop.yml"
|
|
92
94
|
- Gemfile
|
|
93
95
|
- Gemfile.lock
|
|
94
96
|
- LICENSE
|
|
@@ -98,7 +100,6 @@ files:
|
|
|
98
100
|
- inited/.gitignore
|
|
99
101
|
- inited/Gemfile
|
|
100
102
|
- inited/README.md
|
|
101
|
-
- inited/__APP_NAME__
|
|
102
103
|
- inited/config.ru
|
|
103
104
|
- inited/controllers/main.rb
|
|
104
105
|
- inited/lib/README.md
|
|
@@ -109,7 +110,7 @@ files:
|
|
|
109
110
|
- lib/kenji.rb
|
|
110
111
|
- lib/kenji/app.rb
|
|
111
112
|
- lib/kenji/controller.rb
|
|
112
|
-
- lib/kenji/
|
|
113
|
+
- lib/kenji/string-extensions.rb
|
|
113
114
|
- lib/kenji/version.rb
|
|
114
115
|
- spec/1/controllers/main.rb
|
|
115
116
|
- spec/2/controllers/_.rb
|
|
@@ -119,6 +120,7 @@ files:
|
|
|
119
120
|
- spec/4/controllers/main.rb
|
|
120
121
|
- spec/4/controllers/pass.rb
|
|
121
122
|
- spec/5/controllers/main.rb
|
|
123
|
+
- spec/7/controllers/main.rb
|
|
122
124
|
- spec/kenji_spec.rb
|
|
123
125
|
homepage: https://github.com/kballenegger/kenji
|
|
124
126
|
licenses: []
|
|
@@ -152,5 +154,6 @@ test_files:
|
|
|
152
154
|
- spec/4/controllers/main.rb
|
|
153
155
|
- spec/4/controllers/pass.rb
|
|
154
156
|
- spec/5/controllers/main.rb
|
|
157
|
+
- spec/7/controllers/main.rb
|
|
155
158
|
- spec/kenji_spec.rb
|
|
156
159
|
has_rdoc:
|
data/inited/__APP_NAME__
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/ruby
|
|
2
|
-
|
|
3
|
-
class Main
|
|
4
|
-
|
|
5
|
-
def self.init
|
|
6
|
-
require 'rubygems'
|
|
7
|
-
require 'bundler/setup'
|
|
8
|
-
require $root+'/lib/kenji/kenji'
|
|
9
|
-
@@k = Kenji::Kenji.new({}, $root)
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
def self.main args
|
|
13
|
-
verb = args.first.to_sym unless args.empty?
|
|
14
|
-
|
|
15
|
-
# case verb
|
|
16
|
-
# when :import
|
|
17
|
-
# puts "Calling import script..."
|
|
18
|
-
# @@k.controller_for(:import)._caffeine @@k
|
|
19
|
-
# when :process
|
|
20
|
-
# puts "Processing values..."
|
|
21
|
-
# @@k.controller_for(:processing)._value @@k
|
|
22
|
-
# when :configure
|
|
23
|
-
# require $root + '/lib/configure'
|
|
24
|
-
# skip_update = (args[1] =~ /-?-?skip[-_]update/)
|
|
25
|
-
# AnalyticsModule.configure __FILE__, skip_update
|
|
26
|
-
# else
|
|
27
|
-
# puts <<-EOO
|
|
28
|
-
# No verb defined. Usage: ./main [verb]
|
|
29
|
-
#
|
|
30
|
-
# configure [--skip-update]:
|
|
31
|
-
# process the value of users
|
|
32
|
-
# optionally, skip the self-update process
|
|
33
|
-
# import:
|
|
34
|
-
# import install from caffeine.io
|
|
35
|
-
# process:
|
|
36
|
-
# process the value of users
|
|
37
|
-
# EOO
|
|
38
|
-
# end
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
def kenji
|
|
42
|
-
@@k
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
require 'pathname'
|
|
47
|
-
$root = File.dirname Pathname.new(__FILE__).realpath.to_s
|
|
48
|
-
|
|
49
|
-
Main.init unless ARGV.first == 'configure'
|
|
50
|
-
if __FILE__ == $0
|
|
51
|
-
Main.main ARGV
|
|
52
|
-
end
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
# Kenji string extensions
|
|
2
|
-
|
|
3
|
-
class String
|
|
4
|
-
def to_underscore!
|
|
5
|
-
self.gsub!(/(.)([A-Z])/,'\1_\2').downcase!
|
|
6
|
-
end
|
|
7
|
-
def to_underscore
|
|
8
|
-
self.clone.to_underscore!
|
|
9
|
-
end
|
|
10
|
-
def to_camelcase!
|
|
11
|
-
self.replace self.split('_').each{ |s| s.capitalize! }.join('')
|
|
12
|
-
end
|
|
13
|
-
def to_camelcase
|
|
14
|
-
self.clone.to_camelcase!
|
|
15
|
-
end
|
|
16
|
-
end
|