Class: Dorian::Bin

Inherits:
Object
  • Object
show all
Defined in:
lib/dorian/bin.rb

Constant Summary collapse

RUBY_EXTENSIONS =
%w[
  .rb
  .arb
  .axlsx
  .builder
  .fcgi
  .gemfile
  .gemspec
  .god
  .jb
  .jbuilder
  .mspec
  .opal
  .pluginspec
  .podspec
  .rabl
  .rake
  .rbuild
  .rbw
  .rbx
  .ru
  .ruby
  .schema
  .spec
  .thor
  .watchr
].freeze
RUBY_FILENAMES =
%w[
  .irbrc
  .pryrc
  .simplecov
  Appraisals
  Berksfile
  Brewfile
  Buildfile
  Capfile
  Cheffile
  Dangerfile
  Deliverfile
  Fastfile
  Gemfile
  Guardfile
  Jarfile
  Mavenfile
  Podfile
  Puppetfile
  Rakefile
  rakefile
  Schemafile
  Snapfile
  Steepfile
  Thorfile
  Vagabondfile
  Vagrantfile
  buildfile
].freeze
VERSION =
File.read(File.expand_path("../../VERSION", __dir__))
DEFAULT_IO =
:raw
IO =
{
  "csv" => :csv,
  "json" => :json,
  "jsonl" => :jsonl,
  "raw" => :raw,
  "yaml" => :yaml,
  "yml" => :yaml,
  "yamll" => :yamll,
  "ymll" => :yamll
}.freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeBin

Returns a new instance of Bin.



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/dorian/bin.rb', line 101

def initialize
  @parsed =
    Dorian::Arguments.parse(
      colorize: {
        aliases: %i[color c],
        default: true
      },
      fast: {
        alias: :f,
        default: true
      },
      input: {
        type: :string,
        alias: :i
      },
      output: {
        type: :string,
        alias: :o
      },
      parallel: {
        alias: :p
      },
      parallel_type: {
        alias: :pt,
        type: :string,
        default: :processes
      },
      n: {
        type: :integer,
        default: 100
      },
      rails: {
        alias: :r
      },
      stderr: {
        alias: :err,
        default: true
      },
      stdout: {
        alias: :out,
        default: true
      },
      stdin: {
        type: :string,
        alias: :in,
        default: :raw
      },
      write: {
        alias: :w
      },
      deep: :boolean,
      debug: {
        alias: :d
      },
      progress: :boolean,
      headers: {
        default: true
      },
      progress_format: {
        alias: :pf,
        type: :string
      },
      pretty: {
        default: true
      },
      io: :string,
      self: :boolean,
      version: {
        alias: :v
      },
      help: {
        alias: :h
      }
    )

  @arguments = parsed.arguments
  @command = arguments.first
  @ruby = nil
end

Instance Attribute Details

#argumentsObject (readonly)

Returns the value of attribute arguments.



99
100
101
# File 'lib/dorian/bin.rb', line 99

def arguments
  @arguments
end

#commandObject (readonly)

Returns the value of attribute command.



99
100
101
# File 'lib/dorian/bin.rb', line 99

def command
  @command
end

#parsedObject (readonly)

Returns the value of attribute parsed.



99
100
101
# File 'lib/dorian/bin.rb', line 99

def parsed
  @parsed
end

#rubyObject (readonly)

Returns the value of attribute ruby.



99
100
101
# File 'lib/dorian/bin.rb', line 99

def ruby
  @ruby
end

#ruby_afterObject (readonly)

Returns the value of attribute ruby_after.



99
100
101
# File 'lib/dorian/bin.rb', line 99

def ruby_after
  @ruby_after
end

#ruby_beforeObject (readonly)

Returns the value of attribute ruby_before.



99
100
101
# File 'lib/dorian/bin.rb', line 99

def ruby_before
  @ruby_before
end

Class Method Details

.runObject



181
182
183
# File 'lib/dorian/bin.rb', line 181

def self.run(...)
  new(...).run
end

Instance Method Details

#after(input, ruby: @ruby_after || @ruby) ⇒ Object



1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
# File 'lib/dorian/bin.rb', line 1225

def after(input, ruby: @ruby_after || @ruby)
  if ruby.to_i.to_s == ruby
    input[(ruby.to_i)..]
  else
    selected = false

    input.select do |element|
      selected = true if match?(element, ruby:)
      selected
    end
  end
end

#anonymize(input) ⇒ Object



1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
# File 'lib/dorian/bin.rb', line 1266

def anonymize(input)
  if input.is_a?(String)
    input.gsub(/[a-z]/, "a").gsub(/[A-Z]/, "A").gsub(/[0-9]/, "0")
  elsif input.is_a?(Integer)
    0
  elsif input.is_a?(Float)
    0.0
  elsif input.is_a?(TrueClass) || input.is_a?(FalseClass)
    false
  elsif input.nil?
    nil
  elsif input.is_a?(Hash)
    input.transform_values { |value| anonymize(value) }
  elsif input.is_a?(Array)
    input.map { |element| anonymize(element) }
  elsif input.is_a?(Struct)
    anonymize(input.from_deep_struct).to_deep_struct
  else
    raise "#{input.class.inspect} not supported"
  end
end

#before(input, ruby: @ruby_before || @ruby) ⇒ Object



1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
# File 'lib/dorian/bin.rb', line 1238

def before(input, ruby: @ruby_before || @ruby)
  if ruby.to_i.to_s == ruby
    input[..(ruby.to_i)]
  else
    selected = true

    input.select do |element|
      selected.tap { selected = false if match?(element, ruby:) }
    end
  end
end

#between(input, ruby_before: @ruby_before, ruby_after: @ruby_after) ⇒ Object



1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
# File 'lib/dorian/bin.rb', line 1250

def between(input, ruby_before: @ruby_before, ruby_after: @ruby_after)
  if ruby_before.to_i.to_s == ruby_before &&
       ruby_after.to_i.to_s == ruby_after
    input[(ruby_after.to_i)..(ruby_before.to_i)]
  else
    selected = false

    input.select do |element|
      selected = true if match?(element, ruby: ruby_after)
      selected.tap do
        selected = false if match?(element, ruby: ruby_before)
      end
    end
  end
end

#colorize?Boolean

Returns:

  • (Boolean)


1029
1030
1031
# File 'lib/dorian/bin.rb', line 1029

def colorize?
  !!options.colorize
end

#command_afterObject



881
882
883
884
885
886
887
888
889
# File 'lib/dorian/bin.rb', line 881

def command_after
  each(stdin_files + files) do |input|
    outputs(after(lines(reads(File.read(input)))), file: input)
  end

  each(stdin_arguments + arguments) do |input|
    outputs(after(lines(reads(input))))
  end
end

#command_allObject



839
840
841
842
843
# File 'lib/dorian/bin.rb', line 839

def command_all
  each(everything, progress: true) do |input|
    evaluates(it: lines(reads(input)))
  end
end

#command_anonymizeObject



911
912
913
914
915
916
917
918
919
# File 'lib/dorian/bin.rb', line 911

def command_anonymize
  each(stdin_files + files) do |input|
    outputs(anonymize(reads(File.read(input))), file: input)
  end

  each(stdin_arguments + arguments) do |input|
    outputs(anonymize(reads(input)))
  end
end

#command_appendObject



845
846
847
# File 'lib/dorian/bin.rb', line 845

def command_append
  outputs(map(everything) { |input| lines(reads(input)) }.inject(&:+))
end

#command_beforeObject



891
892
893
894
895
896
897
898
899
# File 'lib/dorian/bin.rb', line 891

def command_before
  each(stdin_files + files) do |input|
    outputs(before(reads(File.read(input))), file: input)
  end

  each(stdin_arguments + arguments) do |input|
    outputs(before(lines(reads(input))))
  end
end

#command_betweenObject



901
902
903
904
905
906
907
908
909
# File 'lib/dorian/bin.rb', line 901

def command_between
  each(stdin_files + files) do |input|
    outputs(between(lines(reads(File.read(input)))), file: input)
  end

  each(stdin_arguments + arguments) do |input|
    outputs(between(lines(reads(input))))
  end
end

#command_chatObject



572
573
574
575
576
577
578
# File 'lib/dorian/bin.rb', line 572

def command_chat
  puts completion(
         token: token(".chat"),
         model: "gpt-4o",
         messages: [{ role: :user, content: everything.join("\n") }]
       )
end

#command_commitObject



580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
# File 'lib/dorian/bin.rb', line 580

def command_commit
  system_prompt = "simple, clear, short, lowercase commit message"
  prompt_1 = "for the following diff:"
  prompt_2 = "for the following git status:"
  prompt_3 = "for the following comment:"

  content_1 = short(`git diff --staged`)
  content_2 = short(`git status`)
  content_3 = short(arguments.join("\n"))

  abort "no staged files" if content_1.empty?

  messages = [
    { role: :system, content: system_prompt },
    { role: :system, content: prompt_1 },
    { role: :user, content: content_1 },
    { role: :system, content: prompt_2 },
    { role: :user, content: content_2 },
    { role: :system, content: prompt_3 },
    { role: :user, content: content_3 }
  ]

  message = completion(token: token(".commit"), model: "gpt-4o", messages:)

  Git.open(".").commit(message)

  puts message
end

#command_compareObject



761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
# File 'lib/dorian/bin.rb', line 761

def command_compare
  file_1, file_2 = files
  key_1, key_2 = arguments
  read_1, read_2 =
    files.map.with_index do |file, index|
      read = reads(File.read(file))

      if arguments[index] && read.from_deep_struct.key?(arguments[index])
        read[arguments[index]]
      elsif arguments[index]
        nil
      else
        read
      end
    end

  compare(read_1, read_2, file_1:, file_2:)
end

#command_dirObject



684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
# File 'lib/dorian/bin.rb', line 684

def command_dir
  puts(
    Git
      .open(".")
      .ls_files
      .map(&:first)
      .map { |path| path.split("/").first }
      .select { |path| Dir.exist?(path) }
      .reject { |path| path.start_with?(".") }
      .sort
      .uniq
  )

  puts "." if self?
end

#command_dotObject



728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
# File 'lib/dorian/bin.rb', line 728

def command_dot
  dir = files.first || arguments.first || "."

  ignore_file = File.expand_path("#{dir.chomp("/")}/.dotignore")
  ignore_content = File.exist?(ignore_file) ? File.read(ignore_file) : ""
  ignore_patterns =
    ignore_content
      .lines
      .map(&:strip)
      .reject { |line| line.empty? || line.start_with?("#") }
      .map { |pattern| Regexp.new("\\A#{pattern}\\z") }

  Git
    .open(dir)
    .ls_files
    .map(&:first)
    .each do |file|
      next if ignore_patterns.any? { |pattern| pattern.match?(file) }

      homefile = "#{Dir.home}/#{file}"
      dotfile = File.expand_path("#{dir.chomp("/")}/#{file}")
      if File.exist?(homefile) || File.symlink?(homefile)
        File.delete(homefile)
      end
      FileUtils.mkdir_p(File.dirname(homefile))
      FileUtils.ln_s(dotfile, homefile, verbose: true)
    end
end

#command_eachObject



780
781
782
783
784
# File 'lib/dorian/bin.rb', line 780

def command_each
  each(everything) do |input|
    each(lines(reads(input)), progress: true) { |line| evaluates(it: line) }
  end
end

#command_evalObject



554
555
556
# File 'lib/dorian/bin.rb', line 554

def command_eval
  each(everything) { |thing| outputs(evaluates(ruby: thing)) }
end

#command_formatObject



363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
# File 'lib/dorian/bin.rb', line 363

def command_format
  context = MiniRacer::Context.new
  context.attach("puts", proc { |string| puts(string) })
  context.attach("warn", proc { |string| warn(string) })
  context.attach("read", proc { |path| File.read(path) })
  context.attach(
    "write",
    proc { |path, content| File.write(path, content) }
  )

  root = File.expand_path("../../", __dir__)

  prettier_path = File.join(root, "vendor/prettier/standalone.js")
  prettier_js = File.read(prettier_path)
  context.eval(prettier_js)

  sql_path = File.join(root, "vendor/sql-formatter.js")
  sql_js = File.read(sql_path)
  context.eval("self = {};")
  context.eval(sql_js)
  context.eval("sqlFormatter = self.sqlFormatter;")

  groovy_path = File.join(root, "vendor/groovy-beautify/dist/cjs/index.js")
  groovy_js = File.read(groovy_path)
  context.eval("module = { exports: {} };")
  context.eval("exports = module.exports;")
  context.eval(groovy_js)
  context.eval("groovyBeautify = module.exports;")

  plugins = %w[babel estree typescript html postcss markdown]

  plugins.each do |plugin|
    path = File.join(root, "vendor/prettier/plugins/#{plugin}.js")
    js = File.read(path)
    context.eval("module = { exports: {} };")
    context.eval("exports = module.exports;")
    context.eval(js)
    context.eval("#{plugin} = module.exports;")
  end

  context.eval("plugins = [#{plugins.join(", ")}];")

  context.eval(<<~JS)
    format = async (path, parser) => {
      try {
        const before = read(path);
        let after;

        if (parser === "sql") {
          after = sqlFormatter.format(before);
        } else if (parser === "groovy") {
          after = groovyBeautify(before);
        } else {
          after = await prettier.format(before, { parser, plugins });
        }

        if (before != after) {
          puts(path);
          write(path, after);
        }
      } catch (e) {
        warn(`failed to parse ${path}: ${e.message.split("\\n")[0]}`);
      }
    };
  JS

  if files.any?
    each(files) { |file| format(file, context:) }
  else
    each(
      Git.open(".").ls_files.map(&:first)
    ) { |file| format(file, context:) }
  end
end

#command_lsObject



700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
# File 'lib/dorian/bin.rb', line 700

def command_ls
  puts(
    Git
      .open(".")
      .ls_files
      .map(&:first)
      .map { |path| path.split("/").first }
      .reject { |path| path.start_with?(".") }
      .select { |path| match_filetypes?(path) }
      .sort
      .uniq
  )

  puts "." if self?
end

#command_lstripObject



670
671
672
673
674
675
676
677
678
# File 'lib/dorian/bin.rb', line 670

def command_lstrip
  each(stdin_files + files) do |input|
    outputs(lines(reads(File.read(input)), strip: :lstrip), file: input)
  end

  each(stdin_arguments + arguments) do |input|
    outputs(lines(reads(input), strip: :lstrip))
  end
end

#command_mergeObject



786
787
788
# File 'lib/dorian/bin.rb', line 786

def command_merge
  outputs(map(everything) { |thing| lines(reads(thing)) }.inject(&:+))
end

#command_pluckObject



809
810
811
812
813
814
815
# File 'lib/dorian/bin.rb', line 809

def command_pluck
  outputs(
    map(everything) do |thing|
      map(lines(reads(thing))) { |line| pluck(line) }
    end.inject(&:+)
  )
end

#command_prependObject



849
850
851
852
853
# File 'lib/dorian/bin.rb', line 849

def command_prepend
  outputs(
    map(everything.reverse) { |input| lines(reads(input)) }.inject(&:+)
  )
end

#command_prettyObject



359
360
361
# File 'lib/dorian/bin.rb', line 359

def command_pretty
  command_format
end

#command_readObject



642
643
644
645
646
647
648
# File 'lib/dorian/bin.rb', line 642

def command_read
  each(stdin_files + files) do |input|
    outputs(reads(File.read(input)), file: input)
  end

  each(stdin_arguments + arguments) { |input| outputs(reads(input)) }
end

#command_rejectObject



868
869
870
871
872
873
874
875
876
877
878
879
# File 'lib/dorian/bin.rb', line 868

def command_reject
  each(stdin_files + files) do |input|
    outputs(
      reject(lines(reads(File.read(input)))) { |element| match?(element) },
      file: input
    )
  end

  each(stdin_arguments + arguments) do |input|
    outputs(reject(lines(reads(input))) { |element| match?(element) })
  end
end

#command_releaseObject



438
439
440
441
442
443
# File 'lib/dorian/bin.rb', line 438

def command_release
  File.delete(*Dir["*.gem"])
  system("gem build")
  system("gem push *.gem")
  File.delete(*Dir["*.gem"])
end

#command_renameObject



619
620
621
622
623
624
# File 'lib/dorian/bin.rb', line 619

def command_rename
  from, to = arguments
  files = stdin_files + stdin_arguments + self.files
  (files - directories).each { |file| rename(file, file.gsub(from, to)) }
  directories.each { |dir| rename(dir, dir.gsub(from, to)) }
end

#command_replaceObject



609
610
611
612
613
614
615
616
617
# File 'lib/dorian/bin.rb', line 609

def command_replace
  from, to = arguments

  each(stdin_files + stdin_arguments + files) do |file|
    next if File.directory?(file)

    File.write(file, File.read(file).gsub(from, to))
  end
end

#command_rstripObject



660
661
662
663
664
665
666
667
668
# File 'lib/dorian/bin.rb', line 660

def command_rstrip
  each(stdin_files + files) do |input|
    outputs(lines(reads(File.read(input)), strip: :rstrip), file: input)
  end

  each(stdin_arguments + arguments) do |input|
    outputs(lines(reads(input), strip: :rstrip))
  end
end

#command_selectObject



855
856
857
858
859
860
861
862
863
864
865
866
# File 'lib/dorian/bin.rb', line 855

def command_select
  each(stdin_files + files) do |input|
    outputs(
      select(lines(reads(File.read(input)))) { |element| match?(element) },
      file: input
    )
  end

  each(stdin_arguments + arguments) do |input|
    outputs(select(lines(reads(input))) { |element| match?(element) })
  end
end

#command_shuffleObject



817
818
819
820
821
# File 'lib/dorian/bin.rb', line 817

def command_shuffle
  outputs(
    map(everything) { |thing| lines(reads(thing)) }.inject(&:+).shuffle
  )
end

#command_sortObject



790
791
792
793
794
795
796
797
798
799
# File 'lib/dorian/bin.rb', line 790

def command_sort
  outputs(
    map(everything) { |thing| lines(reads(thing)) }
      .inject(&:+)
      .sort_by do |line|
        result = pluck(line).from_deep_struct
        result.is_a?(Hash) ? result.values : result
      end
  )
end

#command_stripObject



650
651
652
653
654
655
656
657
658
# File 'lib/dorian/bin.rb', line 650

def command_strip
  each(stdin_files + files) do |input|
    outputs(lines(reads(File.read(input)), strip: :strip), file: input)
  end

  each(stdin_arguments + arguments) do |input|
    outputs(lines(reads(input), strip: :strip))
  end
end

#command_submodulesObject



716
717
718
719
720
721
722
723
724
725
726
# File 'lib/dorian/bin.rb', line 716

def command_submodules
  puts(
    File
      .read(".gitmodules")
      .lines
      .grep(/path = /)
      .map { |path| path.split("=").last.strip }
  )

  puts "." if self?
end

#command_tableObject



568
569
570
# File 'lib/dorian/bin.rb', line 568

def command_table
  table(map(everything) { |thing| lines(reads(thing)) }.inject(&:+))
end

#command_tallyObject



823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
# File 'lib/dorian/bin.rb', line 823

def command_tally
  each(everything) do |input|
    outputs(
      JSON.pretty_generate(
        map(lines(reads(input)), progress: true) do |element|
          if ruby.to_s.empty?
            element
          else
            evaluates(it: element, returns: true, stdout: false)
          end
        end.tally
      )
    )
  end
end

#command_thenObject



558
559
560
561
562
563
564
565
566
# File 'lib/dorian/bin.rb', line 558

def command_then
  each(stdin_files + files) do |input|
    outputs(evaluates(it: reads(File.read(input))), file: input)
  end

  each(stdin_arguments + arguments) do |input|
    outputs(evaluates(it: reads(input)))
  end
end

#command_timesObject



550
551
552
# File 'lib/dorian/bin.rb', line 550

def command_times
  map(everything, &:to_i).sum.times { |index| puts index + 1 }
end

#command_topObject



445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
# File 'lib/dorian/bin.rb', line 445

def command_top
  shell = arguments[0] || File.basename(ENV.fetch("SHELL", nil)) || "bash"
  limit = arguments[1] || 10

  history =
    case shell.to_s.downcase
    when "fish"
      File
        .read("#{Dir.home}/.local/share/fish/fish_history")
        .lines
        .grep(/^- cmd: /)
        .map { |line| line.split("- cmd: ", 2).last.strip }
    when "bash"
      File.read("#{Dir.home}/.bash_history").lines.map(&:strip)
    when "zsh"
      File.read("#{Dir.home}/.zsh_history").lines.map(&:strip)
    else
      raise NotImplementedError, shell
    end

  table(
    history
      .map { |line| line.split.first }
      .tally
      .to_a
      .sort_by(&:last)
      .reverse
      .map
      .with_index do |(command, command_count), index|
        {
          "#" => index + 1,
          :count => command_count,
          :percent =>
            "#{(command_count * 100 / history.size.to_f).round(3)}%",
          :command => command
        }
      end
      .first(limit)
  )
end

#command_treeObject



486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
# File 'lib/dorian/bin.rb', line 486

def command_tree
  space = "    "
  right = "└── "
  down = "│   "
  down_and_right = "├── "

  git_ls_files = ->(path) { Git.open(".").ls_files(path).map(&:first) }

  group =
    lambda do |files|
      files
        .group_by { |file| file.split("/").first }
        .transform_values do |values|
          group.call(
            values
              .map { |value| value.split("/")[1..].join("/") }
              .reject(&:empty?)
          )
        end
    end

  print =
    lambda do |key:, values:, index: 0, size: 1, prefix: ""|
      key = "#{key}/" if values.any?
      last = index + 1 == size
      right_prefix = last ? right : down_and_right
      puts prefix + right_prefix + key
      values.each.with_index do |(value_key, value_values), value_index|
        print.call(
          key: value_key,
          values: value_values,
          index: value_index,
          size: values.size,
          prefix: prefix + (last ? space : down)
        )
      end
    end

  keys = (arguments + files)
  keys = ["."] unless keys.any?

  keys.each do |key|
    files =
      git_ls_files
        .call(key)
        .map { |file| parsed.arguments.any? ? file.sub(key, "") : file }
    values = group.call(files)
    key = "#{key}/" if values.any? && key != "." && key[-1] != "/"
    puts key
    values.each.with_index do |(value_key, value_values), value_index|
      print.call(
        key: value_key,
        values: value_values,
        index: value_index,
        size: values.size
      )
    end
  end
end

#command_uniqObject



801
802
803
804
805
806
807
# File 'lib/dorian/bin.rb', line 801

def command_uniq
  outputs(
    map(everything) { |thing| lines(reads(thing)) }
      .inject(&:+)
      .uniq { |line| pluck(line) }
  )
end

#command_writeObject



637
638
639
640
# File 'lib/dorian/bin.rb', line 637

def command_write
  content = read_stdin.join
  each(files + arguments) { |file| File.write(file, content) }
end

#compare(content_1, content_2, file_1:, file_2:, path: ".") ⇒ Object



1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
# File 'lib/dorian/bin.rb', line 1337

def compare(content_1, content_2, file_1:, file_2:, path: ".")
  content_1 = content_1.from_deep_struct
  content_2 = content_2.from_deep_struct

  if content_1.is_a?(Hash) && content_2.is_a?(Hash)
    (content_1.keys + content_2.keys).uniq.each do |key|
      new_path = path == "." ? "#{path}#{key}" : "#{path}.#{key}"

      if content_1[key] && !content_2[key]
        warn "#{new_path} present in #{file_1} but not in #{file_2}"
        next
      elsif !content_1[key] && content_2[key]
        warn "#{new_path} present in #{file_2} but not in #{file_1}"
        next
      end

      compare(
        content_1[key],
        content_2[key],
        path: new_path,
        file_1:,
        file_2:
      )
    end
  elsif content_1.is_a?(Array) && content_2.is_a?(Array)
    (0...([content_1.size, content_2.size].max)).each do |index|
      new_path = "#{path}[#{index}]"
      if content_1[index] && !content_2[index]
        warn "#{new_path} present in #{file_1} but not in #{file_2}"
        next
      elsif !content_1[index] && content_2[index]
        warn "#{new_path} present in #{file_2} but not in #{file_1}"
        next
      end

      compare(
        content_1[index],
        content_2[index],
        path: new_path,
        file_1:,
        file_2:
      )
    end
  elsif content_1.class != content_2.class
    warn(
      "#{path} has #{content_1.class} for #{file_1} " \
        "and #{content_2.class} for #{file_2}"
    )
  end
end

#completion(token:, model:, messages:) ⇒ Object



1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
# File 'lib/dorian/bin.rb', line 1307

def completion(token:, model:, messages:)
  body =
    post(
      "https://api.openai.com/v1/chat/completions",
      headers: {
        "Content-Type" => "application/json",
        "Authorization" => "Bearer #{token}"
      },
      body: { model:, messages: }.to_json
    )

  json = JSON.parse(body)
  output = json.dig("choices", 0, "message", "content")

  if output
    output.strip
  else
    abort JSON.pretty_generate(json)
  end
end

#create_progress_bar(total) ⇒ Object



1219
1220
1221
1222
1223
# File 'lib/dorian/bin.rb', line 1219

def create_progress_bar(total)
  return unless progress?

  Dorian::Progress.create(total:, format: progress_format)
end

#debug?Boolean

Returns:

  • (Boolean)


1073
1074
1075
# File 'lib/dorian/bin.rb', line 1073

def debug?
  !!options.debug
end

#deep?Boolean

Returns:

  • (Boolean)


1021
1022
1023
# File 'lib/dorian/bin.rb', line 1021

def deep?
  !!options.deep
end

#deep_lines(input) ⇒ Object



1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
# File 'lib/dorian/bin.rb', line 1194

def deep_lines(input)
  case input
  when Array
    [input.to_deep_struct] +
      input.flat_map { |element| deep_lines(element) }
  when Hash
    [input.to_deep_struct] +
      input.flat_map { |key, value| deep_lines([key, value]) }
  when Struct
    deep_lines(input.from_deep_struct).to_deep_struct
  else
    [input.to_deep_struct]
  end
end

#directoriesObject



626
627
628
# File 'lib/dorian/bin.rb', line 626

def directories
  (stdin_files + files).select { |file| File.directory?(file) }
end

#each(collection, options: parallel_options, progress: false) ⇒ Object



1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
# File 'lib/dorian/bin.rb', line 1130

def each(collection, options: parallel_options, progress: false, &)
  collection = wrap(collection)
  progress_bar = progress ? create_progress_bar(collection.size) : nil

  if parallel?
    Parallel.each(
      collection,
      **options,
      finish: ->(*) { progress_bar&.increment },
      &
    )
  else
    collection.each do |element|
      yield(element).tap { progress_bar&.increment }
    end
  end
end

#encoderObject



1392
1393
1394
# File 'lib/dorian/bin.rb', line 1392

def encoder
  Tiktoken.encoding_for_model("gpt-4o")
end

#evaluates(ruby: @ruby, it: nil, debug: debug?, , stdout: stdout?, , stderr: stderr?, , colorize: colorize?, , rails: rails?, , fast: fast?, , returns: false) ⇒ Object



1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
# File 'lib/dorian/bin.rb', line 1457

def evaluates(
  ruby: @ruby,
  it: nil,
  debug: debug?,
  stdout: stdout?,
  stderr: stderr?,
  colorize: colorize?,
  rails: rails?,
  fast: fast?,
  returns: false
)
  Dorian::Eval.eval(
    ruby:,
    it:,
    debug:,
    stdout:,
    stderr:,
    colorize:,
    rails:,
    fast:,
    returns:
  ).returned
end

#everythingObject



680
681
682
# File 'lib/dorian/bin.rb', line 680

def everything
  read_stdin_files + stdin_arguments + read_files + arguments
end

#fast?Boolean

Returns:

  • (Boolean)


1033
1034
1035
# File 'lib/dorian/bin.rb', line 1033

def fast?
  !!options.fast
end

#filesObject



546
547
548
# File 'lib/dorian/bin.rb', line 546

def files
  parsed.files
end

#filetype(path) ⇒ Object



1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
# File 'lib/dorian/bin.rb', line 1497

def filetype(path)
  ext = File.extname(path).to_s.downcase

  if File.exists?(".format-ignore")
    return if File.read(".format-ignore").split.map(&:strip).include?(path)
  end

  return :directory if Dir.exist?(path)
  return :symlink if File.symlink?(path)
  return :ruby if RUBY_FILENAMES.include?(File.basename(path))
  return :ruby if RUBY_EXTENSIONS.include?(ext)
  return :json if ext == ".json"
  return :jsonl if ext == ".jsonl"
  return :yaml if ext == ".yaml"
  return :yaml if ext == ".yml"
  return :yamll if ext == ".yamll"
  return :yamll if ext == ".ymll"
  return :csv if ext == ".csv"
  return :js if ext == ".js"
  return :js if ext == ".mjs"
  return :js if ext == ".cjs"
  return :ts if ext == ".ts"
  return :css if ext == ".css"
  return :html if ext == ".html"
  return :html if ext == ".htm"
  return :haml if ext == ".haml"
  return :slim if ext == ".slim"
  return :erb if ext == ".erb"
  return :fish if ext == ".fish"
  return :sql if ext == ".sql"
  return :tex if ext == ".tex"
  return :md if ext == ".md"
  return :md if ext == ".markdown"
  return :png if ext == ".png"
  return :jpeg if ext == ".jpg"
  return :jpeg if ext == ".jpeg"
  return :ico if ext == ".ico"
  return :webp if ext == ".webp"
  return :heic if ext == ".heic"
  return :pdf if ext == ".pdf"
  return :raw if ext == ".raw"
  return :env if path == ".env"
  return :env if path.start_with?(".env.")
  return :sh if path == "Dockerfile"
  return :sh if ext == ".sh"
  return :enc if ext == ".enc"
  return :enc if ext == ".keystore"
  return :pro if ext == ".pro"
  return :txt if ext == ".txt"
  return :bat if ext == ".bat"
  return :xcconfig if ext == ".xcconfig"
  return :pbxproj if ext == ".pbxproj"
  return :xml if ext == ".xml"
  return :xml if ext == ".plist"
  return :xml if ext == ".storyboard"
  return :xml if ext == ".xcscheme"
  return :xml if ext == ".xcworkspacedata"
  return :xml if ext == ".xcprivacy"
  return :xml if ext == ".entitlements"
  return :kotlin if ext == ".kt"
  return :groovy if ext == ".gradle"
  return nil if ext == ".properties"
  return :binary if ext == ".jar"
  return :objectivec if ext == ".h"
  return :objectivec if ext == ".mm"
  return :objectivec if ext == ".m"
  return unless File.exist?(path)

  first_line = File.open(path, &:gets).to_s
  first_line = first_line.encode("UTF-8", invalid: :replace).strip
  return :ruby if first_line == "#!/usr/bin/env ruby"
  return :sh if first_line == "#!/bin/bash"
  return :sh if first_line == "#!/bin/sh"
  return :sh if first_line == "#!/bin/bash -e"
  return :sh if first_line == "#!/usr/bin/env sh"

  nil
end

#format(path, context:) ⇒ Object



1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
# File 'lib/dorian/bin.rb', line 1576

def format(path, context:)
  return if File.symlink?(path)
  return if File.directory?(path)
  return unless File.exist?(path)

  before = File.read(path)

  case filetype(path)
  when :ruby
    after = SyntaxTree.format(before)
  when :haml
    after = SyntaxTree::Haml.format(before)
  when :erb
    after = SyntaxTree::ERB.format(before)
  when :xml
    after = SyntaxTree::XML.format(before)
  when :json
    after = JSON.pretty_generate(JSON.parse(before))
  when :jsonl
    after =
      "#{before.lines.map { |line| JSON.parse(line).to_json }.join("\n")}\n"
  when :csv
    after =
      CSV.generate { |csv| CSV.parse(before).each { |row| csv << row } }
  when :yaml
    after = sort(YAML.safe_load(before)).to_yaml
  when :yamll
    after =
      "#{
        before
          .lines
          .map do |line|
            sort(YAML.safe_load(JSON.parse(line))).to_yaml.to_json
          end
          .join("\n")
      }\n"
  when :js
    context.eval("format(#{path.to_json}, 'babel')")
  when :ts
    context.eval("format(#{path.to_json}, 'typescript')")
  when :html
    context.eval("format(#{path.to_json}, 'html')")
  when :md
    context.eval("format(#{path.to_json}, 'markdown')")
  when :sql
    context.eval("format(#{path.to_json}, 'sql')")
  when :groovy
    context.eval("format(#{path.to_json}, 'groovy')")
  when :css
    context.eval("format(#{path.to_json}, 'css')")
  when :sh
    if system("command -v shfmt > /dev/null 2>&1")
      command = ["shfmt", "-i", "4", path].shelljoin
      stdout, stderr, status = Open3.capture3(command)
      raise stderr unless stderr.empty? && status.success?

      after = stdout
    else
      warn "run: `brew install shfmt` for #{path}"
    end
  when :pdf
    doc = HexaPDF::Document.open(path)
    doc.trailer.info.each_key { |key| doc.trailer.info.delete(key) }
    doc.write(path, update_fields: false)
    after = File.read(path)
  when :tex
    if system("command -v latexindent > /dev/null 2>&1")
      command = ["latexindent", path, "--logfile", "/dev/null"].shelljoin
      stdout, stderr, status = Open3.capture3(command)
      raise stderr unless stderr.empty? && status.success?

      after = stdout.gsub("\t", "  ")
    else
      warn "run: `brew install latexindent` for #{path}"
    end
  when :objectivec
    if system("command -v clang-format > /dev/null 2>&1")
      command = ["clang-format", path].shelljoin
      stdout, stderr, status = Open3.capture3(command)
      raise stderr unless stderr.empty? && status.success?

      after = stdout.gsub("\t", "  ")
    else
      warn "run: `brew install clang-format` for #{path}"
    end
  when :kotlin
    if system("command -v ktlint > /dev/null 2>&1")
      command = ["ktlint", "-F", path].shelljoin
      stdout, stderr, status = Open3.capture3(command)
      raise stderr unless stderr.empty? && status.success?

      after = File.read(path)
    else
      warn "run: `brew install ktlint` for #{path}"
    end
  when :raw, :env, :enc, :txt, :pro, :binary, :slim, :fish, :bat, :xcconfig,
       :pbxproj, :jpeg, :png, :webp, :heic, :ico
    # nothing to do
  else
    case File.basename(path)
    when ".gitignore", ".node-version", ".prettierignore", ".ruby-version",
         ".tool-versions", "Gemfile.lock", "LICENSE", "VERSION", ".rspec",
         "Procfile", "Procfile.dev", "Podfile.lock", ".xcode.env", "CNAME",
         "TODO", ".gitmodules", ".asdfrc", "config", ".dotignore", ".gemrc",
         ".gitconfig", ".gitmessage", ".hushlogin", ".psqlrc", ".vimrc",
         "DIRECTORIES", ".npm-version"
      # nothing to do
    when ".keep"
      File.write(path, "")
    else
      puts "unhandled: #{path}"
    end
  end

  if after && before != after
    puts path
    File.write(path, after)
  end
rescue StandardError => e
  warn "failed to parse #{path}: #{e.message}"
end

#headers?Boolean

Returns:

  • (Boolean)


1081
1082
1083
# File 'lib/dorian/bin.rb', line 1081

def headers?
  !!options.headers
end

#headers_of(content) ⇒ Object



1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
# File 'lib/dorian/bin.rb', line 1109

def headers_of(content)
  return unless content.respond_to?(:first)
  return unless content.first
  return unless content.first.respond_to?(:to_h)
  return unless content.first.to_h.keys.any?

  content.first.to_h.keys
rescue TypeError
  nil
end

#helpObject



1097
1098
1099
# File 'lib/dorian/bin.rb', line 1097

def help
  parsed.help
end

#help?Boolean

Returns:

  • (Boolean)


1093
1094
1095
# File 'lib/dorian/bin.rb', line 1093

def help?
  !!options.help
end

#inputObject



1041
1042
1043
# File 'lib/dorian/bin.rb', line 1041

def input
  io || IO.fetch(options.input.to_s, nil) || io_from_files || DEFAULT_IO
end

#ioObject



1037
1038
1039
# File 'lib/dorian/bin.rb', line 1037

def io
  IO.fetch(options.io.to_s, nil)
end

#io_from_filesObject



1049
1050
1051
# File 'lib/dorian/bin.rb', line 1049

def io_from_files
  IO.fetch(File.extname((stdin_files + files).first || "").delete("."), nil)
end

#lines(input, strip: :rstrip) ⇒ Object



1184
1185
1186
1187
1188
1189
1190
1191
1192
# File 'lib/dorian/bin.rb', line 1184

def lines(input, strip: :rstrip)
  if input.is_a?(String)
    input.lines.map(&strip)
  elsif deep?
    deep_lines(input)
  else
    Array(input)
  end
end

#map(collection, options: parallel_options, progress: false) ⇒ Object



1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
# File 'lib/dorian/bin.rb', line 1148

def map(collection, options: parallel_options, progress: false, &)
  collection = wrap(collection)
  progress_bar = progress ? create_progress_bar(collection.size) : nil

  if parallel?
    Parallel.map(
      collection,
      **options,
      finish: ->(*) { progress_bar&.increment },
      &
    )
  else
    collection.map do |element|
      yield(element).tap { progress_bar&.increment }
    end
  end
end

#match?(element, ruby: @ruby) ⇒ Boolean

Returns:

  • (Boolean)


1288
1289
1290
# File 'lib/dorian/bin.rb', line 1288

def match?(element, ruby: @ruby)
  !!evaluates(ruby:, it: element, stdout: false, returns: true)
end

#match_filetypes?(path, filetypes: arguments) ⇒ Boolean

Returns:

  • (Boolean)


1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
# File 'lib/dorian/bin.rb', line 1396

def match_filetypes?(path, filetypes: arguments)
  return true if filetypes.none?
  return true unless filetypes.intersect?(%w[rb ruby])
  return false if Dir.exist?(path)
  return true if RUBY_FILENAMES.include?(path)
  return true if RUBY_EXTENSIONS.include?(File.extname(path))
  return false unless File.exist?(path)

  first_line = File.open(path, &:gets).to_s
  first_line = first_line.encode("UTF-8", invalid: :replace)

  return true if /\A#!.*ruby\z/.match?(first_line)

  false
end

#nObject



1101
1102
1103
# File 'lib/dorian/bin.rb', line 1101

def n
  options.n
end

#optionsObject



1025
1026
1027
# File 'lib/dorian/bin.rb', line 1025

def options
  parsed.options
end

#outputObject



1045
1046
1047
# File 'lib/dorian/bin.rb', line 1045

def output
  io || IO.fetch(options.output.to_s, nil) || io_from_files || DEFAULT_IO
end

#outputs(content, file: nil) ⇒ Object



921
922
923
924
925
926
927
# File 'lib/dorian/bin.rb', line 921

def outputs(content, file: nil)
  if write? && file
    File.write(file, to_output(content))
  elsif !content.nil?
    puts to_output(content)
  end
end

#parallel?Boolean

Returns:

  • (Boolean)


1053
1054
1055
# File 'lib/dorian/bin.rb', line 1053

def parallel?
  !!options.parallel
end

#parallel_optionsObject



1120
1121
1122
1123
1124
1125
1126
1127
1128
# File 'lib/dorian/bin.rb', line 1120

def parallel_options
  if parallel_type == :processes
    { in_processes: n }
  elsif parallel_type == :threads
    { in_threads: n }
  else
    abort "#{parallel_type.inspect} not supported"
  end
end

#parallel_typeObject



1105
1106
1107
# File 'lib/dorian/bin.rb', line 1105

def parallel_type
  options.parallel_type&.to_sym
end

#pluck(element) ⇒ Object



1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
# File 'lib/dorian/bin.rb', line 1412

def pluck(element)
  element = element.from_deep_struct

  keys =
    if arguments.any?
      arguments
    elsif element.is_a?(Hash)
      element.keys
    elsif element.is_a?(Array)
      (0...(element.size)).map(&:to_s)
    else
      "it"
    end

  results =
    keys.map do |argument|
      if element.is_a?(Array) && argument.to_i.to_s == argument
        element[argument.to_i]
      elsif element.is_a?(Hash) && element.key?(argument)
        { argument => element[argument] }
      else
        evaluates(ruby: argument, it: element.to_deep_struct)
      end
    end

  if results.all?(Hash)
    results.inject(&:merge).to_deep_struct
  else
    results
      .map { |result| result.is_a?(Hash) ? result.values.first : result }
      .to_deep_struct
  end
end

#post(url, headers: {}, body: {}) ⇒ Object



1328
1329
1330
1331
1332
1333
1334
1335
# File 'lib/dorian/bin.rb', line 1328

def post(url, headers: {}, body: {})
  uri = URI.parse(url)
  http = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl = true
  request = Net::HTTP::Post.new(uri.path, headers)
  request.body = body
  http.request(request).body
end

#pretty?Boolean

Returns:

  • (Boolean)


985
986
987
# File 'lib/dorian/bin.rb', line 985

def pretty?
  !!options.pretty
end

#progress?Boolean

Returns:

  • (Boolean)


1077
1078
1079
# File 'lib/dorian/bin.rb', line 1077

def progress?
  !!options.progress
end

#progress_formatObject



1085
1086
1087
# File 'lib/dorian/bin.rb', line 1085

def progress_format
  options.progress_format
end

#rails?Boolean

Returns:

  • (Boolean)


1057
1058
1059
# File 'lib/dorian/bin.rb', line 1057

def rails?
  !!options.rails
end

#read_filesObject



1006
1007
1008
# File 'lib/dorian/bin.rb', line 1006

def read_files
  files.map { |file| File.read(file) }
end

#read_stdinObject



989
990
991
992
993
# File 'lib/dorian/bin.rb', line 989

def read_stdin
  @read_stdin ||= $stdin.each_line.to_a
rescue Interrupt
  abort "interupt in read_stdin"
end

#read_stdin_filesObject



1002
1003
1004
# File 'lib/dorian/bin.rb', line 1002

def read_stdin_files
  stdin_files.map { |file| File.read(file) }
end

#reads(content) ⇒ Object



956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
# File 'lib/dorian/bin.rb', line 956

def reads(content)
  case input
  when :csv
    if headers?
      CSV.parse(content, headers: true).map(&:to_h).to_deep_struct
    else
      CSV.parse(content)
    end
  when :json
    JSON.parse(content).to_deep_struct
  when :jsonl
    map(content.lines) { |line| JSON.parse(line) }.to_deep_struct
  when :raw
    content
  when :yaml
    YAML.safe_load(content).to_deep_struct
  when :yamll
    map(content.lines) do |line|
      YAML.safe_load(JSON.parse(line))
    end.to_deep_struct
  else
    abort "#{input.inspect} not supported"
  end
rescue JSON::ParserError => e
  abort "invalid json: #{e.message}"
rescue Psych::SyntaxError => e
  abort "invalid yaml: #{e.message}"
end

#reject(collection, progress: false) ⇒ Object



1175
1176
1177
1178
1179
1180
1181
1182
# File 'lib/dorian/bin.rb', line 1175

def reject(collection, progress: false, &)
  collection = wrap(collection)
  progress_bar = progress ? create_progress_bar(collection.size) : nil

  collection.reject do |element|
    yield(element).tap { progress_bar&.increment }
  end
end

#rename(old, new) ⇒ Object



630
631
632
633
634
635
# File 'lib/dorian/bin.rb', line 630

def rename(old, new)
  return if old == new

  puts "#{old} -> #{new}"
  File.rename(old, new)
end

#runObject



185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
# File 'lib/dorian/bin.rb', line 185

def run
  abort help if help?
  abort VERSION if version?

  case command&.to_sym
  when :each
    arguments.delete("each")
    @command = :each
    @ruby = arguments.delete_at(0)
    command_each
  when :all
    arguments.delete("all")
    @command = :all
    @ruby = arguments.delete_at(0)
    command_all
  when :before
    arguments.delete("before")
    @command = :before
    @ruby = arguments.delete_at(0)
    command_before
  when :after
    arguments.delete("after")
    @command = :after
    @ruby = arguments.delete_at(0)
    command_after
  when :between
    arguments.delete("between")
    @command = :between
    @ruby_after = arguments.delete_at(0)
    @ruby_before = arguments.delete_at(0)
    command_between
  when :select
    arguments.delete("select")
    @command = :select
    @ruby = arguments.delete_at(0)
    command_select
  when :reject
    arguments.delete("reject")
    @command = :reject
    @ruby = arguments.delete_at(0)
    command_reject
  when :tally
    arguments.delete("tally")
    @command = :tally
    @ruby = arguments.delete_at(0)
    command_tally
  when :anonymize
    arguments.delete("anonymize")
    @command = :anonymize
    command_anonymize
  when :append
    arguments.delete("append")
    @command = :append
    command_append
  when :prepend
    arguments.delete("prepend")
    @command = :prepend
    command_prepend
  when :chat
    arguments.delete("chat")
    @command = :chat
    command_chat
  when :commit
    arguments.delete("commit")
    @command = :commit
    command_commit
  when :compare
    arguments.delete("compare")
    @command = :compare
    command_compare
  when :dir
    arguments.delete("dir")
    @command = :dir
    command_dir
  when :submodules
    arguments.delete("submodules")
    @command = :submodules
    command_submodules
  when :dot
    arguments.delete("dot")
    @command = :dot
    command_dot
  when :eval
    arguments.delete("eval")
    @command = :eval
    command_eval
  when :ls
    arguments.delete("ls")
    @command = :ls
    command_ls
  when :strip
    arguments.delete("strip")
    @command = :strip
    command_strip
  when :rstrip
    arguments.delete("rstrip")
    @command = :rstrip
    command_rstrip
  when :lstrip
    arguments.delete("lstrip")
    @command = :lstrip
    command_lstrip
  when :merge
    arguments.delete("merge")
    @command = :merge
    command_merge
  when :pluck
    arguments.delete("pluck")
    @command = :pluck
    command_pluck
  when :shuffle
    arguments.delete("shuffle")
    @command = :shuffle
    command_shuffle
  when :rename
    arguments.delete("rename")
    @command = :rename
    command_rename
  when :replace
    arguments.delete("replace")
    @command = :replace
    command_replace
  when :sort
    arguments.delete("sort")
    @command = :sort
    command_sort
  when :table
    arguments.delete("table")
    @command = :table
    command_table
  when :then
    arguments.delete("then")
    @command = :then
    @ruby = arguments.delete_at(0)
    command_then
  when :times
    arguments.delete("times")
    @command = :times
    command_times
  when :uniq
    arguments.delete("uniq")
    @command = :uniq
    command_uniq
  when :write
    arguments.delete("write")
    @command = :write
    command_write
  when :release
    arguments.delete("release")
    @command = :release
    command_release
  when :top
    arguments.delete("top")
    @command = :top
    command_top
  when :tree
    arguments.delete("tree")
    @command = :tree
    command_tree
  when :format
    arguments.delete("format")
    @command = :format
    command_format
  when :pretty
    arguments.delete("pretty")
    @command = :pretty
    command_pretty
  else
    arguments.delete("read")
    @command = :read
    command_read
  end
end

#select(collection, progress: false) ⇒ Object



1166
1167
1168
1169
1170
1171
1172
1173
# File 'lib/dorian/bin.rb', line 1166

def select(collection, progress: false, &)
  collection = wrap(collection)
  progress_bar = progress ? create_progress_bar(collection.size) : nil

  collection.select do |element|
    yield(element).tap { progress_bar&.increment }
  end
end

#self?Boolean

Returns:

  • (Boolean)


757
758
759
# File 'lib/dorian/bin.rb', line 757

def self?
  !!options.self
end

#short(string) ⇒ Object



1388
1389
1390
# File 'lib/dorian/bin.rb', line 1388

def short(string)
  string[0..5000]
end

#sort(object) ⇒ Object



1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
# File 'lib/dorian/bin.rb', line 1481

def sort(object)
  object = object.from_deep_struct

  if object.is_a?(Hash)
    object
      .to_a
      .sort_by(&:first)
      .to_h
      .transform_values { |value| sort(value) }
  elsif object.is_a?(Array)
    object.map { |element| sort(element) }
  else
    object
  end
end

#stderr?Boolean

Returns:

  • (Boolean)


1061
1062
1063
# File 'lib/dorian/bin.rb', line 1061

def stderr?
  !!options.stderr
end

#stdinObject



1017
1018
1019
# File 'lib/dorian/bin.rb', line 1017

def stdin
  options.stdin.to_sym
end

#stdin_argumentsObject



1010
1011
1012
1013
1014
1015
# File 'lib/dorian/bin.rb', line 1010

def stdin_arguments
  return [] if files.any? || arguments.any?
  return [] if stdin == :files

  [read_stdin.join]
end

#stdin_filesObject



995
996
997
998
999
1000
# File 'lib/dorian/bin.rb', line 995

def stdin_files
  return [] if files.any? || arguments.any?
  return [] if stdin != :files

  map(read_stdin, &:rstrip)
end

#stdout?Boolean

Returns:

  • (Boolean)


1065
1066
1067
# File 'lib/dorian/bin.rb', line 1065

def stdout?
  !!options.stdout
end

#table(data) ⇒ Object



1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
# File 'lib/dorian/bin.rb', line 1446

def table(data)
  is_hashes = data.first.from_deep_struct.is_a?(Hash)
  headings = is_hashes ? data.first.to_h.keys : nil
  rows = is_hashes ? data.map(&:values) : data.map { |row| wrap(row) }
  if headings
    puts Terminal::Table.new(headings:, rows:)
  else
    puts Terminal::Table.new(rows:)
  end
end

#to_output(content) ⇒ Object



929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
# File 'lib/dorian/bin.rb', line 929

def to_output(content)
  content = content.from_deep_struct

  case output
  when :csv
    "#{headers_of(content)&.to_csv}#{
      map(content) do |element|
        CSV.generate(headers: headers_of(content)) do |csv|
          csv << wrap(element)
        end
      end.join
    }"
  when :json
    pretty? ? JSON.pretty_generate(content) : content.to_json
  when :jsonl
    "#{map(content, &:to_json).join("\n")}\n"
  when :raw
    content
  when :yaml
    content.to_yaml
  when :yamll
    "#{map(map(content, &:to_yaml), &:to_json).join("\n")}\n"
  else
    abort "#{output.inspect} not supported"
  end
end

#token(file) ⇒ Object



1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
# File 'lib/dorian/bin.rb', line 1292

def token(file)
  token_file = File.join(Dir.home, file)

  if File.exist?(token_file)
    token = File.read(token_file).strip
  else
    print "token: "
    token = gets.strip
    File.write(token_file, token)
    puts "token written to #{token_file}"
  end

  token
end

#version?Boolean

Returns:

  • (Boolean)


1089
1090
1091
# File 'lib/dorian/bin.rb', line 1089

def version?
  !!options.version
end

#wrap(ruby) ⇒ Object



1209
1210
1211
1212
1213
1214
1215
1216
1217
# File 'lib/dorian/bin.rb', line 1209

def wrap(ruby)
  if ruby.is_a?(Hash)
    ruby
  elsif ruby.respond_to?(:to_a)
    ruby.to_a
  else
    Array(ruby)
  end
end

#write?Boolean

Returns:

  • (Boolean)


1069
1070
1071
# File 'lib/dorian/bin.rb', line 1069

def write?
  !!options.write
end