picky 4.0.0pre1 → 4.0.0pre2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (112) hide show
  1. data/aux/picky/cli.rb +6 -2
  2. data/lib/picky.rb +10 -8
  3. data/lib/picky/backends/backend.rb +37 -0
  4. data/lib/picky/backends/file.rb +0 -20
  5. data/lib/picky/backends/memory.rb +0 -29
  6. data/lib/picky/backends/redis.rb +74 -15
  7. data/lib/picky/backends/redis/list.rb +1 -1
  8. data/lib/picky/backends/sqlite.rb +0 -27
  9. data/lib/picky/bundle.rb +2 -2
  10. data/lib/picky/bundle_indexed.rb +1 -1
  11. data/lib/picky/bundle_indexing.rb +1 -1
  12. data/lib/picky/categories_indexed.rb +1 -11
  13. data/lib/picky/category.rb +4 -4
  14. data/lib/picky/category/location.rb +25 -0
  15. data/lib/picky/category_realtime.rb +4 -3
  16. data/lib/picky/console.rb +1 -1
  17. data/lib/picky/constants.rb +1 -1
  18. data/lib/picky/ext/maybe_compile.rb +2 -2
  19. data/lib/picky/extensions/object.rb +3 -2
  20. data/lib/picky/generators/aliases.rb +7 -2
  21. data/lib/picky/generators/partial/default.rb +1 -0
  22. data/lib/picky/generators/similarity/default.rb +1 -0
  23. data/lib/picky/generators/similarity/phonetic.rb +13 -2
  24. data/lib/picky/generators/strategy.rb +0 -2
  25. data/lib/picky/generators/weights/constant.rb +1 -2
  26. data/lib/picky/generators/weights/default.rb +1 -0
  27. data/lib/picky/generators/weights/dynamic.rb +1 -1
  28. data/lib/picky/generators/weights/logarithmic.rb +1 -1
  29. data/lib/picky/generators/weights/{runtime.rb → stub.rb} +1 -3
  30. data/lib/picky/index.rb +3 -3
  31. data/lib/picky/index_indexing.rb +0 -2
  32. data/lib/picky/index_realtime.rb +1 -1
  33. data/lib/picky/indexers/base.rb +7 -0
  34. data/lib/picky/indexers/parallel.rb +2 -4
  35. data/lib/picky/indexers/serial.rb +2 -0
  36. data/lib/picky/indexes_indexing.rb +1 -1
  37. data/lib/picky/interfaces/live_parameters/master_child.rb +175 -0
  38. data/lib/picky/interfaces/live_parameters/unicorn.rb +37 -0
  39. data/lib/picky/loader.rb +238 -259
  40. data/lib/picky/query/allocation.rb +19 -10
  41. data/lib/picky/query/combination.rb +7 -1
  42. data/lib/picky/query/combinations.rb +1 -6
  43. data/lib/picky/query/token.rb +26 -36
  44. data/lib/picky/results.rb +18 -17
  45. data/lib/picky/scheduler.rb +2 -1
  46. data/lib/picky/search.rb +1 -1
  47. data/lib/picky/sinatra.rb +6 -6
  48. data/lib/picky/statistics.rb +2 -0
  49. data/lib/picky/tokenizer.rb +8 -8
  50. data/lib/picky/wrappers/bundle/calculation.rb +4 -4
  51. data/lib/picky/wrappers/bundle/location.rb +1 -2
  52. data/lib/tasks/framework.rake +1 -1
  53. data/lib/tasks/statistics.rake +1 -1
  54. data/lib/tasks/try.rake +1 -1
  55. data/lib/tasks/try.rb +1 -1
  56. data/spec/aux/picky/cli_spec.rb +12 -12
  57. data/spec/ext/performant_spec.rb +16 -16
  58. data/spec/functional/backends/file_spec.rb +78 -7
  59. data/spec/functional/backends/memory_spec.rb +78 -7
  60. data/spec/functional/backends/redis_spec.rb +73 -13
  61. data/spec/functional/dynamic_weights_spec.rb +3 -4
  62. data/spec/functional/realtime_spec.rb +2 -2
  63. data/spec/functional/speed_spec.rb +2 -2
  64. data/spec/functional/terminate_early_spec.rb +3 -3
  65. data/spec/lib/analytics_spec.rb +1 -1
  66. data/spec/lib/analyzer_spec.rb +5 -3
  67. data/spec/lib/categories_indexed_spec.rb +38 -20
  68. data/spec/lib/category/location_spec.rb +30 -0
  69. data/spec/lib/character_substituters/west_european_spec.rb +1 -0
  70. data/spec/lib/extensions/hash_spec.rb +6 -5
  71. data/spec/lib/extensions/module_spec.rb +6 -6
  72. data/spec/lib/extensions/object_spec.rb +9 -8
  73. data/spec/lib/extensions/string_spec.rb +1 -1
  74. data/spec/lib/generators/similarity/phonetic_spec.rb +11 -0
  75. data/spec/lib/index_realtime_spec.rb +5 -5
  76. data/spec/lib/interfaces/{live_parameters_spec.rb → live_parameters/master_child_spec.rb} +26 -26
  77. data/spec/lib/interfaces/live_parameters/unicorn_spec.rb +160 -0
  78. data/spec/lib/loader_spec.rb +65 -25
  79. data/spec/lib/query/allocation_spec.rb +25 -22
  80. data/spec/lib/query/combinations_spec.rb +13 -36
  81. data/spec/lib/query/token_spec.rb +144 -131
  82. data/spec/lib/query/tokens_spec.rb +14 -0
  83. data/spec/lib/results_spec.rb +14 -8
  84. data/spec/lib/search_spec.rb +1 -1
  85. data/spec/lib/sinatra_spec.rb +8 -8
  86. metadata +28 -91
  87. data/lib/picky/adapters/rack.rb +0 -34
  88. data/lib/picky/adapters/rack/base.rb +0 -27
  89. data/lib/picky/adapters/rack/live_parameters.rb +0 -37
  90. data/lib/picky/adapters/rack/search.rb +0 -67
  91. data/lib/picky/application.rb +0 -268
  92. data/lib/picky/frontend_adapters/rack.rb +0 -161
  93. data/lib/picky/interfaces/live_parameters.rb +0 -187
  94. data/lib/picky/sources/base.rb +0 -92
  95. data/lib/picky/sources/couch.rb +0 -76
  96. data/lib/picky/sources/csv.rb +0 -83
  97. data/lib/picky/sources/db.rb +0 -189
  98. data/lib/picky/sources/delicious.rb +0 -63
  99. data/lib/picky/sources/mongo.rb +0 -80
  100. data/lib/picky/wrappers/category/location.rb +0 -38
  101. data/lib/tasks/routes.rake +0 -8
  102. data/spec/lib/adapters/rack/base_spec.rb +0 -24
  103. data/spec/lib/adapters/rack/live_parameters_spec.rb +0 -26
  104. data/spec/lib/adapters/rack/query_spec.rb +0 -39
  105. data/spec/lib/application_spec.rb +0 -155
  106. data/spec/lib/frontend_adapters/rack_spec.rb +0 -294
  107. data/spec/lib/sources/base_spec.rb +0 -53
  108. data/spec/lib/sources/couch_spec.rb +0 -114
  109. data/spec/lib/sources/csv_spec.rb +0 -89
  110. data/spec/lib/sources/db_spec.rb +0 -125
  111. data/spec/lib/sources/delicious_spec.rb +0 -94
  112. data/spec/lib/sources/mongo_spec.rb +0 -50
@@ -26,22 +26,31 @@ module Picky
26
26
  def hash
27
27
  @combinations.hash
28
28
  end
29
- def eql? other_allocation
30
- true # FIXME
31
- # @combinations.eql? other_allocation.combinations
32
- end
29
+ # def eql? other
30
+ # self.class == other.class && combinations.eql?(other.combinations)
31
+ # end
33
32
 
34
- # Scores its combinations and caches the result.
33
+ # Asks the backend for the total score and
34
+ # adds the boosts to it.
35
+ #
36
+ # THINK Can the combinations be empty?
35
37
  #
36
38
  def calculate_score weights
37
- @score ||= @combinations.calculate_score(weights)
39
+ @score ||= if @combinations.empty?
40
+ 0
41
+ else
42
+ @backend.weight(@combinations) +
43
+ @combinations.weighted_score(weights)
44
+ end
38
45
  end
39
46
 
40
47
  # Asks the backend for the (intersected) ids.
41
48
  #
49
+ # THINK Can the combinations be empty?
50
+ #
42
51
  def calculate_ids amount, offset
43
- return [] if combinations.empty?
44
- @backend.ids combinations, amount, offset
52
+ return [] if @combinations.empty?
53
+ @backend.ids @combinations, amount, offset
45
54
  end
46
55
 
47
56
  # Ids return by default [].
@@ -82,8 +91,8 @@ module Picky
82
91
  #
83
92
  # Note: Delegates to to_result.
84
93
  #
85
- def to_json
86
- to_result.to_json
94
+ def to_json options = {}
95
+ Yajl::Encoder.encode to_result, options
87
96
  end
88
97
 
89
98
  #
@@ -27,6 +27,12 @@ module Picky
27
27
  @category_name ||= category.name
28
28
  end
29
29
 
30
+ #
31
+ #
32
+ def bundle
33
+ category.bundle_for token
34
+ end
35
+
30
36
  # Returns the weight of this combination.
31
37
  #
32
38
  # Note: Caching is most of the time useful.
@@ -46,7 +52,7 @@ module Picky
46
52
  # The identifier for this combination.
47
53
  #
48
54
  def identifier
49
- @identifier ||= "#{category.bundle_for(token).identifier}:inverted:#{token.text}"
55
+ @identifier ||= "#{bundle.identifier}:inverted:#{token.text}"
50
56
  end
51
57
 
52
58
  # Note: Required for uniq!
@@ -25,12 +25,7 @@ module Picky
25
25
  @combinations.hash
26
26
  end
27
27
 
28
- # Uses user specific weights to calculate a score for the combinations.
29
- #
30
- def calculate_score weights
31
- total_score + weighted_score(weights)
32
- end
33
- def total_score
28
+ def score
34
29
  @combinations.sum &:weight
35
30
  end
36
31
  def weighted_score weights
@@ -11,19 +11,22 @@ module Picky
11
11
  #
12
12
  class Token # :nodoc:all
13
13
 
14
- attr_reader :text, :original, :qualifiers, :user_defined_categories
14
+ attr_reader :text, :original
15
15
  attr_writer :similar
16
+ attr_accessor :user_defined_categories
16
17
 
17
- delegate :blank?, :to => :text
18
+ delegate :blank?,
19
+ :to => :text
18
20
 
19
21
  # Normal initializer.
20
22
  #
21
23
  # Note:
22
24
  # Use this if you do not want a normalized token.
23
25
  #
24
- def initialize text, original = nil
26
+ def initialize text, original = nil, category = nil
25
27
  @text = text
26
28
  @original = original
29
+ @user_defined_categories = [category] if category
27
30
  end
28
31
 
29
32
  # Returns a qualified and normalized token.
@@ -89,10 +92,10 @@ module Picky
89
92
  # So "hello*" will not be partially searched.
90
93
  # So "hello"* will be partially searched.
91
94
  #
92
- @@no_partial = /\"\Z/
93
- @@partial = /\*\Z/
95
+ @@no_partial = /\"\z/
96
+ @@partial = /\*\z/
94
97
  def partialize
95
- self.partial = false and return unless @text !~ @@no_partial
98
+ self.partial = false or return unless @text !~ @@no_partial
96
99
  self.partial = true unless @text !~ @@partial
97
100
  end
98
101
 
@@ -100,11 +103,11 @@ module Picky
100
103
  #
101
104
  # The latter wins.
102
105
  #
103
- @@no_similar = /\"\Z/
104
- @@similar = /\~\Z/
106
+ @@no_similar = /\"\z/
107
+ @@similar = /\~\z/
105
108
  def similarize
106
- self.similar = false and return if @text =~ @@no_similar
107
- self.similar = true if @text =~ @@similar
109
+ self.similar = false or return unless @text !~ @@no_similar
110
+ self.similar = true unless @text !~ @@similar
108
111
  end
109
112
 
110
113
  def similar?
@@ -124,33 +127,13 @@ module Picky
124
127
  index.possible_combinations self
125
128
  end
126
129
 
127
- # Returns a token with the next similar text.
130
+ # Returns all similar tokens for the token.
128
131
  #
129
- # THINK Rewrite this. It is hard to understand. Also spec performance.
130
- #
131
- def next_similar_token category
132
- token = self.dup
133
- token if token.next_similar category.bundle_for(token)
134
- end
135
- # Sets and returns the next similar word.
136
- #
137
- # Note: Also overrides the original.
138
- #
139
- def next_similar bundle
140
- @text = @original = (similarity(bundle).shift || return) if similar?
141
- end
142
- # Lazy similar reader.
143
- #
144
- def similarity bundle = nil
145
- @similarity || @similarity = generate_similarity_for(bundle)
146
- end
147
- # Returns an enumerator that traverses over the similar.
148
- #
149
- # Note: The dup isn't too nice – since it is needed on account of the shift, above.
150
- # (We avoid a StopIteration exception. Which of both is less evil?)
151
- #
152
- def generate_similarity_for bundle
153
- bundle.similar(@text) || []
132
+ def similar_tokens_for category
133
+ similars = category.bundle_for(self).similar @text
134
+ similars.map do |similar|
135
+ self.class.new similar, similar, category
136
+ end
154
137
  end
155
138
 
156
139
  # Splits text into a qualifier and text.
@@ -166,6 +149,13 @@ module Picky
166
149
  end
167
150
  end
168
151
 
152
+ # Internally, qualifiers are nil if there are none.
153
+ # This returns an empty array in this case for a nicer API.
154
+ #
155
+ def qualifiers
156
+ @qualifiers || []
157
+ end
158
+
169
159
  #
170
160
  #
171
161
  def to_result
data/lib/picky/results.rb CHANGED
@@ -30,6 +30,15 @@ module Picky
30
30
  results
31
31
  end
32
32
 
33
+ # This starts the actual processing.
34
+ #
35
+ # Without this, the allocations are not processed,
36
+ # and no ids are calculated.
37
+ #
38
+ def prepare! extra_allocations = nil
39
+ allocations.process! amount, offset, extra_allocations
40
+ end
41
+
33
42
  # Delegates to allocations.
34
43
  #
35
44
  def ids amount = 20
@@ -39,7 +48,7 @@ module Picky
39
48
  # The total results. Delegates to the allocations.
40
49
  #
41
50
  def total
42
- @total || @total = allocations.total || 0
51
+ @total ||= allocations.total || 0
43
52
  end
44
53
 
45
54
  # Duration default is 0.
@@ -48,15 +57,6 @@ module Picky
48
57
  @duration || 0
49
58
  end
50
59
 
51
- # This starts the actual processing.
52
- #
53
- # Without this, the allocations are not processed,
54
- # and no ids are calculated.
55
- #
56
- def prepare! extra_allocations = nil
57
- allocations.process! amount, offset, extra_allocations
58
- end
59
-
60
60
  # Returns a hash with the allocations, offset, duration and total.
61
61
  #
62
62
  def to_hash
@@ -69,7 +69,14 @@ module Picky
69
69
  # Convert to json format.
70
70
  #
71
71
  def to_json options = {}
72
- to_hash.to_json options
72
+ Yajl::Encoder.encode to_hash, options
73
+ end
74
+
75
+ # For logging.
76
+ #
77
+ @@log_time_format = "%Y-%m-%d %H:%M:%S".freeze
78
+ def to_s
79
+ "#{log_type}|#{Time.now.strftime @@log_time_format}|#{'%8f' % duration}|#{'%-50s' % query}|#{'%8d' % total}|#{'%4d' % offset}|#{'%2d' % allocations.size}|"
73
80
  end
74
81
 
75
82
  # The first character in the blog designates what type of query it is.
@@ -80,12 +87,6 @@ module Picky
80
87
  amount.zero?? :'.' : :'>'
81
88
  end
82
89
 
83
- # For logging.
84
- #
85
- def to_s
86
- "#{log_type}|#{Time.now.to_s(:db)}|#{'%8f' % duration}|#{'%-50s' % query}|#{'%8d' % total}|#{'%4d' % offset}|#{'%2d' % allocations.size}|"
87
- end
88
-
89
90
  end
90
91
 
91
92
  end
@@ -6,6 +6,7 @@ module Picky
6
6
 
7
7
  def initialize options = {}
8
8
  @parallel = options[:parallel]
9
+ @factor = options[:factor] || 2
9
10
 
10
11
  configure
11
12
  end
@@ -21,7 +22,7 @@ module Picky
21
22
  end
22
23
 
23
24
  def scheduler
24
- @scheduler ||= Procrastinate::Scheduler.start Procrastinate::SpawnStrategy::Default.new(2)
25
+ @scheduler ||= Procrastinate::Scheduler.start Procrastinate::SpawnStrategy::Default.new(@factor)
25
26
  end
26
27
  else
27
28
  def schedule
data/lib/picky/search.rb CHANGED
@@ -40,7 +40,7 @@ module Picky
40
40
 
41
41
  instance_eval(&Proc.new) if block_given?
42
42
 
43
- @tokenizer ||= Tokenizer.query_default # THINK Not dynamic. Ok?
43
+ @tokenizer ||= Tokenizer.searching # THINK Not dynamic. Ok?
44
44
  @weights ||= Query::Weights.new
45
45
  @ignore_unassigned = false if @ignore_unassigned.nil?
46
46
 
data/lib/picky/sinatra.rb CHANGED
@@ -11,16 +11,16 @@ module Picky
11
11
  private :indexing, :searching
12
12
  end
13
13
 
14
- # Delegates to Application.
14
+ # Sets tokenizer default indexing options.
15
15
  #
16
- def indexing options
17
- Application.indexing options
16
+ def indexing options = {}
17
+ Tokenizer.default_indexing_with options
18
18
  end
19
19
 
20
- # Delegates to Application.
20
+ # Sets tokenizer default searching options.
21
21
  #
22
- def searching options
23
- Application.searching options
22
+ def searching options = {}
23
+ Tokenizer.default_searching_with options
24
24
  end
25
25
 
26
26
  end
@@ -1,5 +1,7 @@
1
1
  # encoding: utf-8
2
2
  #
3
+ require_relative 'analyzer'
4
+
3
5
  module Picky
4
6
 
5
7
  # Gathers various statistics.
@@ -6,18 +6,18 @@ module Picky
6
6
  #
7
7
  class Tokenizer
8
8
 
9
- def self.index_default= new_default
10
- @index_default = new_default
9
+ def self.default_indexing_with options = {}
10
+ @indexing = options.respond_to?(:tokenize) ? options : new(options)
11
11
  end
12
- def self.index_default
13
- @index_default ||= new
12
+ def self.indexing
13
+ @indexing ||= new
14
14
  end
15
15
 
16
- def self.query_default= new_default
17
- @query_default = new_default
16
+ def self.default_searching_with options = {}
17
+ @searching = options.respond_to?(:tokenize) ? options : new(options)
18
18
  end
19
- def self.query_default
20
- @query_default ||= new
19
+ def self.searching
20
+ @searching ||= new
21
21
  end
22
22
 
23
23
  def to_s
@@ -9,10 +9,6 @@ module Picky
9
9
  # Note: A calculation will try to find a float in the index,
10
10
  # not a sym.
11
11
  #
12
- # TODO I really need to allow integers as keys.
13
- # The code below is just not up to the needed quality.
14
- # Use key_format :to_i?
15
- #
16
12
  class Calculation < Wrapper
17
13
 
18
14
  # API.
@@ -26,12 +22,16 @@ module Picky
26
22
 
27
23
  # TODO Symbols. Use a block here?
28
24
  #
25
+ # THINK Move the calculation elsewhere?
26
+ #
29
27
  def ids float_str
30
28
  @bundle.ids calculate(float_str.to_f).to_s
31
29
  end
32
30
 
33
31
  # TODO Symbols.
34
32
  #
33
+ # THINK Move the calculation elsewhere?
34
+ #
35
35
  def weight float_str
36
36
  @bundle.weight calculate(float_str.to_f).to_s
37
37
  end
@@ -8,10 +8,9 @@ module Picky
8
8
  #
9
9
  class Location < Calculation
10
10
 
11
- def initialize bundle, options = {}
11
+ def initialize bundle, user_grid, options = {}
12
12
  super bundle
13
13
 
14
- user_grid = options[:grid] || raise("Gridsize needs to be given for location #{bundle.identifier}.")
15
14
  anchor = options[:anchor] || 0.0
16
15
  precision = options[:precision] || 1
17
16
 
@@ -3,5 +3,5 @@
3
3
  # Note: This is used by tasks to load the framework as a dependency.
4
4
  #
5
5
  task :framework do
6
- require File.expand_path '../../picky', __FILE__
6
+ require_relative '../picky'
7
7
  end
@@ -28,7 +28,7 @@ end
28
28
  namespace :stats do
29
29
 
30
30
  task :prepare => :application do
31
- require File.expand_path('../../picky/statistics', __FILE__)
31
+ require_relative '../picky/statistics'
32
32
  end
33
33
 
34
34
  end
data/lib/tasks/try.rake CHANGED
@@ -5,7 +5,7 @@ task :try, [:text, :index, :category] => :application do |_, options|
5
5
  puts
6
6
  fail "\x1b[31mrake try needs a text to try indexing and query preparation\x1b[m, e.g. rake 'try[yourtext]'." unless options.text
7
7
 
8
- require File.expand_path '../try', __FILE__
8
+ require_relative 'try'
9
9
  try = Picky::Try.new options.text, options.index, options.category
10
10
  try.to_stdout
11
11
  end
data/lib/tasks/try.rb CHANGED
@@ -16,7 +16,7 @@ module Picky
16
16
  end
17
17
 
18
18
  def searched
19
- Picky::Tokenizer.query_default.tokenize(text.dup).first
19
+ Picky::Tokenizer.searching.tokenize(text.dup).first
20
20
  end
21
21
 
22
22
  def output
@@ -4,14 +4,14 @@ require 'spec_helper'
4
4
 
5
5
  # We need to load the CLI file explicitly as the CLI is not loaded with the Loader (not needed in the server, only for script runs).
6
6
  #
7
- require File.expand_path '../../../../aux/picky/cli', __FILE__
7
+ require_relative '../../../aux/picky/cli'
8
8
 
9
9
  describe Picky::CLI do
10
-
10
+
11
11
  describe '.mapping' do
12
12
  it 'returns the right mapping' do
13
13
  Picky::CLI.mapping.should == {
14
- :generate => [Picky::CLI::Generate, :"{client,server,sinatra_client,unicorn_server,all_in_one}", :app_directory_name],
14
+ :generate => [Picky::CLI::Generate, :"{client,server,all_in_one}", :app_directory_name],
15
15
  :help => [Picky::CLI::Help],
16
16
  :live => [Picky::CLI::Live, "host:port/path (default: localhost:8080/admin)", "port (default: 4568)"],
17
17
  :search => [Picky::CLI::Search, :url_or_path, 'amount of ids (default 20)'],
@@ -19,20 +19,20 @@ describe Picky::CLI do
19
19
  }
20
20
  end
21
21
  end
22
-
22
+
23
23
  describe 'instance' do
24
24
  let(:cli) { described_class.new }
25
25
  describe 'execute' do
26
26
  it 'calls generate correctly' do
27
27
  Kernel.should_receive(:system).once.with 'picky-generate one two three'
28
-
28
+
29
29
  cli.execute 'generate', 'one', 'two', 'three'
30
30
  end
31
31
  it 'calls help correctly' do
32
32
  Kernel.should_receive(:puts).once.with <<-HELP
33
33
  Possible commands:
34
- picky generate {client,server,sinatra_client,unicorn_server,all_in_one} app_directory_name
35
- picky help
34
+ picky generate {client,server,all_in_one} app_directory_name
35
+ picky help
36
36
  picky live [host:port/path (default: localhost:8080/admin)] [port (default: 4568)]
37
37
  picky search url_or_path [amount of ids (default 20)]
38
38
  picky stats logfile (e.g. log/search.log) [port (default: 4567)]
@@ -51,7 +51,7 @@ HELP
51
51
  cli.executor_class_for.should == [Picky::CLI::Help]
52
52
  end
53
53
  it 'returns Generator for generate' do
54
- cli.executor_class_for(:generate).should == [Picky::CLI::Generate, :'{client,server,sinatra_client,unicorn_server,all_in_one}', :"app_directory_name"]
54
+ cli.executor_class_for(:generate).should == [Picky::CLI::Generate, :'{client,server,all_in_one}', :"app_directory_name"]
55
55
  end
56
56
  it 'returns Help for help' do
57
57
  cli.executor_class_for(:help).should == [Picky::CLI::Help]
@@ -70,17 +70,17 @@ HELP
70
70
  end
71
71
  end
72
72
  end
73
-
73
+
74
74
  describe Picky::CLI::Live do
75
75
  let(:executor) { Picky::CLI::Live.new }
76
76
  end
77
-
77
+
78
78
  describe Picky::CLI::Base do
79
79
  let(:executor) { Picky::CLI::Base.new }
80
80
  describe 'usage' do
81
81
  it 'calls puts with an usage' do
82
82
  executor.should_receive(:puts).once.with "Usage:\n picky some_name param1 [param2]"
83
-
83
+
84
84
  executor.usage :some_name, [:param1, 'param2']
85
85
  end
86
86
  end
@@ -90,5 +90,5 @@ HELP
90
90
  end
91
91
  end
92
92
  end
93
-
93
+
94
94
  end