diff --git a/Gemfile.lock b/Gemfile.lock index d7f2c54..b1fc68b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - fakerbot (0.3.0) + fakerbot (0.4.0) faker pastel (~> 0.7.2) thor (~> 0.20.0) @@ -67,8 +67,8 @@ GEM json (>= 1.8, < 3) simplecov-html (~> 0.10.0) simplecov-html (0.10.2) - strings (0.1.1) - unicode-display_width (~> 1.3.0) + strings (0.1.2) + unicode-display_width (~> 1.4.0) unicode_utils (~> 1.4.0) term-ansicolor (1.6.0) tins (~> 1.0) @@ -85,7 +85,7 @@ GEM unf (0.1.4) unf_ext unf_ext (0.0.7.5) - unicode-display_width (1.3.3) + unicode-display_width (1.4.0) unicode_utils (1.4.0) PLATFORMS diff --git a/lib/fakerbot/cli.rb b/lib/fakerbot/cli.rb index 2efebdd..398a62c 100644 --- a/lib/fakerbot/cli.rb +++ b/lib/fakerbot/cli.rb @@ -20,8 +20,10 @@ def version desc 'list', 'List all Faker constants' method_option :help, aliases: '-h', type: :boolean, desc: 'Display usage information' + method_option :show_methods, aliases: '-m', type: :boolean, default: true, + desc: 'Display Faker constants with methods' method_option :verbose, aliases: '-v', type: :boolean, - desc: 'Display Faker constants with methods' + desc: 'Include sample Faker output' def list(*) if options[:help] invoke :help, ['list'] @@ -33,8 +35,10 @@ def list(*) desc 'search [Faker]', 'Search Faker method(s)' method_option :help, aliases: '-h', type: :boolean, desc: 'Display usage information' + method_option :show_methods, aliases: '-m', type: :boolean, default: true, + desc: 'Display Faker constants with methods' method_option :verbose, aliases: '-v', type: :boolean, - desc: 'Display Faker constants methods with examples' + desc: 'Include sample Faker output' def search(query) if options[:help] invoke :help, ['search'] diff --git a/lib/fakerbot/command.rb b/lib/fakerbot/command.rb index 6cd92c6..a63ae02 100644 --- a/lib/fakerbot/command.rb +++ b/lib/fakerbot/command.rb @@ -1,43 +1,18 @@ # frozen_string_literal: true require 'forwardable' -require 'pastel' -require 'tty/pager' -require 'tty/tree' +require_relative 'reflector' +require_relative 'renderer' module FakerBot class Command extend Forwardable def_delegators :command, :run + attr_reader :options - def pager - TTY::Pager.new(command: 'less -R') - end - - def screen - TTY::Screen - end - - def tree(input) - TTY::Tree.new do - input.each do |faker, methods| - node Pastel.new.green(faker.to_s) do - methods&.each { |m| leaf Pastel.new.cyan(m.to_s) } - end - end - end - end - - def render(result, output = $stdout) - result_tree = tree(result) - view = result_tree.render - if screen.height < result_tree.nodes.size - # paginate when attached to terminal - output.tty? ? pager.page(view) : output.puts(view) - else - output.puts view - end + def render(result, output) + FakerBot::Renderer.call(result, options, output) end end end diff --git a/lib/fakerbot/commands/list.rb b/lib/fakerbot/commands/list.rb index 994cf74..fafc8b1 100644 --- a/lib/fakerbot/commands/list.rb +++ b/lib/fakerbot/commands/list.rb @@ -10,7 +10,8 @@ def initialize(options) end def execute(output: $stdout) - render FakerBot::Bot.list(verbose: @options[:verbose]), output + result = FakerBot::Reflector.list(show_methods: options[:show_methods]) + render result, output end end end diff --git a/lib/fakerbot/commands/search.rb b/lib/fakerbot/commands/search.rb index d959a56..d9338d8 100644 --- a/lib/fakerbot/commands/search.rb +++ b/lib/fakerbot/commands/search.rb @@ -1,6 +1,5 @@ # frozen_string_literal: true -require 'fakerbot/bot' require_relative '../command' module FakerBot @@ -11,7 +10,7 @@ def initialize(options) end def execute(input, output: $stdout) - render FakerBot::Bot.find(input), output + render FakerBot::Reflector.find(input), output end private diff --git a/lib/fakerbot/bot.rb b/lib/fakerbot/reflector.rb similarity index 74% rename from lib/fakerbot/bot.rb rename to lib/fakerbot/reflector.rb index a608700..82e214f 100644 --- a/lib/fakerbot/bot.rb +++ b/lib/fakerbot/reflector.rb @@ -3,20 +3,22 @@ require 'faker' module FakerBot - class Bot + # Exposes `Faker` reflection methods + # @api private + class Reflector Faker::Base.class_eval do + # Select `Faker` subclasses + # @return [Array] `Faker::Base` sub classes def self.descendants @descendants ||= ObjectSpace.each_object(Class).select do |klass| klass < self end end + # Select public class methods + # @return [Array] public methods def self.my_singleton_methods - if superclass - (singleton_methods - superclass.singleton_methods) - else - singleton_methods - end + singleton_methods(false).select { |m| respond_to?(m) } end end @@ -32,8 +34,8 @@ def find(query) new(query).find end - def list(verbose: false) - new.list(verbose) + def list(show_methods: false) + new.list(show_methods) end end @@ -42,8 +44,8 @@ def find descendants_with_methods end - def list(verbose) - verbose ? all_descendants_with_methods : faker_descendants + def list(show_methods) + show_methods ? all_descendants_with_methods : faker_descendants end private diff --git a/lib/fakerbot/renderer.rb b/lib/fakerbot/renderer.rb new file mode 100644 index 0000000..2241f0a --- /dev/null +++ b/lib/fakerbot/renderer.rb @@ -0,0 +1,77 @@ +# frozen_string_literal: true + +require 'pastel' +require 'tty/pager' +require 'tty/screen' +require 'tty/tree' + +module FakerBot + class Renderer + def self.call(hash, options, output) + new(hash, options, output).call + end + + def initialize(hash, options, output) + @hash = hash + @options = options + @output = output + @crayon = Pastel.new(enabled: output.tty?) + @pager = TTY::Pager.new(command: 'less -R') + @screen = TTY::Screen + @tree = TTY::Tree + end + + def call + data_tree = tree.new(build_tree) + view = data_tree.render + if gt_screen_height?(data_tree) + output.tty? ? pager.page(view) : output.puts(view) + else + output.puts view + end + end + + private + + attr_reader :crayon, :hash, :options, :output, :pager, :screen, :tree + + def build_tree + hash.reduce({}) do |h, (faker, methods)| + h.merge! node(faker, methods) + end + end + + def node(const, methods) + { + crayon.green(const.to_s) => leaf(const, methods) + } + end + + def leaf(const, methods) + (methods || []).map do |m| + crayon.cyan(*leaf_args(m, const)) + end + end + + def leaf_args(method, const) + [method.to_s].tap do |arr| + if verbose? + fake = begin + const.public_send(method) + rescue ArgumentError + 'N/A' + end + arr << crayon.dim.white("=> #{fake.to_s}") + end + end + end + + def gt_screen_height?(data_tree) + data_tree.nodes.size > screen.height + end + + def verbose? + options[:verbose] + end + end +end diff --git a/lib/fakerbot/version.rb b/lib/fakerbot/version.rb index 0f4eb0c..0e2091d 100644 --- a/lib/fakerbot/version.rb +++ b/lib/fakerbot/version.rb @@ -1,3 +1,3 @@ module FakerBot - VERSION = '0.3.0'.freeze + VERSION = '0.4.0'.freeze end diff --git a/spec/fakerbot/bot_spec.rb b/spec/fakerbot/reflector_spec.rb similarity index 64% rename from spec/fakerbot/bot_spec.rb rename to spec/fakerbot/reflector_spec.rb index 446c531..6680614 100644 --- a/spec/fakerbot/bot_spec.rb +++ b/spec/fakerbot/reflector_spec.rb @@ -1,9 +1,9 @@ -RSpec.describe FakerBot::Bot do - subject(:bot) { described_class } +RSpec.describe FakerBot::Reflector do + subject(:reflector) { described_class } describe '.find' do context 'when a match is found' do - let(:result) { bot.find('name') } + let(:result) { reflector.find('name') } it 'it returns the list of matches' do expect(result[Faker::Name]).to include(:name) @@ -13,7 +13,7 @@ context 'when no match is found' do it 'returns an empty array' do - expect(bot.find('foobar')).to be_empty + expect(reflector.find('foobar')).to be_empty end end end diff --git a/spec/integration/list_spec.rb b/spec/integration/list_spec.rb index bbd249d..ddf7d98 100644 --- a/spec/integration/list_spec.rb +++ b/spec/integration/list_spec.rb @@ -8,13 +8,15 @@ fakerbot list Options: - -h, [--help], [--no-help] # Display usage information - -v, [--verbose], [--no-verbose] # Display Faker constants with methods + -h, [--help], [--no-help] # Display usage information + -m, [--show-methods], [--no-show-methods] # Display Faker constants with methods + # Default: true + -v, [--verbose], [--no-verbose] # Include sample Faker output List all Faker constants OUT - expect(output).to eq(expected_output) + expect(output).to match(expected_output) end it 'executes `fakerbot list` command successfully' do @@ -25,6 +27,5 @@ it 'executes `fakerbot list -v` command successfully' do output = `fakerbot list -v` expect(output).to match(/Faker::/) - expect(output).to match(/└──/) end end diff --git a/spec/integration/search_spec.rb b/spec/integration/search_spec.rb index c9c8aac..a7b7c9e 100644 --- a/spec/integration/search_spec.rb +++ b/spec/integration/search_spec.rb @@ -8,13 +8,14 @@ fakerbot search [Faker] Options: - -h, [--help], [--no-help] # Display usage information - -v, [--verbose], [--no-verbose] # Display Faker constants methods with examples + -h, [--help], [--no-help] # Display usage information + -m, [--show-methods], [--no-show-methods] # Display Faker constants with methods + # Default: true + -v, [--verbose], [--no-verbose] # Include sample Faker output Search Faker method(s) OUT - - expect(output).to eq(expected_output) + expect(output).to match(expected_output) end context 'when search query exists' do diff --git a/spec/unit/list_spec.rb b/spec/unit/list_spec.rb index 363bfd6..40113b2 100644 --- a/spec/unit/list_spec.rb +++ b/spec/unit/list_spec.rb @@ -21,11 +21,8 @@ it 'executes successfully' do constant = output.string.lines[0] - method = output.string.lines[1] expect(constant).to match(/Faker::/) - expect(method).not_to match(/Faker::/) - expect(method).to match(/└──/) end end end