incline 0.1.7 → 0.1.8

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cf198e645fba31e26e730a4fcf2deae2b72a8d92
4
- data.tar.gz: 4c5782b14aa7db868e44ec418f0272aa6cbd026a
3
+ metadata.gz: '0254285858902e4313110fddc419672ac8ae267b'
4
+ data.tar.gz: f22408a384aa6aaf8ee1f36195a220d1677fb09c
5
5
  SHA512:
6
- metadata.gz: e4422ca100423246a3eb80de970c1e6958379b14afc7294c07f4e739f66b3b5442414eae78781d5dc577c303baea32663bb5e3e6dc49f50095dd9afce86b3ed7
7
- data.tar.gz: b84f5223ea71524f0f445a6a5cdf0aacb3d85d6e63b39397b30db8ea3ed4272c9994b98e949247a0167f4c0115e4367118b6bf02d52fb18217d795ceebf4fc11
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.7)
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.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.9)
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.7)
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
@@ -17,7 +17,7 @@ module Incline
17
17
  @msg.remote_ip = request.remote_ip
18
18
  @msg.send_message
19
19
  flash[:success] = 'Your message has been sent.'
20
- redirect_to root_url
20
+ redirect_to main_app.root_url
21
21
  else
22
22
  render 'new'
23
23
  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
@@ -59,7 +59,7 @@ module Incline
59
59
  if inline_request?
60
60
  render 'show', formats: [ :json ]
61
61
  else
62
- redirect_to root_url
62
+ redirect_to main_app.root_url
63
63
  end
64
64
  return
65
65
  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,8 @@
1
+ module Incline
2
+ class CLI
3
+
4
+ class CliError < ::RuntimeError; end
5
+ class UsageError < CliError; end
6
+
7
+ end
8
+ end
@@ -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
@@ -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
@@ -318,7 +318,7 @@ module Incline::Extensions
318
318
  rescue ::Incline::NotAuthorized => err
319
319
 
320
320
  flash[:danger] = err.message
321
- redirect_to root_url and return false
321
+ redirect_to main_app.root_url and return false
322
322
  end
323
323
  true
324
324
 
@@ -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?
@@ -1,3 +1,3 @@
1
1
  module Incline
2
- VERSION = "0.1.7"
2
+ VERSION = "0.1.8"
3
3
  end
@@ -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 'div.jumbotron'
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,10 @@
1
+
2
+ class DummyController < ApplicationController
3
+
4
+ allow_anon true
5
+
6
+ def hello
7
+
8
+ end
9
+
10
+ end
@@ -0,0 +1 @@
1
+ <h1>Congrats, this is the DUMMY root.</h1>
@@ -2,5 +2,7 @@ Rails.application.routes.draw do
2
2
 
3
3
  mount Incline::Engine => "/incline"
4
4
 
5
- root 'incline/welcome#home'
5
+ # need to ensure that custom root paths work.
6
+ root 'dummy#hello'
7
+ # root 'incline/welcome#home'
6
8
  end
@@ -131,12 +131,12 @@ module Incline
131
131
  assert_not is_logged_in?
132
132
 
133
133
  # invalid activation token
134
- get incline.edit_account_activation_url('invalid-token', email: user.email)
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.edit_account_activation_url(token, email: user.email)
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.7
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-07-17 00:00:00.000000000 Z
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