morse_searchable 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +3 -0
- data/Rakefile +34 -0
- data/app/controllers/concerns/searchable.rb +126 -0
- data/app/views/api/search/feed.json.jbuilder +22 -0
- data/app/views/api/search/filters.json.jbuilder +10 -0
- data/config/routes.rb +8 -0
- data/lib/morse_searchable.rb +4 -0
- data/lib/morse_searchable/engine.rb +11 -0
- data/lib/morse_searchable/version.rb +3 -0
- data/lib/tasks/morse_searchable_tasks.rake +4 -0
- data/spec/controllers/searchable_spec.rb +145 -0
- data/spec/dummy/README.rdoc +28 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/assets/javascripts/application.js +13 -0
- data/spec/dummy/app/assets/stylesheets/application.css +15 -0
- data/spec/dummy/app/controllers/application_controller.rb +5 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/bin/setup +29 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +29 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +38 -0
- data/spec/dummy/config/environments/production.rb +76 -0
- data/spec/dummy/config/environments/test.rb +42 -0
- data/spec/dummy/config/initializers/assets.rb +11 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/mime_types.rb +4 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +9 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +4 -0
- data/spec/dummy/config/secrets.yml +22 -0
- data/spec/dummy/log/test.log +0 -0
- data/spec/dummy/public/404.html +67 -0
- data/spec/dummy/public/422.html +67 -0
- data/spec/dummy/public/500.html +66 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/rails_helper.rb +52 -0
- data/spec/spec_helper.rb +59 -0
- metadata +269 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 0450f80c82d494d57e0865164277650333f26d54
|
4
|
+
data.tar.gz: ac55b0a7cffbc674b3142f9c90169cafe10147b4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1c08f099e2dd41f0d017807c5ddc56d38eda31139bb6f744f22a5ff319877d7b28250de75ed894c65a443c7bede7e6ffc06aafe0d73d9447b8735f1badc3a769
|
7
|
+
data.tar.gz: 235a99dd6117dfce4b88e33c0de78ff3e96bc74f51c3c1dad06b3aaad004215b5ec33fcdac3f269af9e4b8c05a33b2570aa559ab977d07e22ea8c3425c020e26
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2015 fredmcgroarty
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'MorseSearchable'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.rdoc')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
Bundler::GemHelper.install_tasks
|
23
|
+
|
24
|
+
require 'rake/testtask'
|
25
|
+
|
26
|
+
Rake::TestTask.new(:test) do |t|
|
27
|
+
t.libs << 'lib'
|
28
|
+
t.libs << 'test'
|
29
|
+
t.pattern = 'test/**/*_test.rb'
|
30
|
+
t.verbose = false
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
task default: :test
|
@@ -0,0 +1,126 @@
|
|
1
|
+
module Searchable
|
2
|
+
|
3
|
+
ACCEPTED_INPUT_TYPES = [:select, :checkbox, :radio]
|
4
|
+
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
#current defects - possibly issue handling non validated association fields
|
7
|
+
#not set up to use has many yet
|
8
|
+
|
9
|
+
DEFAULT_FIELDS = [:id]
|
10
|
+
|
11
|
+
def feed
|
12
|
+
respond_to do |format|
|
13
|
+
if sanitized_klass
|
14
|
+
format.json { render 'api/search/feed', layout: false, locals: { feed: data_pool, fields: attributes } }
|
15
|
+
else
|
16
|
+
format.json { render :json => {:error => "Check module configuration settings"}, status: 404 }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def filters
|
22
|
+
respond_to do |format|
|
23
|
+
if sanitized_filters
|
24
|
+
format.json { render 'api/search/filters', layout: false, locals: { filters: build_filters } }
|
25
|
+
else
|
26
|
+
format.json { render :json => {:error => "Check module configuration settings"}, status: 404 }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def apply_scope
|
34
|
+
operable_klass.send(sanitized_klass[:scope])
|
35
|
+
end
|
36
|
+
|
37
|
+
def attributes
|
38
|
+
association_fields = {}
|
39
|
+
association_fields[:main] = sanitized_klass[:attributes]
|
40
|
+
sanitized_filters.each do |obj,attrs|
|
41
|
+
association = klass_to_symbol(obj)
|
42
|
+
if sanitized_klass[:klass].new.respond_to?(association)
|
43
|
+
arr=attrs.select { |atr| atr unless DEFAULT_FIELDS.include?(atr) }
|
44
|
+
association_fields[association] = arr if arr.any?
|
45
|
+
end
|
46
|
+
association_fields
|
47
|
+
end
|
48
|
+
association_fields
|
49
|
+
end
|
50
|
+
|
51
|
+
def build_filters
|
52
|
+
filterable_hash = {}
|
53
|
+
sanitized_filters.each do |filter, options|
|
54
|
+
if filter.select(options[:fields]).any?
|
55
|
+
a=klass_to_symbol(filter)
|
56
|
+
filterable_hash[a]={}
|
57
|
+
filterable_hash[a][:filters_by] = filter.name
|
58
|
+
filterable_hash[a][:collection] = filter.where(id: filter.pluck(:id))
|
59
|
+
filterable_hash[a][:fields] = options[:fields].map {|f| f if filter.instance_methods.include?(f) }
|
60
|
+
filterable_hash[a][:input_type] = options[:input_type]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
filterable_hash
|
64
|
+
end
|
65
|
+
|
66
|
+
def data_pool
|
67
|
+
return apply_scope if sanitized_klass.key? :scope
|
68
|
+
return sanitized_klass[:universe] if sanitized_klass.key? :universe
|
69
|
+
operable_klass.send(:all)
|
70
|
+
end
|
71
|
+
|
72
|
+
def klass_keys(k)
|
73
|
+
k.new.attributes.keys.map { |k| k.to_sym }
|
74
|
+
end
|
75
|
+
|
76
|
+
def klass_to_symbol(k)
|
77
|
+
k.name.underscore.to_sym
|
78
|
+
end
|
79
|
+
|
80
|
+
def operable_klass
|
81
|
+
sanitized_klass[:klass]
|
82
|
+
end
|
83
|
+
|
84
|
+
def sanitize_klass
|
85
|
+
@sanitized_klass = {}
|
86
|
+
if @filter_feed[:klass]
|
87
|
+
@sanitized_klass[:klass] = @filter_feed[:klass]
|
88
|
+
@sanitized_klass[:universe] = @filter_feed[:universe] if @filter_feed.key? :universe
|
89
|
+
@sanitized_klass[:attributes] = []
|
90
|
+
@filter_feed[:non_associated_fields].collect { |naf| @sanitized_klass[:attributes] << naf if @filter_feed[:klass].new.respond_to?(naf) }
|
91
|
+
@sanitized_klass[:scope] = @filter_feed[:scope] if @filter_feed.key?(:scope) and @filter_feed[:klass].respond_to?(@filter_feed[:scope])
|
92
|
+
end
|
93
|
+
@sanitized_klass
|
94
|
+
end
|
95
|
+
|
96
|
+
def sanitize_filters
|
97
|
+
@sanitized_filters = {}
|
98
|
+
@filter_collection.each do |filter|
|
99
|
+
klass = filter[:filterable]
|
100
|
+
attributes = filter[:fields]
|
101
|
+
safe_filters = []
|
102
|
+
safe_filters.push(*DEFAULT_FIELDS)
|
103
|
+
attributes.map { |f| safe_filters << f if klass.new.respond_to?(f) }
|
104
|
+
@sanitized_filters[klass] = {}
|
105
|
+
@sanitized_filters[klass][:fields] = safe_filters
|
106
|
+
# raise ACCEPTED_INPUT_TYPES.inspect
|
107
|
+
@sanitized_filters[klass][:input_type] = filter[:input_type] if ACCEPTED_INPUT_TYPES.include?(filter[:input_type])
|
108
|
+
end
|
109
|
+
@sanitized_filters
|
110
|
+
end
|
111
|
+
|
112
|
+
def sanitized_filters
|
113
|
+
@sanitized_filters
|
114
|
+
end
|
115
|
+
|
116
|
+
def sanitized_klass
|
117
|
+
@sanitized_klass
|
118
|
+
end
|
119
|
+
|
120
|
+
def set_meta_filters(args={})
|
121
|
+
@filter_feed=args[:feed]
|
122
|
+
@filter_collection=args[:collection]
|
123
|
+
sanitize_klass
|
124
|
+
sanitize_filters
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
if feed && feed.any?
|
2
|
+
json.array!(feed) do |item|
|
3
|
+
json.id item.id
|
4
|
+
fields[:main].each do |field|
|
5
|
+
json.set! field, item.send(field)
|
6
|
+
end
|
7
|
+
fields.each do |association, attrs|
|
8
|
+
if association != :main
|
9
|
+
attrs[:fields].each do |field|
|
10
|
+
json.set! association, item.send(association).send(field) if item.send(association)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
json.filters do
|
15
|
+
fields.each do |association, attrs|
|
16
|
+
if association != :main
|
17
|
+
json.set! association, item.send(association).id if item.send(association)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
json.array!(filters) do |type, data|
|
2
|
+
json.title type.to_s.gsub(/_/, " ")
|
3
|
+
json.filter_by type
|
4
|
+
json.input_type data[:input_type]
|
5
|
+
json.filter_options(data[:collection]) do |instance|
|
6
|
+
data[:fields].each do |field|
|
7
|
+
json.set! field, instance.send(field)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
data/config/routes.rb
ADDED
@@ -0,0 +1,145 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
class FakesController
|
3
|
+
include Searchable
|
4
|
+
|
5
|
+
def set_meta_filters_here(args={})
|
6
|
+
set_meta_filters(args)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
describe Searchable do
|
11
|
+
|
12
|
+
let(:sample) { new_sample }
|
13
|
+
let(:some_samples) { [new_sample] }
|
14
|
+
let(:no_samples) { [] }
|
15
|
+
let(:color) { new_color }
|
16
|
+
let(:some_colors) { [new_color] }
|
17
|
+
let(:no_colors) { [] }
|
18
|
+
|
19
|
+
let(:fakes_controller){FakesController.new}
|
20
|
+
let(:feed_non_specific){
|
21
|
+
{feed: {klass: Sample, non_associated_fields: [:barcode]},
|
22
|
+
collection: [
|
23
|
+
{filterable: ClothingType, fields: [:title, :poop]},
|
24
|
+
{filterable: Color, fields: [:title, :code] }
|
25
|
+
]
|
26
|
+
}
|
27
|
+
}
|
28
|
+
let(:feed_universe){
|
29
|
+
{feed: {klass: Sample, universe: some_samples, non_associated_fields: [:barcode]},
|
30
|
+
collection: [
|
31
|
+
{filterable: ClothingType, fields: [:title, :poop]},
|
32
|
+
{filterable: Color, fields: [:title, :code] }
|
33
|
+
]
|
34
|
+
}
|
35
|
+
}
|
36
|
+
let(:feed_vanilla){
|
37
|
+
{feed: {klass: Sample, scope: :all, non_associated_fields: [:barcode]},
|
38
|
+
collection: [
|
39
|
+
{filterable: ClothingType, fields: [:title, :poop]},
|
40
|
+
{filterable: Color, fields: [:title, :code] }
|
41
|
+
]
|
42
|
+
}
|
43
|
+
}
|
44
|
+
let(:feed_vanilla_with_universe){
|
45
|
+
{feed: {klass: Sample, scope: :all, non_associated_fields: [:barcode]},
|
46
|
+
collection: [
|
47
|
+
{filterable: ClothingType, fields: [:title, :poop]},
|
48
|
+
{filterable: Color, universe:some_colors, fields: [:title, :code] }
|
49
|
+
]
|
50
|
+
}
|
51
|
+
}
|
52
|
+
let(:feed_vanilla_with_universe_with_field_types){
|
53
|
+
{feed: {klass: Sample, scope: :all, non_associated_fields: [:barcode]},
|
54
|
+
collection: [
|
55
|
+
{filterable: ClothingType, fields: [:title, :poop], input_type: :select},
|
56
|
+
{filterable: Color, universe:some_colors, fields: [:title, :code], input_types: :select }
|
57
|
+
]
|
58
|
+
}
|
59
|
+
}
|
60
|
+
|
61
|
+
|
62
|
+
|
63
|
+
|
64
|
+
before do
|
65
|
+
fakes_controller.set_meta_filters_here(feed_vanilla)
|
66
|
+
end
|
67
|
+
|
68
|
+
describe "apply_scope" do
|
69
|
+
it "should apply the scope" do
|
70
|
+
expect(Sample).to receive(:all).at_least(:once)
|
71
|
+
fakes_controller.send(:apply_scope)
|
72
|
+
end
|
73
|
+
it "should return an ARR" do
|
74
|
+
|
75
|
+
expect(fakes_controller.send(:apply_scope)).to be_a(Array)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe "build_filters" do
|
80
|
+
it "does something" do
|
81
|
+
fakes_controller.send(:build_filters)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe "data_pool" do
|
86
|
+
context "where a scope is given" do
|
87
|
+
it "should apply the scope" do
|
88
|
+
expect(fakes_controller).to receive(:apply_scope).at_least(:once)
|
89
|
+
fakes_controller.send(:data_pool)
|
90
|
+
end
|
91
|
+
it "should return an ARR" do
|
92
|
+
expect(fakes_controller.send(:data_pool)).to be_a(Array)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
context "where a universe is given" do
|
97
|
+
before do
|
98
|
+
fakes_controller.set_meta_filters_here(feed_universe)
|
99
|
+
end
|
100
|
+
it "should return the universe" do
|
101
|
+
expect(fakes_controller.send(:data_pool)).to eq(some_samples)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
context "where neither scope nor universe is given" do
|
106
|
+
before do
|
107
|
+
fakes_controller.set_meta_filters_here(feed_non_specific)
|
108
|
+
end
|
109
|
+
it "should return the universe" do
|
110
|
+
expect(fakes_controller.send(:data_pool)).to eq(Sample.all)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
describe "operable_klass" do
|
116
|
+
it "should return the correct klass" do
|
117
|
+
expect(fakes_controller.send(:operable_klass)).to eq(Sample)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
describe "sanitize_filters" do
|
122
|
+
context "where a input type is passed" do
|
123
|
+
before do
|
124
|
+
fakes_controller.set_meta_filters_here(feed_vanilla_with_universe_with_field_types)
|
125
|
+
end
|
126
|
+
it "should include the input type in filter hash" do
|
127
|
+
expect(fakes_controller.send(:sanitize_filters)[ClothingType]).to include(:input_type => :select)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
describe "sanitized_filters" do
|
133
|
+
subject{fakes_controller.send(:sanitized_filters)[ClothingType]}
|
134
|
+
it { is_expected.to include(:fields => [:id, :title]) }
|
135
|
+
context "removing junk attributes" do
|
136
|
+
subject{fakes_controller.send(:sanitized_filters)[ClothingType][:fields]}
|
137
|
+
it { is_expected.to_not include(:poop) }
|
138
|
+
describe "it always includes id" do
|
139
|
+
it { is_expected.to include(:id) }
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|
145
|
+
|
@@ -0,0 +1,28 @@
|
|
1
|
+
== README
|
2
|
+
|
3
|
+
This README would normally document whatever steps are necessary to get the
|
4
|
+
application up and running.
|
5
|
+
|
6
|
+
Things you may want to cover:
|
7
|
+
|
8
|
+
* Ruby version
|
9
|
+
|
10
|
+
* System dependencies
|
11
|
+
|
12
|
+
* Configuration
|
13
|
+
|
14
|
+
* Database creation
|
15
|
+
|
16
|
+
* Database initialization
|
17
|
+
|
18
|
+
* How to run the test suite
|
19
|
+
|
20
|
+
* Services (job queues, cache servers, search engines, etc.)
|
21
|
+
|
22
|
+
* Deployment instructions
|
23
|
+
|
24
|
+
* ...
|
25
|
+
|
26
|
+
|
27
|
+
Please feel free to use a different markup language if you do not plan to run
|
28
|
+
<tt>rake doc:app</tt>.
|
data/spec/dummy/Rakefile
ADDED