hairballs 0.0.1 → 0.1.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4d0f86e9c3d02f22b832c4b3edce907001d0db2f
4
- data.tar.gz: 7ddc281fd14c1d9c673b59bd6ca1f218e1722f2a
3
+ metadata.gz: d253d151df5fdf784c216a8785627d1f0881cb01
4
+ data.tar.gz: 45bce4a52cd3d2e01f48a3765b9fef39fffb36bc
5
5
  SHA512:
6
- metadata.gz: 0ed4bf7812d457c37d006c4b6b065a6a05df37aba2991f77fa261aa2939bc51ef1df35a55d861a75b5fc93ffe556d4a102a618f99f84dc4126a174679fa4311b
7
- data.tar.gz: 11bf1690a4fd0c528223c4608bbc757a17720f64dfdc137ffe58abae6e505499c790e32c711203f2d768fabfe09900b6c0f6b9872dbb5da9539e50164f211c53
6
+ metadata.gz: 0ca0f9c162b224b218184fd8afd15731ed52d220ff24032e75896cca7ed3bae272e6da15016f2556cbdcf7ec251e0dddbc01361b5dfd2f6fe1db076dadf1d3ff
7
+ data.tar.gz: baff3f4a22ebfe513bba046a54486bf67da351bee1b8ea7f4365a7a21d9ef7e7f8c63f74775cb0c109b756fce5d28aae93389e368647dee838bb8e00fe38e8a1
data/History.md CHANGED
@@ -1,3 +1,14 @@
1
+ ### 0.1.0 / 2015-02-18
2
+
3
+ * Improvements
4
+ * Added `Hairballs.project_root`, which tries to determine the root
5
+ directory for the project you're working in.
6
+ * Refactored configuration to Hairballs::Configuration.
7
+ * Bug Fixes
8
+ * Fixed nested file completion for the `tab_completion_for_files` plugin.
9
+ * Fixed `Hairballs::LibraryHelpers#find_latest_gem` to find the latest
10
+ instead of the earliest.
11
+
1
12
  ### 0.0.1 / 2014-12-19
2
13
 
3
14
  * Happy Birthday!
data/README.md CHANGED
@@ -105,10 +105,10 @@ end
105
105
  ```ruby
106
106
  # hairballs/themes/turboladen_rails.rb
107
107
  Hairballs.add_theme(:turboladen_rails) do |theme|
108
- theme.libraries do
109
- libs_to_require = %w(irb/completion looksee wirble awesome_print)
108
+ theme.libraries do |libs|
109
+ libs += %w(irb/completion looksee wirble awesome_print)
110
110
 
111
- libs_to_require +
111
+ libs +=
112
112
  case RUBY_PLATFORM
113
113
  when /mswin32|mingw32/
114
114
  %w(win32console)
data/hairballs.gemspec CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
9
9
  spec.authors = ['Steve Loveless']
10
10
  spec.email = ['steve.loveless@gmail.com']
11
11
  spec.summary = 'Like oh-my-zsh, but for IRB.'
12
- spec.description = 'Some tools for making customizing your IRB sessions a little easier.'
12
+ spec.description = spec.summary
13
13
  spec.homepage = 'https://github.com/turboladen/hairballs'
14
14
  spec.license = 'MIT'
15
15
 
@@ -0,0 +1,170 @@
1
+ require 'open3'
2
+ require 'pathname'
3
+ require 'ostruct'
4
+ require 'hairballs/ext/kernel_vputs'
5
+ require 'hairballs/exceptions'
6
+ require 'hairballs/theme'
7
+ require 'hairballs/plugin'
8
+ require 'hairballs/version'
9
+
10
+ class Hairballs
11
+ class Configuration
12
+ # @return [Array<Hairballs::Theme>]
13
+ attr_reader :themes
14
+
15
+ # @return [Hairballs::Theme]
16
+ attr_reader :current_theme
17
+
18
+ # All Hairballs::Plugins available to be loaded.
19
+ #
20
+ # @return [Array<Hairballs::Plugin>]
21
+ attr_reader :plugins
22
+
23
+ # All Hairballs::Plugins that have been loaded.
24
+ #
25
+ # @return [Array<Hairballs::Plugin>]
26
+ attr_reader :loaded_plugins
27
+
28
+ # Used for maintaining all possible completion Procs, thus allowing many
29
+ # different plugins to define a Proc for completion without overriding Procs
30
+ # defined for other plugins.
31
+ #
32
+ # @return [Array<Proc>]
33
+ attr_reader :completion_procs
34
+
35
+ # @return [String]
36
+ attr_reader :project_name
37
+
38
+ # @return [String]
39
+ attr_reader :project_root
40
+
41
+ def initialize
42
+ @themes = []
43
+ @current_theme = nil
44
+ @plugins = []
45
+ @loaded_plugins = []
46
+ @completion_procs = []
47
+ @project_name = nil
48
+ @project_root = nil
49
+ end
50
+
51
+ # @return [String]
52
+ def version
53
+ Hairballs::VERSION
54
+ end
55
+
56
+ # Is IRB getting loaded for a rails console?
57
+ #
58
+ # @return [Boolean]
59
+ def rails?
60
+ ENV.has_key?('RAILS_ENV') || !!defined?(Rails)
61
+ end
62
+
63
+ # Name of the relative directory.
64
+ #
65
+ # @return [String]
66
+ def project_name
67
+ @project_name ||= File.basename(project_root)
68
+ end
69
+
70
+ def project_root
71
+ @project_root ||= find_project_root
72
+ end
73
+
74
+ def project_root?(path)
75
+ File.expand_path(path) == project_root
76
+ end
77
+
78
+ # Builds a new Hairballs::Theme and adds it to the list of `.themes` that
79
+ # can be used.
80
+ #
81
+ # @param [Symbol] name Name to give the new Theme.
82
+ # @return [Theme]
83
+ def add_theme(name)
84
+ theme = Theme.new(name)
85
+
86
+ yield theme
87
+
88
+ themes << theme
89
+ vputs "Added theme: #{name}"
90
+
91
+ theme
92
+ end
93
+
94
+ # Tells IRB to use the Hairballs::Theme of +theme_name+.
95
+ #
96
+ # @param [Symbol] theme_name The name of the Theme to use/switch to.
97
+ def use_theme(theme_name)
98
+ switch_to = themes.find { |theme| theme.name == theme_name }
99
+ fail ThemeUseFailure.new(theme_name) unless switch_to
100
+ vputs "Switched to theme: #{theme_name}"
101
+
102
+ switch_to.use!
103
+ @current_theme = switch_to
104
+
105
+ true
106
+ end
107
+
108
+ # Builds a new Hairballs::Plugin and adds it to thelist of `.plugins` that
109
+ # can be used.
110
+ #
111
+ # @param [Symbol] name
112
+ # @param [Hash] options Plugin-dependent options to define. These get
113
+ # passed along the the Hairballs::Plugin object and are used as attributes
114
+ # of the plugin.
115
+ def add_plugin(name, **options)
116
+ plugin = Plugin.new(name, options)
117
+ yield plugin
118
+ plugins << plugin
119
+ vputs "Added plugin: #{name}"
120
+
121
+ plugin
122
+ end
123
+
124
+ # Searches for the Hairballs::Plugin by the +plugin_name+, then loads it.
125
+ # Raises
126
+ # @param plugin_name [Symbol]
127
+ def load_plugin(plugin_name, **options)
128
+ plugin_to_use = plugins.find { |plugin| plugin.name == plugin_name }
129
+ fail PluginNotFound.new(plugin_name) unless plugin_to_use
130
+ vputs "Using plugin: #{plugin_name}"
131
+
132
+ plugin_to_use.load!(options)
133
+ loaded_plugins << plugin_to_use
134
+
135
+ true
136
+ end
137
+
138
+ private
139
+
140
+ def find_project_root
141
+ if rails?
142
+ ::Rails.root
143
+ else
144
+ root_by_git || root_by_lib_dir
145
+ end
146
+ end
147
+
148
+ def root_by_git
149
+ _stdin, stdout, _stderr = Open3.popen3('git rev-parse --show-toplevel')
150
+
151
+ stdout.gets.strip
152
+ end
153
+
154
+ def root_by_lib_dir
155
+ cwd = Pathname.new(Dir.pwd)
156
+ root = nil
157
+
158
+ cwd.ascend do |dir|
159
+ lib_dir = File.join(dir.to_s, 'lib')
160
+
161
+ if dir.children.find { |c| c.to_s =~ /#{lib_dir}/ && File.exist?(lib_dir) }
162
+ root = dir.to_s
163
+ break
164
+ end
165
+ end
166
+
167
+ root
168
+ end
169
+ end
170
+ end
@@ -1,3 +1,5 @@
1
+ require 'hairballs/ext/kernel_vputs'
2
+
1
3
  class Hairballs
2
4
  # Helpers specifying and requiring dependencies for Themes and Plugins.
3
5
  module LibraryHelpers
@@ -5,14 +7,11 @@ class Hairballs
5
7
  def libraries(libs=nil)
6
8
  return @libraries if @libraries && libs.nil?
7
9
 
8
- @libraries = block_given? ? yield : libs
9
-
10
- unless @libraries.is_a?(Array)
11
- fail ArgumentError,
12
- "Block must return an Array but returned #{@libraries}."
10
+ @libraries = if libs
11
+ libs
12
+ else
13
+ yield([])
13
14
  end
14
-
15
- @libraries
16
15
  end
17
16
 
18
17
  # Requires #libraries on load. If they're not installed, install them. If
@@ -43,10 +42,14 @@ class Hairballs
43
42
  end
44
43
  end
45
44
 
45
+ # Path to the highest version of the gem with the given gem.
46
+ #
47
+ # @param [String] gem_name
48
+ # @return [String]
46
49
  def find_latest_gem(gem_name)
47
50
  the_gem = Dir.glob("#{Gem.dir}/gems/#{gem_name}-*")
48
51
 
49
- the_gem.empty? ? nil : the_gem.first
52
+ the_gem.empty? ? nil : the_gem.sort.last
50
53
  end
51
54
 
52
55
  # Add all gems in the global gemset to the $LOAD_PATH so they can be used
@@ -1,4 +1,5 @@
1
- require_relative 'library_helpers'
1
+ require 'hairballs/exceptions'
2
+ require 'hairballs/library_helpers'
2
3
 
3
4
  class Hairballs
4
5
  # Plugins provide means for adding functionality to your IRB sessions. They
@@ -63,12 +64,12 @@ class Hairballs
63
64
 
64
65
  require_libraries
65
66
 
66
- if @on_load
67
- if @on_load.kind_of?(Proc)
68
- @on_load.call
69
- else
70
- fail PluginLoadFailure, self.name
71
- end
67
+ return unless @on_load
68
+
69
+ if @on_load.kind_of?(Proc)
70
+ @on_load.call
71
+ else
72
+ fail PluginLoadFailure, self.name
72
73
  end
73
74
  end
74
75
  end
@@ -1,4 +1,4 @@
1
- require_relative '../../hairballs'
1
+ require 'hairballs'
2
2
 
3
3
  Hairballs.add_plugin(:awesome_print) do |plugin|
4
4
  plugin.libraries %w(awesome_print)
@@ -1,4 +1,4 @@
1
- require_relative '../../hairballs'
1
+ require 'hairballs'
2
2
 
3
3
  # When any value is returned by evaluating some Ruby, this will check if it is
4
4
  # JSON (by parsing it). If it's JSON-like, it will get formatted prettily and
@@ -1,4 +1,4 @@
1
- require_relative '../../hairballs'
1
+ require 'hairballs'
2
2
 
3
3
  # Returns only the methods not present on basic Objects.
4
4
  #
@@ -1,4 +1,4 @@
1
- require_relative '../../hairballs'
1
+ require 'hairballs'
2
2
 
3
3
  Hairballs.add_plugin(:irb_history, save_history: 1000, eval_history: 20, global_history_file: true) do |plugin|
4
4
  plugin.libraries %w(irb/ext/save-history)
@@ -1,4 +1,4 @@
1
- require_relative '../../hairballs'
1
+ require 'hairballs'
2
2
 
3
3
  # Directly lifted from rbates/dotfiles! Adds +#ri+ to all Objects, letting you
4
4
  # get ri docs from within your IRB session.
@@ -1,4 +1,4 @@
1
- require_relative '../../hairballs'
1
+ require 'hairballs'
2
2
 
3
3
  # Quick and dirty benchmarking of whatever block you pass to the method.
4
4
  #
@@ -1,4 +1,4 @@
1
- require_relative '../../hairballs'
1
+ require 'hairballs'
2
2
 
3
3
  # Adds the Object#require_project_lib method as a shortcut for
4
4
  #
@@ -1,4 +1,4 @@
1
- require_relative '../../hairballs'
1
+ require 'hairballs'
2
2
 
3
3
  # Adds the ability to tab-complete files that are in the current directory.
4
4
  #
@@ -9,10 +9,13 @@ require_relative '../../hairballs'
9
9
  # # Will complete like this...
10
10
  # irb> File.read("README.md"
11
11
  #
12
- Hairballs.add_plugin(:tab_completion_for_files) do |plugin|
12
+ # The +completion_append_character+ is really a Readline option that tells it
13
+ # what to do when you tab-complete a term. It's set to not add anything to the
14
+ # completed term, but you may find it suits you better to append a single space.
15
+ Hairballs.add_plugin(:tab_completion_for_files, completion_append_character: nil) do |plugin|
13
16
  plugin.on_load do
14
17
  Hairballs.completion_procs << proc do |string|
15
- Dir['*'].grep(/^#{Regexp.escape(string)}*/)
18
+ Dir[string + '*'].grep(/^#{Regexp.escape(string)}/)
16
19
  end
17
20
 
18
21
  if defined? ::IRB::InputCompletor::CompletionProc
@@ -25,11 +28,12 @@ Hairballs.add_plugin(:tab_completion_for_files) do |plugin|
25
28
  end.flatten.uniq
26
29
  end
27
30
 
28
- if Readline.respond_to?('basic_word_break_characters=')
29
- Readline.basic_word_break_characters= " \"'\t\n`><=;|&{("
31
+ if Readline.respond_to?(:basic_word_break_characters=)
32
+ original_breaks = Readline.basic_word_break_characters
33
+ Readline.basic_word_break_characters = " \"'#{original_breaks}"
30
34
  end
31
35
 
32
- Readline.completion_append_character = nil
36
+ Readline.completion_append_character = plugin.completion_append_character
33
37
  Readline.completion_proc = completion_proc
34
38
  end
35
39
  end
@@ -1,4 +1,4 @@
1
- require_relative '../../hairballs'
1
+ require 'hairballs'
2
2
 
3
3
  # Just loads Wirble.
4
4
  Hairballs.add_plugin(:wirble) do |plugin|
@@ -1,5 +1,5 @@
1
- require_relative 'library_helpers'
2
- require_relative 'prompt'
1
+ require 'hairballs/library_helpers'
2
+ require 'hairballs/prompt'
3
3
 
4
4
  class Hairballs
5
5
  # Themes primarily provide a means for customizing the look of your IRB
@@ -10,7 +10,7 @@ class Hairballs
10
10
  # +Hairballs.add_theme()+ and +Hairballs.use_theme()+ should cover most use
11
11
  # cases.
12
12
  class Theme
13
- include LibraryHelpers
13
+ include Hairballs::LibraryHelpers
14
14
 
15
15
  # Just an identifier for the Theme. Don't name two themes the same
16
16
  # name--that will cause problems.
@@ -97,7 +97,6 @@ class Hairballs
97
97
  end
98
98
 
99
99
  def set_up_pry_printer
100
- puts "@pompt return format: #{@prompt.return_format}"
101
100
  if @prompt.return_format
102
101
  Pry.config.print = proc do |output, value|
103
102
  output.printf @prompt.return_format, value.inspect
@@ -1,14 +1,14 @@
1
- require_relative '../../hairballs'
1
+ require 'hairballs'
2
2
 
3
3
  Hairballs.add_theme(:turboladen) do |theme|
4
- theme.libraries do
5
- libs_to_require = %w(
4
+ theme.libraries do |libs_to_require|
5
+ libs_to_require += %w(
6
6
  irb/completion
7
7
  looksee
8
8
  colorize
9
9
  )
10
10
 
11
- libs_to_require +
11
+ libs_to_require +=
12
12
  case RUBY_PLATFORM
13
13
  when /mswin32|mingw32/
14
14
  %w(win32console)
@@ -1,14 +1,14 @@
1
- require_relative '../../hairballs'
1
+ require 'hairballs'
2
2
 
3
3
  Hairballs.add_theme(:turboladen_rails) do |theme|
4
- theme.libraries do
5
- libs_to_require = %w(
4
+ theme.libraries do |libs_to_require|
5
+ libs_to_require += %w(
6
6
  irb/completion
7
7
  looksee
8
8
  colorize
9
9
  )
10
10
 
11
- libs_to_require +
11
+ libs_to_require +=
12
12
  case RUBY_PLATFORM
13
13
  when /mswin32|mingw32/
14
14
  %w(win32console)
@@ -1,3 +1,3 @@
1
1
  class Hairballs
2
- VERSION = '0.0.1'
2
+ VERSION = '0.1.0'
3
3
  end
data/lib/hairballs.rb CHANGED
@@ -1,113 +1,33 @@
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'
1
+ require 'forwardable'
2
+ require 'hairballs/version'
3
+ require 'hairballs/configuration'
6
4
 
7
5
  # Home of the Hairballs DSL for defining and using Themes and Plugins.
8
6
  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}"
7
+ # @return [Hairballs::Configuration]
8
+ def self.config
9
+ @@config ||= Hairballs::Configuration.new
10
+ end
83
11
 
84
- plugin_to_use.load!(options)
85
- loaded_plugins << plugin_to_use
12
+ class << self
13
+ extend Forwardable
86
14
 
87
- true
88
- end
15
+ def_delegators :config,
16
+ :themes,
17
+ :add_theme,
18
+ :current_theme,
19
+ :use_theme,
89
20
 
90
- # Name of the relative directory.
91
- #
92
- # @return [String]
93
- def project_name
94
- @project_name ||= File.basename(Dir.pwd)
95
- end
21
+ :plugins,
22
+ :load_plugin,
23
+ :loaded_plugins,
24
+ :add_plugin,
96
25
 
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
26
+ :completion_procs,
103
27
 
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
28
+ :project_name,
29
+ :project_root,
30
+ :version,
31
+ :rails?
112
32
  end
113
33
  end
@@ -0,0 +1,195 @@
1
+ require 'spec_helper'
2
+ require 'hairballs/configuration'
3
+
4
+ RSpec.describe Hairballs::Configuration do
5
+ subject(:config) { described_class.new }
6
+
7
+ describe '#themes' do
8
+ subject { config.themes }
9
+ it { is_expected.to be_an Array }
10
+ it { is_expected.to be_empty }
11
+ end
12
+
13
+ describe '#add_theme' do
14
+ let(:theme) { double 'Hairballs::Theme' }
15
+
16
+ context 'block given and name param is a Symbol' do
17
+ before do
18
+ expect(Hairballs::Theme).to receive(:new).with(:test).and_return theme
19
+ end
20
+
21
+ it 'yields the Theme' do
22
+ expect { |b| subject.add_theme(:test, &b) }.to yield_with_args(theme)
23
+ end
24
+
25
+ it 'adds the Theme to @themes' do
26
+ expect(subject.themes).to receive(:<<).with(theme)
27
+ subject.add_theme(:test) { |_| }
28
+ end
29
+
30
+ it 'returns the Theme' do
31
+ expect(subject.add_theme(:test) { |_| }).to eq theme
32
+ end
33
+ end
34
+
35
+ context 'no block given' do
36
+ before do
37
+ allow(Hairballs::Theme).to receive(:new).and_return theme
38
+ end
39
+
40
+ it 'raises a LocalJumpError' do
41
+ expect { subject.add_theme(:test) }.to raise_exception(LocalJumpError)
42
+ end
43
+ end
44
+ end
45
+
46
+ describe '#use_theme' do
47
+ let(:theme) { double 'Hairballs::Theme', name: :test }
48
+
49
+ context 'theme_name represents an added theme' do
50
+ before do
51
+ expect(subject.themes).to receive(:find).and_return theme
52
+ end
53
+
54
+ it 'does not raise a ThemeUseFailure' do
55
+ allow(theme).to receive(:use!)
56
+ expect { subject.use_theme(:test) }.to_not raise_exception
57
+ end
58
+
59
+ it 'tells the theme to #use!' do
60
+ expect(theme).to receive(:use!)
61
+
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(subject.themes).to receive(:find).and_return nil
77
+
78
+ expect do
79
+ subject.use_theme(:meow)
80
+ end.to raise_exception(Hairballs::ThemeUseFailure)
81
+ end
82
+ end
83
+ end
84
+
85
+ describe '#plugins' do
86
+ subject { config.plugins }
87
+ it { is_expected.to be_an Array }
88
+ end
89
+
90
+ describe '#loaded_plugins' do
91
+ subject { config.loaded_plugins }
92
+ it { is_expected.to be_an Array }
93
+ end
94
+
95
+ describe '#add_plugin' do
96
+ let(:plugin) { double 'Hairballs::Plugin' }
97
+
98
+ context 'block given and name param is a Symbol' do
99
+ before do
100
+ expect(Hairballs::Plugin).to receive(:new).with(:test, {}).and_return plugin
101
+ end
102
+
103
+ it 'yields the Plugin' do
104
+ expect { |b| subject.add_plugin(:test, &b) }.to yield_with_args(plugin)
105
+ end
106
+
107
+ it 'adds the Plugin to @plugins' do
108
+ expect(subject.plugins).to receive(:<<).with(plugin)
109
+ subject.add_plugin(:test) { |_| }
110
+ end
111
+
112
+ it 'returns the Plugin' do
113
+ expect(subject.add_plugin(:test) { |_| }).to eq plugin
114
+ end
115
+ end
116
+
117
+ context 'no block given' do
118
+ before do
119
+ allow(Hairballs::Plugin).to receive(:new).and_return plugin
120
+ end
121
+
122
+ it 'raises a LocalJumpError' do
123
+ expect { subject.add_plugin(:test) }.to raise_exception(LocalJumpError)
124
+ end
125
+ end
126
+ end
127
+
128
+ describe '#load_plugin' do
129
+ let(:plugin) { double 'Hairballs::Plugin', name: :test }
130
+
131
+ context 'theme_name represents an added plugin' do
132
+ before do
133
+ expect(subject.plugins).to receive(:find).and_return plugin
134
+ end
135
+
136
+ it 'does not raise an exception' do
137
+ allow(plugin).to receive(:load!)
138
+
139
+ expect do
140
+ subject.load_plugin(:test)
141
+ end.to_not raise_exception
142
+ end
143
+
144
+ it 'tells the plugin to #load!' do
145
+ expect(plugin).to receive(:load!)
146
+ subject.load_plugin(:test)
147
+ end
148
+
149
+ it 'updates @loaded_plugins with the new theme' do
150
+ allow(plugin).to receive(:load!)
151
+
152
+ expect { subject.load_plugin(:test) }.
153
+ to change { subject.loaded_plugins.size }.
154
+ by(1)
155
+ end
156
+ end
157
+
158
+ context 'plugin_name does not represent an added plugin' do
159
+ it 'raises a PluginNotFound' do
160
+ expect do
161
+ subject.load_plugin(:meow)
162
+ end.to raise_exception(Hairballs::PluginNotFound)
163
+ end
164
+ end
165
+ end
166
+
167
+ describe '.project_name' do
168
+ subject { config.project_name }
169
+ it { is_expected.to eq 'hairballs' }
170
+ end
171
+
172
+ describe '.project_root' do
173
+ before do
174
+ allow(Dir).to receive(:pwd).and_return '/meow/hairballs'
175
+ end
176
+
177
+ subject { config.project_name }
178
+ end
179
+
180
+ describe '.rails?' do
181
+ subject { config.rails? }
182
+
183
+ context 'not using Rails' do
184
+ it { is_expected.to eq false }
185
+ end
186
+
187
+ context 'using Rails' do
188
+ context 'RAILS_ENV is defined' do
189
+ before { ENV['RAILS_ENV'] = 'blargh' }
190
+ after { ENV.delete('RAILS_ENV') }
191
+ it { is_expected.to eq true }
192
+ end
193
+ end
194
+ end
195
+ end
@@ -0,0 +1,72 @@
1
+ require 'spec_helper'
2
+ require 'hairballs/library_helpers'
3
+
4
+ RSpec.describe Hairballs::LibraryHelpers do
5
+ subject { Object.new.extend described_class }
6
+
7
+ describe '#libraries' do
8
+ context 'block given' do
9
+ it 'yields an empty array' do
10
+ expect { |b| subject.libraries(&b) }.to yield_with_args([])
11
+ end
12
+
13
+ it 'allows adding to the yielded array' do
14
+ expect {
15
+ subject.libraries do |libs|
16
+ libs << 'meow'
17
+ end
18
+ }.to change { subject.instance_variable_get(:@libraries) }.
19
+ from(nil).to(['meow'])
20
+ end
21
+ end
22
+
23
+ context 'block not given' do
24
+ it 'sets @libraries to the given param' do
25
+ expect {
26
+ subject.libraries(['meow'])
27
+ }.to change { subject.instance_variable_get(:@libraries) }.
28
+ from(nil).to(['meow'])
29
+ end
30
+ end
31
+
32
+ context 'block AND param given' do
33
+ it 'sets @libraries to the param value' do
34
+ expect {
35
+ subject.libraries(['meow'])
36
+ }.to change { subject.instance_variable_get(:@libraries) }.
37
+ from(nil).to(['meow'])
38
+ end
39
+ end
40
+ end
41
+
42
+ describe '#require_libraries' do
43
+ context '@libraries is nil' do
44
+ it 'returns nil' do
45
+ expect(subject.require_libraries).to be_nil
46
+ end
47
+ end
48
+
49
+ context '@libraries is not nil' do
50
+ before do
51
+ subject.instance_variable_set(:@libraries, %w(one))
52
+ allow(subject).to receive(:vputs)
53
+ Hairballs.define_singleton_method(:rails?) { nil }
54
+ end
55
+
56
+ context 'libraries are not installed' do
57
+ it 'tries two times to require then Gem.install' do
58
+ expect(subject).to receive(:require).with('one').exactly(2).times.and_raise LoadError
59
+ expect(Gem).to receive(:install).with('one').exactly(1).times
60
+
61
+ expect { subject.require_libraries }.to raise_exception LoadError
62
+ end
63
+ end
64
+ end
65
+ end
66
+
67
+ describe '#find_latest_gem' do
68
+ it 'returns the path to the latest version of the gem' do
69
+ expect(subject.find_latest_gem('rake')).to match %r{gems/rake}
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,72 @@
1
+ require 'spec_helper'
2
+ require 'hairballs/plugin'
3
+
4
+ RSpec.describe Hairballs::Plugin do
5
+ subject { described_class.new('test', one: 1, two: 2) }
6
+
7
+ describe '#initialize' do
8
+ context 'with attributes' do
9
+ it 'defines getters and setters for each attribute' do
10
+ expect(subject.one).to eq 1
11
+ subject.one = 1.1
12
+ expect(subject.one).to eq 1.1
13
+
14
+ expect(subject.two).to eq 2
15
+ subject.two = 2.2
16
+ expect(subject.two).to eq 2.2
17
+ end
18
+ end
19
+ end
20
+
21
+ describe '#on_load' do
22
+ let(:the_block) { proc { puts 'hi' } }
23
+
24
+ it 'stores the given block' do
25
+ expect {
26
+ subject.on_load(&the_block)
27
+ }.to change { subject.instance_variable_get(:@on_load) }.
28
+ from(nil).to(the_block)
29
+ end
30
+ end
31
+
32
+ describe '#load!' do
33
+ context 'with attributes' do
34
+ it 'sets the attributes' do
35
+ expect(subject).to receive(:one=).with('thing one')
36
+ expect(subject).to receive(:two=).with('thing two')
37
+ allow(subject).to receive(:require_libraries)
38
+
39
+ subject.load! one: 'thing one', two: 'thing two'
40
+ end
41
+ end
42
+
43
+ context 'without attributes' do
44
+ it 'does not set the attributes' do
45
+ expect(subject).to_not receive(:one=)
46
+ expect(subject).to_not receive(:two=)
47
+ allow(subject).to receive(:require_libraries)
48
+
49
+ subject.load!
50
+ end
51
+ end
52
+
53
+ context 'with @on_load set to a Proc' do
54
+ let(:on_load) { double 'Proc', :kind_of? => true }
55
+ before { subject.instance_variable_set(:@on_load, on_load) }
56
+
57
+ it 'calls the on_load Proc' do
58
+ expect(on_load).to receive(:call)
59
+ subject.load!
60
+ end
61
+ end
62
+
63
+ context 'with @on_load set to not a Proc' do
64
+ let(:on_load) { double 'Proc', :kind_of? => false }
65
+ before { subject.instance_variable_set(:@on_load, on_load) }
66
+
67
+ it 'raises a PluginLoadFailure' do
68
+ expect { subject.load! }.to raise_exception Hairballs::PluginLoadFailure
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,11 @@
1
+ require 'spec_helper'
2
+ require 'hairballs/theme'
3
+
4
+ RSpec.describe Hairballs::Theme do
5
+ subject(:theme) { described_class.new(:test) }
6
+
7
+ describe '#irb_name' do
8
+ subject { theme.irb_name }
9
+ it { is_expected.to eq :TEST }
10
+ end
11
+ end
@@ -2,184 +2,28 @@ require 'spec_helper'
2
2
  require 'hairballs'
3
3
 
4
4
  describe Hairballs do
5
- subject { described_class }
5
+ subject(:hairballs) { described_class }
6
6
 
7
- describe '.themes' do
8
- subject { described_class.themes }
9
- it { is_expected.to be_an Array }
7
+ describe '.config' do
8
+ subject { hairballs.config }
9
+ it { is_expected.to be_a Hairballs::Configuration }
10
10
  end
11
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
12
+ it { is_expected.to respond_to(:themes) }
13
+ it { is_expected.to respond_to(:current_theme) }
14
+ it { is_expected.to respond_to(:add_theme) }
15
+ it { is_expected.to respond_to(:use_theme) }
18
16
 
19
- it 'yields the Theme' do
20
- expect { |b| subject.add_theme(:test, &b) }.to yield_with_args(theme)
21
- end
17
+ it { is_expected.to respond_to(:plugins) }
18
+ it { is_expected.to respond_to(:loaded_plugins) }
19
+ it { is_expected.to respond_to(:add_plugin) }
20
+ it { is_expected.to respond_to(:load_plugin) }
22
21
 
23
- it 'adds the Theme to @themes' do
24
- expect(subject.themes).to receive(:<<).with(theme)
25
- subject.add_theme(:test) { |_| }
26
- end
22
+ it { is_expected.to respond_to(:completion_procs) }
27
23
 
28
- it 'returns the Theme' do
29
- expect(subject.add_theme(:test) { |_| }).to eq theme
30
- end
31
- end
24
+ it { is_expected.to respond_to(:project_name) }
25
+ it { is_expected.to respond_to(:project_root) }
26
+ it { is_expected.to respond_to(:rails?) }
32
27
 
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
28
+ it { is_expected.to respond_to(:version) }
185
29
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hairballs
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Steve Loveless
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-12-19 00:00:00.000000000 Z
11
+ date: 2015-02-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -52,7 +52,7 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '3.1'
55
- description: Some tools for making customizing your IRB sessions a little easier.
55
+ description: Like oh-my-zsh, but for IRB.
56
56
  email:
57
57
  - steve.loveless@gmail.com
58
58
  executables: []
@@ -68,6 +68,7 @@ files:
68
68
  - Rakefile
69
69
  - hairballs.gemspec
70
70
  - lib/hairballs.rb
71
+ - lib/hairballs/configuration.rb
71
72
  - lib/hairballs/exceptions.rb
72
73
  - lib/hairballs/ext/kernel_vputs.rb
73
74
  - lib/hairballs/library_helpers.rb
@@ -86,6 +87,10 @@ files:
86
87
  - lib/hairballs/themes/turboladen.rb
87
88
  - lib/hairballs/themes/turboladen_rails.rb
88
89
  - lib/hairballs/version.rb
90
+ - spec/hairballs/configuration_spec.rb
91
+ - spec/hairballs/library_helpers_spec.rb
92
+ - spec/hairballs/plugin_spec.rb
93
+ - spec/hairballs/theme_spec.rb
89
94
  - spec/hairballs_spec.rb
90
95
  - spec/spec_helper.rb
91
96
  homepage: https://github.com/turboladen/hairballs
@@ -108,11 +113,15 @@ required_rubygems_version: !ruby/object:Gem::Requirement
108
113
  version: '0'
109
114
  requirements: []
110
115
  rubyforge_project:
111
- rubygems_version: 2.2.0
116
+ rubygems_version: 2.2.2
112
117
  signing_key:
113
118
  specification_version: 4
114
119
  summary: Like oh-my-zsh, but for IRB.
115
120
  test_files:
121
+ - spec/hairballs/configuration_spec.rb
122
+ - spec/hairballs/library_helpers_spec.rb
123
+ - spec/hairballs/plugin_spec.rb
124
+ - spec/hairballs/theme_spec.rb
116
125
  - spec/hairballs_spec.rb
117
126
  - spec/spec_helper.rb
118
127
  has_rdoc: