Skip to content

Commit

Permalink
chore: Add renderer docs
Browse files Browse the repository at this point in the history
  • Loading branch information
akabiru committed Aug 7, 2019
1 parent e4b9f8f commit 4d29fd2
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 17 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
![faker-bot-demo](https://user-images.githubusercontent.com/17295175/62558993-c57be080-b882-11e9-972e-7588408d45c3.gif)

:book: [Code Documentation](https://www.rubydoc.info/github/faker-ruby/faker-bot)

## Installation

```bash
Expand Down
5 changes: 2 additions & 3 deletions lib/faker/bot/reflectors/list.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ module Reflectors
# @api private
#
class List < Reflector
# Command output filter
# Output filter
#
# @return [String, nil]
#
Expand Down Expand Up @@ -42,8 +42,7 @@ def initialize(options = {})

# List `Faker::Base` subclasses
#
# @return [Hash{Class => <Array<Symbol>}] when #show_methods is truthy
# @return [Array<Class>] when #show_methods is falsy
# @return [Hash<Class => <Array<Symbol>>] when #show_methods is truthy
#
# @api private
#
Expand Down
45 changes: 37 additions & 8 deletions lib/faker/bot/reflectors/search.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,42 +6,71 @@ module Faker
module Bot
module Reflectors
# Reflection object that searches all `Faker::Base` subclass methods
# * Currently operates at O(n); improvements welcome. :)
#
# @api private
#
class Search < Reflector
# Reflector query
#
# @return [String, nil]
#
# @api private
#
attr_reader :query

# Initialize search reflector
#
# @param query [String] The search query
#
# @api public
#
def initialize(query)
@query = query.downcase

super
end

# Search through `Faker::Base` subclasses and return matching results
#
# @return [Hash<Class => <Array<Symbol>>] when #show_methods is truthy
#
# @api private
#
def call
search_descendants_matching_query
descendants_with_methods
end

private

# Search through `Faker::Base` subclasses and store matching results
#
# @api private
#
def search_descendants_matching_query
faker_descendants.each do |descendant|
methods = descendant.my_singleton_methods
if query_matches_class_name?(descendant.to_s)

if query_matches?(descendant)
store(descendant, methods)
else
store(descendant, methods.select { |method| query_matches_method?(method.to_s) })
store(
descendant,
methods.select { |method| query_matches?(method) }
)
end
end
end

def query_matches_method?(method_name)
method_name.match(/#{query}/)
end

def query_matches_class_name?(class_name)
class_name.downcase.match(/#{query}/)
# Match a subject against the query string
#
# @return [Boolean]
#
# @api private
#
def query_matches?(subject)
subject.to_s.match(/#{query}/i)
end
end
end
Expand Down
109 changes: 103 additions & 6 deletions lib/faker/bot/renderer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,45 @@

module Faker
module Bot
# A class responsible for printing output to an [IO] interface
#
# @api private
#
class Renderer
DEPRECATION_WARNING = ' ( WILL BE DEPRECATED )'
EMPTY = ''
NOT_AVAILABLE = 'N/A'

attr_reader :crayon, :hash, :options, :output, :pager

def self.call(*args)
new(*args).call
end

# Initialize a Render
# @param hash [Hash]
#
# @param hash [Hash<Class => <Array<Symbol>>]
# @param options [Hash]
# @param output [IO]
#
# @api public
#
def initialize(hash, options, output)
@hash = hash
@options = options
@output = output

@crayon = Pastel.new(enabled: output.tty?)
@pager = TTY::Pager.new(command: 'less -R')
end

# Print paginated output if the terminal interface supports pagination.
# Otherwise, just print the full output
#
# @return [IO]
#
# @api private
#
def call
if paginable?
pager.page(render)
Expand All @@ -34,24 +54,54 @@ def call
end
end

# Render the structured data tree
#
# @return [String]
#
# @api private
#
def render
tree.render
end

# Warm up the structured data tree render object
#
# @return [TTY<Tree>]
#
# @api private
#
def tree
@tree ||= TTY::Tree.new(build_tree)
end

# Check whether the terminal interface supports pagination
#
# @return [Boolean]
#
# @api private
#
def paginable?
gt_screen_height? && output.tty?
end

# Check whether the tree size is greater than current screen height
#
# @return [Boolean]
#
# @api private
#
def gt_screen_height?
tree.nodes.size > TTY::Screen.height
end

private

# Build the structured data tree sorted alphabetically
#
# @return [Hash{Class => <Array<Symbol>}]
#
# @api private
#
def build_tree
results = hash.reduce({}) do |h, (const, methods)|
h.merge! node(const, methods&.sort)
Expand All @@ -60,37 +110,84 @@ def build_tree
results.sort_by(&:to_s).to_h
end

# Tree node builder with color
#
# @return [Hash{Class => <Array<Symbol>}]
#
# @api private
#
def node(const, methods)
{
crayon.green(const.to_s) => leaf(const, methods)
}
end

# Tree leaf builder with color
#
# @return [Array<Symbol>]
#
# @api private
#
def leaf(const, methods)
(methods || []).map { |m| crayon.cyan(*leaf_args(m, const)) }
end

def leaf_args(method, const)
[method.to_s].tap { |arr| verbose_output(method, const, arr) if verbose? }
[method.to_s].tap do |arr|
verbose_output(method, const, arr) if verbose?
end
end

# Boolean verbose option flag
#
# @return [Boolean]
#
# @api private
#
def verbose?
options[:verbose]
end

# Generate verbose output via sample fake data
#
# @return [Array<String>]
#
# @api private
#
def verbose_output(method, const, arr)
fake, message = faker_method(method, const)
arr << crayon.dim.white("=> #{fake}") << crayon.dim.magenta.bold(message.to_s)

arr.push(crayon.dim.white("=> #{fake}"))
.push(crayon.dim.magenta.bold(message.to_s))
end

# Send message to Faker object; receive sample fake data
#
# @return [Array<String>]
#
# @api private
#
def faker_method(method, const)
[const.public_send(method), ensure_method_is_supported(method, const)]
[
const.public_send(method),
ensure_method_is_supported(method, const)
]
rescue ArgumentError => _e
['N/A', '']
[NOT_AVAILABLE, EMPTY]
end

# Mark deprecated methods
#
# @return [String]
#
# @api private
#
def ensure_method_is_supported(method, const)
const.respond_to?(:"_deprecated_#{method}") ? ' ( WILL BE DEPRECATED )' : ''
if const.respond_to?(:"_deprecated_#{method}")
DEPRECATION_WARNING
else
EMPTY
end
end
end
end
Expand Down

0 comments on commit 4d29fd2

Please sign in to comment.