scopable 1.1.4 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.codeclimate.yml +1 -1
- data/.rubocop.yml +2 -11
- data/.travis.yml +17 -2
- data/Gemfile +0 -4
- data/LICENSE.md +9 -0
- data/README.md +66 -68
- data/Rakefile +4 -3
- data/lib/scopable.rb +61 -61
- data/lib/scopable/version.rb +2 -2
- data/scopable.gemspec +7 -6
- data/test/scopable_test.rb +139 -0
- data/test/test_helper.rb +34 -0
- metadata +37 -27
- data/.rspec +0 -2
- data/LICENSE +0 -22
- data/spec/scopable_spec.rb +0 -302
- data/spec/spec_helper.rb +0 -99
- data/spec/support/controller.rb +0 -19
- data/spec/support/model.rb +0 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cfe1737f95ddf82cad3f7e18880a932269e8c024
|
4
|
+
data.tar.gz: 7c72a34e7fc98dc5c18a9203a0f58162eb408d16
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ce1539106fde92d8387bcebe041dcaea5e4aecb4400bce5c4c236ff44759c12634083098e047aa938c8963a7ba74fafa51c77f9839dfd3b24180b8efa853b2e4
|
7
|
+
data.tar.gz: 17c57c0f4a9b4a66d814163f525474690fae38e41d737171450da1bb0d0addc234f1afe84ccc101eb9439a0f3fdf116c2b3929113dfd632c2a5e31aba527d250
|
data/.codeclimate.yml
CHANGED
data/.rubocop.yml
CHANGED
@@ -229,6 +229,7 @@ Metrics/CyclomaticComplexity:
|
|
229
229
|
A complexity metric that is strongly correlated to the number
|
230
230
|
of test cases needed to validate a method.
|
231
231
|
Enabled: true
|
232
|
+
Max: 18
|
232
233
|
|
233
234
|
Metrics/LineLength:
|
234
235
|
Description: 'Limit lines to 80 characters.'
|
@@ -528,7 +529,7 @@ Style/DefWithParentheses:
|
|
528
529
|
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#method-parens'
|
529
530
|
Enabled: false
|
530
531
|
|
531
|
-
Style/
|
532
|
+
Style/PreferredHashMethods:
|
532
533
|
Description: 'Checks for use of deprecated Hash methods.'
|
533
534
|
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#hash-key'
|
534
535
|
Enabled: false
|
@@ -1154,13 +1155,3 @@ Style/WordArray:
|
|
1154
1155
|
Description: 'Use %w or %W for arrays of words.'
|
1155
1156
|
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-w'
|
1156
1157
|
Enabled: false
|
1157
|
-
Style/Documentation:
|
1158
|
-
Enabled: no
|
1159
|
-
Metrics/LineLength:
|
1160
|
-
Max: 128
|
1161
|
-
Metrics/MethodLength:
|
1162
|
-
Max: 36
|
1163
|
-
Style/Lambda:
|
1164
|
-
EnforcedStyle: literal
|
1165
|
-
Style/EmptyCaseCondition:
|
1166
|
-
Enabled: false
|
data/.travis.yml
CHANGED
@@ -1,4 +1,19 @@
|
|
1
|
-
|
1
|
+
env:
|
2
|
+
global:
|
3
|
+
- CC_TEST_REPORTER_ID=f8045f650e554707d513f10ef3ba1ed386d711797ffaf44a8610d96f7de7b1da
|
4
|
+
- GIT_COMMITTED_AT=$(if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then git log -1 --pretty=format:%ct; else git log -1 --skip 1 --pretty=format:%ct; fi)
|
2
5
|
language: ruby
|
3
6
|
rvm:
|
4
|
-
- 2.
|
7
|
+
- 2.4.1
|
8
|
+
before_script:
|
9
|
+
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
|
10
|
+
- chmod +x ./cc-test-reporter
|
11
|
+
script:
|
12
|
+
- bundle exec rake
|
13
|
+
# Preferably you will run test-reporter on branch update events. But
|
14
|
+
# if you setup travis to build PR updates only, you don't need to run
|
15
|
+
# the line below
|
16
|
+
- if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT; fi
|
17
|
+
# In the case where travis is setup to build PR updates only,
|
18
|
+
# uncomment the line below
|
19
|
+
# - ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
|
data/Gemfile
CHANGED
data/LICENSE.md
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
# The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright © 2013 Arthur Corenzan
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
6
|
+
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
8
|
+
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
CHANGED
@@ -1,126 +1,124 @@
|
|
1
|
-
[![RubyGems](https://img.shields.io/gem/dt/scopable.svg?style=flat
|
2
|
-
[![Build](https://img.shields.io/travis/corenzan/scopable.svg?style=flat
|
3
|
-
[![Code Climate](https://img.shields.io/codeclimate/github/corenzan/scopable.svg?style=flat
|
4
|
-
[![Test Coverage](https://img.shields.io/codeclimate/coverage/github/corenzan/scopable.svg?style=flat
|
1
|
+
[![RubyGems](https://img.shields.io/gem/dt/scopable.svg?style=flat)](https://rubygems.org/gems/scopable)
|
2
|
+
[![Build](https://img.shields.io/travis/corenzan/scopable.svg?style=flat)](https://travis-ci.org/corenzan/scopable)
|
3
|
+
[![Code Climate](https://img.shields.io/codeclimate/github/corenzan/scopable.svg?style=flat)](https://codeclimate.com/github/corenzan/scopable)
|
4
|
+
[![Test Coverage](https://img.shields.io/codeclimate/coverage/github/corenzan/scopable.svg?style=flat)](https://codeclimate.com/github/corenzan/scopable/coverage)
|
5
5
|
|
6
6
|
# Scopable
|
7
7
|
|
8
|
-
>
|
8
|
+
> Easy parametric query building in Rails.
|
9
9
|
|
10
10
|
## Installation
|
11
11
|
|
12
|
-
|
12
|
+
Simply add it to your Gemfile.
|
13
13
|
|
14
14
|
```ruby
|
15
15
|
gem 'scopable'
|
16
16
|
```
|
17
17
|
|
18
|
-
And
|
18
|
+
And update your Gems.
|
19
19
|
|
20
20
|
```shell
|
21
|
-
$ bundle
|
21
|
+
$ bundle update
|
22
22
|
```
|
23
23
|
|
24
|
-
|
24
|
+
Please note that as of version 2.0 **the API has drastically changed**. If you're **already using version 1.x** and don't want to update your application right now, you should stick with it:
|
25
25
|
|
26
|
-
```
|
27
|
-
|
26
|
+
```ruby
|
27
|
+
gem 'scopable', '~> 1.0'
|
28
28
|
```
|
29
29
|
|
30
|
-
##
|
30
|
+
## About
|
31
31
|
|
32
|
-
|
32
|
+
**Scopable** is useful when you need to build one or more queries based on incoming parameters in the request. Very much like [has_scope](https://github.com/plataformatec/has_scope) except it's decoupled from the controller, making it easier to test and much more flexible.
|
33
33
|
|
34
|
-
|
35
|
-
class PostsController < ApplicationController
|
36
|
-
include Scopable
|
34
|
+
### Example
|
37
35
|
|
38
|
-
|
36
|
+
Say you have a model and a controller for your blog. Something like this:
|
39
37
|
|
40
|
-
|
41
|
-
|
38
|
+
```
|
39
|
+
└── app
|
40
|
+
├── models
|
41
|
+
└── post.rb
|
42
|
+
└── controllers
|
43
|
+
└── posts_controller.rb
|
42
44
|
```
|
43
45
|
|
44
|
-
|
46
|
+
First let's create a new directory named `scopes` along with `models` and `controllers`. There you create a file called `post_scope.rb`, and inside it you define a class that inherits from `Scopable`.
|
45
47
|
|
46
48
|
```ruby
|
47
|
-
class
|
48
|
-
|
49
|
-
|
50
|
-
scope :search, param: :q
|
49
|
+
class PostScope < Scopable
|
50
|
+
model Post
|
51
51
|
|
52
|
-
|
53
|
-
|
52
|
+
scope :search do
|
53
|
+
where('title LIKE ?', value)
|
54
54
|
end
|
55
|
-
end
|
56
|
-
```
|
57
|
-
|
58
|
-
Now whenever the parameter `q` is present in `params`, the scope `#search` will be called on your model and given the value of `params[:q]` as argument. Otherwise you would have to write something like this:
|
59
55
|
|
60
|
-
|
61
|
-
|
62
|
-
@posts = Post.search(params[:q])
|
63
|
-
else
|
64
|
-
@posts = Post.all
|
56
|
+
scope :published_on do
|
57
|
+
where(published_at: value.to_time)
|
65
58
|
end
|
59
|
+
end
|
66
60
|
```
|
67
61
|
|
68
|
-
|
62
|
+
Finally, in `PostsController` you use `PostScope.resolve` to conditionally resolve the scopes based on given parameters.
|
69
63
|
|
70
64
|
```ruby
|
71
|
-
class
|
72
|
-
|
73
|
-
|
74
|
-
# Filter by category.
|
75
|
-
scope :category do |relation, value|
|
76
|
-
relation.where(category_id: value.to_i)
|
65
|
+
class PostsController < ApplicationController
|
66
|
+
def index
|
67
|
+
@posts = PostScope.resolve(scope_params)
|
77
68
|
end
|
78
69
|
|
79
|
-
|
80
|
-
scope :includes, force: :author
|
81
|
-
|
82
|
-
# Pagination.
|
83
|
-
scope :page, default: 1
|
84
|
-
|
85
|
-
# Sort by creation date.
|
86
|
-
scope :order, force: { created_at: :desc }
|
70
|
+
private
|
87
71
|
|
88
|
-
def
|
89
|
-
|
72
|
+
def scope_params
|
73
|
+
params.permit(:search, :published_on).to_h
|
90
74
|
end
|
91
75
|
end
|
92
76
|
```
|
93
77
|
|
94
|
-
Now
|
78
|
+
Now when any combination of the parameters `search` and `published_on` are present, their respective conditions are going to be applied on the relation. i.e. If your request path looks like this:
|
95
79
|
|
96
80
|
```
|
97
|
-
|
81
|
+
/?search=bananas&published_on=2007-07-19
|
98
82
|
```
|
99
83
|
|
100
|
-
|
84
|
+
`@posts` will look like this:
|
101
85
|
|
102
|
-
```
|
103
|
-
Post.where(
|
86
|
+
```
|
87
|
+
Post.where('title LIKE ?', 'bananas').where(published_on: '2007-07-19 00:00:00 +0000')
|
104
88
|
```
|
105
89
|
|
106
|
-
|
107
|
-
|
108
|
-
Also values like `true/false`, `on/off`, `yes/no` are **cast as boolean**, and when given a boolean value the scope is either called with no arguments or skipped entirely. For instance, if you set a scope like `scope :draft` then request the URL `/posts?draft=yes` it would be like just calling `Post.draft`. But if you request `/posts?draft=no` it does nothing.
|
109
|
-
|
110
|
-
### Options
|
90
|
+
You can also pass some options when you're defining scopes for more advanced use cases. Read on.
|
111
91
|
|
112
|
-
|
92
|
+
#### Options
|
113
93
|
|
114
94
|
Key | Description
|
115
95
|
------------|--------------------------------------------------------------------------------------------------------------
|
116
96
|
`:param` | Name of the parameter that activates the scope.
|
117
97
|
`:default` | Default value for the scope in case the parameter is missing.
|
118
|
-
`:
|
98
|
+
`:value` | Force a value to the scope regardless of the request parameters.
|
119
99
|
`:required` | Calls `#none` on the model if parameter is absent (blank or nil) and there's no default value set.
|
120
|
-
`:
|
121
|
-
`:
|
122
|
-
`&block` | Block will be
|
100
|
+
`:if` | ...
|
101
|
+
`:unless` | ...
|
102
|
+
`&block` | Block will be used to produce the resulting relation with two parameters: the relation at this step and the scope value from params.
|
103
|
+
|
104
|
+
## Collaboration
|
105
|
+
|
106
|
+
If you'd like to contribute to the project, in any form, you're most welcome, but please bear in mind that as it is with any open-source software your suggestions are subject to the discretion of the project maintainers, and what you see as an issue, someone else may see as a feature.
|
107
|
+
|
108
|
+
We encourage you to:
|
109
|
+
|
110
|
+
- Open a new issue with suggestions, concerns, or problems you may have.
|
111
|
+
- Chip in existing discussions and present your opinion on the subject.
|
112
|
+
- Send pull-requests with test covered bug fixes or new features.
|
113
|
+
- Send pull-requests with new tests you miss in the suite.
|
114
|
+
|
115
|
+
Remember to:
|
116
|
+
|
117
|
+
- Be respectful.
|
118
|
+
- Use proper grammar in discussions and in commit messages.
|
119
|
+
- Follow the established coding style.
|
120
|
+
- Explain why you're making the changes in a pull-request.
|
123
121
|
|
124
122
|
## License
|
125
123
|
|
126
|
-
|
124
|
+
[The MIT License](LICENSE.md) © 2013 Arthur Corenzan
|
data/Rakefile
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
task :test do
|
2
|
+
require_relative 'test/test_helper.rb'
|
3
|
+
end
|
4
|
+
task default: :test
|
data/lib/scopable.rb
CHANGED
@@ -1,56 +1,50 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
require 'active_support'
|
2
|
+
require 'active_support/core_ext'
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
end
|
7
|
-
|
8
|
-
def scoped(model, params)
|
9
|
-
scopes.reduce(model) do |relation, scope|
|
10
|
-
name, options = *scope
|
11
|
-
|
12
|
-
# Controller actions where this scope should be applied.
|
13
|
-
# Accepts either a literal value or a lambda. nil with disable the option.
|
14
|
-
only = options[:only]
|
15
|
-
only = instance_exec(&only) if only.respond_to?(:call)
|
16
|
-
|
17
|
-
# Enfore :only option.
|
18
|
-
next relation unless only.nil? || Array.wrap(only).map(&:to_s).include?(action_name)
|
4
|
+
class Scopable
|
5
|
+
attr_reader :model, :scopes
|
19
6
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
# Enfore :except option.
|
26
|
-
next relation if except.present? && Array.wrap(except).map(&:to_s).include?(action_name)
|
7
|
+
def initialize(model = nil, scopes = nil)
|
8
|
+
@model = model || self.class.model
|
9
|
+
@scopes = scopes || self.class.scopes
|
10
|
+
end
|
27
11
|
|
28
|
-
|
29
|
-
|
30
|
-
|
12
|
+
def delegator(relation, value, params)
|
13
|
+
SimpleDelegator.new(relation).tap do |delegator|
|
14
|
+
delegator.define_singleton_method(:value) do
|
15
|
+
value
|
16
|
+
end
|
17
|
+
delegator.define_singleton_method(:params) do
|
18
|
+
params
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
31
22
|
|
32
|
-
|
33
|
-
|
23
|
+
def resolve(params = {})
|
24
|
+
params = params.with_indifferent_access
|
34
25
|
|
35
|
-
|
36
|
-
|
37
|
-
value = options[:default] if value.nil?
|
26
|
+
scopes.reduce(model) do |relation, scope|
|
27
|
+
name, options = *scope
|
38
28
|
|
39
|
-
#
|
40
|
-
|
41
|
-
value = options[:force] if options.key?(:force)
|
29
|
+
# Resolve param name.
|
30
|
+
param = options[:param] || name
|
42
31
|
|
43
|
-
#
|
44
|
-
value =
|
32
|
+
# Resolve a value for the scope.
|
33
|
+
value = options[:value] || params[param] || options[:default]
|
45
34
|
|
46
|
-
#
|
47
|
-
|
48
|
-
required = instance_exec(&required) if required.respond_to?(:call)
|
35
|
+
# When value is empty treat it as nil.
|
36
|
+
value = nil if value.respond_to?(:empty?) && value.empty?
|
49
37
|
|
50
|
-
#
|
51
|
-
|
38
|
+
# When a nil value was given either skip the scope or bail with #none (if the required options was used).
|
39
|
+
if value.nil?
|
40
|
+
if options[:required]
|
41
|
+
break relation.none
|
42
|
+
else
|
43
|
+
next relation
|
44
|
+
end
|
45
|
+
end
|
52
46
|
|
53
|
-
#
|
47
|
+
# Cast boolean-like strings.
|
54
48
|
case value.to_s
|
55
49
|
when /\A(false|no|off)\z/
|
56
50
|
value = false
|
@@ -58,20 +52,20 @@ module Scopable
|
|
58
52
|
value = true
|
59
53
|
end
|
60
54
|
|
61
|
-
#
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
block = options[:block]
|
55
|
+
# Enforce 'if' option.
|
56
|
+
if options[:if]
|
57
|
+
next relation unless delegator(relation, value, params).instance_exec(&options[:if])
|
58
|
+
end
|
66
59
|
|
67
|
-
|
68
|
-
|
60
|
+
# Enforce 'unless' option.
|
61
|
+
if options[:unless]
|
62
|
+
next relation if delegator(relation, value, params).instance_exec(&options[:unless])
|
69
63
|
end
|
70
64
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
65
|
+
# When a block is present, use that, otherwise call the scope method.
|
66
|
+
if options[:block].present?
|
67
|
+
delegator(relation, value, params).instance_exec(&options[:block])
|
68
|
+
elsif value == true
|
75
69
|
relation.send(name)
|
76
70
|
else
|
77
71
|
relation.send(name, value)
|
@@ -79,13 +73,19 @@ module Scopable
|
|
79
73
|
end
|
80
74
|
end
|
81
75
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
end
|
76
|
+
def self.resolve(params = {})
|
77
|
+
new.resolve(params)
|
78
|
+
end
|
86
79
|
|
87
|
-
|
88
|
-
|
89
|
-
|
80
|
+
def self.model(model = nil)
|
81
|
+
@model ||= model
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.scopes
|
85
|
+
@scopes ||= {}
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.scope(name, options = {}, &block)
|
89
|
+
scopes.store name, options.merge(block: block)
|
90
90
|
end
|
91
91
|
end
|
data/lib/scopable/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
|
2
|
-
VERSION =
|
1
|
+
class Scopable
|
2
|
+
VERSION = '2.0.0'.freeze
|
3
3
|
end
|
data/scopable.gemspec
CHANGED
@@ -8,8 +8,8 @@ Gem::Specification.new do |spec|
|
|
8
8
|
spec.version = Scopable::VERSION
|
9
9
|
spec.authors = ['Arthur Corenzan']
|
10
10
|
spec.email = ['arthur@corenzan.com']
|
11
|
-
spec.summary =
|
12
|
-
spec.description =
|
11
|
+
spec.summary = 'Easy parametric query building in Rails.'
|
12
|
+
spec.description = 'Scopable allows you to create objects that produce complex queries based on incoming parameters hash which are testable and easy to understand.'
|
13
13
|
spec.homepage = 'https://github.com/corenzan/scopable'
|
14
14
|
spec.license = 'MIT'
|
15
15
|
|
@@ -20,9 +20,10 @@ Gem::Specification.new do |spec|
|
|
20
20
|
|
21
21
|
spec.required_ruby_version = '>= 2.2.2'
|
22
22
|
|
23
|
-
spec.add_runtime_dependency 'activesupport', '
|
23
|
+
spec.add_runtime_dependency 'activesupport', '~> 5.0'
|
24
24
|
|
25
|
-
spec.add_development_dependency 'bundler', '~> 1.
|
26
|
-
spec.add_development_dependency '
|
27
|
-
spec.add_development_dependency '
|
25
|
+
spec.add_development_dependency 'bundler', '~> 1.15'
|
26
|
+
spec.add_development_dependency 'minitest', '~> 5.10'
|
27
|
+
spec.add_development_dependency 'rubocop', '~> 0.49'
|
28
|
+
spec.add_development_dependency 'simplecov', '~> 0.14'
|
28
29
|
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
class ScopableTest < Minitest::Test
|
2
|
+
extend ActiveSupport::Testing::Declarative
|
3
|
+
|
4
|
+
def assert_scope(scopable, scope, value)
|
5
|
+
assert_includes(scopable.scopes, scope)
|
6
|
+
assert_equal(value, scopable.scopes[scope])
|
7
|
+
end
|
8
|
+
|
9
|
+
def refute_scope(scopable, scope)
|
10
|
+
refute_includes(scopable.scopes, scope)
|
11
|
+
assert_nil(scopable.scopes[scope])
|
12
|
+
end
|
13
|
+
|
14
|
+
test 'initialize' do
|
15
|
+
model = Model.new
|
16
|
+
scopable = Scopable.new(model, fuzzy: {}, jumbo: {})
|
17
|
+
assert_equal(model, scopable.model)
|
18
|
+
assert_includes(scopable.scopes, :fuzzy)
|
19
|
+
assert_includes(scopable.scopes, :jumbo)
|
20
|
+
end
|
21
|
+
|
22
|
+
test 'dsl' do
|
23
|
+
model = Model.new
|
24
|
+
scopable = Class.new(Scopable) do
|
25
|
+
model model
|
26
|
+
scope :fuzzy
|
27
|
+
scope :jumbo
|
28
|
+
end
|
29
|
+
assert_equal(model, scopable.new.model)
|
30
|
+
assert_includes(scopable.new.scopes, :fuzzy)
|
31
|
+
assert_includes(scopable.new.scopes, :jumbo)
|
32
|
+
end
|
33
|
+
|
34
|
+
test 'one scope, matching parameter' do
|
35
|
+
model = Model.new
|
36
|
+
scopable = Scopable.new(model, fuzzy: {})
|
37
|
+
params = { fuzzy: 'fuzzy' }
|
38
|
+
assert_scope(scopable.resolve(params), :fuzzy, 'fuzzy')
|
39
|
+
end
|
40
|
+
|
41
|
+
test 'multiple scopes, matching parameters' do
|
42
|
+
model = Model.new
|
43
|
+
scopable = Scopable.new(model, fuzzy: {}, jumbo: {})
|
44
|
+
params = { fuzzy: 'fuzzy', jumbo: 'jumbo' }
|
45
|
+
assert_scope(scopable.resolve(params), :fuzzy, 'fuzzy')
|
46
|
+
assert_scope(scopable.resolve(params), :jumbo, 'jumbo')
|
47
|
+
end
|
48
|
+
|
49
|
+
test 'multiple scopes, missing one parameter' do
|
50
|
+
model = Model.new
|
51
|
+
scopable = Scopable.new(model, fuzzy: {}, jumbo: {})
|
52
|
+
params = { jumbo: 'jumbo' }
|
53
|
+
assert_scope(scopable.resolve(params), :jumbo, 'jumbo')
|
54
|
+
refute_scope(scopable.resolve(params), :fuzzy)
|
55
|
+
end
|
56
|
+
|
57
|
+
test 'matching param with blank value' do
|
58
|
+
model = Model.new
|
59
|
+
scopable = Scopable.new(model, fuzzy: {})
|
60
|
+
params = { fuzzy: '' }
|
61
|
+
refute_scope(scopable.resolve(params), :fuzzy)
|
62
|
+
end
|
63
|
+
|
64
|
+
test 'absent param' do
|
65
|
+
model = Model.new
|
66
|
+
scopable = Scopable.new(model, fuzzy: {})
|
67
|
+
refute_scope(scopable.resolve, :fuzzy)
|
68
|
+
end
|
69
|
+
|
70
|
+
test 'matching param with true-like value' do
|
71
|
+
model = Model.new
|
72
|
+
scopable = Scopable.new(model, fuzzy: {})
|
73
|
+
assert_scope(scopable.resolve(fuzzy: 'on'), :fuzzy, true)
|
74
|
+
assert_scope(scopable.resolve(fuzzy: 'yes'), :fuzzy, true)
|
75
|
+
assert_scope(scopable.resolve(fuzzy: 'true'), :fuzzy, true)
|
76
|
+
end
|
77
|
+
|
78
|
+
test 'matching param with false-like value' do
|
79
|
+
model = Model.new
|
80
|
+
scopable = Scopable.new(model, fuzzy: {})
|
81
|
+
assert_scope(scopable.resolve(fuzzy: 'no'), :fuzzy, false)
|
82
|
+
assert_scope(scopable.resolve(fuzzy: 'off'), :fuzzy, false)
|
83
|
+
assert_scope(scopable.resolve(fuzzy: 'false'), :fuzzy, false)
|
84
|
+
end
|
85
|
+
|
86
|
+
test 'param option' do
|
87
|
+
model = Model.new
|
88
|
+
scopable = Scopable.new(model, fuzzy: { param: :f })
|
89
|
+
params = { f: 'fuzzy' }
|
90
|
+
assert_scope(scopable.resolve(params), :fuzzy, 'fuzzy')
|
91
|
+
end
|
92
|
+
|
93
|
+
test 'value option' do
|
94
|
+
model = Model.new
|
95
|
+
scopable = Scopable.new(model, fuzzy: { value: 'fuzzy' })
|
96
|
+
assert_scope(scopable.resolve, :fuzzy, 'fuzzy')
|
97
|
+
end
|
98
|
+
|
99
|
+
test 'default option' do
|
100
|
+
model = Model.new
|
101
|
+
scopable = Scopable.new(model, fuzzy: { default: 'fuzzy' })
|
102
|
+
assert_scope(scopable.resolve, :fuzzy, 'fuzzy')
|
103
|
+
params = { fuzzy: 'jumbo' }
|
104
|
+
assert_scope(scopable.resolve(params), :fuzzy, 'jumbo')
|
105
|
+
end
|
106
|
+
|
107
|
+
test 'required option' do
|
108
|
+
model = Model.new
|
109
|
+
scopable = Scopable.new(model, fuzzy: { required: true })
|
110
|
+
assert_equal(:none, scopable.resolve)
|
111
|
+
params = { fuzzy: 'fuzzy' }
|
112
|
+
assert_scope(scopable.resolve(params), :fuzzy, 'fuzzy')
|
113
|
+
end
|
114
|
+
|
115
|
+
test 'if option' do
|
116
|
+
model = Model.new
|
117
|
+
scopable = Scopable.new(model, fuzzy: { if: -> { params[:quack] } })
|
118
|
+
params = { fuzzy: 'fuzzy' }
|
119
|
+
refute_scope(scopable.resolve(params), :fuzzy)
|
120
|
+
params = { fuzzy: 'fuzzy', quack: true }
|
121
|
+
assert_scope(scopable.resolve(params), :fuzzy, 'fuzzy')
|
122
|
+
end
|
123
|
+
|
124
|
+
test 'unless option' do
|
125
|
+
model = Model.new
|
126
|
+
scopable = Scopable.new(model, fuzzy: { unless: -> { params[:quack] } })
|
127
|
+
params = { fuzzy: 'fuzzy', quack: true }
|
128
|
+
refute_scope(scopable.resolve(params), :fuzzy)
|
129
|
+
params = { fuzzy: 'fuzzy' }
|
130
|
+
assert_scope(scopable.resolve(params), :fuzzy, 'fuzzy')
|
131
|
+
end
|
132
|
+
|
133
|
+
test 'block option' do
|
134
|
+
model = Model.new
|
135
|
+
scopable = Scopable.new(model, fuzzy: { block: -> { jumbo(value) } })
|
136
|
+
params = { fuzzy: 'fuzzy' }
|
137
|
+
assert_scope(scopable.resolve(params), :jumbo, 'fuzzy')
|
138
|
+
end
|
139
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'simplecov'
|
2
|
+
|
3
|
+
# Generate coverage report.
|
4
|
+
SimpleCov.start
|
5
|
+
|
6
|
+
require 'minitest/autorun'
|
7
|
+
require 'active_support/testing/declarative'
|
8
|
+
|
9
|
+
# Mock model for testing scope chains.
|
10
|
+
class Model
|
11
|
+
def scopes
|
12
|
+
@scopes ||= {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def fuzzy(value = true)
|
16
|
+
scopes.store(:fuzzy, value)
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
def jumbo(value = true)
|
21
|
+
scopes.store(:jumbo, value)
|
22
|
+
self
|
23
|
+
end
|
24
|
+
|
25
|
+
def none
|
26
|
+
:none
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Load the Gem.
|
31
|
+
require_relative '../lib/scopable'
|
32
|
+
|
33
|
+
# Run test suite.
|
34
|
+
require_relative 'scopable_test'
|
metadata
CHANGED
@@ -1,72 +1,87 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: scopable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Arthur Corenzan
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-06-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '5.0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '5.0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: bundler
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '1.
|
33
|
+
version: '1.15'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.15'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '5.10'
|
34
48
|
type: :development
|
35
49
|
prerelease: false
|
36
50
|
version_requirements: !ruby/object:Gem::Requirement
|
37
51
|
requirements:
|
38
52
|
- - "~>"
|
39
53
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
54
|
+
version: '5.10'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
56
|
+
name: rubocop
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
44
58
|
requirements:
|
45
59
|
- - "~>"
|
46
60
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
61
|
+
version: '0.49'
|
48
62
|
type: :development
|
49
63
|
prerelease: false
|
50
64
|
version_requirements: !ruby/object:Gem::Requirement
|
51
65
|
requirements:
|
52
66
|
- - "~>"
|
53
67
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
68
|
+
version: '0.49'
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
70
|
+
name: simplecov
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
58
72
|
requirements:
|
59
73
|
- - "~>"
|
60
74
|
- !ruby/object:Gem::Version
|
61
|
-
version: '
|
75
|
+
version: '0.14'
|
62
76
|
type: :development
|
63
77
|
prerelease: false
|
64
78
|
version_requirements: !ruby/object:Gem::Requirement
|
65
79
|
requirements:
|
66
80
|
- - "~>"
|
67
81
|
- !ruby/object:Gem::Version
|
68
|
-
version: '
|
69
|
-
description:
|
82
|
+
version: '0.14'
|
83
|
+
description: Scopable allows you to create objects that produce complex queries based
|
84
|
+
on incoming parameters hash which are testable and easy to understand.
|
70
85
|
email:
|
71
86
|
- arthur@corenzan.com
|
72
87
|
executables: []
|
@@ -75,20 +90,17 @@ extra_rdoc_files: []
|
|
75
90
|
files:
|
76
91
|
- ".codeclimate.yml"
|
77
92
|
- ".gitignore"
|
78
|
-
- ".rspec"
|
79
93
|
- ".rubocop.yml"
|
80
94
|
- ".travis.yml"
|
81
95
|
- Gemfile
|
82
|
-
- LICENSE
|
96
|
+
- LICENSE.md
|
83
97
|
- README.md
|
84
98
|
- Rakefile
|
85
99
|
- lib/scopable.rb
|
86
100
|
- lib/scopable/version.rb
|
87
101
|
- scopable.gemspec
|
88
|
-
-
|
89
|
-
-
|
90
|
-
- spec/support/controller.rb
|
91
|
-
- spec/support/model.rb
|
102
|
+
- test/scopable_test.rb
|
103
|
+
- test/test_helper.rb
|
92
104
|
homepage: https://github.com/corenzan/scopable
|
93
105
|
licenses:
|
94
106
|
- MIT
|
@@ -109,12 +121,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
109
121
|
version: '0'
|
110
122
|
requirements: []
|
111
123
|
rubyforge_project:
|
112
|
-
rubygems_version: 2.6.
|
124
|
+
rubygems_version: 2.6.12
|
113
125
|
signing_key:
|
114
126
|
specification_version: 4
|
115
|
-
summary:
|
127
|
+
summary: Easy parametric query building in Rails.
|
116
128
|
test_files:
|
117
|
-
-
|
118
|
-
-
|
119
|
-
- spec/support/controller.rb
|
120
|
-
- spec/support/model.rb
|
129
|
+
- test/scopable_test.rb
|
130
|
+
- test/test_helper.rb
|
data/.rspec
DELETED
data/LICENSE
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
Copyright (c) 2015-2017 Arthur Corenzan
|
2
|
-
|
3
|
-
MIT License
|
4
|
-
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
-
a copy of this software and associated documentation files (the
|
7
|
-
"Software"), to deal in the Software without restriction, including
|
8
|
-
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
-
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
-
permit persons to whom the Software is furnished to do so, subject to
|
11
|
-
the following conditions:
|
12
|
-
|
13
|
-
The above copyright notice and this permission notice shall be
|
14
|
-
included in all copies or substantial portions of the Software.
|
15
|
-
|
16
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
-
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
-
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
-
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
-
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
-
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
-
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/spec/scopable_spec.rb
DELETED
@@ -1,302 +0,0 @@
|
|
1
|
-
require 'active_support/all'
|
2
|
-
require_relative '../lib/scopable.rb'
|
3
|
-
require_relative 'support/controller.rb'
|
4
|
-
require_relative 'support/model.rb'
|
5
|
-
|
6
|
-
describe Scopable do
|
7
|
-
it 'creates class variable #scopes' do
|
8
|
-
expect(Controller).to respond_to(:scopes)
|
9
|
-
expect(Controller.scopes).to eq({})
|
10
|
-
end
|
11
|
-
|
12
|
-
it 'adds class method #scope' do
|
13
|
-
expect(Controller).to respond_to(:scope)
|
14
|
-
end
|
15
|
-
|
16
|
-
it 'adds instance method #scoped' do
|
17
|
-
expect(Controller.new).to respond_to(:scoped)
|
18
|
-
end
|
19
|
-
|
20
|
-
#
|
21
|
-
# Test single scope, no options.
|
22
|
-
#
|
23
|
-
describe 'with one optional scope' do
|
24
|
-
let :controller do
|
25
|
-
Class.new(Controller) do
|
26
|
-
scope :search
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
context 'with the parameter absent' do
|
31
|
-
subject :action do
|
32
|
-
controller.new
|
33
|
-
end
|
34
|
-
|
35
|
-
it 'should skip the scope' do
|
36
|
-
expect(action.relation.scopes).to be_empty
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
context 'with the parameter present' do
|
41
|
-
subject :action do
|
42
|
-
controller.new(nil, search: 'test')
|
43
|
-
end
|
44
|
-
|
45
|
-
it 'should apply the scope' do
|
46
|
-
expect(action.relation.scopes).to include(search: 'test')
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
context 'with the parameter present but blank' do
|
51
|
-
subject :action do
|
52
|
-
controller.new(nil, search: '')
|
53
|
-
end
|
54
|
-
|
55
|
-
it 'should skip the scope' do
|
56
|
-
expect(action.relation.scopes).to be_empty
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
#
|
62
|
-
# Test two optional scopes, with 0, 1, and 2 matching parameters.
|
63
|
-
#
|
64
|
-
describe 'with two optional scopes' do
|
65
|
-
let :controller do
|
66
|
-
Class.new(Controller) do
|
67
|
-
scope :color
|
68
|
-
scope :size
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
context 'without parameters' do
|
73
|
-
subject :action do
|
74
|
-
controller.new
|
75
|
-
end
|
76
|
-
|
77
|
-
it 'should skip the scopes' do
|
78
|
-
expect(action.relation.scopes).to be_empty
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
context 'with parameter matching the first scope' do
|
83
|
-
subject :action do
|
84
|
-
controller.new(nil, color: 'black')
|
85
|
-
end
|
86
|
-
|
87
|
-
it 'should apply only the first scope' do
|
88
|
-
expect(action.relation.scopes.size).to eq(1)
|
89
|
-
expect(action.relation.scopes).to include(color: 'black')
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
context 'with parameter matching the second scope' do
|
94
|
-
subject :action do
|
95
|
-
controller.new(nil, size: 'm')
|
96
|
-
end
|
97
|
-
|
98
|
-
it 'should apply only the second scope' do
|
99
|
-
expect(action.relation.scopes.size).to eq(1)
|
100
|
-
expect(action.relation.scopes).to include(size: 'm')
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
context 'with parameters matching both scopes' do
|
105
|
-
subject :action do
|
106
|
-
controller.new(nil, size: 'm', color: 'black')
|
107
|
-
end
|
108
|
-
|
109
|
-
it 'should apply both scopes' do
|
110
|
-
expect(action.relation.scopes.size).to eq(2)
|
111
|
-
expect(action.relation.scopes).to include(size: 'm', color: 'black')
|
112
|
-
end
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
#
|
117
|
-
# Test :required option.
|
118
|
-
#
|
119
|
-
describe 'with :required option' do
|
120
|
-
let :controller do
|
121
|
-
Class.new(Controller) do
|
122
|
-
scope :active, required: true
|
123
|
-
end
|
124
|
-
end
|
125
|
-
|
126
|
-
context 'with no matching parameters' do
|
127
|
-
subject :action do
|
128
|
-
controller.new
|
129
|
-
end
|
130
|
-
|
131
|
-
it 'should apply #none' do
|
132
|
-
expect(action.relation.scopes).to include(none: true)
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
136
|
-
context 'with one parameter matching' do
|
137
|
-
subject :action do
|
138
|
-
controller.new(nil, active: 'yes')
|
139
|
-
end
|
140
|
-
|
141
|
-
it 'should apply one of the scopes' do
|
142
|
-
expect(action.relation.scopes.size).to eq(1)
|
143
|
-
expect(action.relation.scopes).to include(active: true)
|
144
|
-
end
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
|
-
#
|
149
|
-
# Test :except option.
|
150
|
-
#
|
151
|
-
describe 'with :except option' do
|
152
|
-
let :controller do
|
153
|
-
Class.new(Controller) do
|
154
|
-
scope :filter, except: :index
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
|
-
context 'with matching parameter in the exception action' do
|
159
|
-
subject :action do
|
160
|
-
controller.new(:index, filter: 'yes')
|
161
|
-
end
|
162
|
-
|
163
|
-
it 'should skip the scope' do
|
164
|
-
expect(action.relation.scopes).to be_empty
|
165
|
-
end
|
166
|
-
end
|
167
|
-
|
168
|
-
context 'with matching parameter in a different action' do
|
169
|
-
subject :action do
|
170
|
-
controller.new(nil, filter: 'yes')
|
171
|
-
end
|
172
|
-
|
173
|
-
it 'should apply the scope' do
|
174
|
-
expect(action.relation.scopes).to include(filter: true)
|
175
|
-
end
|
176
|
-
end
|
177
|
-
end
|
178
|
-
|
179
|
-
#
|
180
|
-
# Test :only option.
|
181
|
-
#
|
182
|
-
describe 'with :only option' do
|
183
|
-
let :controller do
|
184
|
-
Class.new(Controller) do
|
185
|
-
scope :filter, only: :index
|
186
|
-
end
|
187
|
-
end
|
188
|
-
|
189
|
-
context 'with matching parameter in the only action' do
|
190
|
-
subject :action do
|
191
|
-
controller.new(:index, filter: 'yes')
|
192
|
-
end
|
193
|
-
|
194
|
-
it 'should apply the scope' do
|
195
|
-
expect(action.relation.scopes).to include(filter: true)
|
196
|
-
end
|
197
|
-
end
|
198
|
-
|
199
|
-
context 'with matching parameter in a different action' do
|
200
|
-
subject :action do
|
201
|
-
controller.new(nil, filter: 'yes')
|
202
|
-
end
|
203
|
-
|
204
|
-
it 'should skip the scope' do
|
205
|
-
expect(action.relation.scopes).to be_empty
|
206
|
-
end
|
207
|
-
end
|
208
|
-
end
|
209
|
-
|
210
|
-
#
|
211
|
-
# Test :param option.
|
212
|
-
#
|
213
|
-
describe 'with :param option' do
|
214
|
-
let :controller do
|
215
|
-
Class.new(Controller) do
|
216
|
-
scope :search, param: :q
|
217
|
-
end
|
218
|
-
end
|
219
|
-
|
220
|
-
context 'with matching parameter' do
|
221
|
-
subject :action do
|
222
|
-
controller.new(nil, q: 'test')
|
223
|
-
end
|
224
|
-
|
225
|
-
it 'should apply the scope' do
|
226
|
-
expect(action.relation.scopes).to include(search: 'test')
|
227
|
-
end
|
228
|
-
end
|
229
|
-
|
230
|
-
context 'without matching parameter' do
|
231
|
-
subject :action do
|
232
|
-
controller.new(nil, search: 'test')
|
233
|
-
end
|
234
|
-
|
235
|
-
it 'should skip the scope' do
|
236
|
-
expect(action.relation.scopes).to be_empty
|
237
|
-
end
|
238
|
-
end
|
239
|
-
end
|
240
|
-
|
241
|
-
#
|
242
|
-
# Test :default option.
|
243
|
-
#
|
244
|
-
describe 'with :default option' do
|
245
|
-
let :controller do
|
246
|
-
Class.new(Controller) do
|
247
|
-
scope :page, default: 1
|
248
|
-
end
|
249
|
-
end
|
250
|
-
|
251
|
-
context 'with matching parameter' do
|
252
|
-
subject :action do
|
253
|
-
controller.new(nil, page: 2)
|
254
|
-
end
|
255
|
-
|
256
|
-
it 'should overwrite the default value' do
|
257
|
-
expect(action.relation.scopes).to include(page: 2)
|
258
|
-
end
|
259
|
-
end
|
260
|
-
|
261
|
-
context 'without matching parameter' do
|
262
|
-
subject :action do
|
263
|
-
controller.new(nil)
|
264
|
-
end
|
265
|
-
|
266
|
-
it 'should use the default value' do
|
267
|
-
expect(action.relation.scopes).to include(page: 1)
|
268
|
-
end
|
269
|
-
end
|
270
|
-
end
|
271
|
-
|
272
|
-
#
|
273
|
-
# Test :force option.
|
274
|
-
#
|
275
|
-
describe 'with :force option' do
|
276
|
-
let :controller do
|
277
|
-
Class.new(Controller) do
|
278
|
-
scope :sort, force: :id
|
279
|
-
end
|
280
|
-
end
|
281
|
-
|
282
|
-
context 'without matching parameter' do
|
283
|
-
subject :action do
|
284
|
-
controller.new(nil)
|
285
|
-
end
|
286
|
-
|
287
|
-
it 'should use the forced value' do
|
288
|
-
expect(action.relation.scopes).to include(sort: :id)
|
289
|
-
end
|
290
|
-
end
|
291
|
-
|
292
|
-
context 'with matching parameter' do
|
293
|
-
subject :action do
|
294
|
-
controller.new(nil, sort: :name)
|
295
|
-
end
|
296
|
-
|
297
|
-
it 'should still use the forced value' do
|
298
|
-
expect(action.relation.scopes).to include(sort: :id)
|
299
|
-
end
|
300
|
-
end
|
301
|
-
end
|
302
|
-
end
|
data/spec/spec_helper.rb
DELETED
@@ -1,99 +0,0 @@
|
|
1
|
-
require 'simplecov'
|
2
|
-
SimpleCov.start
|
3
|
-
|
4
|
-
# This file was generated by the `rspec --init` command. Conventionally, all
|
5
|
-
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
6
|
-
# The generated `.rspec` file contains `--require spec_helper` which will cause
|
7
|
-
# this file to always be loaded, without a need to explicitly require it in any
|
8
|
-
# files.
|
9
|
-
#
|
10
|
-
# Given that it is always loaded, you are encouraged to keep this file as
|
11
|
-
# light-weight as possible. Requiring heavyweight dependencies from this file
|
12
|
-
# will add to the boot time of your test suite on EVERY test run, even for an
|
13
|
-
# individual file that may not need all of that loaded. Instead, consider making
|
14
|
-
# a separate helper file that requires the additional dependencies and performs
|
15
|
-
# the additional setup, and require it from the spec files that actually need
|
16
|
-
# it.
|
17
|
-
#
|
18
|
-
# The `.rspec` file also contains a few flags that are not defaults but that
|
19
|
-
# users commonly want.
|
20
|
-
#
|
21
|
-
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
22
|
-
RSpec.configure do |config|
|
23
|
-
# rspec-expectations config goes here. You can use an alternate
|
24
|
-
# assertion/expectation library such as wrong or the stdlib/minitest
|
25
|
-
# assertions if you prefer.
|
26
|
-
config.expect_with :rspec do |expectations|
|
27
|
-
# This option will default to `true` in RSpec 4. It makes the `description`
|
28
|
-
# and `failure_message` of custom matchers include text for helper methods
|
29
|
-
# defined using `chain`, e.g.:
|
30
|
-
# be_bigger_than(2).and_smaller_than(4).description
|
31
|
-
# # => "be bigger than 2 and smaller than 4"
|
32
|
-
# ...rather than:
|
33
|
-
# # => "be bigger than 2"
|
34
|
-
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
35
|
-
end
|
36
|
-
|
37
|
-
# rspec-mocks config goes here. You can use an alternate test double
|
38
|
-
# library (such as bogus or mocha) by changing the `mock_with` option here.
|
39
|
-
config.mock_with :rspec do |mocks|
|
40
|
-
# Prevents you from mocking or stubbing a method that does not exist on
|
41
|
-
# a real object. This is generally recommended, and will default to
|
42
|
-
# `true` in RSpec 4.
|
43
|
-
mocks.verify_partial_doubles = true
|
44
|
-
end
|
45
|
-
|
46
|
-
# The settings below are suggested to provide a good initial experience
|
47
|
-
# with RSpec, but feel free to customize to your heart's content.
|
48
|
-
=begin
|
49
|
-
# These two settings work together to allow you to limit a spec run
|
50
|
-
# to individual examples or groups you care about by tagging them with
|
51
|
-
# `:focus` metadata. When nothing is tagged with `:focus`, all examples
|
52
|
-
# get run.
|
53
|
-
config.filter_run :focus
|
54
|
-
config.run_all_when_everything_filtered = true
|
55
|
-
|
56
|
-
# Allows RSpec to persist some state between runs in order to support
|
57
|
-
# the `--only-failures` and `--next-failure` CLI options. We recommend
|
58
|
-
# you configure your source control system to ignore this file.
|
59
|
-
config.example_status_persistence_file_path = "spec/examples.txt"
|
60
|
-
|
61
|
-
# Limits the available syntax to the non-monkey patched syntax that is
|
62
|
-
# recommended. For more details, see:
|
63
|
-
# - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/
|
64
|
-
# - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
|
65
|
-
# - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode
|
66
|
-
config.disable_monkey_patching!
|
67
|
-
|
68
|
-
# This setting enables warnings. It's recommended, but in some cases may
|
69
|
-
# be too noisy due to issues in dependencies.
|
70
|
-
config.warnings = true
|
71
|
-
|
72
|
-
# Many RSpec users commonly either run the entire suite or an individual
|
73
|
-
# file, and it's useful to allow more verbose output when running an
|
74
|
-
# individual spec file.
|
75
|
-
if config.files_to_run.one?
|
76
|
-
# Use the documentation formatter for detailed output,
|
77
|
-
# unless a formatter has already been configured
|
78
|
-
# (e.g. via a command-line flag).
|
79
|
-
config.default_formatter = 'doc'
|
80
|
-
end
|
81
|
-
|
82
|
-
# Print the 10 slowest examples and example groups at the
|
83
|
-
# end of the spec run, to help surface which specs are running
|
84
|
-
# particularly slow.
|
85
|
-
config.profile_examples = 10
|
86
|
-
|
87
|
-
# Run specs in random order to surface order dependencies. If you find an
|
88
|
-
# order dependency and want to debug it, you can fix the order by providing
|
89
|
-
# the seed, which is printed after each run.
|
90
|
-
# --seed 1234
|
91
|
-
config.order = :random
|
92
|
-
|
93
|
-
# Seed global randomization in this process using the `--seed` CLI option.
|
94
|
-
# Setting this allows you to use `--seed` to deterministically reproduce
|
95
|
-
# test failures related to randomization by passing the same `--seed` value
|
96
|
-
# as the one that triggered the failure.
|
97
|
-
Kernel.srand config.seed
|
98
|
-
=end
|
99
|
-
end
|
data/spec/support/controller.rb
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
class Controller
|
2
|
-
include Scopable
|
3
|
-
|
4
|
-
def initialize(action_name = nil, params = {})
|
5
|
-
@action_name, @params = action_name, params.freeze
|
6
|
-
end
|
7
|
-
|
8
|
-
def params
|
9
|
-
@params
|
10
|
-
end
|
11
|
-
|
12
|
-
def action_name
|
13
|
-
@action_name.to_s
|
14
|
-
end
|
15
|
-
|
16
|
-
def relation
|
17
|
-
scoped(Model.new, params)
|
18
|
-
end
|
19
|
-
end
|