scopable 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a92105be0449f880038bc7d58e0af2d2a1873531
4
- data.tar.gz: 2718a557dda1ec4d724ea0f9ab231fe0ffd45a43
3
+ metadata.gz: db1ac9caa504b39e2c3bd583a410c40cbe822c5a
4
+ data.tar.gz: a06474713343b047e5bd2b3004fe356a6c19d0f2
5
5
  SHA512:
6
- metadata.gz: 7c0cadb182a7e2171f6e4d97db016bcad90e6d6a2abc7d9f5146827643699bc21190e5a3ac92c7f977ed5ae3b103e3eb9228d2aca4fb89bfb2f259501a988971
7
- data.tar.gz: dbcb6bffaeea5d1101b0385ccc9537604552506e73fa97a3c3a3c607618aabd57ff85b384b5908d79bad23bf651112d68c5b411dd3793390ec0f7d9a03ff5eaa
6
+ metadata.gz: 8e90dc92efeed46a6def9c9ed3efe173e998f37618283d8fd2a2113eb2e1af6a7d74dbf65373e2a41b25817f68b6c3ca17f65296eb8b9e3a6bd498f4f633d4c8
7
+ data.tar.gz: f28f405579fe0e12c3dcde190e9fc3d1951e4ef1eb65277c9da13e054fa0b52e1fce7fc5efe4e11ee5037153434e54ee51a8d3e04885e19a125188482995f65d
@@ -0,0 +1,17 @@
1
+ ---
2
+ engines:
3
+ rubocop:
4
+ enabled: true
5
+ brakeman:
6
+ enabled: true
7
+ duplication:
8
+ enabled: true
9
+ config:
10
+ languages:
11
+ - ruby
12
+
13
+ ratings:
14
+ paths:
15
+ - "lib/**.rb"
16
+ exclude_paths:
17
+ - spec/**/*
data/.gitignore CHANGED
@@ -1,5 +1,4 @@
1
1
  .DS_Store
2
-
3
2
  /.bundle/
4
3
  /.yardoc
5
4
  /Gemfile.lock
@@ -10,6 +9,7 @@
10
9
  /spec/reports/
11
10
  /tmp/
12
11
  *.bundle
12
+ *.gem
13
13
  *.so
14
14
  *.o
15
15
  *.a
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
@@ -0,0 +1,10 @@
1
+ Style/Documentation:
2
+ Enabled: no
3
+ Metrics/LineLength:
4
+ Max: 128
5
+ Metrics/MethodLength:
6
+ Max: 32
7
+ Style/Lambda:
8
+ EnforcedStyle: literal
9
+ Style/EmptyCaseCondition:
10
+ Enabled: false
@@ -0,0 +1,4 @@
1
+ ---
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.1
data/Gemfile CHANGED
@@ -1,5 +1,3 @@
1
1
  source 'https://rubygems.org'
2
-
3
2
  gemspec
4
-
5
- gem 'activesupport', '~> 4.2.0'
3
+ gem 'codeclimate-test-reporter', group: :test, require: nil
@@ -1,4 +1,4 @@
1
- Copyright (c) 2015 Arthur Corenzan
1
+ Copyright (c) 2015-2017 Arthur Corenzan
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -1,6 +1,10 @@
1
1
  # Scopable
2
2
 
3
- > Apply scopes to your query based on available parameters
3
+ > Apply or skip model scopes based on options and request parameters.
4
+
5
+ [![Code Climate](https://codeclimate.com/github/haggen/scopable/badges/gpa.svg?1)](https://codeclimate.com/github/haggen/scopable)
6
+ [![Test Coverage](https://codeclimate.com/github/haggen/scopable/badges/coverage.svg?1)](https://codeclimate.com/github/haggen/scopable/coverage)
7
+ [![Build](https://travis-ci.org/haggen/scopable.svg)](https://travis-ci.org/haggen/scopable)
4
8
 
5
9
  ## Installation
6
10
 
@@ -12,11 +16,15 @@ gem 'scopable'
12
16
 
13
17
  And then execute:
14
18
 
15
- $ bundle
19
+ ```shell
20
+ $ bundle
21
+ ```
16
22
 
17
- Or install it yourself as:
23
+ Or install it yourself with:
18
24
 
19
- $ gem install scopable
25
+ ```shell
26
+ $ gem install scopable
27
+ ```
20
28
 
21
29
  ## Usage
22
30
 
@@ -26,7 +34,7 @@ Configure scopes in your controller:
26
34
  class PostsController < ApplicationController
27
35
  include Scopable
28
36
 
29
- scope :search, :param => :q
37
+ scope :search, param: :q
30
38
 
31
39
  # ...
32
40
  end
@@ -40,10 +48,73 @@ def index
40
48
  end
41
49
  ```
42
50
 
43
- Now, whenever the parameter `q` is present at `params`, it will call `#search` on your model passing its value as argument.
51
+ Now whenever the parameter `q` is present in `params`, `#search` will be called on your model passing the parameter's value as argument.
52
+
53
+ Another example:
54
+
55
+ ```ruby
56
+ class PostController < ApplicationController
57
+ include Scopable
58
+
59
+ scope :by_date, param: :date do |relation, value|
60
+ relation.where(created_at: Date.parse(value))
61
+ end
62
+
63
+ scope :by_author, param: :author
64
+
65
+ scope :order, force: { created_at: :desc }
66
+
67
+ def index
68
+ @posts = scoped(Post, params)
69
+ end
70
+ end
71
+ ```
72
+
73
+ Now say your URL look like this:
74
+
75
+ ```
76
+ /posts?date=2016-1-6&author=2
77
+ ```
78
+
79
+ The resulting relation would be:
80
+
81
+ ```ruby
82
+ Post.where(created_at: '6/1/2016').by_author(2).order(created_at: :desc)
83
+ ```
84
+
85
+ **Note that order matters!** The scopes will be applied in the same order they are configured.
86
+
87
+ Also note values like `true/false`, `on/off`, `yes/no` are treated like boolean, and when the value is evaluated to `true` or `false` the scope is called with no arguments, or skipped, respectively.
88
+
89
+ ```ruby
90
+ scope :active
91
+ ```
92
+
93
+ With a URL like this:
44
94
 
45
- TODO: Add more examples.
46
- TODO: Layout `scope` options.
95
+ ```ruby
96
+ /?active=yes
97
+ ```
98
+
99
+ Would be equivalent to:
100
+
101
+ ```ruby
102
+ Model.active
103
+ ```
104
+
105
+ ## Options
106
+
107
+ No option is required. By default it assumes both scope and parameter have the same name.
108
+
109
+ Key | Description
110
+ ------------|--------------------------------------------------------------------------------------------------------------
111
+ `:param` | Name of the parameter that activates the scope.
112
+ `:default` | Default value for the scope in case the parameter is missing.
113
+ `:force` | Force a value to the scope regardless of the request parameters.
114
+ `:required` | Calls `#none` on the model if parameter is absent and no default value is given.
115
+ `:only` | The scope will only be applied to these actions.
116
+ `:except` | The scope will be applied to all actions except these.
117
+ `&block` | Block will be called in the context of the action and will be given the current relation and evaluated value.
47
118
 
48
119
  ## Contributing
49
120
 
@@ -52,3 +123,7 @@ TODO: Layout `scope` options.
52
123
  3. Commit your changes (`git commit -am 'Add some feature'`)
53
124
  4. Push to the branch (`git push origin my-new-feature`)
54
125
  5. Create a new Pull Request
126
+
127
+ ## License
128
+
129
+ See [LICENSE.txt](LICENSE.txt).
data/Rakefile CHANGED
@@ -1,2 +1,3 @@
1
- require "bundler/gem_tasks"
2
-
1
+ require 'rspec/core/rake_task'
2
+ RSpec::Core::RakeTask.new('spec')
3
+ task default: :spec
@@ -1,60 +1,90 @@
1
- require "scopable/version"
2
-
3
1
  module Scopable
4
2
  extend ActiveSupport::Concern
5
3
 
6
- included do
7
- cattr_reader :scopes do
8
- Hash.new
9
- end
10
-
11
- helper_method :active_scopes
12
- end
13
-
14
- def active_scopes
15
- scopes.map do |name, scope|
16
- name if scope[:active]
17
- end.compact
4
+ def scopes
5
+ self.class.scopes
18
6
  end
19
7
 
20
- def scoped(resource, params)
21
- scopes.reduce(resource) do |resource, scope|
8
+ def scoped(model, params)
9
+ scopes.reduce(model) do |relation, scope|
22
10
  name, scope = *scope
23
11
 
24
- param, force, default, except, only, fn = scope.values_at(:param, :force, :default, :except, :only, :fn)
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 = scope[:only]
15
+ only = instance_exec(&only) if only.respond_to?(:call)
16
+
17
+ # Enfore :only option.
18
+ break relation unless only.nil? || Array.wrap(only).map(&:to_s).include?(action_name)
19
+
20
+ # Controller actions where this scope should be ignored.
21
+ # Accepts either a literal value or a lambda. nil with disable the option.
22
+ except = scope[:except]
23
+ except = instance_exec(&except) if except.respond_to?(:call)
24
+
25
+ # Enfore :except option.
26
+ break relation if except.present? && Array.wrap(except).map(&:to_s).include?(action_name)
27
+
28
+ # Name of the request parameters which value will be used in this scope.
29
+ # Defaults to the name of the scope.
30
+ param = scope.fetch(:param, name)
31
+
32
+ # Use the value from the request parameter or fall back to the default.
33
+ value = params[param]
25
34
 
26
- force = self.instance_exec(&force) if force.respond_to?(:call)
35
+ # If parameter is not present use the :default option.
36
+ # Accepts either a literal value or a lambda.
37
+ value = scope[:default] if value.nil?
27
38
 
28
- param = param || name
29
- value = force || params[param] || default
39
+ # Forces the scope to use the given value given in the :force option.
40
+ # Accepts either a literal value or a lambda.
41
+ value = scope[:force] if scope.key?(:force)
30
42
 
31
- value = nil if except.present? && Array.wrap(except).map(&:to_s).include?(action_name)
32
- value = nil unless only.nil? || Array.wrap(only).map(&:to_s).include?(action_name)
43
+ # If either :default or :force options were procs, evaluate them.
44
+ value = instance_exec(&value) if value.respond_to?(:call)
33
45
 
34
- if value.nil? || value.to_s =~ /\A(false|no|off)\z/
35
- scope.update(:active => false)
36
- resource
46
+ # The :required option makes sure there's a value present, otherwise return an empty scope (Model#none).
47
+ required = scope[:required]
48
+ required = instance_exec(&required) if required.respond_to?(:call)
49
+
50
+ # Enforce the :required option.
51
+ return relation.none if required && value.nil?
52
+
53
+ # Parses values like 'on/off', 'true/false', and 'yes/no' to an actual boolean value.
54
+ case value.to_s
55
+ when /\A(false|no|off)\z/
56
+ value = false
57
+ when /\A(true|yes|on)\z/
58
+ value = true
59
+ end
60
+
61
+ # For advanced scopes that require more than a method call on the model.
62
+ # When a block is given, it is ran no matter the scope value.
63
+ # The proc will be given the model being scoped and the resulting value from the options above, and it'll be executed inside the context of the controller's action.
64
+ block = scope[:block]
65
+
66
+ if block.nil? && value.nil?
67
+ break relation
68
+ end
69
+
70
+ case
71
+ when block.present?
72
+ instance_exec(relation, value, &block)
73
+ when value == true
74
+ relation.send(name)
37
75
  else
38
- scope.update(:active => true)
39
-
40
- value = nil if value == 'nil'
41
-
42
- if fn
43
- self.instance_exec(resource, value, &fn)
44
- else
45
- if value.to_s =~ /\A(true|yes|on)\z/
46
- resource.send(name)
47
- else
48
- resource.send(name, value)
49
- end
50
- end
76
+ relation.send(name, value)
51
77
  end
52
78
  end
53
79
  end
54
80
 
55
81
  module ClassMethods
56
- def scope(name, opts = {}, &fn)
57
- scopes.store name, opts.merge(:fn => fn)
82
+ def scopes
83
+ @scopes ||= {}
84
+ end
85
+
86
+ def scope(name, options = {}, &block)
87
+ scopes.store name, options.merge(block: block)
58
88
  end
59
89
  end
60
90
  end
@@ -1,3 +1,3 @@
1
1
  module Scopable
2
- VERSION = "1.0.0"
2
+ VERSION = "1.1.0"
3
3
  end
@@ -4,22 +4,24 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
  require 'scopable/version'
5
5
 
6
6
  Gem::Specification.new do |spec|
7
- spec.name = "scopable"
7
+ spec.name = 'scopable'
8
8
  spec.version = Scopable::VERSION
9
- spec.authors = ["Arthur Corenzan"]
10
- spec.email = ["arthur@corenzan.com"]
11
- spec.summary = %q{Apply scopes to your query based on available parameters}
9
+ spec.authors = ['Arthur Corenzan']
10
+ spec.email = ['arthur@corenzan.com']
11
+ spec.summary = %q{Apply or skip model scopes based on options and request parameters.}
12
12
  # spec.description = %q{}
13
- spec.homepage = "https://github.com/haggen/scopable"
14
- spec.license = "MIT"
13
+ spec.homepage = 'https://github.com/haggen/scopable'
14
+ spec.license = 'MIT'
15
15
 
16
16
  spec.files = `git ls-files -z`.split("\x0")
17
17
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
- spec.require_paths = ["lib"]
19
+ spec.require_paths = ['lib']
20
20
 
21
- spec.add_runtime_dependency "activesupport", ">= 3.2", "< 5.0"
21
+ spec.add_runtime_dependency 'activesupport', '>= 3.2'
22
+ # spec.add_runtime_dependency 'railties', '>= 4.2.0', '< 5.1'
22
23
 
23
- spec.add_development_dependency "bundler", "~> 1.7"
24
- spec.add_development_dependency "rake", "~> 10.0"
24
+ spec.add_development_dependency 'bundler', '~> 1.7'
25
+ spec.add_development_dependency 'rake', '~> 10.0'
26
+ spec.add_development_dependency 'rspec'
25
27
  end
@@ -0,0 +1,281 @@
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, with and without matching parameters.
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 'without matching parameters' 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 one matching parameter' 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
+ end
50
+
51
+ #
52
+ # Test two optional scopes, with 0, 1, and 2 matching parameters.
53
+ #
54
+ describe 'with two optional scope' do
55
+ let :controller do
56
+ Class.new(Controller) do
57
+ scope :search
58
+ scope :page
59
+ end
60
+ end
61
+
62
+ context 'with no matching parameters' do
63
+ subject :action do
64
+ controller.new
65
+ end
66
+
67
+ it 'should skip the scope' do
68
+ expect(action.relation.scopes).to be_empty
69
+ end
70
+ end
71
+
72
+ context 'with one matching parameter' do
73
+ subject :action do
74
+ controller.new(nil, search: 'test')
75
+ end
76
+
77
+ it 'should apply one of the scopes' do
78
+ expect(action.relation.scopes.size).to eq(1)
79
+ expect(action.relation.scopes).to include(search: 'test')
80
+ end
81
+ end
82
+
83
+ context 'with two matching parameters' do
84
+ subject :action do
85
+ controller.new(nil, search: 'test', page: 2)
86
+ end
87
+
88
+ it 'should apply both scopes' do
89
+ expect(action.relation.scopes.size).to eq(2)
90
+ expect(action.relation.scopes).to include(search: 'test', page: 2)
91
+ end
92
+ end
93
+ end
94
+
95
+ #
96
+ # Test :required option.
97
+ #
98
+ describe 'with :required option' do
99
+ let :controller do
100
+ Class.new(Controller) do
101
+ scope :active, required: true
102
+ end
103
+ end
104
+
105
+ context 'with no matching parameters' do
106
+ subject :action do
107
+ controller.new
108
+ end
109
+
110
+ it 'should apply #none' do
111
+ expect(action.relation.scopes).to include(none: true)
112
+ end
113
+ end
114
+
115
+ context 'with one parameter matching' do
116
+ subject :action do
117
+ controller.new(nil, active: 'yes')
118
+ end
119
+
120
+ it 'should apply one of the scopes' do
121
+ expect(action.relation.scopes.size).to eq(1)
122
+ expect(action.relation.scopes).to include(active: true)
123
+ end
124
+ end
125
+ end
126
+
127
+ #
128
+ # Test :except option.
129
+ #
130
+ describe 'with :except option' do
131
+ let :controller do
132
+ Class.new(Controller) do
133
+ scope :filter, except: :index
134
+ end
135
+ end
136
+
137
+ context 'with matching parameter in the exception action' do
138
+ subject :action do
139
+ controller.new(:index, filter: 'yes')
140
+ end
141
+
142
+ it 'should skip the scope' do
143
+ expect(action.relation.scopes).to be_empty
144
+ end
145
+ end
146
+
147
+ context 'with matching parameter in a different action' do
148
+ subject :action do
149
+ controller.new(nil, filter: 'yes')
150
+ end
151
+
152
+ it 'should apply the scope' do
153
+ expect(action.relation.scopes).to include(filter: true)
154
+ end
155
+ end
156
+ end
157
+
158
+ #
159
+ # Test :only option.
160
+ #
161
+ describe 'with :only option' do
162
+ let :controller do
163
+ Class.new(Controller) do
164
+ scope :filter, only: :index
165
+ end
166
+ end
167
+
168
+ context 'with matching parameter in the only action' do
169
+ subject :action do
170
+ controller.new(:index, 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
+
178
+ context 'with matching parameter in a different action' do
179
+ subject :action do
180
+ controller.new(nil, filter: 'yes')
181
+ end
182
+
183
+ it 'should skip the scope' do
184
+ expect(action.relation.scopes).to be_empty
185
+ end
186
+ end
187
+ end
188
+
189
+ #
190
+ # Test :param option.
191
+ #
192
+ describe 'with :param option' do
193
+ let :controller do
194
+ Class.new(Controller) do
195
+ scope :search, param: :q
196
+ end
197
+ end
198
+
199
+ context 'with matching parameter' do
200
+ subject :action do
201
+ controller.new(nil, q: 'test')
202
+ end
203
+
204
+ it 'should apply the scope' do
205
+ expect(action.relation.scopes).to include(search: 'test')
206
+ end
207
+ end
208
+
209
+ context 'without matching parameter' do
210
+ subject :action do
211
+ controller.new(nil, search: 'test')
212
+ end
213
+
214
+ it 'should skip the scope' do
215
+ expect(action.relation.scopes).to be_empty
216
+ end
217
+ end
218
+ end
219
+
220
+ #
221
+ # Test :default option.
222
+ #
223
+ describe 'with :default option' do
224
+ let :controller do
225
+ Class.new(Controller) do
226
+ scope :page, default: 1
227
+ end
228
+ end
229
+
230
+ context 'with matching parameter' do
231
+ subject :action do
232
+ controller.new(nil, page: 2)
233
+ end
234
+
235
+ it 'should overwrite the default value' do
236
+ expect(action.relation.scopes).to include(page: 2)
237
+ end
238
+ end
239
+
240
+ context 'without matching parameter' do
241
+ subject :action do
242
+ controller.new(nil)
243
+ end
244
+
245
+ it 'should use the default value' do
246
+ expect(action.relation.scopes).to include(page: 1)
247
+ end
248
+ end
249
+ end
250
+
251
+ #
252
+ # Test :force option.
253
+ #
254
+ describe 'with :force option' do
255
+ let :controller do
256
+ Class.new(Controller) do
257
+ scope :sort, force: :id
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 forced value' do
267
+ expect(action.relation.scopes).to include(sort: :id)
268
+ end
269
+ end
270
+
271
+ context 'with matching parameter' do
272
+ subject :action do
273
+ controller.new(nil, sort: :name)
274
+ end
275
+
276
+ it 'should still use the forced value' do
277
+ expect(action.relation.scopes).to include(sort: :id)
278
+ end
279
+ end
280
+ end
281
+ end
@@ -0,0 +1,99 @@
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
@@ -0,0 +1,19 @@
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
@@ -0,0 +1,10 @@
1
+ class Model
2
+ def scopes
3
+ @scopes ||= {}
4
+ end
5
+
6
+ def method_missing(name, value = true)
7
+ scopes.store(name, value)
8
+ self
9
+ end
10
+ end
metadata CHANGED
@@ -1,63 +1,71 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: scopable
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.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: 2015-03-20 00:00:00.000000000 Z
11
+ date: 2017-02-27 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
19
  version: '3.2'
20
- - - "<"
21
- - !ruby/object:Gem::Version
22
- version: '5.0'
23
20
  type: :runtime
24
21
  prerelease: false
25
22
  version_requirements: !ruby/object:Gem::Requirement
26
23
  requirements:
27
- - - ">="
24
+ - - '>='
28
25
  - !ruby/object:Gem::Version
29
26
  version: '3.2'
30
- - - "<"
31
- - !ruby/object:Gem::Version
32
- version: '5.0'
33
27
  - !ruby/object:Gem::Dependency
34
28
  name: bundler
35
29
  requirement: !ruby/object:Gem::Requirement
36
30
  requirements:
37
- - - "~>"
31
+ - - ~>
38
32
  - !ruby/object:Gem::Version
39
33
  version: '1.7'
40
34
  type: :development
41
35
  prerelease: false
42
36
  version_requirements: !ruby/object:Gem::Requirement
43
37
  requirements:
44
- - - "~>"
38
+ - - ~>
45
39
  - !ruby/object:Gem::Version
46
40
  version: '1.7'
47
41
  - !ruby/object:Gem::Dependency
48
42
  name: rake
49
43
  requirement: !ruby/object:Gem::Requirement
50
44
  requirements:
51
- - - "~>"
45
+ - - ~>
52
46
  - !ruby/object:Gem::Version
53
47
  version: '10.0'
54
48
  type: :development
55
49
  prerelease: false
56
50
  version_requirements: !ruby/object:Gem::Requirement
57
51
  requirements:
58
- - - "~>"
52
+ - - ~>
59
53
  - !ruby/object:Gem::Version
60
54
  version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
61
69
  description:
62
70
  email:
63
71
  - arthur@corenzan.com
@@ -65,7 +73,11 @@ executables: []
65
73
  extensions: []
66
74
  extra_rdoc_files: []
67
75
  files:
68
- - ".gitignore"
76
+ - .codeclimate.yml
77
+ - .gitignore
78
+ - .rspec
79
+ - .rubocop.yml
80
+ - .travis.yml
69
81
  - Gemfile
70
82
  - LICENSE.txt
71
83
  - README.md
@@ -73,6 +85,10 @@ files:
73
85
  - lib/scopable.rb
74
86
  - lib/scopable/version.rb
75
87
  - scopable.gemspec
88
+ - spec/scopable_spec.rb
89
+ - spec/spec_helper.rb
90
+ - spec/support/controller.rb
91
+ - spec/support/model.rb
76
92
  homepage: https://github.com/haggen/scopable
77
93
  licenses:
78
94
  - MIT
@@ -83,18 +99,22 @@ require_paths:
83
99
  - lib
84
100
  required_ruby_version: !ruby/object:Gem::Requirement
85
101
  requirements:
86
- - - ">="
102
+ - - '>='
87
103
  - !ruby/object:Gem::Version
88
104
  version: '0'
89
105
  required_rubygems_version: !ruby/object:Gem::Requirement
90
106
  requirements:
91
- - - ">="
107
+ - - '>='
92
108
  - !ruby/object:Gem::Version
93
109
  version: '0'
94
110
  requirements: []
95
111
  rubyforge_project:
96
- rubygems_version: 2.4.5
112
+ rubygems_version: 2.0.14.1
97
113
  signing_key:
98
114
  specification_version: 4
99
- summary: Apply scopes to your query based on available parameters
100
- test_files: []
115
+ summary: Apply or skip model scopes based on options and request parameters.
116
+ test_files:
117
+ - spec/scopable_spec.rb
118
+ - spec/spec_helper.rb
119
+ - spec/support/controller.rb
120
+ - spec/support/model.rb