scopable 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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