diff --git a/Gemfile.lock b/Gemfile.lock index ac300ad..d7f2c54 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - fakerbot (0.2.4) + fakerbot (0.3.0) faker pastel (~> 0.7.2) thor (~> 0.20.0) diff --git a/lib/fakerbot/bot.rb b/lib/fakerbot/bot.rb index 94105f8..a608700 100644 --- a/lib/fakerbot/bot.rb +++ b/lib/fakerbot/bot.rb @@ -20,10 +20,10 @@ def self.my_singleton_methods end end - attr_reader :matching_descendants, :query + attr_reader :descendants_with_methods, :query - def initialize(query) - @matching_descendants = Hash.new { |h, k| h[k] = [] } + def initialize(query = nil) + @descendants_with_methods = Hash.new { |h, k| h[k] = [] } @query = query end @@ -31,26 +31,41 @@ class << self def find(query) new(query).find end + + def list(verbose: false) + new.list(verbose) + end end def find search_descendants_matching_query - matching_descendants + descendants_with_methods + end + + def list(verbose) + verbose ? all_descendants_with_methods : faker_descendants end private + def all_descendants_with_methods + faker_descendants.each do |faker| + store(faker, faker.my_singleton_methods) + end + descendants_with_methods + end + def search_descendants_matching_query faker_descendants.each do |faker| methods = faker.my_singleton_methods matching = methods.select { |m| m.match?(/#{query}/i) } - save_matching(faker, matching) + store(faker, matching) end end - def save_matching(descendant, matching) - return if matching.empty? - matching_descendants[descendant].concat(matching) + def store(descendant, methods) + return if methods.empty? + descendants_with_methods[descendant].concat(methods) end def faker_descendants diff --git a/lib/fakerbot/cli.rb b/lib/fakerbot/cli.rb index 39860a1..2efebdd 100644 --- a/lib/fakerbot/cli.rb +++ b/lib/fakerbot/cli.rb @@ -3,6 +3,7 @@ require 'thor' require 'fakerbot/cli' require 'fakerbot/version' +require 'fakerbot/commands/list' require 'fakerbot/commands/search' module FakerBot @@ -16,11 +17,24 @@ def version end map %w[--version -v] => :version + desc 'list', 'List all Faker constants' + method_option :help, aliases: '-h', type: :boolean, + desc: 'Display usage information' + method_option :verbose, aliases: '-v', type: :boolean, + desc: 'Display Faker constants with methods' + def list(*) + if options[:help] + invoke :help, ['list'] + else + FakerBot::Commands::List.new(options).execute + end + end + desc 'search [Faker]', 'Search Faker method(s)' method_option :help, aliases: '-h', type: :boolean, desc: 'Display usage information' method_option :verbose, aliases: '-v', type: :boolean, - desc: 'Display Faker classes with methods' + desc: 'Display Faker constants methods with examples' def search(query) if options[:help] invoke :help, ['search'] diff --git a/lib/fakerbot/command.rb b/lib/fakerbot/command.rb new file mode 100644 index 0000000..6cd92c6 --- /dev/null +++ b/lib/fakerbot/command.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +require 'forwardable' +require 'pastel' +require 'tty/pager' +require 'tty/tree' + +module FakerBot + class Command + extend Forwardable + + def_delegators :command, :run + + 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 + end + end +end diff --git a/lib/fakerbot/commands/list.rb b/lib/fakerbot/commands/list.rb new file mode 100644 index 0000000..994cf74 --- /dev/null +++ b/lib/fakerbot/commands/list.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +require_relative '../command' + +module FakerBot + module Commands + class List < FakerBot::Command + def initialize(options) + @options = options + end + + def execute(output: $stdout) + render FakerBot::Bot.list(verbose: @options[:verbose]), output + end + end + end +end diff --git a/lib/fakerbot/commands/search.rb b/lib/fakerbot/commands/search.rb index 3e754ee..d959a56 100644 --- a/lib/fakerbot/commands/search.rb +++ b/lib/fakerbot/commands/search.rb @@ -1,45 +1,24 @@ # frozen_string_literal: true -require 'pastel' -require 'tty/pager' -require 'tty/tree' require 'fakerbot/bot' +require_relative '../command' module FakerBot module Commands - class Search + class Search < FakerBot::Command def initialize(options) @options = options - @pager = TTY::Pager.new(command: 'less -R') - @screen = TTY::Screen end - def execute(input) - render FakerBot::Bot.find(input) + def execute(input, output: $stdout) + render FakerBot::Bot.find(input), output end private - attr_reader :screen, :pager - - def render(result) + def render(result, output) return not_found if result.empty? - output = tree(result) - if screen.height < output.nodes.size - pager.page output.render - else - puts output.render - end - 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 + super(result, output) end def not_found diff --git a/lib/fakerbot/templates/list/.gitkeep b/lib/fakerbot/templates/list/.gitkeep new file mode 100644 index 0000000..792d600 --- /dev/null +++ b/lib/fakerbot/templates/list/.gitkeep @@ -0,0 +1 @@ +# diff --git a/lib/fakerbot/version.rb b/lib/fakerbot/version.rb index f074552..0f4eb0c 100644 --- a/lib/fakerbot/version.rb +++ b/lib/fakerbot/version.rb @@ -1,3 +1,3 @@ module FakerBot - VERSION = '0.2.4'.freeze + VERSION = '0.3.0'.freeze end diff --git a/spec/integration/list_spec.rb b/spec/integration/list_spec.rb new file mode 100644 index 0000000..bbd249d --- /dev/null +++ b/spec/integration/list_spec.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +RSpec.describe '`fakerbot list` command', type: :cli do + it 'executes `fakerbot help list` command successfully' do + output = `fakerbot help list` + expected_output = <<~OUT + Usage: + fakerbot list + + Options: + -h, [--help], [--no-help] # Display usage information + -v, [--verbose], [--no-verbose] # Display Faker constants with methods + + List all Faker constants + OUT + + expect(output).to eq(expected_output) + end + + it 'executes `fakerbot list` command successfully' do + output = `fakerbot list` + expect(output).to match(/Faker::/) + end + + 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 new file mode 100644 index 0000000..c9c8aac --- /dev/null +++ b/spec/integration/search_spec.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +RSpec.describe '`fakerbot search` command', type: :cli do + it 'executes `fakerbot help search` command successfully' do + output = `fakerbot help search` + expected_output = <<~OUT + Usage: + fakerbot search [Faker] + + Options: + -h, [--help], [--no-help] # Display usage information + -v, [--verbose], [--no-verbose] # Display Faker constants methods with examples + + Search Faker method(s) + OUT + + expect(output).to eq(expected_output) + end + + context 'when search query exists' do + it 'returns results' do + output = `fakerbot search name` + expect(output).to match(/Faker::/) + expect(output).to match(/└──/) + end + end + + context 'when search query does not exist' do + it 'returns a not found message' do + output = `fakerbot search asdasdhk` + expect(output).to match(/Sorry, we couldn't find a match/) + end + end +end diff --git a/spec/unit/list_spec.rb b/spec/unit/list_spec.rb new file mode 100644 index 0000000..363bfd6 --- /dev/null +++ b/spec/unit/list_spec.rb @@ -0,0 +1,31 @@ +require 'fakerbot/commands/list' + +RSpec.describe FakerBot::Commands::List do + let(:output) { StringIO.new } + let(:options) { {} } + let(:command) { FakerBot::Commands::List.new(options) } + + before do + command.execute(output: output) + end + + context 'when single `list` command' do + it 'executes successfully' do + expect(output.string).to match(/Faker/) + expect(output.string.lines.size).to be_positive + end + end + + context 'when `list -v` verbose command' do + let(:options) { { verbose: true } } + + 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 diff --git a/spec/unit/search_spec.rb b/spec/unit/search_spec.rb new file mode 100644 index 0000000..a86e936 --- /dev/null +++ b/spec/unit/search_spec.rb @@ -0,0 +1,28 @@ +require 'fakerbot/commands/search' + +RSpec.describe FakerBot::Commands::Search do + let(:output) { StringIO.new } + let(:options) { {} } + let(:command) { described_class.new(options) } + + context 'when query object exists' do + before do + command.execute('image', output: output) + end + + it 'returns results' do + expect(output.string).to match(/Faker/) + expect(output.string.lines.size).to be_positive + end + end + + context 'when query object does not exist' do + before do + command.execute('hasjdhaksjd', output: output) + end + + it 'returns nil' do + expect(output.string).to be_empty + end + end +end