hairballs 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.rspec +1 -0
- data/Gemfile +4 -0
- data/History.md +3 -0
- data/LICENSE.txt +22 -0
- data/README.md +250 -0
- data/Rakefile +4 -0
- data/hairballs.gemspec +24 -0
- data/lib/hairballs/exceptions.rb +22 -0
- data/lib/hairballs/ext/kernel_vputs.rb +7 -0
- data/lib/hairballs/library_helpers.rb +85 -0
- data/lib/hairballs/plugin.rb +75 -0
- data/lib/hairballs/plugins/awesome_print.rb +9 -0
- data/lib/hairballs/plugins/colorize_json.rb +30 -0
- data/lib/hairballs/plugins/interesting_methods.rb +22 -0
- data/lib/hairballs/plugins/irb_history.rb +37 -0
- data/lib/hairballs/plugins/object_ri.rb +20 -0
- data/lib/hairballs/plugins/quick_benchmark.rb +18 -0
- data/lib/hairballs/plugins/require_project_lib.rb +17 -0
- data/lib/hairballs/plugins/tab_completion_for_files.rb +35 -0
- data/lib/hairballs/plugins/wirble.rb +11 -0
- data/lib/hairballs/prompt.rb +64 -0
- data/lib/hairballs/theme.rb +110 -0
- data/lib/hairballs/themes/turboladen.rb +34 -0
- data/lib/hairballs/themes/turboladen_rails.rb +33 -0
- data/lib/hairballs/version.rb +3 -0
- data/lib/hairballs.rb +113 -0
- data/spec/hairballs_spec.rb +185 -0
- data/spec/spec_helper.rb +64 -0
- metadata +118 -0
@@ -0,0 +1,17 @@
|
|
1
|
+
require_relative '../../hairballs'
|
2
|
+
|
3
|
+
# Adds the Object#require_project_lib method as a shortcut for
|
4
|
+
#
|
5
|
+
# require 'lib/my_library' # Or...
|
6
|
+
# require 'lib/my/nested/project' # i.e. gem = my-nested-project
|
7
|
+
#
|
8
|
+
Hairballs.add_plugin(:require_project_lib) do |plugin|
|
9
|
+
plugin.on_load do
|
10
|
+
Object.class_eval do
|
11
|
+
def require_project_lib
|
12
|
+
require_dir = File.join(*Hairballs.project_name.split('-'))
|
13
|
+
require_relative "#{Dir.pwd}/lib/#{require_dir}"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require_relative '../../hairballs'
|
2
|
+
|
3
|
+
# Adds the ability to tab-complete files that are in the current directory.
|
4
|
+
#
|
5
|
+
# Example:
|
6
|
+
#
|
7
|
+
# # If in the hairballs project root, doing this...
|
8
|
+
# irb> File.read("READ⇥
|
9
|
+
# # Will complete like this...
|
10
|
+
# irb> File.read("README.md"
|
11
|
+
#
|
12
|
+
Hairballs.add_plugin(:tab_completion_for_files) do |plugin|
|
13
|
+
plugin.on_load do
|
14
|
+
Hairballs.completion_procs << proc do |string|
|
15
|
+
Dir['*'].grep(/^#{Regexp.escape(string)}*/)
|
16
|
+
end
|
17
|
+
|
18
|
+
if defined? ::IRB::InputCompletor::CompletionProc
|
19
|
+
Hairballs.completion_procs << ::IRB::InputCompletor::CompletionProc
|
20
|
+
end
|
21
|
+
|
22
|
+
completion_proc = Proc.new do |string|
|
23
|
+
Hairballs.completion_procs.map do |proc|
|
24
|
+
proc.call(string)
|
25
|
+
end.flatten.uniq
|
26
|
+
end
|
27
|
+
|
28
|
+
if Readline.respond_to?('basic_word_break_characters=')
|
29
|
+
Readline.basic_word_break_characters= " \"'\t\n`><=;|&{("
|
30
|
+
end
|
31
|
+
|
32
|
+
Readline.completion_append_character = nil
|
33
|
+
Readline.completion_proc = completion_proc
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
class Hairballs
|
2
|
+
# Hairballs representation of IRB.conf[:PROMPT]. Method names here make
|
3
|
+
# prompt types clearer.
|
4
|
+
#
|
5
|
+
# TODO: Make it nicer to define Pry prompts.
|
6
|
+
class Prompt
|
7
|
+
# @param [Boolean]
|
8
|
+
attr_accessor :auto_indent
|
9
|
+
|
10
|
+
# The normal prompt string. Same as
|
11
|
+
# IRB.conf[:PROMPT][ prompt name ][:PROMPT_I]
|
12
|
+
#
|
13
|
+
# @param [String]
|
14
|
+
attr_accessor :normal
|
15
|
+
alias_method :i, :normal
|
16
|
+
alias_method :i=, :normal=
|
17
|
+
|
18
|
+
# The prompt for when strings wrap multiple lines. Same as
|
19
|
+
# IRB.conf[:PROMPT][ prompt name ][:PROMPT_S]
|
20
|
+
#
|
21
|
+
# @param [String]
|
22
|
+
attr_accessor :continued_string
|
23
|
+
alias_method :s, :continued_string
|
24
|
+
alias_method :s=, :continued_string=
|
25
|
+
|
26
|
+
# The prompt for when statements wrap multiple lines. Same as
|
27
|
+
# IRB.conf[:PROMPT][ prompt name ][:PROMPT_C]
|
28
|
+
#
|
29
|
+
# @param [String]
|
30
|
+
attr_accessor :continued_statement
|
31
|
+
alias_method :c, :continued_statement
|
32
|
+
alias_method :c=, :continued_statement=
|
33
|
+
|
34
|
+
# The prompt for when statements include indentation. Same as
|
35
|
+
# IRB.conf[:PROMPT][ prompt name ][:PROMPT_N]
|
36
|
+
#
|
37
|
+
# @param [String]
|
38
|
+
attr_accessor :indented_code
|
39
|
+
alias_method :n, :indented_code
|
40
|
+
alias_method :n=, :indented_code=
|
41
|
+
|
42
|
+
# The prompt for return values. Same as
|
43
|
+
# IRB.conf[:PROMPT][ prompt name ][:RETURN]
|
44
|
+
#
|
45
|
+
# @param [String]
|
46
|
+
attr_accessor :return_format
|
47
|
+
|
48
|
+
# @return [Hash] A set of key/value pairs that can be used to pass to a
|
49
|
+
# IRB.conf[:PROMPT][ prompt name ].
|
50
|
+
def irb_configuration
|
51
|
+
vputs 'Setting up prompt...'
|
52
|
+
|
53
|
+
prompt_values = {}
|
54
|
+
prompt_values[:AUTO_INDENT] = @auto_indent if @auto_indent
|
55
|
+
prompt_values[:PROMPT_C] = continued_statement unless continued_statement.empty?
|
56
|
+
prompt_values[:PROMPT_I] = normal unless normal.empty?
|
57
|
+
prompt_values[:PROMPT_N] = indented_code unless indented_code.empty?
|
58
|
+
prompt_values[:PROMPT_S] = continued_string unless continued_string.empty?
|
59
|
+
prompt_values[:RETURN] = @return_format if @return_format
|
60
|
+
|
61
|
+
prompt_values
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
require_relative 'library_helpers'
|
2
|
+
require_relative 'prompt'
|
3
|
+
|
4
|
+
class Hairballs
|
5
|
+
# Themes primarily provide a means for customizing the look of your IRB
|
6
|
+
# prompt, although you're at liberty to also do any other Ruby stuff you'd
|
7
|
+
# like, including load up common Hairballs::Plugins.
|
8
|
+
#
|
9
|
+
# Unless you know you do, you probably don't need to use this directly;
|
10
|
+
# +Hairballs.add_theme()+ and +Hairballs.use_theme()+ should cover most use
|
11
|
+
# cases.
|
12
|
+
class Theme
|
13
|
+
include LibraryHelpers
|
14
|
+
|
15
|
+
# Just an identifier for the Theme. Don't name two themes the same
|
16
|
+
# name--that will cause problems.
|
17
|
+
#
|
18
|
+
# @param [Symbol]
|
19
|
+
attr_accessor :name
|
20
|
+
|
21
|
+
# Tells Hairballs to do some hackery to let Themes use gems that aren't
|
22
|
+
# specified in your app's Gemfile. This alleviates you from having to
|
23
|
+
# declare gems in your Gemfile simply for the sake of managing your personal
|
24
|
+
# IRB preferences.
|
25
|
+
#
|
26
|
+
# @param [Boolean]
|
27
|
+
attr_accessor :extend_bundler
|
28
|
+
|
29
|
+
# @param name [Symbol]
|
30
|
+
def initialize(name)
|
31
|
+
@name = name
|
32
|
+
@prompt = Prompt.new
|
33
|
+
@extend_bundler = false
|
34
|
+
end
|
35
|
+
|
36
|
+
# Tell IRB to use this Theme.
|
37
|
+
def use!
|
38
|
+
do_bundler_extending if @extend_bundler
|
39
|
+
require_libraries
|
40
|
+
set_up_prompt
|
41
|
+
end
|
42
|
+
|
43
|
+
# The name of the Theme, but in the format that IRB.conf[:PROMPT] likes (an
|
44
|
+
# all-caps Symbol).
|
45
|
+
#
|
46
|
+
# @return [Symbol]
|
47
|
+
def irb_name
|
48
|
+
@name.to_s.upcase.to_sym
|
49
|
+
end
|
50
|
+
|
51
|
+
# @return [Hairballs::Prompt]
|
52
|
+
def prompt(&block)
|
53
|
+
if block_given?
|
54
|
+
@prompt_block = block
|
55
|
+
else
|
56
|
+
@prompt
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
#---------------------------------------------------------------------------
|
61
|
+
# PRIVATES
|
62
|
+
#---------------------------------------------------------------------------
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
# Does all the things that are required for getting IRB to use your Theme.
|
67
|
+
def set_up_prompt
|
68
|
+
@prompt_block.call(@prompt)
|
69
|
+
|
70
|
+
if IRB.conf[:PROMPT].nil? && defined?(Pry)
|
71
|
+
set_up_pry_prompt
|
72
|
+
set_up_pry_printer
|
73
|
+
elsif IRB.conf[:PROMPT]
|
74
|
+
set_up_irb_prompt
|
75
|
+
else
|
76
|
+
message = "[Hairballs] Seems like you're not using Pry *or* IRB."
|
77
|
+
puts "#{message} Skipping prompt setup."
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def set_up_irb_prompt
|
82
|
+
IRB.conf[:PROMPT][irb_name] = @prompt.irb_configuration
|
83
|
+
IRB.conf[:PROMPT_MODE] = irb_name
|
84
|
+
IRB.CurrentContext.prompt_mode = irb_name if IRB.CurrentContext
|
85
|
+
end
|
86
|
+
|
87
|
+
def set_up_pry_prompt
|
88
|
+
if @prompt.normal && @prompt.continued_statement
|
89
|
+
::Pry.config.prompt = [
|
90
|
+
proc { @prompt.normal }, proc { @prompt.continued_statement }
|
91
|
+
]
|
92
|
+
elsif @prompt.normal
|
93
|
+
::Pry.config.prompt = -> { @prompt.normal }
|
94
|
+
else
|
95
|
+
vputs 'Neither "normal" nor "continued_statement" prompts configured.'
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def set_up_pry_printer
|
100
|
+
puts "@pompt return format: #{@prompt.return_format}"
|
101
|
+
if @prompt.return_format
|
102
|
+
Pry.config.print = proc do |output, value|
|
103
|
+
output.printf @prompt.return_format, value.inspect
|
104
|
+
end
|
105
|
+
else
|
106
|
+
vputs '"return_format" not configured.'
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require_relative '../../hairballs'
|
2
|
+
|
3
|
+
Hairballs.add_theme(:turboladen) do |theme|
|
4
|
+
theme.libraries do
|
5
|
+
libs_to_require = %w(
|
6
|
+
irb/completion
|
7
|
+
looksee
|
8
|
+
colorize
|
9
|
+
)
|
10
|
+
|
11
|
+
libs_to_require +
|
12
|
+
case RUBY_PLATFORM
|
13
|
+
when /mswin32|mingw32/
|
14
|
+
%w(win32console)
|
15
|
+
when /darwin/
|
16
|
+
%w(terminal-notifier)
|
17
|
+
else
|
18
|
+
[]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
theme.prompt do |prompt|
|
23
|
+
preface = proc do |status = ' '|
|
24
|
+
"⟪#{Hairballs.project_name.light_blue}⟫#{status}%03n"
|
25
|
+
end
|
26
|
+
|
27
|
+
prompt.auto_indent = true
|
28
|
+
prompt.normal = "#{preface.call}:%i> "
|
29
|
+
prompt.continued_string = "#{preface.call('❊%l'.yellow)}:%i> "
|
30
|
+
prompt.continued_statement = "#{preface.call('❊?'.yellow)}:%i> "
|
31
|
+
prompt.indented_code = "#{preface.call('✚ '.yellow)}:%i> "
|
32
|
+
prompt.return_format = "➥ %s\n"
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require_relative '../../hairballs'
|
2
|
+
|
3
|
+
Hairballs.add_theme(:turboladen_rails) do |theme|
|
4
|
+
theme.libraries do
|
5
|
+
libs_to_require = %w(
|
6
|
+
irb/completion
|
7
|
+
looksee
|
8
|
+
colorize
|
9
|
+
)
|
10
|
+
|
11
|
+
libs_to_require +
|
12
|
+
case RUBY_PLATFORM
|
13
|
+
when /mswin32|mingw32/
|
14
|
+
%w(win32console)
|
15
|
+
when /darwin/
|
16
|
+
%w(terminal-notifier)
|
17
|
+
else
|
18
|
+
[]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
theme.extend_bundler = true
|
23
|
+
|
24
|
+
theme.prompt do |prompt|
|
25
|
+
prompt.auto_indent = true
|
26
|
+
preface = Hairballs.project_name.light_blue
|
27
|
+
prompt.normal = "#{preface}> "
|
28
|
+
prompt.continued_string = "#{preface}#{'❊%l'.yellow}> "
|
29
|
+
prompt.continued_statement = "#{preface}#{'⇥'.yellow} %i> "
|
30
|
+
prompt.indented_code = "#{preface}#{'⇥'.yellow} %i> "
|
31
|
+
prompt.return_format = "➥ %s\n"
|
32
|
+
end
|
33
|
+
end
|
data/lib/hairballs.rb
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
require_relative 'hairballs/ext/kernel_vputs'
|
2
|
+
require_relative 'hairballs/exceptions'
|
3
|
+
require_relative 'hairballs/theme'
|
4
|
+
require_relative 'hairballs/plugin'
|
5
|
+
require_relative 'hairballs/version'
|
6
|
+
|
7
|
+
# Home of the Hairballs DSL for defining and using Themes and Plugins.
|
8
|
+
class Hairballs
|
9
|
+
class << self
|
10
|
+
# @return [Hairballs::Theme]
|
11
|
+
attr_reader :current_theme
|
12
|
+
|
13
|
+
# @return [Array<Hairballs::Theme>]
|
14
|
+
def themes
|
15
|
+
@themes ||= []
|
16
|
+
end
|
17
|
+
|
18
|
+
# Builds a new Hairballs::Theme and adds it to the list of `.themes` that
|
19
|
+
# can be used.
|
20
|
+
#
|
21
|
+
# @param name [Symbol] Name to give the new Theme.
|
22
|
+
# @return [Theme]
|
23
|
+
def add_theme(name)
|
24
|
+
theme = Theme.new(name)
|
25
|
+
yield theme
|
26
|
+
themes << theme
|
27
|
+
vputs "Added theme: #{name}"
|
28
|
+
|
29
|
+
theme
|
30
|
+
end
|
31
|
+
|
32
|
+
# Tells IRB to use the Hairballs::Theme of +theme_name+.
|
33
|
+
#
|
34
|
+
# @param theme_name [Symbol] The name of the Theme to use/switch to.
|
35
|
+
def use_theme(theme_name)
|
36
|
+
switch_to = themes.find { |theme| theme.name == theme_name }
|
37
|
+
fail ThemeUseFailure.new(theme_name) unless switch_to
|
38
|
+
vputs "Switched to theme: #{theme_name}"
|
39
|
+
|
40
|
+
switch_to.use!
|
41
|
+
@current_theme = switch_to
|
42
|
+
|
43
|
+
true
|
44
|
+
end
|
45
|
+
|
46
|
+
# All Hairballs::Plugins available to be loaded.
|
47
|
+
#
|
48
|
+
# @return [Array<Hairballs::Plugin>]
|
49
|
+
def plugins
|
50
|
+
@plugins ||= []
|
51
|
+
end
|
52
|
+
|
53
|
+
# All Hairballs::Plugins that have been loaded.
|
54
|
+
#
|
55
|
+
# @return [Array<Hairballs::Plugin>]
|
56
|
+
def loaded_plugins
|
57
|
+
@loaded_plugins ||= []
|
58
|
+
end
|
59
|
+
|
60
|
+
# Builds a new Hairballs::Plugin and adds it to thelist of `.plugins` that
|
61
|
+
# can be used.
|
62
|
+
#
|
63
|
+
# @param name [Symbol]
|
64
|
+
# @param options [Hash] Plugin-dependent options to define. These get
|
65
|
+
# passed along the the Hairballs::Plugin object and are used as attributes
|
66
|
+
# of the plugin.
|
67
|
+
def add_plugin(name, **options)
|
68
|
+
plugin = Plugin.new(name, options)
|
69
|
+
yield plugin
|
70
|
+
plugins << plugin
|
71
|
+
vputs "Added plugin: #{name}"
|
72
|
+
|
73
|
+
plugin
|
74
|
+
end
|
75
|
+
|
76
|
+
# Searches for the Hairballs::Plugin by the +plugin_name+, then loads it.
|
77
|
+
# Raises
|
78
|
+
# @param plugin_name [Symbol]
|
79
|
+
def load_plugin(plugin_name, **options)
|
80
|
+
plugin_to_use = plugins.find { |plugin| plugin.name == plugin_name }
|
81
|
+
fail PluginNotFound.new(plugin_name) unless plugin_to_use
|
82
|
+
vputs "Using plugin: #{plugin_name}"
|
83
|
+
|
84
|
+
plugin_to_use.load!(options)
|
85
|
+
loaded_plugins << plugin_to_use
|
86
|
+
|
87
|
+
true
|
88
|
+
end
|
89
|
+
|
90
|
+
# Name of the relative directory.
|
91
|
+
#
|
92
|
+
# @return [String]
|
93
|
+
def project_name
|
94
|
+
@project_name ||= File.basename(Dir.pwd)
|
95
|
+
end
|
96
|
+
|
97
|
+
# Is IRB getting loaded for a rails console?
|
98
|
+
#
|
99
|
+
# @return [Boolean]
|
100
|
+
def rails?
|
101
|
+
ENV.has_key?('RAILS_ENV') || !!defined?(Rails)
|
102
|
+
end
|
103
|
+
|
104
|
+
# Used for maintaining all possible completion Procs, thus allowing many
|
105
|
+
# different plugins to define a Proc for completion without overriding Procs
|
106
|
+
# defined for other plugins.
|
107
|
+
#
|
108
|
+
# @return [Array<Proc>]
|
109
|
+
def completion_procs
|
110
|
+
@completion_procs ||= []
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,185 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'hairballs'
|
3
|
+
|
4
|
+
describe Hairballs do
|
5
|
+
subject { described_class }
|
6
|
+
|
7
|
+
describe '.themes' do
|
8
|
+
subject { described_class.themes }
|
9
|
+
it { is_expected.to be_an Array }
|
10
|
+
end
|
11
|
+
|
12
|
+
describe '.add_theme' do
|
13
|
+
let(:theme) { double 'Hairballs::Theme' }
|
14
|
+
context 'block given and name param is a Symbol' do
|
15
|
+
before do
|
16
|
+
expect(Hairballs::Theme).to receive(:new).with(:test).and_return theme
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'yields the Theme' do
|
20
|
+
expect { |b| subject.add_theme(:test, &b) }.to yield_with_args(theme)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'adds the Theme to @themes' do
|
24
|
+
expect(subject.themes).to receive(:<<).with(theme)
|
25
|
+
subject.add_theme(:test) { |_| }
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'returns the Theme' do
|
29
|
+
expect(subject.add_theme(:test) { |_| }).to eq theme
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'no block given' do
|
34
|
+
before do
|
35
|
+
allow(Hairballs::Theme).to receive(:new).and_return theme
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'raises a LocalJumpError' do
|
39
|
+
expect { subject.add_theme(:test) }.to raise_exception(LocalJumpError)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe '.use_theme' do
|
45
|
+
let(:theme) { double 'Hairballs::Theme', name: :test }
|
46
|
+
|
47
|
+
context 'theme_name represents an added theme' do
|
48
|
+
before(:example) do
|
49
|
+
expect(subject.themes).to receive(:find).and_return theme
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'does not raise a ThemeUseFailure' do
|
53
|
+
allow(theme).to receive(:use!)
|
54
|
+
|
55
|
+
expect do
|
56
|
+
subject.use_theme(:test)
|
57
|
+
end.to_not raise_exception
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'tells the theme to #use!' do
|
61
|
+
expect(theme).to receive(:use!)
|
62
|
+
subject.use_theme(:test)
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'sets @current_theme to the new theme' do
|
66
|
+
allow(theme).to receive(:use!)
|
67
|
+
|
68
|
+
expect { subject.use_theme(:test) }.
|
69
|
+
to change { subject.instance_variable_get(:@current_theme) }.
|
70
|
+
to(theme)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
context 'theme_name does not represent an added theme' do
|
75
|
+
it 'raises a ThemeUseFailure' do
|
76
|
+
expect do
|
77
|
+
described_class.use_theme(:meow)
|
78
|
+
end.to raise_exception(Hairballs::ThemeUseFailure)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe '.plugins' do
|
84
|
+
subject { described_class.plugins }
|
85
|
+
it { is_expected.to be_an Array }
|
86
|
+
end
|
87
|
+
|
88
|
+
describe '.loaded_plugins' do
|
89
|
+
subject { described_class.loaded_plugins }
|
90
|
+
it { is_expected.to be_an Array }
|
91
|
+
end
|
92
|
+
|
93
|
+
describe '.add_plugin' do
|
94
|
+
let(:plugin) { double 'Hairballs::Plugin' }
|
95
|
+
|
96
|
+
context 'block given and name param is a Symbol' do
|
97
|
+
before do
|
98
|
+
expect(Hairballs::Plugin).to receive(:new).with(:test, {}).and_return plugin
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'yields the Plugin' do
|
102
|
+
expect { |b| subject.add_plugin(:test, &b) }.to yield_with_args(plugin)
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'adds the Plugin to @plugins' do
|
106
|
+
expect(subject.plugins).to receive(:<<).with(plugin)
|
107
|
+
subject.add_plugin(:test) { |_| }
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'returns the Plugin' do
|
111
|
+
expect(subject.add_plugin(:test) { |_| }).to eq plugin
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
context 'no block given' do
|
116
|
+
before do
|
117
|
+
allow(Hairballs::Plugin).to receive(:new).and_return plugin
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'raises a LocalJumpError' do
|
121
|
+
expect { subject.add_plugin(:test) }.to raise_exception(LocalJumpError)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
describe '.load_plugin' do
|
127
|
+
let(:plugin) { double 'Hairballs::Plugin', name: :test }
|
128
|
+
|
129
|
+
context 'theme_name represents an added plugin' do
|
130
|
+
before(:example) do
|
131
|
+
expect(subject.plugins).to receive(:find).and_return plugin
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'does not raise an exception' do
|
135
|
+
allow(plugin).to receive(:load!)
|
136
|
+
|
137
|
+
expect do
|
138
|
+
subject.load_plugin(:test)
|
139
|
+
end.to_not raise_exception
|
140
|
+
end
|
141
|
+
|
142
|
+
it 'tells the plugin to #load!' do
|
143
|
+
expect(plugin).to receive(:load!)
|
144
|
+
subject.load_plugin(:test)
|
145
|
+
end
|
146
|
+
|
147
|
+
it 'updates @loaded_plugins with the new theme' do
|
148
|
+
allow(plugin).to receive(:load!)
|
149
|
+
|
150
|
+
expect { subject.load_plugin(:test) }.
|
151
|
+
to change { subject.loaded_plugins.size }.
|
152
|
+
by(1)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
context 'plugin_name does not represent an added plugin' do
|
157
|
+
it 'raises a PluginNotFound' do
|
158
|
+
expect do
|
159
|
+
described_class.load_plugin(:meow)
|
160
|
+
end.to raise_exception(Hairballs::PluginNotFound)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
describe '.project_name' do
|
166
|
+
subject { described_class.project_name }
|
167
|
+
it { is_expected.to eq 'hairballs' }
|
168
|
+
end
|
169
|
+
|
170
|
+
describe '.rails?' do
|
171
|
+
subject { described_class.rails? }
|
172
|
+
|
173
|
+
context 'not using Rails' do
|
174
|
+
it { is_expected.to eq false }
|
175
|
+
end
|
176
|
+
|
177
|
+
context 'using Rails' do
|
178
|
+
context 'RAILS_ENV is defined' do
|
179
|
+
before { ENV['RAILS_ENV'] = 'blargh' }
|
180
|
+
after { ENV.delete('RAILS_ENV') }
|
181
|
+
it { is_expected.to eq true }
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
$:.unshift(File.expand_path('../lib/hairballs', File.dirname(__FILE__)))
|
2
|
+
|
3
|
+
RSpec.configure do |config|
|
4
|
+
config.expect_with :rspec do |expectations|
|
5
|
+
# This option will default to `true` in RSpec 4. It makes the `description`
|
6
|
+
# and `failure_message` of custom matchers include text for helper methods
|
7
|
+
# defined using `chain`, e.g.:
|
8
|
+
# be_bigger_than(2).and_smaller_than(4).description
|
9
|
+
# # => "be bigger than 2 and smaller than 4"
|
10
|
+
# ...rather than:
|
11
|
+
# # => "be bigger than 2"
|
12
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
13
|
+
end
|
14
|
+
|
15
|
+
config.mock_with :rspec do |mocks|
|
16
|
+
# Prevents you from mocking or stubbing a method that does not exist on
|
17
|
+
# a real object. This is generally recommended, and will default to
|
18
|
+
# `true` in RSpec 4.
|
19
|
+
mocks.verify_partial_doubles = true
|
20
|
+
end
|
21
|
+
|
22
|
+
# The settings below are suggested to provide a good initial experience
|
23
|
+
# with RSpec, but feel free to customize to your heart's content.
|
24
|
+
# These two settings work together to allow you to limit a spec run
|
25
|
+
# to individual examples or groups you care about by tagging them with
|
26
|
+
# `:focus` metadata. When nothing is tagged with `:focus`, all examples
|
27
|
+
# get run.
|
28
|
+
# config.filter_run :focus
|
29
|
+
# config.run_all_when_everything_filtered = true
|
30
|
+
|
31
|
+
# Limits the available syntax to the non-monkey patched syntax that is recommended.
|
32
|
+
# For more details, see:
|
33
|
+
# - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
|
34
|
+
# - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
|
35
|
+
# - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
|
36
|
+
# config.disable_monkey_patching!
|
37
|
+
|
38
|
+
# Many RSpec users commonly either run the entire suite or an individual
|
39
|
+
# file, and it's useful to allow more verbose output when running an
|
40
|
+
# individual spec file.
|
41
|
+
# if config.files_to_run.one?
|
42
|
+
# # Use the documentation formatter for detailed output,
|
43
|
+
# # unless a formatter has already been configured
|
44
|
+
# # (e.g. via a command-line flag).
|
45
|
+
# config.default_formatter = 'doc'
|
46
|
+
# end
|
47
|
+
|
48
|
+
# Print the 10 slowest examples and example groups at the
|
49
|
+
# end of the spec run, to help surface which specs are running
|
50
|
+
# particularly slow.
|
51
|
+
config.profile_examples = 3
|
52
|
+
|
53
|
+
# Run specs in random order to surface order dependencies. If you find an
|
54
|
+
# order dependency and want to debug it, you can fix the order by providing
|
55
|
+
# the seed, which is printed after each run.
|
56
|
+
# --seed 1234
|
57
|
+
config.order = :random
|
58
|
+
|
59
|
+
# Seed global randomization in this process using the `--seed` CLI option.
|
60
|
+
# Setting this allows you to use `--seed` to deterministically reproduce
|
61
|
+
# test failures related to randomization by passing the same `--seed` value
|
62
|
+
# as the one that triggered the failure.
|
63
|
+
# Kernel.srand config.seed
|
64
|
+
end
|