acts_as_explorable 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +16 -0
- data/.rspec +2 -0
- data/.travis.yml +23 -0
- data/Appraisals +12 -0
- data/Gemfile +15 -0
- data/Guardfile +5 -0
- data/MIT-LICENSE +20 -0
- data/README.md +52 -0
- data/Rakefile +22 -0
- data/acts_as_explorable.gemspec +28 -0
- data/gemfiles/activerecord_4.0.gemfile +19 -0
- data/gemfiles/activerecord_4.1.gemfile +19 -0
- data/gemfiles/activerecord_4.2.gemfile +20 -0
- data/lib/acts_as_explorable/configuration.rb +9 -0
- data/lib/acts_as_explorable/element/base.rb +8 -0
- data/lib/acts_as_explorable/element/dynamic_filter.rb +15 -0
- data/lib/acts_as_explorable/element/in.rb +18 -0
- data/lib/acts_as_explorable/element/sort.rb +20 -0
- data/lib/acts_as_explorable/element.rb +83 -0
- data/lib/acts_as_explorable/explorable.rb +76 -0
- data/lib/acts_as_explorable/ext/string.rb +24 -0
- data/lib/acts_as_explorable/parser.rb +61 -0
- data/lib/acts_as_explorable/query.rb +36 -0
- data/lib/acts_as_explorable/version.rb +3 -0
- data/lib/acts_as_explorable.rb +45 -0
- data/lib/tasks/acts_as_explorable_tasks.rake +4 -0
- data/spec/factories/players.rb +57 -0
- data/spec/lib/acts_as_explorable/element/dynamic_filter_spec.rb +19 -0
- data/spec/lib/acts_as_explorable/element/in.rb +12 -0
- data/spec/lib/acts_as_explorable/element_spec.rb +16 -0
- data/spec/lib/acts_as_explorable/explorable_spec.rb +28 -0
- data/spec/lib/acts_as_explorable/parser_spec.rb +35 -0
- data/spec/lib/acts_as_explorable/query_spec.rb +51 -0
- data/spec/spec_helper.rb +24 -0
- data/spec/support/database.rb +64 -0
- data/spec/support/database_cleaner.rb +21 -0
- metadata +185 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: cf09cef7bef8fb847eceda7eb6ab0ffe5a3d9d72
|
4
|
+
data.tar.gz: a7b61b25db90271f6787c8e3140ca097500f5445
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3856e51cda905223c8d4c3d35456d7db75d69573bf810c0d61f911f0432e3069f24ea53c1bb749690d8ad58e4750755d63a46f0c7592fabf258b5f650698d16b
|
7
|
+
data.tar.gz: 8f2fec4eb042b5a9f48aa665cc37c51b3a5e7251c9e9f485de2b415db2bc9c8df0a450dd97c74efaacd03011118bbfdea5e19c6f3d9814cef64d50077e6d0f6f
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
language: ruby
|
2
|
+
rvm:
|
3
|
+
- 2.1
|
4
|
+
- 2.2
|
5
|
+
- 2.0.0
|
6
|
+
- rbx-2
|
7
|
+
|
8
|
+
gemfile:
|
9
|
+
- gemfiles/activerecord_4.0.gemfile
|
10
|
+
- gemfiles/activerecord_4.1.gemfile
|
11
|
+
- gemfiles/activerecord_4.2.gemfile
|
12
|
+
|
13
|
+
sudo: false
|
14
|
+
|
15
|
+
bundler_args: '--without local_development yard --jobs 3 --retry 3'
|
16
|
+
|
17
|
+
script: bundle exec rake
|
18
|
+
|
19
|
+
matrix:
|
20
|
+
fast_finish: true
|
21
|
+
allow_failures:
|
22
|
+
- gemfile: gemfiles/activerecord_edge.gemfile
|
23
|
+
- rvm: rbx-2
|
data/Appraisals
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
appraise "activerecord-4.0" do
|
2
|
+
gem "activerecord", github: "rails/rails" , branch: '4-0-stable'
|
3
|
+
end
|
4
|
+
|
5
|
+
appraise "activerecord-4.1" do
|
6
|
+
gem "activerecord", github: "rails/rails" , branch: '4-1-stable'
|
7
|
+
end
|
8
|
+
|
9
|
+
appraise "activerecord-4.2" do
|
10
|
+
gem "railties", github: "rails/rails" , branch: '4-2-stable'
|
11
|
+
gem "activerecord", github: "rails/rails" , branch: '4-2-stable'
|
12
|
+
end
|
data/Gemfile
ADDED
data/Guardfile
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2015 Mathias Schneider
|
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.md
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# Acts As Explorable
|
2
|
+
|
3
|
+
[![Build Status](https://travis-ci.org/hiasinho/acts_as_explorable.svg?branch=develop)](https://travis-ci.org/hiasinho/acts_as_explorable) [![Code Climate](https://codeclimate.com/github/hiasinho/acts_as_explorable/badges/gpa.svg)](https://codeclimate.com/github/hiasinho/acts_as_explorable) [![Inline docs](http://inch-ci.org/github/hiasinho/acts_as_explorable.svg?branch=develop)](http://inch-ci.org/github/hiasinho/acts_as_explorable)
|
4
|
+
|
5
|
+
Acts As Explorable is a Ruby Gem specifically written for ActiveRecord models.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
### Supported Ruby and Rails versions
|
10
|
+
|
11
|
+
* Ruby 2.0.0, 2.1.0
|
12
|
+
* Rails 4.0, 4.1, 4.2+
|
13
|
+
|
14
|
+
### Install
|
15
|
+
|
16
|
+
THIS GEM IS NOT RELEASED YET. SO, THIS WON'T WORK!
|
17
|
+
|
18
|
+
Just add the following to your Gemfile.
|
19
|
+
|
20
|
+
```ruby
|
21
|
+
gem 'acts_as_explorable', '~> 0.0.1'
|
22
|
+
```
|
23
|
+
|
24
|
+
And follow that up with a ``bundle install``.
|
25
|
+
|
26
|
+
## Usage
|
27
|
+
|
28
|
+
TODO
|
29
|
+
|
30
|
+
## Testing
|
31
|
+
|
32
|
+
All tests follow the RSpec format and are located in the spec directory.
|
33
|
+
They can be run with:
|
34
|
+
|
35
|
+
```
|
36
|
+
rake spec
|
37
|
+
```
|
38
|
+
|
39
|
+
## License
|
40
|
+
|
41
|
+
Acts as explorable is released under the [MIT License](http://www.opensource.org/licenses/MIT).
|
42
|
+
|
43
|
+
## TODO
|
44
|
+
|
45
|
+
### v0.1
|
46
|
+
- Fill this README file
|
47
|
+
- Release v0.0.1 to rubygems
|
48
|
+
|
49
|
+
###v0.x
|
50
|
+
- Add tests for postgres and mysql
|
51
|
+
- Query string validation helper for use in forms
|
52
|
+
- Use methods in addition to fields
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
|
4
|
+
desc 'Default: run specs.'
|
5
|
+
task default: :spec
|
6
|
+
|
7
|
+
require 'rspec/core/rake_task'
|
8
|
+
RSpec::Core::RakeTask.new do |t|
|
9
|
+
t.pattern = 'spec/**/*_spec.rb'
|
10
|
+
end
|
11
|
+
|
12
|
+
require 'yard'
|
13
|
+
YARD::Rake::YardocTask.new do |t|
|
14
|
+
t.options << '--output-dir' << './doc'
|
15
|
+
t.options << '--no-private'
|
16
|
+
t.options << '--protected'
|
17
|
+
t.options << '--readme' << 'README.md'
|
18
|
+
t.options << '--hide-tag' << 'return'
|
19
|
+
t.options << '--hide-tag' << 'param'
|
20
|
+
end
|
21
|
+
|
22
|
+
Bundler::GemHelper.install_tasks
|
@@ -0,0 +1,28 @@
|
|
1
|
+
$LOAD_PATH.push File.expand_path('../lib', __FILE__)
|
2
|
+
require 'acts_as_explorable/version'
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = 'acts_as_explorable'
|
6
|
+
s.version = ActsAsExplorable::VERSION
|
7
|
+
s.authors = ['Mathias Schneider']
|
8
|
+
s.email = ['mathias@hiasinho.com']
|
9
|
+
s.homepage = 'https://github.com/hiasinho/acts_as_explorable'
|
10
|
+
s.summary = 'Adds GitHub-like search function to your models'
|
11
|
+
s.description = 'Adds GitHub-like search function to your models'
|
12
|
+
s.license = 'MIT'
|
13
|
+
|
14
|
+
s.files = `git ls-files`.split("\n")
|
15
|
+
s.test_files = `git ls-files -- {spec}/*`.split("\n")
|
16
|
+
s.require_paths = ['lib']
|
17
|
+
s.required_ruby_version = '>= 2.0.0'
|
18
|
+
|
19
|
+
s.add_runtime_dependency 'activerecord', ['>= 4.0', '< 5']
|
20
|
+
|
21
|
+
s.add_development_dependency 'sqlite3'
|
22
|
+
|
23
|
+
s.add_development_dependency 'rspec-rails'
|
24
|
+
s.add_development_dependency 'rspec-its'
|
25
|
+
s.add_development_dependency 'rspec'
|
26
|
+
s.add_development_dependency 'factory_girl_rails', '~> 4.0'
|
27
|
+
s.add_development_dependency 'database_cleaner'
|
28
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# This file was generated by Appraisal
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
gem "activerecord", :github => "rails/rails", :branch => "4-0-stable"
|
6
|
+
|
7
|
+
group :doc do
|
8
|
+
gem "yard"
|
9
|
+
end
|
10
|
+
|
11
|
+
group :local_development do
|
12
|
+
gem "guard"
|
13
|
+
gem "guard-rspec"
|
14
|
+
gem "appraisal"
|
15
|
+
gem "rake"
|
16
|
+
gem "byebug", :platform => :mri_21
|
17
|
+
end
|
18
|
+
|
19
|
+
gemspec :path => "../"
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# This file was generated by Appraisal
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
gem "activerecord", :github => "rails/rails", :branch => "4-1-stable"
|
6
|
+
|
7
|
+
group :doc do
|
8
|
+
gem "yard"
|
9
|
+
end
|
10
|
+
|
11
|
+
group :local_development do
|
12
|
+
gem "guard"
|
13
|
+
gem "guard-rspec"
|
14
|
+
gem "appraisal"
|
15
|
+
gem "rake"
|
16
|
+
gem "byebug", :platform => :mri_21
|
17
|
+
end
|
18
|
+
|
19
|
+
gemspec :path => "../"
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# This file was generated by Appraisal
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
gem "railties", :github => "rails/rails", :branch => "4-2-stable"
|
6
|
+
gem "activerecord", :github => "rails/rails", :branch => "4-2-stable"
|
7
|
+
|
8
|
+
group :doc do
|
9
|
+
gem "yard"
|
10
|
+
end
|
11
|
+
|
12
|
+
group :local_development do
|
13
|
+
gem "guard"
|
14
|
+
gem "guard-rspec"
|
15
|
+
gem "appraisal"
|
16
|
+
gem "rake"
|
17
|
+
gem "byebug", :platform => :mri_21
|
18
|
+
end
|
19
|
+
|
20
|
+
gemspec :path => "../"
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module ActsAsExplorable::Element
|
2
|
+
#
|
3
|
+
# Generates a +where+ clause to look up the searched string in the given columns
|
4
|
+
#
|
5
|
+
class DynamicFilter < Base
|
6
|
+
def after_init
|
7
|
+
@query_type = :where
|
8
|
+
end
|
9
|
+
|
10
|
+
def render
|
11
|
+
@query_parts << table[type].lower.in(@parameters)
|
12
|
+
@full_query = @query_parts.first
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module ActsAsExplorable::Element
|
2
|
+
#
|
3
|
+
# Generates a +where+ clause to look up the searched string in the given columns
|
4
|
+
#
|
5
|
+
class In < Base
|
6
|
+
def after_init
|
7
|
+
@query_type = :where
|
8
|
+
end
|
9
|
+
|
10
|
+
def render
|
11
|
+
@parameters.each do |f|
|
12
|
+
@query_parts <<
|
13
|
+
table[f.to_sym].matches_any(@query_string.map { |q| "%#{q}%" })
|
14
|
+
end
|
15
|
+
@full_query = @query_parts.inject(:or)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module ActsAsExplorable::Element
|
2
|
+
#
|
3
|
+
# Generates an +order+ query part to sort by the given columns
|
4
|
+
#
|
5
|
+
class Sort < Base
|
6
|
+
def after_init
|
7
|
+
@query_type = :reorder
|
8
|
+
end
|
9
|
+
|
10
|
+
def render
|
11
|
+
@full_query = @parameters.map do |f|
|
12
|
+
if f =~ /(-asc|-desc)/
|
13
|
+
{ f.split('-').first.to_sym => f.split('-').last.to_sym }
|
14
|
+
else
|
15
|
+
{ f.to_sym => :desc }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'acts_as_explorable/element/base'
|
2
|
+
require 'acts_as_explorable/element/in'
|
3
|
+
require 'acts_as_explorable/element/sort'
|
4
|
+
require 'acts_as_explorable/element/dynamic_filter'
|
5
|
+
|
6
|
+
module ActsAsExplorable
|
7
|
+
module Element
|
8
|
+
attr_accessor :query_type, :model, :parameters, :query_string, :query_parts,
|
9
|
+
:full_query
|
10
|
+
|
11
|
+
#
|
12
|
+
# This method acts as a factory to build a concrete element
|
13
|
+
#
|
14
|
+
# @example
|
15
|
+
# ActsAsExplorable::Element.build(:in, 'Zlatan in:first_name', Player)
|
16
|
+
#
|
17
|
+
# @param type [Symbol] The element type to be build
|
18
|
+
# @param query [String] The query string
|
19
|
+
# @param model [ActiveRecord::Base] Anactive record model
|
20
|
+
#
|
21
|
+
# @return [ActsAsExplorable::Element] A concrete element type
|
22
|
+
def self.build(type, query, model)
|
23
|
+
klass = Module.nesting.last.const_get('Element').const_get(type.to_s.camelize)
|
24
|
+
instance = klass.new(query, model, type)
|
25
|
+
rescue NameError
|
26
|
+
DynamicFilter.new(query, model, type)
|
27
|
+
end
|
28
|
+
|
29
|
+
def initialize(query, model, element_type = nil)
|
30
|
+
query = query.to_acts_as_explorable(ActsAsExplorable.filters.keys)
|
31
|
+
|
32
|
+
@type = element_type if element_type
|
33
|
+
@model = model
|
34
|
+
@query_string = query[:values]
|
35
|
+
@query_parts = []
|
36
|
+
filter_parameters(query[:params])
|
37
|
+
after_init
|
38
|
+
|
39
|
+
render if @parameters.present?
|
40
|
+
end
|
41
|
+
|
42
|
+
def after_init; end
|
43
|
+
|
44
|
+
def execute(query_object)
|
45
|
+
query_object.send(@query_type, @full_query)
|
46
|
+
end
|
47
|
+
|
48
|
+
protected
|
49
|
+
|
50
|
+
def filter_parameters(params)
|
51
|
+
return unless params[type]
|
52
|
+
@parameters = params[type].select do |f|
|
53
|
+
filters.find do |e|
|
54
|
+
/#{e.to_sym}(?:-\w+)?/ =~ f
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
#
|
60
|
+
# Returns the Arel table for the current model
|
61
|
+
#
|
62
|
+
# @return [Arel::Table] Arel table for the current model
|
63
|
+
def table
|
64
|
+
@model.arel_table
|
65
|
+
end
|
66
|
+
|
67
|
+
def render
|
68
|
+
fail "`#render` needs to be implemented for #{self.class.name}"
|
69
|
+
end
|
70
|
+
|
71
|
+
def type
|
72
|
+
@type.to_sym || self.class.name.demodulize.underscore.to_sym
|
73
|
+
end
|
74
|
+
|
75
|
+
#
|
76
|
+
# Returns the customized filters
|
77
|
+
#
|
78
|
+
# @return [Hash] The customized filters
|
79
|
+
def filters
|
80
|
+
ActsAsExplorable.filters[type]
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module ActsAsExplorable
|
2
|
+
module Explorable
|
3
|
+
def self.extended(base)
|
4
|
+
base.class_eval do
|
5
|
+
def self.explorable?
|
6
|
+
false
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
# Configure ActsAsExplorable's behavior in a model.
|
12
|
+
#
|
13
|
+
# The plugin can be customized using parameters or through a `block`.
|
14
|
+
#
|
15
|
+
# class Player < ActiveRecord::Base
|
16
|
+
# extend ActsAsExplorable
|
17
|
+
# explorable in: [:first_name, :last_name, :position, :city, :club],
|
18
|
+
# sort: [:first_name, :last_name, :position, :city, :club, :created_at],
|
19
|
+
# position: ['GK', 'MF', 'FW']
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# Using a block (TODO: This will be available in future versions):
|
23
|
+
#
|
24
|
+
# class Player < ActiveRecord::Base
|
25
|
+
# extend ActsAsExplorable
|
26
|
+
# explorable do |config|
|
27
|
+
# config.filters = {
|
28
|
+
# in: [:first_name, :last_name, :position, :city, :club],
|
29
|
+
# sort: [:first_name, :last_name, :position, :city, :club, :created_at],
|
30
|
+
# position: ['GK', 'MF', 'FW']
|
31
|
+
# }
|
32
|
+
# end
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# @yield Provides access to the model class's config, which
|
36
|
+
# allows to customize types and filters
|
37
|
+
#
|
38
|
+
# @yieldparam config The model class's {ActsAsExplorable::Configuration config}.
|
39
|
+
def explorable(filters = {}, &_block)
|
40
|
+
class_eval do
|
41
|
+
def self.explorable?
|
42
|
+
true
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
if block_given?
|
47
|
+
ActsAsExplorable.setup { |config| yield config }
|
48
|
+
else
|
49
|
+
explorable_set_filters filters
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
protected
|
54
|
+
|
55
|
+
# Configure ActsAsExplorable's permitted filters per type in a model.
|
56
|
+
#
|
57
|
+
# class Person < ActiveRecord::Base
|
58
|
+
# extend ActsAsExplorable
|
59
|
+
# explorable_filters in: [:first_name, :last_name, :city],
|
60
|
+
# sort: [:first_name, :last_name, :city, :created_at]
|
61
|
+
# end
|
62
|
+
#
|
63
|
+
# @param [Hash] filters Filters for types
|
64
|
+
# @return [Array] Permitted filters
|
65
|
+
#
|
66
|
+
def explorable_set_filters(filters = {})
|
67
|
+
ActsAsExplorable.filters = filters if filters.present?
|
68
|
+
|
69
|
+
ActsAsExplorable.filters.each_pair do |f, _a|
|
70
|
+
ActsAsExplorable.filters[f].map!(&:downcase)
|
71
|
+
end
|
72
|
+
|
73
|
+
ActsAsExplorable.filters
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
#
|
2
|
+
# A String extension for ActsAsExplorable
|
3
|
+
#
|
4
|
+
class String
|
5
|
+
# Converts the String into a Hash for ActsAsExplorable.
|
6
|
+
#
|
7
|
+
# == Returns:
|
8
|
+
# A Hash providing 2 keys:
|
9
|
+
# - <tt>:values</tt> holds the search text values
|
10
|
+
# - <tt>:params</tt> holds parameters (fields) to search in
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# query = "Foo Bar in:name,body sort:created_at-asc"
|
14
|
+
# query.to_acts_as_explorable
|
15
|
+
# # => {:values=>["Foo", "Bar"], :params=>{:in=>["name", "body"], :sort=>["created_at-asc"]}}
|
16
|
+
#
|
17
|
+
# @param keys [Array<String, Symbol>, nil] Array of accepted keys
|
18
|
+
#
|
19
|
+
# @return [Hash] Converted query
|
20
|
+
def to_acts_as_explorable(*keys)
|
21
|
+
return nil if self.blank?
|
22
|
+
ActsAsExplorable::Parser.transform(self, *keys)
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module ActsAsExplorable
|
2
|
+
##
|
3
|
+
# Transforms query strings to a Hash that can be used by ActsAsExplorable.
|
4
|
+
#
|
5
|
+
# @example
|
6
|
+
# ActsAsExplorable::Parser.transform('Zlatan in:first_name')
|
7
|
+
# => { values: ["Zlatan"], params: { in: ["first_name"] } }
|
8
|
+
class Parser
|
9
|
+
attr_reader :values, :params, :props
|
10
|
+
|
11
|
+
#
|
12
|
+
# Returns a transformed query Hash using the given query string
|
13
|
+
# @param query_string [String] A query string
|
14
|
+
# @param keys [Array] An Array of transformation rules
|
15
|
+
#
|
16
|
+
# @return [Hash] Transformed query
|
17
|
+
def self.transform(query_string, *keys)
|
18
|
+
instance = new(query_string)
|
19
|
+
instance.parse(*keys)
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize(query_string)
|
23
|
+
@query_string = query_string
|
24
|
+
split_query_string
|
25
|
+
end
|
26
|
+
|
27
|
+
#
|
28
|
+
# Parses the query string
|
29
|
+
# @param keys [Hash] An Array of transformation rules
|
30
|
+
#
|
31
|
+
# @return [Hash] Transformed query
|
32
|
+
def parse(*keys)
|
33
|
+
split_query_string
|
34
|
+
|
35
|
+
@props.each do |p|
|
36
|
+
key, params = p.split(':').first.to_sym, p.split(':').last.split(',')
|
37
|
+
next if !keys.flatten.include?(key) && !keys.empty?
|
38
|
+
@params[key] ||= []
|
39
|
+
@params[key] = @params[key] | params.map(&:downcase)
|
40
|
+
end
|
41
|
+
|
42
|
+
{ values: @values, params: @params }
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def split_query_string
|
48
|
+
@values = []
|
49
|
+
@params = {}
|
50
|
+
@props = []
|
51
|
+
|
52
|
+
@query_string.split(/\s+/).each do |q|
|
53
|
+
if q =~ /\w+:[\w,-]+/
|
54
|
+
@props << q
|
55
|
+
else
|
56
|
+
@values << q
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module ActsAsExplorable
|
2
|
+
#
|
3
|
+
# Adds the search scope to a model
|
4
|
+
#
|
5
|
+
module Query
|
6
|
+
#
|
7
|
+
# Initiates a search with the given query string and returns an
|
8
|
+
# <tt>ActiveRecord::Relation</tt> scope object.
|
9
|
+
#
|
10
|
+
# The query string's *syntax* is described in {file:docs/yard/README.md#syntax the Readme File}
|
11
|
+
#
|
12
|
+
# The search method can be used just like a scope.
|
13
|
+
#
|
14
|
+
# Foo.search("Foo Bar in:name,body sort:created_at-asc").to_sql
|
15
|
+
# # => "SELECT foo.* FROM foo WHERE (foo.body ILIKE '%Foo%' OR foo.body ILIKE '%Bar%') ORDER BY foo.created_at ASC"
|
16
|
+
#
|
17
|
+
# It is also possible to put it in a scope chain like this:
|
18
|
+
#
|
19
|
+
# Foo.published.search("Foo Bar in:name,body sort:created_at-asc")
|
20
|
+
#
|
21
|
+
# @param [String] query_string A query string
|
22
|
+
# @return [ActiveRecord::Relation] Returns an <tt>ActiveRecord::Relation</tt> scope object
|
23
|
+
#
|
24
|
+
def search(query_string)
|
25
|
+
parts = ActsAsExplorable.filters.keys.map { |t| Element.build(t, query_string, self) }
|
26
|
+
|
27
|
+
result = all
|
28
|
+
|
29
|
+
parts.compact.each do |part|
|
30
|
+
result = part.execute(result)
|
31
|
+
end
|
32
|
+
|
33
|
+
result
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
require 'active_support/inflector'
|
3
|
+
require 'acts_as_explorable/version'
|
4
|
+
require 'acts_as_explorable/configuration'
|
5
|
+
require 'acts_as_explorable/parser'
|
6
|
+
require 'acts_as_explorable/ext/string'
|
7
|
+
require 'acts_as_explorable/element'
|
8
|
+
require 'acts_as_explorable/explorable'
|
9
|
+
require 'acts_as_explorable/query'
|
10
|
+
|
11
|
+
#
|
12
|
+
# ActsAsExplorable Plugin
|
13
|
+
#
|
14
|
+
# @author hiasinho
|
15
|
+
#
|
16
|
+
module ActsAsExplorable
|
17
|
+
def self.extended(base)
|
18
|
+
base.extend Query
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.method_missing(method_name, *args, &block)
|
22
|
+
if @configuration.respond_to?(method_name)
|
23
|
+
@configuration.send(method_name, *args, &block)
|
24
|
+
else
|
25
|
+
super
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.respond_to?(method_name, _include_private = false)
|
30
|
+
@configuration.respond_to? method_name
|
31
|
+
end
|
32
|
+
|
33
|
+
protected
|
34
|
+
|
35
|
+
def self.setup
|
36
|
+
@configuration ||= Configuration.new
|
37
|
+
yield @configuration if block_given?
|
38
|
+
end
|
39
|
+
|
40
|
+
setup
|
41
|
+
end
|
42
|
+
|
43
|
+
ActiveSupport.on_load(:active_record) do
|
44
|
+
extend ActsAsExplorable::Explorable
|
45
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
FactoryGirl.define do
|
2
|
+
factory :player, aliases: [:zlatan, :god_of_football] do
|
3
|
+
first_name 'Zlatan'
|
4
|
+
last_name 'Ibrahimovic'
|
5
|
+
position 'FW'
|
6
|
+
city 'Paris'
|
7
|
+
club 'PSG'
|
8
|
+
|
9
|
+
factory :manuel, aliases: [:goalkeeper] do
|
10
|
+
first_name 'Manuel'
|
11
|
+
last_name 'Neuer'
|
12
|
+
position 'GK'
|
13
|
+
city 'München'
|
14
|
+
club 'FC Bayern München'
|
15
|
+
end
|
16
|
+
|
17
|
+
factory :bastian, aliases: [:schweini] do
|
18
|
+
first_name 'Bastian'
|
19
|
+
last_name 'Schweinsteiger'
|
20
|
+
position 'MF'
|
21
|
+
city 'München'
|
22
|
+
club 'FC Bayern München'
|
23
|
+
end
|
24
|
+
|
25
|
+
factory :sascha do
|
26
|
+
first_name 'Sascha'
|
27
|
+
last_name 'Mölders'
|
28
|
+
position 'FW'
|
29
|
+
city 'Augsburg'
|
30
|
+
club 'FC Augsburg'
|
31
|
+
end
|
32
|
+
|
33
|
+
factory :christiano, aliases: [:forward] do
|
34
|
+
first_name 'Christiano'
|
35
|
+
last_name 'Ronaldo'
|
36
|
+
position 'FW'
|
37
|
+
city 'Madrid'
|
38
|
+
club 'Real Madrid'
|
39
|
+
end
|
40
|
+
|
41
|
+
factory :toni do
|
42
|
+
first_name 'Toni'
|
43
|
+
last_name 'Kroos'
|
44
|
+
position 'MF'
|
45
|
+
city 'Madrid'
|
46
|
+
club 'Real Madrid'
|
47
|
+
end
|
48
|
+
|
49
|
+
factory :fernando do
|
50
|
+
first_name 'Fernando'
|
51
|
+
last_name 'Torres'
|
52
|
+
position 'FW'
|
53
|
+
city 'Madrid'
|
54
|
+
club 'Athletico Madrid'
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ActsAsExplorable::Element::DynamicFilter do
|
4
|
+
|
5
|
+
subject { ActsAsExplorable::Element::DynamicFilter }
|
6
|
+
let (:element) { ActsAsExplorable::Element }
|
7
|
+
|
8
|
+
it 'should return a dynamic filter element' do
|
9
|
+
expect(element.build(:position, 'position:gk,mf,fw', Player)).to be_instance_of(subject)
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should filter by position' do
|
13
|
+
create(:goalkeeper)
|
14
|
+
create(:forward)
|
15
|
+
|
16
|
+
expect(Player.search('position:gk').count).to eq(1)
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ActsAsExplorable::Element::In do
|
4
|
+
|
5
|
+
subject { ActsAsExplorable::Element::In }
|
6
|
+
let (:element) { ActsAsExplorable::Element }
|
7
|
+
|
8
|
+
it 'should return a dynamic filter element' do
|
9
|
+
expect(element.build(:in, 'test in:first_name', Player)).to be_instance_of(subject)
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ActsAsExplorable::Element do
|
4
|
+
|
5
|
+
subject { ActsAsExplorable::Element }
|
6
|
+
|
7
|
+
describe '.build' do
|
8
|
+
it 'should be callable' do
|
9
|
+
expect(subject).to respond_to(:build)
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should create an element' do
|
13
|
+
expect(ActsAsExplorable::Element.build(:position, 'position:gk', Player)).to be_instance_of(ActsAsExplorable::Element::DynamicFilter)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
def create_player(klass = Player, factory: :player)
|
4
|
+
klass.create(attributes_for(factory))
|
5
|
+
end
|
6
|
+
|
7
|
+
describe ActsAsExplorable::Explorable do
|
8
|
+
|
9
|
+
let(:zlatan) { create(:zlatan) }
|
10
|
+
let(:manuel) { create(:manuel) }
|
11
|
+
let(:bastian) { create(:bastian) }
|
12
|
+
let(:christiano) { create(:christiano) }
|
13
|
+
let(:toni) { create(:toni) }
|
14
|
+
let(:fernando) { create(:fernando) }
|
15
|
+
|
16
|
+
it 'should not be explorable' do
|
17
|
+
expect(NotExplorable).not_to be_explorable
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'should be explorable' do
|
21
|
+
expect(Explorable).to be_explorable
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should be customizable through a block' do
|
25
|
+
skip 'TODO: This will be available in future versions'
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ActsAsExplorable::Parser do
|
4
|
+
|
5
|
+
subject { ActsAsExplorable::Parser }
|
6
|
+
|
7
|
+
describe '#new' do
|
8
|
+
it 'should be called with a query string' do
|
9
|
+
expect { subject.new }.to raise_error
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe '.transform' do
|
14
|
+
it 'should respond with a hash' do
|
15
|
+
expect(subject.transform('Zlatan in:first_name')).to be_a(Hash)
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'should have a result with :values and :params keys' do
|
19
|
+
expect(subject.transform('Zlatan in:first_name'))
|
20
|
+
.to have_key(:values)
|
21
|
+
.and have_key(:params)
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should not have a result with a key :props' do
|
25
|
+
expect(subject.transform('Zlatan in:first_name'))
|
26
|
+
.not_to have_key(:props)
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should transform the string to a hash' do
|
30
|
+
expect(subject.transform('Zlatan in:first_name'))
|
31
|
+
.to eq(values: ['Zlatan'], params: { in: ['first_name'] })
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ActsAsExplorable::Query do
|
4
|
+
|
5
|
+
let(:zlatan) { create(:zlatan) }
|
6
|
+
let(:manuel) { create(:manuel) }
|
7
|
+
let(:bastian) { create(:bastian) }
|
8
|
+
let(:christiano) { create(:christiano) }
|
9
|
+
let(:toni) { create(:toni) }
|
10
|
+
let(:fernando) { create(:fernando) }
|
11
|
+
|
12
|
+
context 'in:' do
|
13
|
+
it '`first_name` should find by first name' do
|
14
|
+
[zlatan, christiano]
|
15
|
+
expect(Player.search('Zlatan in:first_name').count).to eq(1)
|
16
|
+
end
|
17
|
+
|
18
|
+
it '`club` should find by club' do
|
19
|
+
[zlatan, manuel, bastian]
|
20
|
+
expect(Player.search('Bayern in:club').count).to eq(2)
|
21
|
+
end
|
22
|
+
|
23
|
+
it '`city` should find by city' do
|
24
|
+
[zlatan, bastian, christiano, toni, fernando]
|
25
|
+
expect(Player.search('Madrid in:city').count).to eq(3)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'sort:' do
|
30
|
+
before(:each) do
|
31
|
+
[christiano, zlatan, bastian]
|
32
|
+
end
|
33
|
+
|
34
|
+
it '`first_name` should order descending by first name' do
|
35
|
+
expect(Player.search('sort:first_name').first).to eq(zlatan)
|
36
|
+
end
|
37
|
+
|
38
|
+
it '`first_name-desc` should order descending by first name' do
|
39
|
+
expect(Player.search('sort:first_name-desc').first).to eq(zlatan)
|
40
|
+
end
|
41
|
+
|
42
|
+
it '`first_name-asc` should order ascending by first name' do
|
43
|
+
expect(Player.search('sort:first_name-asc').last).to eq(zlatan)
|
44
|
+
end
|
45
|
+
|
46
|
+
it '`position-desc,first_name-asc` should order by position then by first name' do
|
47
|
+
expect(Player.search('sort:position-desc,first_name-asc').first).to eq(bastian)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
begin
|
2
|
+
require 'byebug'
|
3
|
+
rescue LoadError
|
4
|
+
end
|
5
|
+
$LOAD_PATH << '.' unless $LOAD_PATH.include?('.')
|
6
|
+
$LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__))
|
7
|
+
require 'logger'
|
8
|
+
|
9
|
+
require File.expand_path('../../lib/acts_as_explorable', __FILE__)
|
10
|
+
I18n.enforce_available_locales = true
|
11
|
+
require 'rails'
|
12
|
+
require 'rspec/its'
|
13
|
+
require 'sqlite3'
|
14
|
+
require 'factory_girl'
|
15
|
+
require 'database_cleaner'
|
16
|
+
|
17
|
+
Dir['./spec/support/**/*.rb'].sort.each { |f| require f }
|
18
|
+
|
19
|
+
RSpec.configure do |config|
|
20
|
+
config.include FactoryGirl::Syntax::Methods
|
21
|
+
config.raise_errors_for_deprecations!
|
22
|
+
end
|
23
|
+
|
24
|
+
FactoryGirl.find_definitions
|
@@ -0,0 +1,64 @@
|
|
1
|
+
ActiveRecord::Migration.verbose = false
|
2
|
+
ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
|
3
|
+
ActiveRecord::Base.logger = Logger.new(File.join(File.dirname(__FILE__), '../debug.log'))
|
4
|
+
ActiveRecord::Base.logger.level = ENV['TRAVIS'] ? ::Logger::ERROR : ::Logger::DEBUG
|
5
|
+
|
6
|
+
ActiveRecord::Schema.define(version: 1) do
|
7
|
+
create_table :players do |t|
|
8
|
+
t.string :first_name
|
9
|
+
t.string :last_name
|
10
|
+
t.string :position, limit: 2
|
11
|
+
t.string :city
|
12
|
+
t.string :club
|
13
|
+
|
14
|
+
t.timestamps null: false
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class ActsAsExplorable::TestModelBase < ActiveRecord::Base
|
19
|
+
self.table_name = :players
|
20
|
+
end
|
21
|
+
|
22
|
+
class Player < ActsAsExplorable::TestModelBase
|
23
|
+
extend ActsAsExplorable
|
24
|
+
explorable in: [:first_name, :last_name, :position, :city, :club],
|
25
|
+
sort: [:first_name, :last_name, :position, :city, :club, :created_at],
|
26
|
+
position: %w(GK MF FW)
|
27
|
+
end
|
28
|
+
|
29
|
+
class ArgumentsPlayer < ActsAsExplorable::TestModelBase
|
30
|
+
extend ActsAsExplorable
|
31
|
+
explorable in: [:first_name, :last_name, :position, :city, :club],
|
32
|
+
sort: [:first_name, :last_name, :position, :city, :club, :created_at],
|
33
|
+
position: %w(GK MF FW)
|
34
|
+
end
|
35
|
+
|
36
|
+
class BlockPlayer < ActsAsExplorable::TestModelBase
|
37
|
+
extend ActsAsExplorable
|
38
|
+
explorable do |config|
|
39
|
+
config.filters = {
|
40
|
+
in: [:first_name, :last_name, :position, :city, :club],
|
41
|
+
sort: [:first_name, :last_name, :position, :city, :club, :created_at],
|
42
|
+
position: %w(GK MF FW)
|
43
|
+
}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class BlockPlayer < ActsAsExplorable::TestModelBase
|
48
|
+
extend ActsAsExplorable
|
49
|
+
explorable in: [:first_name, :last_name, :position, :city, :club],
|
50
|
+
sort: [:first_name, :last_name, :position, :city, :club, :created_at],
|
51
|
+
position: %w(GK MF FW)
|
52
|
+
|
53
|
+
explorable do |_config|
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class Explorable < ActsAsExplorable::TestModelBase
|
59
|
+
extend ActsAsExplorable
|
60
|
+
explorable
|
61
|
+
end
|
62
|
+
|
63
|
+
class NotExplorable < ActsAsExplorable::TestModelBase
|
64
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
RSpec.configure do |config|
|
2
|
+
|
3
|
+
config.before(:suite) do
|
4
|
+
DatabaseCleaner.clean_with(:truncation)
|
5
|
+
DatabaseCleaner.strategy = :transaction
|
6
|
+
DatabaseCleaner.clean
|
7
|
+
end
|
8
|
+
|
9
|
+
config.after(:suite) do
|
10
|
+
DatabaseCleaner.clean
|
11
|
+
end
|
12
|
+
|
13
|
+
config.before(:each) do
|
14
|
+
DatabaseCleaner.start
|
15
|
+
end
|
16
|
+
|
17
|
+
config.after(:each) do
|
18
|
+
DatabaseCleaner.clean
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
metadata
ADDED
@@ -0,0 +1,185 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: acts_as_explorable
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mathias Schneider
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-01-17 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activerecord
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '4.0'
|
20
|
+
- - "<"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '5'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '4.0'
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '5'
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: sqlite3
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
40
|
+
type: :development
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rspec-rails
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: rspec-its
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '0'
|
68
|
+
type: :development
|
69
|
+
prerelease: false
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: rspec
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
82
|
+
type: :development
|
83
|
+
prerelease: false
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
- !ruby/object:Gem::Dependency
|
90
|
+
name: factory_girl_rails
|
91
|
+
requirement: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - "~>"
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '4.0'
|
96
|
+
type: :development
|
97
|
+
prerelease: false
|
98
|
+
version_requirements: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - "~>"
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '4.0'
|
103
|
+
- !ruby/object:Gem::Dependency
|
104
|
+
name: database_cleaner
|
105
|
+
requirement: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - ">="
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
type: :development
|
111
|
+
prerelease: false
|
112
|
+
version_requirements: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '0'
|
117
|
+
description: Adds GitHub-like search function to your models
|
118
|
+
email:
|
119
|
+
- mathias@hiasinho.com
|
120
|
+
executables: []
|
121
|
+
extensions: []
|
122
|
+
extra_rdoc_files: []
|
123
|
+
files:
|
124
|
+
- ".gitignore"
|
125
|
+
- ".rspec"
|
126
|
+
- ".travis.yml"
|
127
|
+
- Appraisals
|
128
|
+
- Gemfile
|
129
|
+
- Guardfile
|
130
|
+
- MIT-LICENSE
|
131
|
+
- README.md
|
132
|
+
- Rakefile
|
133
|
+
- acts_as_explorable.gemspec
|
134
|
+
- gemfiles/activerecord_4.0.gemfile
|
135
|
+
- gemfiles/activerecord_4.1.gemfile
|
136
|
+
- gemfiles/activerecord_4.2.gemfile
|
137
|
+
- lib/acts_as_explorable.rb
|
138
|
+
- lib/acts_as_explorable/configuration.rb
|
139
|
+
- lib/acts_as_explorable/element.rb
|
140
|
+
- lib/acts_as_explorable/element/base.rb
|
141
|
+
- lib/acts_as_explorable/element/dynamic_filter.rb
|
142
|
+
- lib/acts_as_explorable/element/in.rb
|
143
|
+
- lib/acts_as_explorable/element/sort.rb
|
144
|
+
- lib/acts_as_explorable/explorable.rb
|
145
|
+
- lib/acts_as_explorable/ext/string.rb
|
146
|
+
- lib/acts_as_explorable/parser.rb
|
147
|
+
- lib/acts_as_explorable/query.rb
|
148
|
+
- lib/acts_as_explorable/version.rb
|
149
|
+
- lib/tasks/acts_as_explorable_tasks.rake
|
150
|
+
- spec/factories/players.rb
|
151
|
+
- spec/lib/acts_as_explorable/element/dynamic_filter_spec.rb
|
152
|
+
- spec/lib/acts_as_explorable/element/in.rb
|
153
|
+
- spec/lib/acts_as_explorable/element_spec.rb
|
154
|
+
- spec/lib/acts_as_explorable/explorable_spec.rb
|
155
|
+
- spec/lib/acts_as_explorable/parser_spec.rb
|
156
|
+
- spec/lib/acts_as_explorable/query_spec.rb
|
157
|
+
- spec/spec_helper.rb
|
158
|
+
- spec/support/database.rb
|
159
|
+
- spec/support/database_cleaner.rb
|
160
|
+
homepage: https://github.com/hiasinho/acts_as_explorable
|
161
|
+
licenses:
|
162
|
+
- MIT
|
163
|
+
metadata: {}
|
164
|
+
post_install_message:
|
165
|
+
rdoc_options: []
|
166
|
+
require_paths:
|
167
|
+
- lib
|
168
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
169
|
+
requirements:
|
170
|
+
- - ">="
|
171
|
+
- !ruby/object:Gem::Version
|
172
|
+
version: 2.0.0
|
173
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
174
|
+
requirements:
|
175
|
+
- - ">="
|
176
|
+
- !ruby/object:Gem::Version
|
177
|
+
version: '0'
|
178
|
+
requirements: []
|
179
|
+
rubyforge_project:
|
180
|
+
rubygems_version: 2.2.2
|
181
|
+
signing_key:
|
182
|
+
specification_version: 4
|
183
|
+
summary: Adds GitHub-like search function to your models
|
184
|
+
test_files: []
|
185
|
+
has_rdoc:
|