mundane-search 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. data/Guardfile +11 -0
  2. data/README.md +61 -9
  3. data/Rakefile +1 -1
  4. data/bin/coderay +16 -0
  5. data/bin/erubis +16 -0
  6. data/bin/guard +16 -0
  7. data/bin/pry +16 -0
  8. data/bin/rackup +16 -0
  9. data/bin/rake +1 -1
  10. data/bin/sprockets +16 -0
  11. data/bin/thor +16 -0
  12. data/bin/tilt +16 -0
  13. data/lib/columns_hash.rb +38 -0
  14. data/lib/mundane-search.rb +14 -6
  15. data/lib/mundane-search/buildable.rb +25 -0
  16. data/lib/mundane-search/builder.rb +32 -18
  17. data/lib/mundane-search/filter_canister.rb +27 -3
  18. data/lib/mundane-search/filters.rb +7 -8
  19. data/lib/mundane-search/filters/attribute_match.rb +6 -7
  20. data/lib/mundane-search/filters/attribute_substring.rb +14 -0
  21. data/lib/mundane-search/filters/base.rb +5 -2
  22. data/lib/mundane-search/filters/blank_params_are_nil.rb +1 -1
  23. data/lib/mundane-search/filters/operator.rb +18 -0
  24. data/lib/mundane-search/filters/shortcuts.rb +33 -0
  25. data/lib/mundane-search/filters/typical.rb +28 -3
  26. data/lib/mundane-search/initial_stack.rb +13 -0
  27. data/lib/mundane-search/railtie.rb +7 -0
  28. data/lib/mundane-search/result.rb +23 -8
  29. data/lib/mundane-search/result_model.rb +65 -0
  30. data/lib/mundane-search/stack.rb +38 -0
  31. data/lib/mundane-search/version.rb +1 -1
  32. data/lib/mundane-search/view_helpers.rb +24 -0
  33. data/mundane-search.gemspec +7 -0
  34. data/script/console +6 -0
  35. data/spec/active_record_setup.rb +2 -45
  36. data/spec/buildable_integration_spec.rb +14 -0
  37. data/spec/buildable_spec.rb +19 -0
  38. data/spec/builder_integration_spec.rb +26 -5
  39. data/spec/builder_spec.rb +13 -18
  40. data/spec/columns_hash_spec.rb +37 -0
  41. data/spec/demo_data.rb +50 -0
  42. data/spec/filter_canister_spec.rb +46 -0
  43. data/spec/filters/attribute_match_integration_spec.rb +2 -2
  44. data/spec/filters/attribute_match_spec.rb +27 -0
  45. data/spec/filters/attribute_substring_spec.rb +27 -0
  46. data/spec/filters/base_spec.rb +39 -7
  47. data/spec/filters/blank_params_are_nil_spec.rb +11 -0
  48. data/spec/filters/operator_integration_spec.rb +20 -0
  49. data/spec/filters/operator_spec.rb +28 -0
  50. data/spec/filters/shortcuts_integration_spec.rb +16 -0
  51. data/spec/filters/shortcuts_spec.rb +15 -0
  52. data/spec/filters/typical_spec.rb +68 -0
  53. data/spec/form_integration_spec.rb +29 -0
  54. data/spec/initial_stack_spec.rb +13 -0
  55. data/spec/minitest_helper.rb +93 -4
  56. data/spec/result_integration_spec.rb +24 -0
  57. data/spec/result_model_spec.rb +50 -0
  58. data/spec/result_spec.rb +10 -28
  59. data/spec/search_form_for_integration_spec.rb +19 -0
  60. data/spec/simple_form_integration_spec.rb +36 -0
  61. data/spec/simple_search_form_for_integration_spec.rb +25 -0
  62. data/spec/stack_spec.rb +40 -0
  63. metadata +167 -6
  64. data/lib/mundane-search/filters/helpers.rb +0 -44
  65. data/lib/mundane-search/initial_result.rb +0 -7
@@ -0,0 +1,14 @@
1
+ require_relative 'minitest_helper'
2
+
3
+ describe MundaneSearch::Buildable do
4
+ let(:built) do
5
+ Class.new do
6
+ include MundaneSearch::Buildable
7
+ end
8
+ end
9
+
10
+ it "should limit results using exact match filter" do
11
+ built.use MundaneSearch::Filters::ExactMatch, param_key: "foo"
12
+ built.call(collection, params).must_equal(['bar'])
13
+ end
14
+ end
@@ -0,0 +1,19 @@
1
+ require_relative 'minitest_helper'
2
+
3
+ describe MundaneSearch::Buildable do
4
+ let(:built) do
5
+ Class.new do
6
+ include MundaneSearch::Buildable
7
+ end
8
+ end
9
+
10
+ it "should have a search builder object" do
11
+ built.builder.must_be_kind_of MundaneSearch::Builder
12
+ end
13
+
14
+ it "should add proxy methods" do
15
+ [:use, :result_for, :call, :employ].each do |method|
16
+ built.must_respond_to method
17
+ end
18
+ end
19
+ end
@@ -1,14 +1,35 @@
1
1
  require_relative 'minitest_helper'
2
2
 
3
3
  describe MundaneSearch::Builder do
4
- Builder = MundaneSearch::Builder
4
+ let(:built) { MundaneSearch::Builder.new }
5
+
6
+ it "should return unchanged collection on call" do
7
+ built.call(collection, params).must_equal(collection)
8
+ end
9
+
10
+ it "should tollerate nil params (convert to empty hash)" do
11
+ built.call(collection, nil).must_equal(collection)
12
+ end
5
13
 
6
14
  it "should limit results using exact match filter" do
15
+ built.use MundaneSearch::Filters::ExactMatch, param_key: "foo"
16
+ built.call(collection, params).must_equal(['bar'])
17
+ end
7
18
 
8
- built = Builder.new do
9
- use MundaneSearch::Filters::ExactMatch, param_key: "foo"
10
- end
19
+ it "should translate symbols into MundaneSearch::Filter class (if available)" do
20
+ built.use :exact_match, param_key: "foo"
21
+ built.call(collection, params).must_equal(['bar'])
22
+ end
23
+
24
+ it "should translate strings into MundaneSearch::Filter class (if available)" do
25
+ built.use "exact_match", param_key: "foo"
26
+ built.call(collection, params).must_equal(['bar'])
27
+ end
11
28
 
29
+ it "should looks for Object:: level filters when translating strings" do
30
+ Object.const_set :MockFilter, Class.new(MundaneSearch::Filters::ExactMatch)
31
+ built.use :mock_filter, param_key: "foo"
12
32
  built.call(collection, params).must_equal(['bar'])
33
+ Object.send(:remove_const, :MockFilter)
13
34
  end
14
- end
35
+ end
data/spec/builder_spec.rb CHANGED
@@ -1,25 +1,20 @@
1
1
  require_relative 'minitest_helper'
2
2
 
3
3
  describe MundaneSearch::Builder do
4
- Builder = MundaneSearch::Builder
4
+ let(:built) { MundaneSearch::Builder.new }
5
+ let(:null_filter) { Object.new }
6
+ let(:canister) { Minitest::Mock.new }
5
7
 
6
- let(:collection) { %w(foo bar baz) }
7
- let(:params) { { 'foo' => 'bar' } }
8
-
9
- describe "empty search" do
10
- let(:builder) { Builder.new }
11
-
12
- it "should return unchanged collection on call" do
13
- builder.call(collection, params).must_equal(collection)
14
- end
15
- end
8
+ # what does a builder actually do?
9
+ # 1. Store search configuration (use, result_class, filter_canister)
10
+ # a. Can "use" (et al) be called?
11
+ # i. use can be verified
12
+ # 2. Run search (call, result_for)
13
+ # a. ... by creating a result object
16
14
 
17
15
  it "should use middleware" do
18
- builder = Builder.new do
19
- use NothingFilterForTest
20
- end
21
- canister = builder.filters.first
22
- canister.must_be_kind_of MundaneSearch::FilterCanister
23
- canister.filter.must_equal NothingFilterForTest
16
+ expectation = ->(filter, *args) { filter.must_equal null_filter }
17
+ built.define_singleton_method :build_filter_canister, ->{ expectation }
18
+ built.use(null_filter)
24
19
  end
25
- end
20
+ end
@@ -0,0 +1,37 @@
1
+ require_relative 'minitest_helper'
2
+
3
+ describe ColumnsHash do
4
+ [:binary, :boolean, :date, :datetime, :decimal, :float, :integer, :primary_key, :string, :text, :time, :timestamp].each do |type|
5
+ it "should respond to all ActiveRecord type #{type}" do
6
+ ColumnsHash.generate({type: type}).must_be_kind_of ColumnsHash::Attribute
7
+ end
8
+ end
9
+
10
+ it "should have 255 limit when string" do
11
+ ColumnsHash.generate({type: :string}).limit.must_equal 255
12
+ end
13
+
14
+ [:float, :integer].each do |type|
15
+ it "should be number? true when #{type}" do
16
+ ColumnsHash.generate({type: type}).number?.must_equal true
17
+ end
18
+ end
19
+
20
+ describe "applied to class" do
21
+ let(:columned_class) do
22
+ Class.new do
23
+ include ColumnsHash
24
+ end
25
+ end
26
+ let(:columned) { columned_class.new }
27
+
28
+ it "should have a columns_hash" do
29
+ columned_class.columns_hash.must_be_kind_of Hash
30
+ end
31
+
32
+ it "should store (and retrieve) a column type" do
33
+ columned_class.attribute_column(:title, :string)
34
+ columned.column_for_attribute(:title).type.must_equal :string
35
+ end
36
+ end
37
+ end
data/spec/demo_data.rb ADDED
@@ -0,0 +1,50 @@
1
+ def demo_data
2
+ [
3
+ {
4
+ title: "A Tale of Two Cities",
5
+ author: "Charles Dickens",
6
+ publication_date: Date.parse('31-12-1859'),
7
+ first_purchased_at: Time.utc(1859,"jan",1,20,15,1),
8
+ sold: 200_000_000
9
+ },
10
+ {
11
+ title: "The Lord of the Rings",
12
+ author: "J. R. R. Tolkien",
13
+ publication_date: Date.parse('31-12-1954'),
14
+ first_purchased_at: Time.utc(1954,"jan",1,20,15,1),
15
+ sold: 150_000_000
16
+ },
17
+ {
18
+ title: "The Little Prince (Le Petit Prince)",
19
+ author: "Antoine de Saint-Exupery",
20
+ publication_date: Date.parse('31-12-1943'),
21
+ first_purchased_at: Time.utc(1943,"jan",1,20,15,1),
22
+ sold: 140_000_000
23
+ },
24
+ {
25
+ title: "The Hobbit",
26
+ author: "J. R. R. Tolkien",
27
+ publication_date: Date.parse('31-12-1937'),
28
+ first_purchased_at: Time.utc(1937,"jan",1,20,15,1),
29
+ sold: 100_000_000
30
+ },
31
+ {
32
+ title: "Hong lou meng (Dream of the Red Chamber/The Story of the Stone)",
33
+ author: "Cao Xueqin",
34
+ publication_date: Date.parse('31-12-1754'),
35
+ first_purchased_at: Time.utc(1754,"jan",1,20,15,1),
36
+ sold: 100_000_000
37
+ },
38
+ {
39
+ title: "And Then There Were None",
40
+ author: "Agatha Christie",
41
+ publication_date: Date.parse('31-12-1939'),
42
+ first_purchased_at: Time.utc(1939,"jan",1,20,15,1),
43
+ sold: 100_000_000
44
+ }
45
+ ]
46
+ end
47
+
48
+ def open_struct_books
49
+ demo_data.collect {|d| OpenStruct.new(d) }
50
+ end
@@ -0,0 +1,46 @@
1
+ require_relative 'minitest_helper'
2
+
3
+ describe MundaneSearch::FilterCanister do
4
+ it "should build filters with options" do
5
+ filter = Minitest::Mock.new
6
+ options = {param_key: 'baz'}
7
+ result = Object.new
8
+ filter.expect(:constants, [])
9
+ filter.expect(:new, result, [collection, params, options])
10
+ filter_canister = MundaneSearch::FilterCanister.new(filter, options)
11
+ filter_canister.build(collection, params).must_equal(result)
12
+ filter.verify
13
+ end
14
+
15
+ it "should use subconstant based on class of collection" do
16
+ object_filter = Minitest::Mock.new
17
+ collection, result, options = Object.new, Object.new, Object.new
18
+ object_filter.expect(:new, result, [collection, params, options])
19
+
20
+ base_filter = Class.new
21
+ base_filter::Object = object_filter
22
+
23
+ filter_canister = MundaneSearch::FilterCanister.new(base_filter, options)
24
+ filter_canister.build(collection, params).must_equal(result)
25
+ object_filter.verify
26
+ end
27
+
28
+ it "should report param_key" do
29
+ param_key = Object.new
30
+ filter_canister = MundaneSearch::FilterCanister.new(nil, param_key: param_key)
31
+ filter_canister.param_key.must_equal param_key
32
+ end
33
+
34
+ it "should report param_key_type" do
35
+ param_key_type = Object.new
36
+ filter_canister = MundaneSearch::FilterCanister.new(nil, param_key_type: param_key_type)
37
+ filter_canister.param_key_type.must_equal param_key_type
38
+ end
39
+
40
+ it "should take param_key_type from filter, if not supplied in options" do
41
+ filter = Minitest::Mock.new
42
+ filter.expect(:param_key_type, :date)
43
+ filter_canister = MundaneSearch::FilterCanister.new(filter)
44
+ filter_canister.param_key_type.must_equal :date
45
+ end
46
+ end
@@ -10,8 +10,8 @@ describe MundaneSearch::Filters::AttributeMatch do
10
10
  let(:all_books) { Book.scoped }
11
11
  let(:a_tale_of_two_cities) { Book.first }
12
12
 
13
- it "should something" do
14
- built = Builder.new do
13
+ it "should match based on param_key" do
14
+ built = MundaneSearch::Builder.new do
15
15
  use MundaneSearch::Filters::AttributeMatch, param_key: "author"
16
16
  end
17
17
 
@@ -0,0 +1,27 @@
1
+ require_relative '../minitest_helper'
2
+ require_relative '../demo_data'
3
+
4
+ describe MundaneSearch::Filters::AttributeMatch do
5
+ let(:books) { open_struct_books }
6
+ let(:a_tale) { books.first }
7
+ it "should match param key" do
8
+ am = MundaneSearch::Filters::AttributeMatch
9
+ filter = am.new(books, {'title' => "A Tale of Two Cities"}, {param_key: 'title'})
10
+
11
+ filter.filtered_collection.must_equal([a_tale])
12
+ end
13
+
14
+ describe MundaneSearch::Filters::AttributeMatch::ActiveRecord do
15
+ let(:am) { MundaneSearch::Filters::AttributeMatch::ActiveRecord }
16
+ it "should filter with 'where'" do
17
+ collection = Minitest::Mock.new
18
+ params = { 'title' => "A Tale of Two Cities" }
19
+ result = Object.new
20
+
21
+ filter = am.new(collection, params, param_key: 'title')
22
+ collection.expect(:where, result, [params])
23
+ filter.filtered_collection.must_equal(result)
24
+ collection.verify
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ require_relative '../minitest_helper'
2
+ require_relative '../demo_data'
3
+
4
+ describe MundaneSearch::Filters::AttributeSubstring do
5
+ let(:books) { open_struct_books }
6
+ let(:a_tale) { books.first }
7
+ it "should match param key" do
8
+ am = MundaneSearch::Filters::AttributeSubstring
9
+ filter = am.new(books, {'title' => "Tale of Two"}, {param_key: 'title'})
10
+
11
+ filter.filtered_collection.must_equal([a_tale])
12
+ end
13
+
14
+ describe MundaneSearch::Filters::AttributeSubstring::ActiveRecord do
15
+ let(:am) { MundaneSearch::Filters::AttributeSubstring::ActiveRecord }
16
+ it "should filter with 'where'" do
17
+ collection = Minitest::Mock.new
18
+ params = { 'title' => "Tale of Two" }
19
+ result = Object.new
20
+
21
+ filter = am.new(collection, params, param_key: 'title')
22
+ collection.expect(:where, result, [["title LIKE ?", "%Tale of Two%"]])
23
+ filter.filtered_collection.must_equal(result)
24
+ collection.verify
25
+ end
26
+ end
27
+ end
@@ -1,13 +1,45 @@
1
1
  require_relative '../minitest_helper'
2
2
 
3
3
  describe MundaneSearch::Filters::Base do
4
- Base = MundaneSearch::Filters::Base
4
+ let(:base) { MundaneSearch::Filters::Base.new(collection, params) }
5
+ let(:standin) { Object.new }
5
6
 
6
- let(:collection) { %w(foo bar baz) }
7
- let(:params) { { 'foo' => 'bar' } }
7
+ describe "#call" do
8
+ it "should equal [filtered_collection, filtered_params]" do
9
+ collection, params = Object.new, Object.new
10
+ base.expects(:filtered_collection).returns(collection)
11
+ base.expects(:filtered_params).returns(params)
12
+ base.call.must_equal [collection, params]
13
+ end
8
14
 
9
- it "should pass through" do
10
- base = Base.new(collection, params)
11
- base.call.must_equal [collection, params]
15
+ it "should equal [collection, params] when apply? is false" do
16
+ collection, params = Object.new, Object.new
17
+ def base.filtered_params ; end
18
+ def base.filtered_collection ; end
19
+ base.expects(:apply?).returns(false)
20
+ base.expects(:collection).returns(collection)
21
+ base.expects(:params).returns(params)
22
+ base.call.must_equal [collection, params]
23
+ end
12
24
  end
13
- end
25
+
26
+ describe "#apply?" do
27
+ it "should be true" do
28
+ assert base.apply?
29
+ end
30
+ end
31
+
32
+ describe "#filtered_collection" do
33
+ it "should contain collection" do
34
+ base.expects(:collection).returns(standin)
35
+ base.filtered_collection.must_equal standin
36
+ end
37
+ end
38
+
39
+ describe "#filtered_params" do
40
+ it "should contain params" do
41
+ base.expects(:params).returns(standin)
42
+ base.filtered_params.must_equal standin
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,11 @@
1
+ require_relative '../minitest_helper'
2
+
3
+ describe MundaneSearch::Filters::BlankParamsAreNil do
4
+ it "should remove empty values from params" do
5
+ params = { empty_string: '', empty_array: [], empty_hash: {} }
6
+ bpan = MundaneSearch::Filters::BlankParamsAreNil.new([], params)
7
+ bpan.filtered_params[:empty_string].must_be_nil
8
+ bpan.filtered_params[:empty_array].must_be_nil
9
+ bpan.filtered_params[:empty_hash].must_be_nil
10
+ end
11
+ end
@@ -0,0 +1,20 @@
1
+ require_relative '../minitest_helper'
2
+ require_relative '../active_record_setup'
3
+
4
+ describe MundaneSearch::Filters::Operator do
5
+ before do
6
+ DatabaseCleaner.clean
7
+ populate_books!
8
+ end
9
+
10
+ let(:all_books) { Book.scoped }
11
+ let(:a_tale_of_two_cities) { Book.first }
12
+
13
+ it "should match based on param_key" do
14
+ built = MundaneSearch::Builder.new do
15
+ use MundaneSearch::Filters::Operator, { param_key: 'sold', operator: :> }
16
+ end
17
+
18
+ built.call(all_books, {"sold" => 199_999_999}).must_equal([a_tale_of_two_cities])
19
+ end
20
+ end
@@ -0,0 +1,28 @@
1
+ require_relative '../minitest_helper'
2
+ require_relative '../demo_data'
3
+
4
+ describe MundaneSearch::Filters::Operator do
5
+ let(:books) { open_struct_books }
6
+ let(:a_tale) { books.first }
7
+
8
+ it "should match param key" do
9
+ o = MundaneSearch::Filters::Operator
10
+ filter = o.new(books, {'sold' => 199_999_999}, { param_key: 'sold', operator: :> })
11
+
12
+ filter.filtered_collection.must_equal([a_tale])
13
+ end
14
+
15
+ describe MundaneSearch::Filters::Operator::ActiveRecord do
16
+ let(:o) { MundaneSearch::Filters::Operator::ActiveRecord }
17
+ it "should filter with 'where'" do
18
+ collection = Minitest::Mock.new
19
+ params = { 'sold' => 199_999_999 }
20
+ result = Object.new
21
+
22
+ filter = o.new(collection, params, { param_key: 'sold', operator: :> })
23
+ collection.expect(:where, result, [["sold > ?", 199_999_999]])
24
+ filter.filtered_collection.must_equal(result)
25
+ collection.verify
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,16 @@
1
+ require_relative '../minitest_helper'
2
+ require_relative '../demo_data'
3
+
4
+ describe MundaneSearch::Filters::Shortcuts do
5
+
6
+ let(:all_books) { open_struct_books }
7
+ let(:a_tale_of_two_cities) { all_books.first }
8
+
9
+ it "should match based on param_key" do
10
+ built = MundaneSearch::Builder.new do
11
+ employ :attribute_filter, param_key: 'title'
12
+ end
13
+
14
+ built.call(all_books, {"title" => "A Tale of Two Cities"}).must_equal([a_tale_of_two_cities])
15
+ end
16
+ end