morse_searchable 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +3 -0
  4. data/Rakefile +34 -0
  5. data/app/controllers/concerns/searchable.rb +126 -0
  6. data/app/views/api/search/feed.json.jbuilder +22 -0
  7. data/app/views/api/search/filters.json.jbuilder +10 -0
  8. data/config/routes.rb +8 -0
  9. data/lib/morse_searchable.rb +4 -0
  10. data/lib/morse_searchable/engine.rb +11 -0
  11. data/lib/morse_searchable/version.rb +3 -0
  12. data/lib/tasks/morse_searchable_tasks.rake +4 -0
  13. data/spec/controllers/searchable_spec.rb +145 -0
  14. data/spec/dummy/README.rdoc +28 -0
  15. data/spec/dummy/Rakefile +6 -0
  16. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  17. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  18. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  19. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  20. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  21. data/spec/dummy/bin/bundle +3 -0
  22. data/spec/dummy/bin/rails +4 -0
  23. data/spec/dummy/bin/rake +4 -0
  24. data/spec/dummy/bin/setup +29 -0
  25. data/spec/dummy/config.ru +4 -0
  26. data/spec/dummy/config/application.rb +29 -0
  27. data/spec/dummy/config/boot.rb +5 -0
  28. data/spec/dummy/config/environment.rb +5 -0
  29. data/spec/dummy/config/environments/development.rb +38 -0
  30. data/spec/dummy/config/environments/production.rb +76 -0
  31. data/spec/dummy/config/environments/test.rb +42 -0
  32. data/spec/dummy/config/initializers/assets.rb +11 -0
  33. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  34. data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
  35. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  36. data/spec/dummy/config/initializers/inflections.rb +16 -0
  37. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  38. data/spec/dummy/config/initializers/session_store.rb +3 -0
  39. data/spec/dummy/config/initializers/wrap_parameters.rb +9 -0
  40. data/spec/dummy/config/locales/en.yml +23 -0
  41. data/spec/dummy/config/routes.rb +4 -0
  42. data/spec/dummy/config/secrets.yml +22 -0
  43. data/spec/dummy/log/test.log +0 -0
  44. data/spec/dummy/public/404.html +67 -0
  45. data/spec/dummy/public/422.html +67 -0
  46. data/spec/dummy/public/500.html +66 -0
  47. data/spec/dummy/public/favicon.ico +0 -0
  48. data/spec/rails_helper.rb +52 -0
  49. data/spec/spec_helper.rb +59 -0
  50. metadata +269 -0
@@ -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
@@ -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.
@@ -0,0 +1,3 @@
1
+ = MorseSearchable
2
+
3
+ This project rocks and uses MIT-LICENSE.
@@ -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
@@ -0,0 +1,8 @@
1
+ MorseSearchable::Engine.routes.draw do
2
+ concern :searchable do
3
+ collection do
4
+ get :feed
5
+ get :filters
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,4 @@
1
+ require "morse_searchable/engine"
2
+
3
+ module MorseSearchable
4
+ end
@@ -0,0 +1,11 @@
1
+ module MorseSearchable
2
+ class Engine < ::Rails::Engine
3
+
4
+ config.generators do |g|
5
+ g.test_framework :rspec, :fixture => false
6
+ # g.fixture_replacement :factory_girl, :dir => 'spec/factories'
7
+ g.assets false
8
+ g.helper false
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ module MorseSearchable
2
+ VERSION = "0.0.3"
3
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :morse_searchable do
3
+ # # Task goes here
4
+ # end
@@ -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>.
@@ -0,0 +1,6 @@
1
+ # Add your own tasks in files placed in lib/tasks ending in .rake,
2
+ # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3
+
4
+ require File.expand_path('../config/application', __FILE__)
5
+
6
+ Rails.application.load_tasks