cetus 0.1.38 → 0.1.39

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.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/bin/cetus +148 -51
  3. data/cetus.gemspec +1 -1
  4. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0adbfa483176301769749c1f1756cf6ae616ee572ad5b2ca56c28b2339f0386f
4
- data.tar.gz: 2506a1e5264337d8648022083d7c37a6616d36e1be12b65d29b5172211cbdc67
3
+ metadata.gz: 501ef34e9afd0b98b8cfda66aa5c98fb46d6529f507e95c18951833edc467a0f
4
+ data.tar.gz: 722a4926a42bd426cfac568b2166c041b6a4dda63939e04ea5b6e5c2f3127de6
5
5
  SHA512:
6
- metadata.gz: 7e18775e80ebc7cf5367e4040fb27c73eb2cff2e0cabac603afd1958de18cc1a82055b51c3b13eaaef2b03c0937b9aeff045a29f255b4ab8f7221821e0dc97f0
7
- data.tar.gz: d7efa83ac9196fc74768e6fad5e87df973ec9ba49a44921310462cfe834cd53bb288be52d76f92d7ca6d45b9363e0545dac584ea5a3e47dad28e2fd0344c0daa
6
+ metadata.gz: b19bc7bbf440a8c9df34165c35e5b19134e9b3e3023e348e8eec52a770e784359ca02b7d8618d3b311b9bb802676f252274c8fe8e228a1ddf825fd0016a438eb
7
+ data.tar.gz: 41ad81c2cda30e5a20cc35ea22d4ec0e96630d3e907053713f733d3d61b6ba0fb42a6d9e1de8e367733aeca8a26cb2465f5af3dd7e1848a79cc8a78027640881
data/bin/cetus CHANGED
@@ -8,7 +8,7 @@
8
8
  # Author: rkumar http://github.com/rkumar/cetus/
9
9
  # Date: 2013-02-17 - 17:48
10
10
  # License: GPL
11
- # Last update: 2019-04-19 09:51
11
+ # Last update: 2019-04-21 23:46
12
12
  # --------------------------------------------------------------------------- #
13
13
  # cetus.rb Copyright (C) 2012-2019 rahul kumar
14
14
  # == CHANGELOG
@@ -19,6 +19,7 @@
19
19
  # 2019-03-04 - changed quit to q (earlier Q)
20
20
  # 2019-03-04 - first dirs then files
21
21
  # 2019-03-22 - refactoring the code, esp run()
22
+ # 2019-04-21 - search_as_you_type
22
23
  # == TODO
23
24
 
24
25
  require 'readline'
@@ -29,6 +30,8 @@ require 'shellwords'
29
30
  require 'fileutils'
30
31
  require 'logger'
31
32
  @log = Logger.new(File.expand_path('~/tmp/log.txt'))
33
+ now = Time.now.strftime("%Y-%m-%d %H:%M:%S")
34
+ @log.info "========== cetus started at : #{now} ================= ----------"
32
35
  # @log = Logger.new('log.txt')
33
36
 
34
37
  ## INSTALLATION
@@ -36,7 +39,7 @@ require 'logger'
36
39
  # alias c=~/bin/cetus.rb
37
40
  # c
38
41
 
39
- VERSION = '0.1.38.0'
42
+ VERSION = '0.1.39.0'
40
43
  CONFIG_PATH = ENV['XDG_CONFIG_HOME'] || File.join(ENV['HOME'], '.config')
41
44
  CONFIG_FILE = "#{CONFIG_PATH}/cetus/conf.yml"
42
45
 
@@ -64,7 +67,7 @@ CONFIG_FILE = "#{CONFIG_PATH}/cetus/conf.yml"
64
67
  ':' => 'subcommand',
65
68
  "'" => 'goto_bookmark',
66
69
  '"' => 'file_starting_with',
67
- '/' => 'enter_regex',
70
+ '/' => 'ask_regex',
68
71
  'M-p' => 'prev_page',
69
72
  'M-n' => 'next_page',
70
73
  'PgUp' => 'prev_page',
@@ -191,6 +194,20 @@ def reset_terminal
191
194
  # Show user input.
192
195
  system 'stty echo'
193
196
  end
197
+ # Call before shelling to editor pager and when exiting
198
+ def partial_reset_terminal
199
+ # Reset the terminal to a useable state (undo all changes).
200
+ # '\e[?7h': Re-enable line wrapping.
201
+ # '\e[?25h': Unhide the cursor.
202
+ # '\e[2J': Clear the terminal.
203
+ # '\e[;r': Set the scroll region to its default value.
204
+ # Also sets cursor to (0,0).
205
+ # '\e[?1049l: Restore main screen buffer.
206
+ print "\e[?7h\e[?25h\e[;r\e[?1049l"
207
+
208
+ # Show user input.
209
+ system 'stty echo'
210
+ end
194
211
 
195
212
  # copied from fff
196
213
  # call AFTER shelling to most or vim
@@ -215,7 +232,7 @@ end
215
232
  def readline prompt='>'
216
233
  clear_last_line
217
234
  print "\r"
218
- # do we need to clear till end of line, see enter_regex commented
235
+ # do we need to clear till end of line, see ask_regex commented
219
236
  # unhide cursor
220
237
  print "\e[?25h"
221
238
  system 'stty echo'
@@ -318,7 +335,7 @@ PAGER_COMMAND = {
318
335
  unknown: 'open'
319
336
  }.freeze
320
337
  @dir_position = {}
321
- @movement = @old_cursor = nil # cursor movement has happened only, don't repaint
338
+ @cursor_movement = @old_cursor = nil # cursor movement has happened only, don't repaint
322
339
  ## FLAGS
323
340
  @multiple_selection = true # single select
324
341
  @group_dirs = true
@@ -345,7 +362,8 @@ PAGER_COMMAND = {
345
362
  editor_mode: false,
346
363
  selection_mode: false, # typing hint adds to selection, does not open
347
364
  debug_flag: false,
348
- toggle_filename_status_line: true
365
+ toggle_filename_status_line: true,
366
+ instant_search: true
349
367
  }
350
368
  # @cycles = { truncate_from: :center,
351
369
  # show_hidden: nil }
@@ -662,8 +680,6 @@ def redraw flag=false
662
680
  end
663
681
 
664
682
  def draw_directory
665
- # all this seems to be happening for each keystroke even if
666
- # not really required. FIXME maybe reduce and call when required
667
683
 
668
684
  # view consists of all files (filtered by pattern if necessary)
669
685
  # viewport is only that subset of view that is displayed on screen
@@ -751,13 +767,15 @@ def resolve_key key
751
767
  ret = true
752
768
  clear_message
753
769
 
770
+ return true if search_as_you_type(key)
771
+
754
772
  if key.match?(/^[a-pr-zZ]$/)
755
773
  # hint mode
756
774
  ret = select_hint @viewport, key
757
775
  elsif key == 'BACKSPACE'
758
- # do we really need this TODO
759
776
  @patt = @patt[0..-2] if @patt && !@patt.empty?
760
777
  @message = @patt = nil if @patt == ''
778
+ @title = nil unless @patt
761
779
  elsif '0123456789'.include? key
762
780
  resolve_numeric_key key
763
781
  else
@@ -798,6 +816,70 @@ def resolve_numeric_key key
798
816
  message "Created bookmark #{key}."
799
817
  end
800
818
 
819
+ IGNORE_KEY = true
820
+ PROCESS_KEY = false
821
+
822
+ # Display search results as user types search pattern.
823
+ # returning false allows these keys to be processed
824
+ # returning true means that these keys will be ignored
825
+ def search_as_you_type key
826
+ return false unless @searching
827
+
828
+ # @log.info "sayt: #{key}, patt: #{@patt}"
829
+
830
+ # escape should nil patt and come out of search.
831
+ if key == 'ESCAPE'
832
+ exit_search
833
+ return IGNORE_KEY
834
+ end
835
+
836
+ # We exit the search but screen shows search results.
837
+ if key == 'ENTER' # || key == 'TAB'
838
+ @searching = false
839
+ redraw_required true # ??? any effect ?
840
+ @mode = 'NORM'
841
+ # nilifying patt means we will not see searched results. we want to stay there.
842
+ @title = 'Search Results (ESCAPE to return)'
843
+ message ' '
844
+ status_line
845
+ return IGNORE_KEY
846
+ end
847
+
848
+ if key == 'BACKSPACE' && @patt
849
+ @patt = @patt[0..-2]
850
+ exit_search if @patt == ''
851
+ return IGNORE_KEY
852
+ end
853
+
854
+ # XXX should not changing directory cancel the search ?
855
+ if key == 'UP' || key == 'DOWN' || key == 'RIGHT' || key == 'LEFT'
856
+ return PROCESS_KEY
857
+ end
858
+
859
+ # if key is not one of allowed keys, then cancel the search.
860
+ # No, we allow it as in toggle or menu ?
861
+ unless key.match?(/^[a-zA-Z0-9\. _]$/)
862
+ # exit_search
863
+ # @mode = @title = nil
864
+ # redraw_required true
865
+ return PROCESS_KEY
866
+ end
867
+
868
+ @mode = 'INST'
869
+ @title = 'Search results: Use ENTER to return, ESCAPE to cancel.'
870
+ @patt ||= '' + '' # rubocop suggestion to get unfrozen string
871
+ @patt += key
872
+ message "> #{@patt} "
873
+ redraw_required true
874
+
875
+ return IGNORE_KEY
876
+ end
877
+
878
+ def exit_search
879
+ @searching = @mode = @title = @patt = nil
880
+ status_line
881
+ end
882
+
801
883
  def set_bookmark key, dir=Dir.pwd
802
884
  @bookmarks[key] = dir
803
885
  end
@@ -870,7 +952,6 @@ def columnate ary, siz
870
952
 
871
953
  elsif unformatted_len < wid
872
954
 
873
- # f = f.ljust(wid)
874
955
  f << ' ' * (wid - unformatted_len)
875
956
 
876
957
  end
@@ -1335,7 +1416,9 @@ def escape
1335
1416
  @viewctr = 0
1336
1417
  @title = nil
1337
1418
  @filterstr = 'M'
1419
+ @searching = false
1338
1420
  @message = nil
1421
+ @mode = nil
1339
1422
  visual_block_clear
1340
1423
  refresh
1341
1424
  end
@@ -1465,11 +1548,21 @@ def goto_bookmark key=nil
1465
1548
  end
1466
1549
 
1467
1550
  ## take regex from user, to run on files on screen, user can filter file names
1468
- def enter_regex
1469
- # print 'Enter (regex) pattern: '
1470
- # move to beginning of line, and clear till EOL
1471
- # print "\r\e[K"
1472
- @patt = readline '/'
1551
+ def ask_regex
1552
+ unless cget(:instant_search)
1553
+ @log.info "Inside ask_regex readline."
1554
+ @patt = readline '/'
1555
+ @title = 'Search Results: (Press Escape to return)'
1556
+ @mode = 'SEARCH'
1557
+ return
1558
+ end
1559
+
1560
+ # this is the instant search as you type thingie
1561
+ @patt = nil
1562
+ @log.info "Inside ask_regex instant."
1563
+ message 'I/'
1564
+ @searching = true
1565
+ status_line
1473
1566
  end
1474
1567
 
1475
1568
  # page/scroll down.
@@ -1684,7 +1777,8 @@ def toggle_menu
1684
1777
  'S' => :selection_mode,
1685
1778
  v: :visual_mode,
1686
1779
  t: :truncate_from,
1687
- n: :enhanced_mode }
1780
+ n: :enhanced_mode,
1781
+ i: :instant_search }
1688
1782
  # TODO: add other values at end, what key ?
1689
1783
 
1690
1784
  _, menu_text = menu 'Toggle Menu', h
@@ -1971,7 +2065,6 @@ def filter_menu
1971
2065
  @files = files
1972
2066
  @stact = 0
1973
2067
  @message = "Filtered on #{menu_text}. Press ESC to return."
1974
- # redraw
1975
2068
  else
1976
2069
  perror 'Sorry, No files. '
1977
2070
  @title = nil
@@ -1998,7 +2091,6 @@ def select_from_used_dirs
1998
2091
  @title = 'Used Directories'
1999
2092
  home = File.expand_path '~'
2000
2093
  @files = @used_dirs.uniq.map { |path| path.sub(home.to_s, '~') }
2001
- # redraw
2002
2094
  end
2003
2095
 
2004
2096
  def select_from_visited_files
@@ -2006,7 +2098,6 @@ def select_from_visited_files
2006
2098
  @title = 'Visited Files'
2007
2099
  home = File.expand_path '~'
2008
2100
  @files = @visited_files.uniq.map { |path| path.sub(home.to_s, '~') }
2009
- # redraw
2010
2101
  end
2011
2102
 
2012
2103
  # maybe unused ??? XXX
@@ -2110,7 +2201,6 @@ def create_bookmark
2110
2201
  else
2111
2202
  perror 'Bookmark must be alpha character or number.'
2112
2203
  end
2113
- # redraw
2114
2204
  end
2115
2205
 
2116
2206
  # allow user to exit using :q :wq :x
@@ -2163,10 +2253,15 @@ def subcommand
2163
2253
  else
2164
2254
  perror "Don't know about command #{command}. Try :h or :help"
2165
2255
  end
2166
- # redraw
2167
2256
  end
2168
2257
 
2169
2258
  def quit_command
2259
+ # if we are in some mode, like search results then 'q' should come out.
2260
+ if @mode
2261
+ escape
2262
+ return
2263
+ end
2264
+
2170
2265
  if @modified
2171
2266
  last_line
2172
2267
  puts 'Press y to save bookmarks before quitting ' if @modified
@@ -2188,7 +2283,6 @@ def views
2188
2283
  @viewctr = 0 if @viewctr > views.size
2189
2284
 
2190
2285
  @files = `zsh -c 'print -rl -- *(#{@sorto}#{@hidden}M)'`.split("\n")
2191
- # redraw
2192
2286
  end
2193
2287
 
2194
2288
  def child_dirs
@@ -2197,7 +2291,6 @@ def child_dirs
2197
2291
  # @files = `zsh -c 'print -rl -- *(/#{@sorto}#{@hidden}M)'`.split("\n")
2198
2292
  @files = dirs
2199
2293
  message "#{@files.size} directories."
2200
- # redraw
2201
2294
  end
2202
2295
 
2203
2296
  def dirs dir='*'
@@ -2218,7 +2311,6 @@ def dirtree
2218
2311
  # files1 = `zsh -c 'print -rl -- **/*(/#{@sorto}#{@hidden}M)'`.split("\n")
2219
2312
  @files = Dir['**/']
2220
2313
  message "#{@files.size} files."
2221
- # redraw
2222
2314
  end
2223
2315
 
2224
2316
  #
@@ -2230,7 +2322,6 @@ def tree
2230
2322
  # @files = `zsh -c 'print -rl -- **/*(#{@sorto}#{@hidden}M)'`.split("\n")
2231
2323
  @files = Dir['**/*']
2232
2324
  message "#{@files.size} files."
2233
- # redraw
2234
2325
  end
2235
2326
 
2236
2327
  # lists recent files in current dir
@@ -2241,7 +2332,6 @@ def recent_files
2241
2332
  # zsh D DOT_GLOB, show dot files
2242
2333
  # zsh om order on modification time
2243
2334
  @files = `zsh -c 'print -rl -- **/*(Dom[1,15])'`.split("\n").reject { |f| f[0] == '.' }
2244
- # redraw
2245
2335
  end
2246
2336
 
2247
2337
  def select_current
@@ -2702,7 +2792,6 @@ def column_next direction=0
2702
2792
  # We are trying to maintain offset
2703
2793
  @cursor += @grows if @cursor < 0
2704
2794
  end
2705
- # redraw
2706
2795
  end
2707
2796
 
2708
2797
  # currently i am only passing the action in from the list there as a key
@@ -2901,7 +2990,6 @@ def ag
2901
2990
  return
2902
2991
  end
2903
2992
  @files = files
2904
- # redraw
2905
2993
  end
2906
2994
 
2907
2995
  def ffind
@@ -2917,7 +3005,6 @@ def ffind
2917
3005
  else
2918
3006
  @files = files
2919
3007
  end
2920
- # redraw
2921
3008
  end
2922
3009
 
2923
3010
  def locate
@@ -2933,7 +3020,6 @@ def locate
2933
3020
  else
2934
3021
  @files = files
2935
3022
  end
2936
- # redraw
2937
3023
  end
2938
3024
 
2939
3025
  ## takes directories from the z program, if you use autojump you can
@@ -2950,7 +3036,6 @@ def z_interface
2950
3036
  @files.collect! do |f|
2951
3037
  f.sub(/#{home}/, '~')
2952
3038
  end
2953
- # redraw
2954
3039
  end
2955
3040
 
2956
3041
  def vidir
@@ -2963,25 +3048,25 @@ end
2963
3048
 
2964
3049
  ## scroll cursor down
2965
3050
  def cursor_scroll_dn
2966
- @movement = :down
3051
+ @cursor_movement = :down
2967
3052
  moveto(pos + MSCROLL)
2968
3053
  end
2969
3054
 
2970
3055
  def cursor_scroll_up
2971
- @movement = :up
3056
+ @cursor_movement = :up
2972
3057
  moveto(pos - MSCROLL)
2973
3058
  end
2974
3059
 
2975
3060
  # move cursor down a line
2976
3061
  def cursor_dn
2977
- @movement = :down
3062
+ @cursor_movement = :down
2978
3063
  @old_cursor = @cursor
2979
3064
  moveto(pos + 1)
2980
3065
  end
2981
3066
 
2982
3067
  def cursor_up
2983
3068
  @old_cursor = @cursor
2984
- @movement = :up
3069
+ @cursor_movement = :up
2985
3070
  moveto(pos - 1)
2986
3071
  end
2987
3072
 
@@ -2999,8 +3084,8 @@ def moveto position
2999
3084
 
3000
3085
  # try to stop it from landing on separator
3001
3086
  if current_file == SEPARATOR
3002
- @cursor += 1 if @movement == :down
3003
- @cursor -= 1 if @movement == :up
3087
+ @cursor += 1 if @cursor_movement == :down
3088
+ @cursor -= 1 if @cursor_movement == :up
3004
3089
  return
3005
3090
  end
3006
3091
 
@@ -3019,13 +3104,13 @@ def moveto position
3019
3104
  # @sta = @cursor
3020
3105
  end
3021
3106
 
3022
- @movement = nil if oldsta != @sta # we need to redraw
3107
+ @cursor_movement = nil if oldsta != @sta # we need to redraw
3023
3108
 
3024
3109
  star = [orig, @cursor].min
3025
3110
  fin = [orig, @cursor].max
3026
3111
  return unless @visual_mode
3027
3112
 
3028
- @movement = nil # visual mode needs to redraw page
3113
+ @cursor_movement = nil # visual mode needs to redraw page
3029
3114
 
3030
3115
  # PWD has to be there in selction
3031
3116
  if selected? current_file
@@ -3039,8 +3124,6 @@ def moveto position
3039
3124
  add_to_selection @view[star..fin]
3040
3125
  end
3041
3126
  message "#{@selected_files.count} files selected. "
3042
- # ensure
3043
- # redraw
3044
3127
  end
3045
3128
  # --
3046
3129
 
@@ -3262,7 +3345,8 @@ def scripts binding=nil
3262
3345
 
3263
3346
  unless binding
3264
3347
  title = 'Select a script'
3265
- script_path = '~/.config/cetus/scripts'
3348
+ # script_path = '~/.config/cetus/scripts'
3349
+ script_path = File.join(CONFIG_PATH, 'cetus', 'scripts')
3266
3350
  binding = `find #{script_path} -type f | fzf --prompt="#{title} :"`.chomp
3267
3351
  return if binding.nil? || binding == ''
3268
3352
  end
@@ -3284,7 +3368,7 @@ def scripts binding=nil
3284
3368
  # script may ignore this and use selected_files
3285
3369
 
3286
3370
  # reset clears the screen, we don't want that. just unhide cursor and echo keys TODO
3287
- reset_terminal
3371
+ partial_reset_terminal
3288
3372
  system %( #{binding} "#{current_file}" )
3289
3373
 
3290
3374
  # system %(echo "#{cf}" | xargs #{binding})
@@ -3297,9 +3381,13 @@ end
3297
3381
  # this is quite important and should not be left to a script
3298
3382
  # example of calling a script from somewhere directly without selection
3299
3383
  def execute_script filename
3300
- script_path = '~/.config/cetus/scripts'
3301
- script = File.expand_path(File.join(script_path, 'filename'))
3302
- return unless File.exist? script
3384
+ # script_path = '~/.config/cetus/scripts'
3385
+ script_path = File.join(CONFIG_PATH, 'cetus', 'scripts')
3386
+ script = File.expand_path(File.join(script_path, filename))
3387
+ unless File.exist? script
3388
+ perror "Unable to find #{filename}: #{script}"
3389
+ return
3390
+ end
3303
3391
 
3304
3392
  scripts script
3305
3393
  end
@@ -3316,6 +3404,7 @@ def generators
3316
3404
 
3317
3405
  title = 'Select a generator'
3318
3406
  script_path = '~/.config/cetus/generators'
3407
+ script_path = File.join(CONFIG_PATH, 'cetus', 'generators')
3319
3408
  binding = `find #{script_path} -type f | fzf --prompt="#{title} :"`.chomp
3320
3409
  return if binding.nil? || binding == ''
3321
3410
 
@@ -3714,12 +3803,12 @@ end
3714
3803
  # returns true if only cursor moved and redrawing not required
3715
3804
  def only_cursor_moved?
3716
3805
  # only movement has happened within this page, don't redraw
3717
- return unless @movement && @old_cursor
3806
+ return unless @cursor_movement && @old_cursor
3718
3807
 
3719
3808
  # if cursor has not moved (first or last item on screen)
3720
3809
  # keep testing this out 2019-04-14 -
3721
3810
  if @old_cursor == @cursor
3722
- @movement = false
3811
+ @cursor_movement = false
3723
3812
  return true # next in the loop
3724
3813
  end
3725
3814
 
@@ -3732,10 +3821,15 @@ def only_cursor_moved?
3732
3821
  end
3733
3822
 
3734
3823
  place_cursor
3735
- @movement = false
3824
+ @cursor_movement = false
3736
3825
  return true # next in the loop
3737
3826
  end
3738
3827
 
3828
+ # indicate that only cursor moved, so don't redraw or rescan
3829
+ def only_cursor_moved flag=true
3830
+ @cursor_movement = flag
3831
+ end
3832
+
3739
3833
  # main loop which calls all other programs
3740
3834
  def run
3741
3835
  Signal.trap('EXIT') do
@@ -3751,7 +3845,7 @@ def run
3751
3845
  redraw true
3752
3846
  place_cursor
3753
3847
 
3754
- # do we need this, have they changed after redraw
3848
+ # do we need this, have they changed after redraw XXX
3755
3849
  @patt = nil
3756
3850
  @sta = 0
3757
3851
 
@@ -3764,12 +3858,15 @@ def run
3764
3858
  next
3765
3859
  end
3766
3860
 
3861
+ break if @quitting
3862
+
3767
3863
  next if only_cursor_moved?
3768
3864
 
3865
+ next unless @redraw_required # no change, or ignored key
3866
+
3769
3867
  redraw rescan?
3770
3868
  place_cursor
3771
3869
 
3772
- break if @quitting
3773
3870
  end
3774
3871
  write_curdir
3775
3872
  puts 'bye'
@@ -6,7 +6,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
6
6
 
7
7
  Gem::Specification.new do |spec|
8
8
  spec.name = 'cetus'
9
- spec.version = '0.1.38'
9
+ spec.version = '0.1.39'
10
10
  spec.authors = ['Rahul Kumar']
11
11
  spec.email = ['oneness.univ@gmail.com']
12
12
  spec.description = %q{lightning fast file navigator}
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cetus
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.38
4
+ version: 0.1.39
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rahul Kumar
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-04-19 00:00:00.000000000 Z
11
+ date: 2019-04-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler