mundane-search 0.0.1 → 0.0.2

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.
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