diff --git a/README.md b/README.md index 2548c09..b8b1b87 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,8 @@ If bundler is not being used to manage dependencies, install the gem by executin ```sh Usage: flatito PATH [options] -Example: flatito . -k hello +Example: flatito . -k "search string" -e "json,yaml" +Example: cat file.yaml | flatito -k "search string" -h, --help Prints this help -V, --version Show version diff --git a/exe/flatito b/exe/flatito index b077efc..cfa9d3d 100755 --- a/exe/flatito +++ b/exe/flatito @@ -6,14 +6,15 @@ require "flatito" require "optparse" # If no arguments are given, print help -ARGV << "-h" if ARGV.empty? +stdin = $stdin.read unless $stdin.tty? +ARGV << "-h" if ARGV.empty? && !stdin options = {} OptionParser.new do |opts| opts.banner = <<~HEREDOC Usage: flatito PATH [options] - Example: flatito . -k hello - + Example: flatito . -k "search string" -e "json,yaml" + Example: cat file.yaml | flatito -k "search string" HEREDOC opts.on("-h", "--help", "Prints this help") do @@ -43,8 +44,10 @@ OptionParser.new do |opts| end end.parse! -begin +Flatito::Config.prepare_with_options(options) + +if stdin + Flatito.flat_content(stdin, options) +else Flatito.search(ARGV, options) -rescue Interrupt - warn "\nInterrupted" end diff --git a/lib/flatito.rb b/lib/flatito.rb index a618b2c..df3c213 100644 --- a/lib/flatito.rb +++ b/lib/flatito.rb @@ -9,9 +9,20 @@ require_relative "flatito/yaml_with_line_number" require_relative "flatito/renderer" require_relative "flatito/regex_from_search" +require_relative "flatito/print_items" +require_relative "flatito/config" module Flatito - def self.search(paths, options) - Finder.new(paths, options).call + class << self + def search(paths, options = {}) + Finder.new(paths, options).call + rescue Interrupt + warn "\nInterrupted" + end + + def flat_content(content, options = {}) + items = FlattenYaml.items_from_content(content) + PrintItems.new(options[:search]).print(items) + end end end diff --git a/lib/flatito/config.rb b/lib/flatito/config.rb new file mode 100644 index 0000000..fe51d2d --- /dev/null +++ b/lib/flatito/config.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module Flatito + module Config + @stdout = $stdout + @stderr = $stderr + @stdin = $stdin + + class << self + attr_accessor :renderer, :stdout, :stder, :stdin + + def prepare_with_options(options) + self.renderer = Renderer.build(options) + end + end + end +end diff --git a/lib/flatito/finder.rb b/lib/flatito/finder.rb index 34ea41a..2593928 100644 --- a/lib/flatito/finder.rb +++ b/lib/flatito/finder.rb @@ -8,14 +8,14 @@ class Finder DEFAULT_EXTENSIONS = %w[json yml yaml].freeze - attr_reader :paths, :search, :extensions, :options, :renderer + attr_reader :paths, :search, :extensions, :options, :print_items def initialize(paths, options = {}) @paths = paths @search = options[:search] @extensions = prepare_extensions(options[:extensions] || DEFAULT_EXTENSIONS) @options = options - @renderer = Renderer.build(options) + @print_items = PrintItems.new(search) end def call @@ -36,14 +36,13 @@ def call private - def flat_and_filter(pathname) - items = FlattenYaml.new(pathname).items - items = filter_by_search(items) if search - - return unless items.any? + def renderer + Config.renderer + end - renderer.print_pathname(pathname) - renderer.print_items(items) + def flat_and_filter(pathname) + items = FlattenYaml.items_from_path(pathname) + print_items.print(items, pathname) end def prepare_extensions(extensions) @@ -51,11 +50,5 @@ def prepare_extensions(extensions) ext.start_with?(".") ? ext : ".#{ext}" end end - - def filter_by_search(items) - items.select do |item| - regex.match?(item.key) - end - end end end diff --git a/lib/flatito/flatten_yaml.rb b/lib/flatito/flatten_yaml.rb index 3fdb5c1..bea8838 100644 --- a/lib/flatito/flatten_yaml.rb +++ b/lib/flatito/flatten_yaml.rb @@ -1,9 +1,26 @@ # frozen_string_literal: true +require_relative "utils" module Flatito class FlattenYaml + include Utils + Item = Struct.new(:key, :value, :line, keyword_init: true) - def initialize(pathname) + class << self + def items_from_path(pathname) + content = File.read(pathname) + new(content, pathname: pathname).items + end + + def items_from_content(content) + new(content).items + end + end + + attr_reader :content, :pathname + + def initialize(content, pathname: nil) + @content = content @pathname = pathname end @@ -23,28 +40,20 @@ def flatten_hash(hash, prefix = nil) end end end - rescue StandardError => e - warn "Error parsing #{@pathname}, #{e.message}" end def with_line_numbers handler = YAMLWithLineNumber::TreeBuilder.new handler.parser = Psych::Parser.new(handler) - handler.parser.parse(File.read(@pathname)) + handler.parser.parse(content) YAMLWithLineNumber::VisitorsToRuby.create.accept(handler.root) rescue Psych::SyntaxError - warn "Invalid YAML #{@pathname}" - + warn "Invalid format #{pathname}" [] - rescue StandardError => e - warn "Error parsing #{@pathname}, #{e.message}" - + rescue StandardError + warn "Error parsing #{pathname}" [] end - - def truncate(string, max = 50) - string.length > max ? "#{string[0...max]}..." : string - end end end diff --git a/lib/flatito/print_items.rb b/lib/flatito/print_items.rb new file mode 100644 index 0000000..8558a02 --- /dev/null +++ b/lib/flatito/print_items.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +module Flatito + class PrintItems + include RegexFromSearch + attr_reader :search + + def initialize(search) + @search = search + end + + def print(items, pathname = nil) + items = filter_by_search(items) if search + return unless items.any? + + renderer.print_pathname(pathname) if pathname + renderer.print_items(items) + end + + def filter_by_search(items) + items.select do |item| + regex.match?(item.key) + end + end + + def renderer + Config.renderer + end + end +end diff --git a/lib/flatito/renderer.rb b/lib/flatito/renderer.rb index a5d7d7b..0949bd0 100644 --- a/lib/flatito/renderer.rb +++ b/lib/flatito/renderer.rb @@ -2,25 +2,23 @@ require "io/console" require_relative "regex_from_search" +require_relative "utils" module Flatito class Renderer - include RegexFromSearch - def self.build(options) - if tty? + if Config.stdout.tty? Renderer::TTY.new(options) else Renderer::Plain.new(options) end end - - def self.tty? - $stdout.tty? - end end class Base + include Utils + include RegexFromSearch + attr_reader :search, :no_color def initialize(options) @@ -33,7 +31,7 @@ def print_file_progress(pathname); end def ending; end def print_pathname(pathname) - puts colorize(pathname.to_s, :light_blue) + stdout.puts colorize(pathname.to_s, :light_blue) end def print_items(items) @@ -42,8 +40,8 @@ def print_items(items) items.each do |item| print_item(item, line_number_padding) end - puts - flush + stdout.puts + stdout.flush end def print_item(item, line_number_padding) @@ -54,19 +52,11 @@ def print_item(item, line_number_padding) "" end - puts "#{line_number} #{matched_string(item.key)} #{value}" + stdout.puts "#{line_number} #{matched_string(item.key)} #{value}" end private - def flush - stdout.flush - end - - def regex - @regex ||= Regexp.new(search) - end - def matched_string(string) return string if search.nil? || no_color? @@ -80,12 +70,8 @@ def no_color? ENV["TERM"] == "dumb" || ENV["NO_COLOR"] == "true" || no_color == true end - def truncate(string, max = 50) - string.length > max ? "#{string[0...max]}..." : string - end - def stdout - $stdout + Config.stdout end def colorize(string, color) @@ -95,7 +81,7 @@ def colorize(string, color) class Renderer::Plain < Base def ending - puts + stdout.puts end end @@ -128,19 +114,19 @@ def print_pathname(pathname) def ending clear_line show_cursor - puts + stdout.puts end def hide_cursor - print HIDE_CURSOR + stdout.print HIDE_CURSOR end def show_cursor - print SHOW_CURSOR + stdout.print SHOW_CURSOR end def clear_line - print CLEAR_LINE + stdout.print CLEAR_LINE end private diff --git a/lib/flatito/utils.rb b/lib/flatito/utils.rb new file mode 100644 index 0000000..733023a --- /dev/null +++ b/lib/flatito/utils.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +module Flatito + module Utils + def truncate(string, max = 50) + string.length > max ? "#{string[0...max]}..." : string + end + end +end diff --git a/lib/flatito/yaml_with_line_number.rb b/lib/flatito/yaml_with_line_number.rb index e70fbd3..2c0e90b 100644 --- a/lib/flatito/yaml_with_line_number.rb +++ b/lib/flatito/yaml_with_line_number.rb @@ -30,8 +30,6 @@ def respond_to_missing?(*) class ClassLoader < Psych::ClassLoader def find(klassname) - @cache[klassname] ||= resolve(klassname) - rescue ArgumentError Whatever.new(klassname) end end diff --git a/test/fixtures/with_ruby_objects.yml b/test/fixtures/with_ruby_objects.yml index b52c464..d667799 100644 --- a/test/fixtures/with_ruby_objects.yml +++ b/test/fixtures/with_ruby_objects.yml @@ -3,6 +3,6 @@ two: !ruby/object:OpenStruct table: one: One two: Two -three: !ruby/object:UnknowClass +three: !ruby/object:UnknownClass one: One two: Two diff --git a/test/flatito/config_test.rb b/test/flatito/config_test.rb new file mode 100644 index 0000000..40c1340 --- /dev/null +++ b/test/flatito/config_test.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +require "test_helper" + +class Flatito::ConfigTest < Minitest::Test + class DummyTTY < StringIO + def tty? + true + end + end + + class DummyNotTTY < StringIO + def tty? + false + end + end + + test "when stdout is tty set renderer as tty" do + Flatito::Config.stdout = DummyTTY.new + Flatito::Config.prepare_with_options({}) + + assert_kind_of Flatito::Renderer::TTY, Flatito::Config.renderer + end + + test "when stdout is not tty set renderer as plain" do + Flatito::Config.stdout = DummyNotTTY.new + Flatito::Config.prepare_with_options({}) + + assert_kind_of Flatito::Renderer::Plain, Flatito::Config.renderer + end +end diff --git a/test/flatito/flatten_yaml_test.rb b/test/flatito/flatten_yaml_test.rb index 8be2c11..405caab 100644 --- a/test/flatito/flatten_yaml_test.rb +++ b/test/flatito/flatten_yaml_test.rb @@ -4,7 +4,7 @@ class Flatito::FlattenYamlTest < Minitest::Test test "a json" do - items = Flatito::FlattenYaml.new("test/fixtures/a.json").items + items = Flatito::FlattenYaml.items_from_path("test/fixtures/a.json") assert_equal 5, items.size assert_equal "one", items[0].key @@ -13,7 +13,7 @@ class Flatito::FlattenYamlTest < Minitest::Test end test "no nested" do - items = Flatito::FlattenYaml.new("test/fixtures/no_nested.yml").items + items = Flatito::FlattenYaml.items_from_path("test/fixtures/no_nested.yml") assert_equal 3, items.size assert_equal "one", items[0].key @@ -22,7 +22,7 @@ class Flatito::FlattenYamlTest < Minitest::Test end test "nested" do - items = Flatito::FlattenYaml.new("test/fixtures/nested.yml").items + items = Flatito::FlattenYaml.items_from_path("test/fixtures/nested.yml") assert_equal 3, items.size assert_equal "nested1.one", items[0].key @@ -39,7 +39,7 @@ class Flatito::FlattenYamlTest < Minitest::Test end test "with merging hashes" do - items = Flatito::FlattenYaml.new("test/fixtures/merge.yml").items + items = Flatito::FlattenYaml.items_from_path("test/fixtures/merge.yml") assert_equal 3, items.size @@ -57,7 +57,7 @@ class Flatito::FlattenYamlTest < Minitest::Test end test "multiline values" do - items = Flatito::FlattenYaml.new("test/fixtures/multiline.yml").items + items = Flatito::FlattenYaml.items_from_path("test/fixtures/multiline.yml") assert_equal "en.long_message", items[0].key assert_equal 2, items[0].line @@ -67,12 +67,26 @@ class Flatito::FlattenYamlTest < Minitest::Test end test "with ruby objects" do - items = Flatito::FlattenYaml.new("test/fixtures/with_ruby_objects.yml").items + items = Flatito::FlattenYaml.items_from_path("test/fixtures/with_ruby_objects.yml") assert_equal 3, items.size assert_equal "two", items[1].key assert_equal "[object: OpenStruct]", items[1].value assert_equal 2, items[1].line + + assert_equal "three", items[2].key + assert_equal "[object: UnknownClass]", items[2].value + assert_equal 6, items[2].line + end + + test "items from content with duplicated keys" do + content = File.read("test/fixtures/no_nested.yml") + items = Flatito::FlattenYaml.items_from_content(content) + + assert_equal 3, items.size + assert_equal "one", items[0].key + assert_equal "One", items[0].value + assert_equal 1, items[0].line end end