incline 0.1.7 → 0.1.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +4 -4
- data/app/controllers/incline/account_activations_controller.rb +2 -2
- data/app/controllers/incline/contact_controller.rb +1 -1
- data/app/controllers/incline/password_resets_controller.rb +2 -2
- data/app/controllers/incline/sessions_controller.rb +2 -2
- data/app/controllers/incline/users_controller.rb +1 -1
- data/exe/incline +12 -0
- data/lib/incline/cli/errors.rb +8 -0
- data/lib/incline/cli/helpers/yaml.rb +743 -0
- data/lib/incline/cli/usage.rb +77 -0
- data/lib/incline/cli/version.rb +23 -0
- data/lib/incline/cli.rb +120 -0
- data/lib/incline/extensions/action_controller_base.rb +1 -1
- data/lib/incline/extensions/test_case.rb +8 -2
- data/lib/incline/version.rb +1 -1
- data/test/cli/yaml_contents_test.rb +88 -0
- data/test/controllers/incline/welcome_controller_test.rb +3 -3
- data/test/dummy/app/controllers/dummy_controller.rb +10 -0
- data/test/dummy/app/views/dummy/hello.html.erb +1 -0
- data/test/dummy/config/routes.rb +3 -1
- data/test/integration/incline/users_signup_test.rb +2 -2
- metadata +11 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '0254285858902e4313110fddc419672ac8ae267b'
|
4
|
+
data.tar.gz: f22408a384aa6aaf8ee1f36195a220d1677fb09c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e8f26912a02f65f50705909925952a0478bed454cc32a3d5eff854a1186d030e72905370f3791fd40dae334c72fffad0d8cf2177e4077e8924cdc9b4028ae61a
|
7
|
+
data.tar.gz: 200de0b8b26cabf2303f94ee7df3d5aad0c3ec7c7a8745871df0825d3c16e6b4039da7115f50b70972310c661ba0b7ba50ee4a69d292b353cd1714301e3f63a9
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
incline (0.1.
|
4
|
+
incline (0.1.8)
|
5
5
|
ansi (~> 1.5.0)
|
6
6
|
bcrypt
|
7
7
|
bootstrap-sass
|
@@ -58,7 +58,7 @@ GEM
|
|
58
58
|
tzinfo (~> 1.1)
|
59
59
|
ansi (1.5.0)
|
60
60
|
arel (6.0.4)
|
61
|
-
autoprefixer-rails (7.1.2.
|
61
|
+
autoprefixer-rails (7.1.2.3)
|
62
62
|
execjs
|
63
63
|
bcrypt (3.1.11)
|
64
64
|
bootstrap-sass (3.3.7)
|
@@ -151,7 +151,7 @@ GEM
|
|
151
151
|
sprockets (>= 2.8, < 4.0)
|
152
152
|
sprockets-rails (>= 2.0, < 4.0)
|
153
153
|
tilt (>= 1.1, < 3)
|
154
|
-
shells (0.1.
|
154
|
+
shells (0.1.10)
|
155
155
|
net-ssh (~> 3.0.2)
|
156
156
|
rubyserial (~> 0.4.0)
|
157
157
|
spawnling (2.1.6)
|
@@ -165,7 +165,7 @@ GEM
|
|
165
165
|
sqlite3 (1.3.13)
|
166
166
|
thor (0.19.4)
|
167
167
|
thread_safe (0.3.6)
|
168
|
-
tilt (2.0.
|
168
|
+
tilt (2.0.8)
|
169
169
|
tiny_tds (1.3.0)
|
170
170
|
mini_portile2 (~> 2.0)
|
171
171
|
tzinfo (1.2.3)
|
@@ -8,7 +8,7 @@ module Incline
|
|
8
8
|
def edit
|
9
9
|
if logged_in?
|
10
10
|
flash[:danger] = 'You cannot reactivate your account.'
|
11
|
-
redirect_to root_url
|
11
|
+
redirect_to main_app.root_url
|
12
12
|
else
|
13
13
|
user = User.find_by(email: params[:email].downcase)
|
14
14
|
if user && !user.activated? && user.authenticated?(:activation, params[:id])
|
@@ -18,7 +18,7 @@ module Incline
|
|
18
18
|
redirect_to user
|
19
19
|
else
|
20
20
|
flash[:danger] = 'Invalid activation link'
|
21
|
-
redirect_to root_url
|
21
|
+
redirect_to main_app.root_url
|
22
22
|
end
|
23
23
|
end
|
24
24
|
end
|
@@ -40,7 +40,7 @@ module Incline
|
|
40
40
|
end
|
41
41
|
|
42
42
|
flash[:info] = 'An email with password reset information has been sent to you.'
|
43
|
-
redirect_to root_url
|
43
|
+
redirect_to main_app.root_url
|
44
44
|
end
|
45
45
|
|
46
46
|
##
|
@@ -98,7 +98,7 @@ module Incline
|
|
98
98
|
|
99
99
|
def valid_user
|
100
100
|
unless @user && @user.enabled? && @user.activated? && @user.authenticated?(:reset, params[:id])
|
101
|
-
redirect_to root_url
|
101
|
+
redirect_to main_app.root_url
|
102
102
|
end
|
103
103
|
end
|
104
104
|
|
@@ -29,7 +29,7 @@ module Incline
|
|
29
29
|
redirect_back_or @user
|
30
30
|
else
|
31
31
|
flash[:safe_warning] = 'Your account has not yet been activated.<br/>Check your email for the activation link.'
|
32
|
-
redirect_to root_url
|
32
|
+
redirect_to main_app.root_url
|
33
33
|
end
|
34
34
|
else
|
35
35
|
# deny login.
|
@@ -42,7 +42,7 @@ module Incline
|
|
42
42
|
# DELETE /incline/logout
|
43
43
|
def destroy
|
44
44
|
log_out if logged_in?
|
45
|
-
redirect_to root_url
|
45
|
+
redirect_to main_app.root_url
|
46
46
|
end
|
47
47
|
|
48
48
|
end
|
data/exe/incline
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# If installed as a gem, this won't do anything, however if we are
|
4
|
+
# working from the source, this will ensure our lib path is searched
|
5
|
+
# for require and include calls.
|
6
|
+
git_path = File.expand_path('../../.git', __FILE__)
|
7
|
+
if File.exist?(git_path)
|
8
|
+
$:.unshift File.expand_path('../../lib', __FILE__)
|
9
|
+
end
|
10
|
+
|
11
|
+
require 'incline/cli'
|
12
|
+
Incline::CLI.new.execute(*ARGV)
|
@@ -0,0 +1,743 @@
|
|
1
|
+
module Incline
|
2
|
+
module CliHelpers
|
3
|
+
|
4
|
+
##
|
5
|
+
# Adds YAML text helper methods.
|
6
|
+
#
|
7
|
+
# Does not parse YAML files, but does allow for updating raw text files to be YAML compliant.
|
8
|
+
module Yaml
|
9
|
+
|
10
|
+
class YamlError < ::Incline::CLI::CliError; end
|
11
|
+
|
12
|
+
##
|
13
|
+
# Helper class to process the YAML file contents easily.
|
14
|
+
class YamlContents
|
15
|
+
|
16
|
+
##
|
17
|
+
# Creates a new YAML contents.
|
18
|
+
def initialize(content)
|
19
|
+
@content = content.to_s.gsub("\r\n", "\n").strip + "\n"
|
20
|
+
end
|
21
|
+
|
22
|
+
##
|
23
|
+
# Returns the YAML contents.
|
24
|
+
def to_s
|
25
|
+
@content
|
26
|
+
end
|
27
|
+
|
28
|
+
##
|
29
|
+
# Adds a key to the YAML contents if it is missing.
|
30
|
+
# Does nothing to the key if it exists.
|
31
|
+
#
|
32
|
+
# add_key [ "default", "name" ], "george"
|
33
|
+
#
|
34
|
+
# The 'key' should be an array defining the path.
|
35
|
+
#
|
36
|
+
# Value can be nil, a string, a symbol, a number, or a boolean.
|
37
|
+
#
|
38
|
+
# The 'make_safe_value' option can be used to provide an explicit text value.
|
39
|
+
# This can be useful if you want to add a specific value, like an ERB command.
|
40
|
+
#
|
41
|
+
# add_key [ "default", "name" ], "<%= ENV[\"DEFAULT_USER\"] %>", false
|
42
|
+
#
|
43
|
+
# You can also use a hash for the value to specify advanced options.
|
44
|
+
# Currently only three advanced options are recognized.
|
45
|
+
#
|
46
|
+
# The first option, :value, simply sets the value. If this is the only
|
47
|
+
# hash key provided, then the value supplied is treated as if it was the original
|
48
|
+
# value. In other words, only setting :value is the same as not using a hash and
|
49
|
+
# just passing in the value, so the value must be nil, a string, a symbol, a number,
|
50
|
+
# or a boolean.
|
51
|
+
#
|
52
|
+
# The second option, :safe, works the opposite of the 'make_safe_value' parameter.
|
53
|
+
# If :safe is a non-false value, then it is like 'make_safe_value' is set to false.
|
54
|
+
# If :safe is a false value, then it is like 'make_safe_value' is set to true.
|
55
|
+
# The :safe value can be set to true and the :value option can set the value, or
|
56
|
+
# the :safe value can be set to the value directly since all strings are non-false.
|
57
|
+
#
|
58
|
+
# The third option, :before_section, tells add_key to insert the section before the
|
59
|
+
# named section (if the new section doesn't exist). This can be useful if the named
|
60
|
+
# section is going to be referencing the key you are adding. Otherwise, when a
|
61
|
+
# section needs to be added, it gets added to the end of the file.
|
62
|
+
#
|
63
|
+
# Returns the contents object.
|
64
|
+
def add_key(key, value, make_safe_value = true)
|
65
|
+
|
66
|
+
# ensure the parent structure exists!
|
67
|
+
if key.count > 1
|
68
|
+
add_key(key[0...-1], nil)
|
69
|
+
else
|
70
|
+
# If the base key already exists, no need to look further.
|
71
|
+
return self if @content =~ /^#{key.first}:/
|
72
|
+
|
73
|
+
unless @content[0] == '#'
|
74
|
+
@content = "# File modified by Incline v#{Incline::VERSION}.\n" + @content
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
val_name = key.last
|
79
|
+
|
80
|
+
# construct a regular expression to find the parent group and value.
|
81
|
+
rex_str = '^('
|
82
|
+
rex_prefix = /\A./
|
83
|
+
key.each_with_index do |attr,level|
|
84
|
+
lev = (level < 1 ? '' : ('\\s\\s' * (level)))
|
85
|
+
if lev != ''
|
86
|
+
rex_str += '(?:' + lev + '[^\\n]*\\n)*'
|
87
|
+
end
|
88
|
+
if level == key.count - 1
|
89
|
+
if level == 0
|
90
|
+
# At level 0 we cheat and use a very simple regular expression to confirm the section exists.
|
91
|
+
rex_str = "^#{attr}:"
|
92
|
+
# If it doesn't exists, the prefix regex will usually want to put the new section at the end of the
|
93
|
+
# file. However if the :before_section option is set, and the other section exists, then we
|
94
|
+
# want to ensure that we are putting the new section before it.
|
95
|
+
#
|
96
|
+
# Down below we take care to reverse the replacement string when key.count == 1.
|
97
|
+
rex_prefix =
|
98
|
+
if value.is_a?(::Hash) && value[:before_section] && @content =~ /^#{value[:before_section]}:/
|
99
|
+
/(^#{value[:before_section]}:)/
|
100
|
+
else
|
101
|
+
/(\z)/ # match the end of the contents.
|
102
|
+
end
|
103
|
+
else
|
104
|
+
rex_str += ')'
|
105
|
+
rex_prefix = Regexp.new(rex_str)
|
106
|
+
rex_str += '(' + lev + attr + ':.*\\n)'
|
107
|
+
end
|
108
|
+
else
|
109
|
+
rex_str += lev + attr + ':.*\\n'
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
rex = Regexp.new(rex_str)
|
114
|
+
|
115
|
+
if @content =~ rex
|
116
|
+
# all good.
|
117
|
+
elsif @content =~ rex_prefix
|
118
|
+
if make_safe_value
|
119
|
+
value = safe_value(value)
|
120
|
+
value = add_value_offset(key, value)
|
121
|
+
elsif value.is_a?(::Hash)
|
122
|
+
value = value[:value]
|
123
|
+
end
|
124
|
+
value = '' if value =~ /\A\s*\z/
|
125
|
+
# Should be true thanks to first step in this method.
|
126
|
+
# Capture 1 would be the parent group.
|
127
|
+
# When key.count == 1 then we want to put our new value before capture 1.
|
128
|
+
# Otherwise we put our new value after capture 1.
|
129
|
+
rep = if key.count == 1
|
130
|
+
"\n#{val_name}:#{value}\n\\1"
|
131
|
+
else
|
132
|
+
"\\1#{' ' * (key.count - 1)}#{val_name}:#{value}\n"
|
133
|
+
end
|
134
|
+
|
135
|
+
@content.gsub! rex_prefix, rep
|
136
|
+
else
|
137
|
+
raise ::Incline::CliHelpers::Yaml::YamlError, "Failed to create parent group for '#{key.join('/')}'."
|
138
|
+
end
|
139
|
+
|
140
|
+
self
|
141
|
+
end
|
142
|
+
|
143
|
+
|
144
|
+
##
|
145
|
+
# Adds a key to the YAML contents if it is missing.
|
146
|
+
# Does nothing to the key if it exists.
|
147
|
+
#
|
148
|
+
# add_key_with_comment [ "default", "name" ], "george", "this is the name of the default user"
|
149
|
+
#
|
150
|
+
# The 'key' should be an array defining the path.
|
151
|
+
# If the 'comment' is blank (nil or ''), then it will not modify the comment.
|
152
|
+
# Use a whitespace string (' ') to indicate that you want a blank comment added.
|
153
|
+
#
|
154
|
+
# Value can be nil, a string, a symbol, a number, or a boolean.
|
155
|
+
# Value can also be a hash according to #add_key.
|
156
|
+
#
|
157
|
+
# Returns the contents object.
|
158
|
+
def add_key_with_comment(key, value, comment)
|
159
|
+
if comment.to_s == ''
|
160
|
+
add_key key, value
|
161
|
+
else
|
162
|
+
add_key key, value_with_comment(key, value, comment), false
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
##
|
167
|
+
# Sets a key in the YAML contents.
|
168
|
+
# Adds the key if it is missing, replaces it if it already exists.
|
169
|
+
#
|
170
|
+
# set_key [ "default", "name" ], "george"
|
171
|
+
#
|
172
|
+
# The 'key' should be an array defining the path.
|
173
|
+
#
|
174
|
+
# Value can be nil, a string, a symbol, a number, or a boolean.
|
175
|
+
# Value can also be a hash according to #add_key.
|
176
|
+
#
|
177
|
+
# The 'make_safe_value' option can be used to provide an explicit text value.
|
178
|
+
# This can be useful if you want to add a specific value, like an ERB command.
|
179
|
+
#
|
180
|
+
# set_key [ "default", "name" ], "<%= ENV[\"DEFAULT_USER\"] %>", false
|
181
|
+
#
|
182
|
+
# Returns the contents object.
|
183
|
+
def set_key(key, value, make_safe_value = true)
|
184
|
+
|
185
|
+
# construct a regular expression to find the value and not confuse it with any other value in the file.
|
186
|
+
rex_str = '^('
|
187
|
+
key.each_with_index do |attr,level|
|
188
|
+
lev = (level < 1 ? '' : ('\\s\\s' * (level)))
|
189
|
+
if lev != ''
|
190
|
+
rex_str += '(?:' + lev + '.*\\n)*'
|
191
|
+
end
|
192
|
+
if level == key.count - 1
|
193
|
+
rex_str += lev + attr + ':)\\s*([^#\\n]*)?(#[^\\n]*)?\\n'
|
194
|
+
else
|
195
|
+
rex_str += lev + attr + ':.*\\n'
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
rex = Regexp.new(rex_str)
|
200
|
+
|
201
|
+
if @content =~ rex
|
202
|
+
if make_safe_value
|
203
|
+
value = safe_value(value)
|
204
|
+
value = add_value_offset(key, value)
|
205
|
+
elsif value.is_a?(::Hash)
|
206
|
+
value = value[:value]
|
207
|
+
end
|
208
|
+
value = '' if value =~ /\A\s*\z/
|
209
|
+
# Capture 1 is everything before the value.
|
210
|
+
# Capture 2 is going to be just the value.
|
211
|
+
# Capture 3 is the comment (if any). This allows us to propagate comments if we change a value.
|
212
|
+
if $2 != value
|
213
|
+
rep = "\\1#{value}\\3\n"
|
214
|
+
@content.gsub! rex, rep
|
215
|
+
end
|
216
|
+
|
217
|
+
self
|
218
|
+
else
|
219
|
+
add_key(key, value, make_safe_value)
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
##
|
224
|
+
# Sets a key in the YAML contents.
|
225
|
+
# Adds the key if it is missing, replaces it if it already exists.
|
226
|
+
#
|
227
|
+
# set_key_with_comment [ "default", "name" ], "george", "this is the name of the default user"
|
228
|
+
#
|
229
|
+
# The 'key' should be an array defining the path.
|
230
|
+
# If the 'comment' is blank (nil or ''), then it will not modify the comment.
|
231
|
+
# Use a whitespace string (' ') to indicate that you want a blank comment added.
|
232
|
+
#
|
233
|
+
# Value can be nil, a string, a symbol, a number, or a boolean.
|
234
|
+
# Value can also be a hash according to #add_key.
|
235
|
+
#
|
236
|
+
# Returns the contents object.
|
237
|
+
def set_key_with_comment(key, value, comment)
|
238
|
+
if comment.to_s == ''
|
239
|
+
set_key key, value
|
240
|
+
else
|
241
|
+
set_key key, value_with_comment(key, value, comment), false
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
##
|
246
|
+
# Removes the specified key from the contents.
|
247
|
+
#
|
248
|
+
# Returns an array containing the contents of the key.
|
249
|
+
# The first element will be for the key itself. If the
|
250
|
+
# key had child keys, then they will also be included in
|
251
|
+
# the array.
|
252
|
+
#
|
253
|
+
# The returned array will contain hashes for each removed
|
254
|
+
# key.
|
255
|
+
#
|
256
|
+
# data = remove_key %w(pet dog)
|
257
|
+
#
|
258
|
+
# [
|
259
|
+
# {
|
260
|
+
# :key => [ "pet", "dog" ],
|
261
|
+
# :value => "",
|
262
|
+
# :safe => true,
|
263
|
+
# :comment => "This list has the family dogs."
|
264
|
+
# },
|
265
|
+
# {
|
266
|
+
# :key => [ "pet", "dog", "sadie" ],
|
267
|
+
# :value => "",
|
268
|
+
# :safe => true,
|
269
|
+
# :comment => ""
|
270
|
+
# },
|
271
|
+
# {
|
272
|
+
# :key => [ "pet", "dog", "sadie", "breed" ],
|
273
|
+
# :value => "boxer",
|
274
|
+
# :safe => true,
|
275
|
+
# :comment => ""
|
276
|
+
# },
|
277
|
+
# {
|
278
|
+
# :key => [ "pet", "dog", "sadie", "dob" ],
|
279
|
+
# :value => "\"2016-06-01\"",
|
280
|
+
# :safe => true,
|
281
|
+
# :comment => "Estimated date of birth since she was a rescue."
|
282
|
+
# }
|
283
|
+
# ]
|
284
|
+
#
|
285
|
+
# The returned hashes can be fired right back into #add_key.
|
286
|
+
#
|
287
|
+
# data.each do |item|
|
288
|
+
# add_key_with_comment item[:key], item, item[:comment]
|
289
|
+
# end
|
290
|
+
#
|
291
|
+
# This method can be used to move a section within the file.
|
292
|
+
#
|
293
|
+
# # remove the 'familes' section from the file.
|
294
|
+
# section = remove_key [ "families" ]
|
295
|
+
# item = section.delete(section.first)
|
296
|
+
#
|
297
|
+
# # add the 'familes' section back in before the 'pets' section.
|
298
|
+
# add_key_with_comment item[:key], { before_section: "pets" }.merge(item), item[:comment]
|
299
|
+
#
|
300
|
+
# # add the data back into the 'familes' section.
|
301
|
+
# section.each do |item|
|
302
|
+
# add_key_with_comment item[:key], item, item[:comment]
|
303
|
+
# end
|
304
|
+
#
|
305
|
+
def remove_key(key)
|
306
|
+
rex_str = '(^'
|
307
|
+
key.each_with_index do |attr,level|
|
308
|
+
lev = (level < 1 ? '' : ('\\s\\s' * level))
|
309
|
+
if lev != ''
|
310
|
+
rex_str += '(?:' + lev + '.*\\n)*'
|
311
|
+
end
|
312
|
+
if level == key.count - 1
|
313
|
+
if level == 0
|
314
|
+
rex_str = '(^)(' + attr + ':[^\\n]*\\n(?:\\s\\s[^\\n]*\\n)*)'
|
315
|
+
else
|
316
|
+
rex_str += ')(' + lev + attr + ':[^\\n]*\\n(?:' + lev + '\\s\\s[^\\n]*\\n)*)'
|
317
|
+
end
|
318
|
+
else
|
319
|
+
rex_str += lev + attr + ':[^\\n]*\\n'
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
# match result 1 is the parent key structure leading up to the key to be extracted.
|
324
|
+
# match result 2 is the key with all child elements to be extracted.
|
325
|
+
|
326
|
+
rex = Regexp.new(rex_str)
|
327
|
+
|
328
|
+
if @content =~ rex
|
329
|
+
|
330
|
+
# cache the key contents
|
331
|
+
key_content = $2
|
332
|
+
|
333
|
+
# remove the key from the main contents.
|
334
|
+
@content.gsub!(rex, "\\1")
|
335
|
+
|
336
|
+
# and separate into lines.
|
337
|
+
lines = extract_to_array(key_content)
|
338
|
+
|
339
|
+
ret = []
|
340
|
+
|
341
|
+
base_key = key.length == 1 ? [] : key[0...-1]
|
342
|
+
|
343
|
+
last_line = nil
|
344
|
+
|
345
|
+
lines.each do |line|
|
346
|
+
level = line[:level]
|
347
|
+
if level > 0 && line[:key]
|
348
|
+
# go from base 1 to base 0
|
349
|
+
level -= 1
|
350
|
+
|
351
|
+
# make sure the base key is the right length for the current key.
|
352
|
+
while level > base_key.length
|
353
|
+
base_key.push '?' # hopefully this never occurs.
|
354
|
+
end
|
355
|
+
while level < base_key.length
|
356
|
+
base_key.pop
|
357
|
+
end
|
358
|
+
|
359
|
+
# add our key to the base key.
|
360
|
+
# if the next key is below it, this ensures the parent structure is correct.
|
361
|
+
# if the next key is higher or at the same level the above loops should make it correct.
|
362
|
+
base_key << line[:key]
|
363
|
+
|
364
|
+
last_line = {
|
365
|
+
key: base_key,
|
366
|
+
value: line[:value].to_s,
|
367
|
+
comment: line[:comment],
|
368
|
+
safe: true
|
369
|
+
}
|
370
|
+
|
371
|
+
ret << last_line
|
372
|
+
elsif level > 0 && line[:comment]
|
373
|
+
if last_line && last_line[:key].length == level
|
374
|
+
if last_line[:comment]
|
375
|
+
last_line[:comment] += "\n" + line[:comment]
|
376
|
+
else
|
377
|
+
last_line[:comment] = "\n" + line[:comment]
|
378
|
+
end
|
379
|
+
end
|
380
|
+
end
|
381
|
+
end
|
382
|
+
|
383
|
+
ret
|
384
|
+
else
|
385
|
+
[]
|
386
|
+
end
|
387
|
+
end
|
388
|
+
|
389
|
+
##
|
390
|
+
# Realigns the file.
|
391
|
+
#
|
392
|
+
# All values and comments will line up at each level when complete.
|
393
|
+
def realign!
|
394
|
+
lines = extract_to_array(@content)
|
395
|
+
|
396
|
+
# reset the offsets.
|
397
|
+
value_offsets.clear
|
398
|
+
comment_offsets.clear
|
399
|
+
|
400
|
+
# get value offsets.
|
401
|
+
lines.each do |line|
|
402
|
+
level = line[:level]
|
403
|
+
if level > 0 && line[:key]
|
404
|
+
key_len = line[:key].length + 2 # include the colon and a space
|
405
|
+
if key_len > level_value_offset(level)
|
406
|
+
set_level_value_offset level, key_len
|
407
|
+
end
|
408
|
+
end
|
409
|
+
end
|
410
|
+
|
411
|
+
# get comment offsets.
|
412
|
+
lines.each do |line|
|
413
|
+
level = line[:level]
|
414
|
+
if level > 0 && line[:value]
|
415
|
+
voff = level_value_offset(level)
|
416
|
+
val_len = line[:value] ? line[:value].length : 0
|
417
|
+
coff = voff + val_len + 1 # add a space after the value.
|
418
|
+
if coff > level_comment_offset(level)
|
419
|
+
set_level_comment_offset level, coff
|
420
|
+
end
|
421
|
+
end
|
422
|
+
end
|
423
|
+
|
424
|
+
# convert the lines back into strings with proper spacing.
|
425
|
+
lines = lines.map do |line|
|
426
|
+
level = line[:level]
|
427
|
+
if level > 0
|
428
|
+
if line[:key]
|
429
|
+
# a key: value line.
|
430
|
+
key = line[:key] + ':'
|
431
|
+
key = key.ljust(level_value_offset(level), ' ') unless line[:value].to_s == '' && line[:comment].to_s == ''
|
432
|
+
val = line[:value].to_s
|
433
|
+
val = val.ljust(level_comment_offset(level) - level_value_offset(level), ' ') unless line[:comment].to_s == ''
|
434
|
+
comment = line[:comment] ? "# #{line[:comment]}" : ''
|
435
|
+
(' ' * (level - 1)) + key + val + comment
|
436
|
+
else
|
437
|
+
# just a comment line.
|
438
|
+
(' ' * (level - 1)) +
|
439
|
+
(' ' * level_comment_offset(level)) +
|
440
|
+
"# #{line[:comment]}"
|
441
|
+
end
|
442
|
+
else
|
443
|
+
line[:value] # return the original value
|
444
|
+
end
|
445
|
+
end
|
446
|
+
|
447
|
+
@content = lines.join("\n") + "\n"
|
448
|
+
end
|
449
|
+
|
450
|
+
##
|
451
|
+
# Gets the value offset for the specified level.
|
452
|
+
#
|
453
|
+
# The offset is based on the beginning of the level in question.
|
454
|
+
# There will always be at least one whitespace before the value.
|
455
|
+
# For instance an offset of 10 for level 2 would be like this:
|
456
|
+
#
|
457
|
+
# one:
|
458
|
+
# two: value
|
459
|
+
# some_long_name: value
|
460
|
+
# # 0123456789^
|
461
|
+
#
|
462
|
+
def level_value_offset(level)
|
463
|
+
value_offsets[level] || 0
|
464
|
+
end
|
465
|
+
|
466
|
+
##
|
467
|
+
# Sets the value offset for the specified level.
|
468
|
+
#
|
469
|
+
# The offset is based on the beginning of the level in question.
|
470
|
+
# There will always be at least one whitespace before the value.
|
471
|
+
# For instance an offset of 10 for level 2 would be like this:
|
472
|
+
#
|
473
|
+
# one:
|
474
|
+
# two: value
|
475
|
+
# some_long_name: value
|
476
|
+
# # 0123456789^
|
477
|
+
#
|
478
|
+
def set_level_value_offset(level, offset)
|
479
|
+
value_offsets[level] = offset
|
480
|
+
end
|
481
|
+
|
482
|
+
##
|
483
|
+
# Gets the comment offset for the specified level.
|
484
|
+
#
|
485
|
+
# The offset is based on the beginning of the level in question.
|
486
|
+
# There will always be at least one whitespace before the value
|
487
|
+
# and at least one whitespace between the value and a comment.
|
488
|
+
# For instance an offset of 15 for level 2 would be like this:
|
489
|
+
#
|
490
|
+
# one:
|
491
|
+
# two: value # comment
|
492
|
+
# some_long_name: value # comment
|
493
|
+
# # 012345678901234^
|
494
|
+
#
|
495
|
+
def level_comment_offset(level)
|
496
|
+
comment_offsets[level] || 0
|
497
|
+
end
|
498
|
+
|
499
|
+
##
|
500
|
+
# Sets the comment offset for the specified level.
|
501
|
+
#
|
502
|
+
# The offset is based on the beginning of the level in question.
|
503
|
+
# There will always be at least one whitespace before the value
|
504
|
+
# and at least one whitespace between the value and a comment.
|
505
|
+
# For instance an offset of 15 for level 2 would be like this:
|
506
|
+
#
|
507
|
+
# one:
|
508
|
+
# two: value # comment
|
509
|
+
# some_long_name: value # comment
|
510
|
+
# # 012345678901234^
|
511
|
+
#
|
512
|
+
def set_level_comment_offset(level, offset)
|
513
|
+
comment_offsets[level] = offset
|
514
|
+
end
|
515
|
+
|
516
|
+
##
|
517
|
+
# Allows comparing the contents against a regular expression.
|
518
|
+
def =~(regexp)
|
519
|
+
@content =~ regexp
|
520
|
+
end
|
521
|
+
|
522
|
+
private
|
523
|
+
|
524
|
+
def extract_to_array(data)
|
525
|
+
# match 1 = lead white
|
526
|
+
# ([ \t]*)
|
527
|
+
# match 2 = key name
|
528
|
+
# (\S+):
|
529
|
+
# match 3 = value with leading whitespace
|
530
|
+
# ((?:[ \t]*(?:"(?:[^"]*(?:(?:\\{1}|\\{3}|\\{5}|\\{7}|\\{9})")?)*"|'(?:[^']*(?:(?:\\{1}|\\{3}|\\{5}|\\{7}|\\{9})')?)*'|[^\s#"']+))*)
|
531
|
+
# ignore white between value and comment (if any).
|
532
|
+
# [ \t]*
|
533
|
+
# match 4 = comment (if any).
|
534
|
+
# (?:#([^\n]*))?
|
535
|
+
line_regex = /\A([ \t]*)(\S+):((?:[ \t]*(?:"(?:[^"]*(?:(?:\\{1}|\\{3}|\\{5}|\\{7}|\\{9})")?)*"|'(?:[^']*(?:(?:\\{1}|\\{3}|\\{5}|\\{7}|\\{9})')?)*'|[^\s#"']+))*)[ \t]*(?:#([^\n]*))?\z/
|
536
|
+
last_level = 0
|
537
|
+
data.split("\n").map do |raw_line|
|
538
|
+
# assuming the file is valid any lines not matching the regex should be comments or blank.
|
539
|
+
match = line_regex.match(raw_line)
|
540
|
+
if match # a key: value line
|
541
|
+
last_level = (match[1].length / 2).to_i + 1 # one level per 2 spaces.
|
542
|
+
{
|
543
|
+
level: last_level,
|
544
|
+
key: match[2].strip,
|
545
|
+
value: match[3] ? match[3].strip : nil,
|
546
|
+
comment: match[4] ? match[4].lstrip : nil
|
547
|
+
}
|
548
|
+
elsif raw_line =~ /\A(\s*)#(.*)\z/ # a comment
|
549
|
+
whitespace = $1
|
550
|
+
raw_line = $2
|
551
|
+
level =
|
552
|
+
if whitespace.length >= (last_level * 2)
|
553
|
+
last_level
|
554
|
+
else
|
555
|
+
(whitespace.length / 2).to_i + 1
|
556
|
+
end
|
557
|
+
raw_line = raw_line[1..-1] if raw_line[0] == ' '
|
558
|
+
{
|
559
|
+
level: level,
|
560
|
+
comment: raw_line
|
561
|
+
}
|
562
|
+
else
|
563
|
+
{
|
564
|
+
level: 0,
|
565
|
+
value: raw_line
|
566
|
+
}
|
567
|
+
end
|
568
|
+
end
|
569
|
+
end
|
570
|
+
|
571
|
+
def value_with_comment(key, value, comment)
|
572
|
+
vh = value.is_a?(::Hash) ? value : { safe: false, value: value, before_section: nil }
|
573
|
+
unless vh[:safe]
|
574
|
+
vh[:value] = add_comment(key, add_value_offset(key, safe_value(vh[:value])), comment)
|
575
|
+
vh[:safe] = true
|
576
|
+
end
|
577
|
+
vh
|
578
|
+
end
|
579
|
+
|
580
|
+
def add_value_offset(key, safe_value)
|
581
|
+
key_len = key.last.to_s.length + 1 # add one for the colon.
|
582
|
+
voff = level_value_offset(key.count) - key_len
|
583
|
+
voff = 1 if voff < 1
|
584
|
+
|
585
|
+
(' ' * voff) + safe_value
|
586
|
+
end
|
587
|
+
|
588
|
+
def add_comment(key, safe_value, comment)
|
589
|
+
key_len = key.last.to_s.length + 1 # add one for the colon.
|
590
|
+
coff = level_comment_offset(key.count) - key_len - safe_value.length
|
591
|
+
coff = 1 if coff < 1
|
592
|
+
coff_total = ((key.count - 1) * 2) + value.length + coff
|
593
|
+
|
594
|
+
safe_value + (' ' * coff) + '# ' + comment.to_s.gsub("\r\n", "\n").gsub("\n", "\n#{' ' * coff_total}# ")
|
595
|
+
end
|
596
|
+
|
597
|
+
def value_offsets
|
598
|
+
@value_offsets ||= [ ]
|
599
|
+
end
|
600
|
+
|
601
|
+
def comment_offsets
|
602
|
+
@comment_offsets ||= [ ]
|
603
|
+
end
|
604
|
+
|
605
|
+
# always returns a string, even for safe values.
|
606
|
+
def safe_value(value)
|
607
|
+
# Allows the user to specify the value as a hash option without marking it as safe.
|
608
|
+
if value.is_a?(::Hash) && value[:value] && !value[:safe]
|
609
|
+
value = value[:value]
|
610
|
+
end
|
611
|
+
|
612
|
+
if value.is_a?(::Hash) && value[:safe]
|
613
|
+
# If the user specifies a safe value, return it as-is.
|
614
|
+
(value[:value] || value[:safe]).to_s
|
615
|
+
elsif value.nil?
|
616
|
+
# If the value is nil, return an empty string.
|
617
|
+
''
|
618
|
+
else
|
619
|
+
# Otherwise process the value to make it YAML compliant.
|
620
|
+
unless value.is_a?(::String) || value.is_a?(::Symbol) || value.is_a?(::Numeric) || value.is_a?(::TrueClass) || value.is_a?(::FalseClass)
|
621
|
+
raise ArgumentError, "'value' must be a value type (string, symbol, number, boolean)"
|
622
|
+
end
|
623
|
+
|
624
|
+
if value.is_a?(::String)
|
625
|
+
if value =~ /\A\s*\z/m || # Empty or filled with whitespace
|
626
|
+
value =~ /\A\s/m || # Starts with whitespace
|
627
|
+
value =~ /\s\z/m || # Ends with whitespace
|
628
|
+
value =~ /\A[+-]?\d+(\.\d*)?\z/ || # Contains a probable number
|
629
|
+
value =~ /\A0(b[01]*|x[0-9a-f]*)\z/i # Another probable number in binary or hex format.
|
630
|
+
value.inspect
|
631
|
+
elsif value =~ /\A([0-9]*[a-z]|[a-z])([a-z0-9_ .,=-]*[a-z0-9_])*\z/i
|
632
|
+
value
|
633
|
+
else
|
634
|
+
value.inspect
|
635
|
+
end
|
636
|
+
else
|
637
|
+
value.inspect
|
638
|
+
end
|
639
|
+
end
|
640
|
+
|
641
|
+
end
|
642
|
+
|
643
|
+
end
|
644
|
+
|
645
|
+
protected
|
646
|
+
|
647
|
+
##
|
648
|
+
# Repairs a YAML file, creating it if necessary.
|
649
|
+
#
|
650
|
+
# The optional parameters define what all will be done by this method before yielding to a supplied block.
|
651
|
+
# The contents of the YAML file are yielded to the block and the block should return the modified contents.
|
652
|
+
#
|
653
|
+
# repair_yaml "config/database.yml" do |contents|
|
654
|
+
# contents.add_key [ "test", "database" ], "db/test.sqlite3"
|
655
|
+
# end
|
656
|
+
#
|
657
|
+
# If 'env_ref_set' is set to a non-empty value, then it defines the anchor providing default values
|
658
|
+
# for the environment sections. The default value is 'default'.
|
659
|
+
#
|
660
|
+
# If 'ensure_default' is set, then a 'default' section with a 'default' anchor will be created at the
|
661
|
+
# beginning of the file unless a 'default' section already exists in the file.
|
662
|
+
# The default value is 'true'.
|
663
|
+
#
|
664
|
+
# If 'ensure_env' is set, then the environment sections will be created if missing. This ensures that
|
665
|
+
# a 'development', 'test', and 'production' section all exist. The default value is 'true'.
|
666
|
+
#
|
667
|
+
# If 'realign' is set, then the entire file will be processed. All values will be aligned at each
|
668
|
+
# level and so will all comments. The default is 'true'.
|
669
|
+
#
|
670
|
+
# The default options on an empty file will generate a 'default' section before yielding and then
|
671
|
+
# fill in the environment sections after the block returns.
|
672
|
+
#
|
673
|
+
# default: &default
|
674
|
+
#
|
675
|
+
# development:
|
676
|
+
# <<: *default
|
677
|
+
#
|
678
|
+
# test:
|
679
|
+
# <<: *default
|
680
|
+
#
|
681
|
+
# production:
|
682
|
+
# <<: *default
|
683
|
+
#
|
684
|
+
def repair_yaml(filename, env_ref_sect = 'default', ensure_default = true, ensure_env = true, realign = true) # :doc:
|
685
|
+
dirty = false
|
686
|
+
stat = :updated
|
687
|
+
|
688
|
+
# ensure the file exists.
|
689
|
+
unless File.exist?(filename)
|
690
|
+
File.write filename, "# File created by Incline v#{Incline::VERSION}.\n"
|
691
|
+
stat = :created
|
692
|
+
dirty = true
|
693
|
+
end
|
694
|
+
|
695
|
+
orig_contents = File.read(filename)
|
696
|
+
|
697
|
+
contents = YamlContents.new(orig_contents)
|
698
|
+
|
699
|
+
if ensure_default
|
700
|
+
contents.set_key %w(default), '&default', false
|
701
|
+
end
|
702
|
+
|
703
|
+
yield contents if block_given?
|
704
|
+
|
705
|
+
if ensure_env
|
706
|
+
%w(development test production).each do |sect|
|
707
|
+
contents.add_key [ sect ], nil
|
708
|
+
end
|
709
|
+
end
|
710
|
+
|
711
|
+
# Potential bug, and I'm not even sure if it would be or not.
|
712
|
+
# But since we use relatively simple regular expressions to perform the set action,
|
713
|
+
# the << "key" can only exist once and would be overridden.
|
714
|
+
# That means that a section would not be able to include multiple anchors.
|
715
|
+
# Like I said before, I'm not sure if that would actually be a bug or not.
|
716
|
+
#
|
717
|
+
# Luckily, the YAML files aren't meant to be overly complex so this shouldn't show
|
718
|
+
# up regularly if at all.
|
719
|
+
unless env_ref_sect.to_s.strip == ''
|
720
|
+
%w(development test production).each do |sect|
|
721
|
+
contents.set_key [ sect, '<<' ], '*' + env_ref_sect
|
722
|
+
end
|
723
|
+
end
|
724
|
+
|
725
|
+
contents.realign! if realign
|
726
|
+
|
727
|
+
unless dirty
|
728
|
+
dirty = (orig_contents != contents.to_s)
|
729
|
+
end
|
730
|
+
|
731
|
+
if dirty
|
732
|
+
File.write filename, contents.to_s
|
733
|
+
say_status stat, filename, :green
|
734
|
+
else
|
735
|
+
say_status :unchanged, filename, :blue
|
736
|
+
end
|
737
|
+
end
|
738
|
+
|
739
|
+
|
740
|
+
|
741
|
+
end
|
742
|
+
end
|
743
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
|
2
|
+
module Incline
|
3
|
+
class CLI
|
4
|
+
|
5
|
+
##
|
6
|
+
# Defines the 'usage' command for the CLI.
|
7
|
+
class Usage
|
8
|
+
|
9
|
+
##
|
10
|
+
# Creates a new 'usage' command for the CLI.
|
11
|
+
def initialize
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
##
|
16
|
+
# Shows usage information for the application.
|
17
|
+
def run
|
18
|
+
|
19
|
+
commands = Incline::CLI::valid_commands.sort{|a,b| a[0] <=> b[0]}
|
20
|
+
|
21
|
+
msg = ANSI.ansi(:bold) { "Usage: #{$PROGRAM_NAME} command <arguments>" }
|
22
|
+
msg += "\nValid Commands:\n"
|
23
|
+
|
24
|
+
commands.each do |(name,klass,params)|
|
25
|
+
comment = get_run_comment(klass)
|
26
|
+
comment = "(No description)" if comment.to_s.strip == ''
|
27
|
+
comment = ' ' + comment.gsub("\n", "\n ")
|
28
|
+
msg += " #{name}"
|
29
|
+
pend = ''
|
30
|
+
params.each do |t,p|
|
31
|
+
msg += ' '
|
32
|
+
if t == :req
|
33
|
+
msg += p.to_s
|
34
|
+
elsif t == :opt
|
35
|
+
msg += '[' + p.to_s
|
36
|
+
pend += ']'
|
37
|
+
else
|
38
|
+
msg += '<...>'
|
39
|
+
end
|
40
|
+
end
|
41
|
+
msg += "\n" + comment + "\n"
|
42
|
+
end
|
43
|
+
|
44
|
+
STDOUT.print msg
|
45
|
+
msg
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def get_run_comment(klass)
|
51
|
+
meth = klass.instance_method(:run)
|
52
|
+
return '' unless meth
|
53
|
+
file,line_num = meth.source_location
|
54
|
+
return '' unless file && line_num
|
55
|
+
return '' unless File.exist?(file)
|
56
|
+
|
57
|
+
source_lines = File.read(file).gsub("\r\n", "\n").split("\n")
|
58
|
+
|
59
|
+
comments = []
|
60
|
+
|
61
|
+
# line_num is 1 based so we need to subtract 1 to get the line with the method definition
|
62
|
+
# then we subtract 1 again to get the first line before the method definition.
|
63
|
+
line_num -= 2
|
64
|
+
|
65
|
+
while line_num >= 0 && source_lines[line_num] =~ /\A(?:\s*)(?:#(?:\s(.*))?)?\z/
|
66
|
+
line = $1.to_s
|
67
|
+
comments << line
|
68
|
+
line_num -= 1
|
69
|
+
end
|
70
|
+
|
71
|
+
comments.reverse.join("\n")
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
|
2
|
+
module Incline
|
3
|
+
class CLI
|
4
|
+
|
5
|
+
##
|
6
|
+
# Defines the 'version' command for the CLI.
|
7
|
+
class Version
|
8
|
+
|
9
|
+
##
|
10
|
+
# Creates a new 'version' command for the CLI.
|
11
|
+
def initialize
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
##
|
16
|
+
# Shows the version of the Incline library.
|
17
|
+
def run
|
18
|
+
STDOUT.puts "Incline v#{Incline::VERSION}"
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/incline/cli.rb
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'ansi/code'
|
2
|
+
require 'incline/version'
|
3
|
+
require 'incline/cli/errors'
|
4
|
+
|
5
|
+
require 'incline/cli/helpers/yaml'
|
6
|
+
|
7
|
+
require 'incline/cli/version'
|
8
|
+
require 'incline/cli/usage'
|
9
|
+
|
10
|
+
|
11
|
+
module Incline
|
12
|
+
class CLI
|
13
|
+
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
def execute(*args)
|
20
|
+
begin
|
21
|
+
if args.empty? || %w(usage help /? -? -help --help).include?(args.first)
|
22
|
+
process_command(:usage)
|
23
|
+
else
|
24
|
+
process_command(*args)
|
25
|
+
end
|
26
|
+
rescue UsageError => err
|
27
|
+
STDERR.puts err.message
|
28
|
+
process_command(:usage)
|
29
|
+
rescue CliError => err
|
30
|
+
STDERR.puts ANSI.code(:red) { 'ERROR:' }
|
31
|
+
STDERR.puts err.message
|
32
|
+
rescue RuntimeError => err
|
33
|
+
STDERR.puts ANSI.code(:red) { 'FATAL ERROR:' }
|
34
|
+
STDERR.puts err.inspect
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def process_command(command, *args)
|
39
|
+
command = command.to_sym
|
40
|
+
cmd_info = self.class.command_list.find{|c| c[:method] == command}
|
41
|
+
if cmd_info
|
42
|
+
args = args.dup
|
43
|
+
args = []
|
44
|
+
cmd_info[:new_params].each do |(type,name)|
|
45
|
+
if type == :rest
|
46
|
+
args += args
|
47
|
+
break
|
48
|
+
elsif type == :req
|
49
|
+
if args.empty?
|
50
|
+
raise UsageError, "Missing required parameter '#{name}' for command '#{command}'."
|
51
|
+
end
|
52
|
+
args << args.delete_at(0)
|
53
|
+
elsif type == :opt
|
54
|
+
if args.empty?
|
55
|
+
break
|
56
|
+
else
|
57
|
+
args << args.delete_at(0)
|
58
|
+
end
|
59
|
+
else
|
60
|
+
raise UsageError, "Unknown parameter type '#{type}' for command '#{command}'."
|
61
|
+
end
|
62
|
+
end
|
63
|
+
cmd_object = cmd_info[:klass].new(*args)
|
64
|
+
cmd_object.send(:run)
|
65
|
+
else
|
66
|
+
raise UsageError, "Unknown command '#{command}'."
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.valid_commands
|
71
|
+
command_list.map do |cmd_info|
|
72
|
+
[ cmd_info[:method], cmd_info[:klass], cmd_info[:new_params] ]
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def self.command_list
|
79
|
+
@command_list ||=
|
80
|
+
begin
|
81
|
+
constants.map do |c|
|
82
|
+
klass = const_get c
|
83
|
+
# class must have a :new class method and a :run instance method.
|
84
|
+
if klass.instance_methods.include?(:run) && klass.methods.include?(:new)
|
85
|
+
m = klass.method(:new)
|
86
|
+
new_params = m.parameters.select{ |p| p[1] && (p[0] == :req || p[0] == :opt || p[0] == :rest) }.freeze
|
87
|
+
m = klass.instance_method(:run)
|
88
|
+
run_params = m.parameters.select{ |p| p[1] && (p[0] == :req || p[0] == :opt || p[0] == :rest) }.freeze
|
89
|
+
if run_params.count == 0
|
90
|
+
{
|
91
|
+
name: c.to_s,
|
92
|
+
method: c.to_s.gsub(/(.)([A-Z])/,"\\1_\\2").downcase.to_sym,
|
93
|
+
new_params: new_params,
|
94
|
+
klass: klass,
|
95
|
+
valid: true
|
96
|
+
}
|
97
|
+
else
|
98
|
+
{
|
99
|
+
name: c.to_s,
|
100
|
+
valid: false,
|
101
|
+
failure_reason: 'Method :run expects parameters.'
|
102
|
+
}
|
103
|
+
end
|
104
|
+
else
|
105
|
+
{
|
106
|
+
name: c,
|
107
|
+
valid: false,
|
108
|
+
failure_reason: 'Missing :new or :run.'
|
109
|
+
}
|
110
|
+
end
|
111
|
+
end.select do |c|
|
112
|
+
c[:valid]
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
|
118
|
+
|
119
|
+
end
|
120
|
+
end
|
@@ -216,11 +216,17 @@ module Incline::Extensions
|
|
216
216
|
end
|
217
217
|
|
218
218
|
##
|
219
|
-
# Make sure main_app is available and working correctly.
|
219
|
+
# Make sure main_app is available and working correctly in tests.
|
220
220
|
def main_app
|
221
221
|
Rails.application.class.routes.url_helpers
|
222
222
|
end
|
223
|
-
|
223
|
+
|
224
|
+
##
|
225
|
+
# Make sure incline is available and working correctly in tests.
|
226
|
+
def incline
|
227
|
+
Incline::Engine.routes.url_helpers
|
228
|
+
end
|
229
|
+
|
224
230
|
##
|
225
231
|
# Determines if a user is logged into the test session
|
226
232
|
def is_logged_in?
|
data/lib/incline/version.rb
CHANGED
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'incline/cli'
|
3
|
+
|
4
|
+
class YamlContentsTest < ActiveSupport::TestCase
|
5
|
+
|
6
|
+
ALIGNED_YAML = <<-YAML
|
7
|
+
default: &default
|
8
|
+
alpha: # comment for alpha
|
9
|
+
bravo: # comment for bravo
|
10
|
+
one: &one
|
11
|
+
charlie: # comment for charlie
|
12
|
+
item_1: :simple # Level 3 remains aligned in all sections.
|
13
|
+
two: &two
|
14
|
+
delta:
|
15
|
+
item_1: "This is a \\"string\\" with a '#' character in it." # A nice long string on level 3.
|
16
|
+
item_2: true # Just true.
|
17
|
+
# This comment is aligned with the comment above.
|
18
|
+
three: &three
|
19
|
+
echo:
|
20
|
+
item_1: <%= Rails.application.secrets["db"]["password"] %> # Ironically, the same length as the string above.
|
21
|
+
YAML
|
22
|
+
|
23
|
+
TOP_OF_FILE = "# Top of file.\n"
|
24
|
+
|
25
|
+
SIMPLE_ADD_KEY_RESULT = <<-YAML.strip
|
26
|
+
# Top of file.
|
27
|
+
|
28
|
+
default:
|
29
|
+
one:
|
30
|
+
alpha: true
|
31
|
+
YAML
|
32
|
+
|
33
|
+
SIMPLE_REPLACE_RESULT = <<-YAML.strip
|
34
|
+
# Top of file.
|
35
|
+
|
36
|
+
default:
|
37
|
+
one:
|
38
|
+
alpha: false
|
39
|
+
YAML
|
40
|
+
|
41
|
+
MULTIPLE_ADD_RESULT = <<-YAML.strip
|
42
|
+
# Top of file.
|
43
|
+
|
44
|
+
one:
|
45
|
+
alpha: 1
|
46
|
+
delta: 4
|
47
|
+
|
48
|
+
two:
|
49
|
+
bravo: 2
|
50
|
+
echo: 5
|
51
|
+
|
52
|
+
three:
|
53
|
+
charlie: 3
|
54
|
+
foxtrot: 6
|
55
|
+
YAML
|
56
|
+
|
57
|
+
test 'does not modify unnecessarily on realign' do
|
58
|
+
contents = Incline::CliHelpers::Yaml::YamlContents.new(ALIGNED_YAML)
|
59
|
+
contents.realign!
|
60
|
+
assert_equal ALIGNED_YAML, contents.to_s
|
61
|
+
end
|
62
|
+
|
63
|
+
test 'should insert as appropriate' do
|
64
|
+
contents = Incline::CliHelpers::Yaml::YamlContents.new(TOP_OF_FILE)
|
65
|
+
contents.add_key %w(default one alpha), true
|
66
|
+
assert_equal SIMPLE_ADD_KEY_RESULT, contents.to_s.strip
|
67
|
+
end
|
68
|
+
|
69
|
+
test 'should replace as appropriate' do
|
70
|
+
contents = Incline::CliHelpers::Yaml::YamlContents.new(SIMPLE_ADD_KEY_RESULT)
|
71
|
+
contents.set_key %w(default one alpha), false
|
72
|
+
assert_equal SIMPLE_REPLACE_RESULT, contents.to_s.strip
|
73
|
+
end
|
74
|
+
|
75
|
+
test 'multiple add works as expected' do
|
76
|
+
contents = Incline::CliHelpers::Yaml::YamlContents.new(TOP_OF_FILE)
|
77
|
+
|
78
|
+
contents.add_key %w(one alpha), 1
|
79
|
+
contents.add_key %w(two bravo), 2
|
80
|
+
contents.add_key %w(three charlie), 3
|
81
|
+
contents.add_key %w(one delta), 4
|
82
|
+
contents.add_key %w(two echo), 5
|
83
|
+
contents.add_key %w(three foxtrot), 6
|
84
|
+
|
85
|
+
assert_equal MULTIPLE_ADD_RESULT, contents.to_s.strip
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
@@ -1,13 +1,13 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
3
|
class WelcomeControllerTest < ActionDispatch::IntegrationTest
|
4
|
-
|
4
|
+
|
5
|
+
# 2017-08-03: root path should be changeable by actual app. remove jumbotron check and instead do some link checks that should always be present.
|
5
6
|
test 'should get root_path' do
|
6
7
|
get root_path
|
7
8
|
assert_response :success
|
8
9
|
assert_select 'title', full_title
|
9
|
-
assert_select '
|
10
|
-
assert_select 'a[href=?]', root_path, count: 2
|
10
|
+
assert_select 'a[href=?]', main_app.root_path, count: 2
|
11
11
|
assert_select 'a[href=?]', incline.login_path, count: 1
|
12
12
|
assert_select 'a[href=?]', incline.contact_path, count: 1
|
13
13
|
end
|
@@ -0,0 +1 @@
|
|
1
|
+
<h1>Congrats, this is the DUMMY root.</h1>
|
data/test/dummy/config/routes.rb
CHANGED
@@ -131,12 +131,12 @@ module Incline
|
|
131
131
|
assert_not is_logged_in?
|
132
132
|
|
133
133
|
# invalid activation token
|
134
|
-
get incline.
|
134
|
+
get incline.edit_account_activation_path('invalid-token', email: user.email)
|
135
135
|
assert_not is_logged_in?
|
136
136
|
assert_not user.reload.activated?
|
137
137
|
|
138
138
|
# valid activation token
|
139
|
-
get incline.
|
139
|
+
get incline.edit_account_activation_path(token, email: user.email)
|
140
140
|
assert user.reload.activated?
|
141
141
|
follow_redirect!
|
142
142
|
assert_template 'incline/users/show'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: incline
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Beau Barker
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-08-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -378,6 +378,7 @@ files:
|
|
378
378
|
- db/migrate/20170622195742_add_non_standard_to_action_security.rb
|
379
379
|
- db/migrate/20170622230422_add_visible_to_action_security.rb
|
380
380
|
- db/seeds.rb
|
381
|
+
- exe/incline
|
381
382
|
- exe/new_incline_app
|
382
383
|
- lib/generators/incline/install_generator.rb
|
383
384
|
- lib/generators/incline/templates/_app_menu_anon.html.erb
|
@@ -393,6 +394,11 @@ files:
|
|
393
394
|
- lib/generators/incline/templates/incline_version.rb
|
394
395
|
- lib/incline.rb
|
395
396
|
- lib/incline/auth_engine_base.rb
|
397
|
+
- lib/incline/cli.rb
|
398
|
+
- lib/incline/cli/errors.rb
|
399
|
+
- lib/incline/cli/helpers/yaml.rb
|
400
|
+
- lib/incline/cli/usage.rb
|
401
|
+
- lib/incline/cli/version.rb
|
396
402
|
- lib/incline/data_tables_request.rb
|
397
403
|
- lib/incline/date_time_formats.rb
|
398
404
|
- lib/incline/engine.rb
|
@@ -446,6 +452,7 @@ files:
|
|
446
452
|
- lib/templates/jbuilder/scaffold/index.json.jbuilder
|
447
453
|
- lib/templates/jbuilder/scaffold/show.json.jbuilder
|
448
454
|
- lib/templates/rails/scaffold_controller/controller.rb
|
455
|
+
- test/cli/yaml_contents_test.rb
|
449
456
|
- test/controllers/incline/access_groups_controller_test.rb
|
450
457
|
- test/controllers/incline/access_test_controller_test.rb
|
451
458
|
- test/controllers/incline/contact_controller_test.rb
|
@@ -458,10 +465,12 @@ files:
|
|
458
465
|
- test/dummy/app/assets/stylesheets/application.css
|
459
466
|
- test/dummy/app/controllers/application_controller.rb
|
460
467
|
- test/dummy/app/controllers/concerns/.keep
|
468
|
+
- test/dummy/app/controllers/dummy_controller.rb
|
461
469
|
- test/dummy/app/helpers/application_helper.rb
|
462
470
|
- test/dummy/app/mailers/.keep
|
463
471
|
- test/dummy/app/models/.keep
|
464
472
|
- test/dummy/app/models/concerns/.keep
|
473
|
+
- test/dummy/app/views/dummy/hello.html.erb
|
465
474
|
- test/dummy/app/views/layouts/application.html.erb
|
466
475
|
- test/dummy/app/views/layouts/incline/_app_menu_anon.html.erb
|
467
476
|
- test/dummy/app/views/layouts/incline/_app_menu_authenticated.html.erb
|