filterable-by 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e533d4d75b6d63e5fb7618d137ea0d3c74b33556
4
+ data.tar.gz: c2b26115a0d7c1a035230914d54aa47fa9d4de16
5
+ SHA512:
6
+ metadata.gz: 5e3720c7264c8ca31db45f5393778f3f3795c655e5f440bc5e49cda15bdf7c50988e1bccf48ed7871b3fc80d286b4181bf8f3c0228a854acb987e76ef32b4655
7
+ data.tar.gz: 78c319fb3bd03c79094f72268dfaa589930985809e5ff2bcd524420980e01f30ebd2fb9e10e2174e2dee93fec50dcf2c499e0d27bde165256ff044a0b451e0c4
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ spec/tmp
2
+ pkg
3
+ *.gem
data/.rubocop.yml ADDED
@@ -0,0 +1,7 @@
1
+ inherit_from: .rubocop_todo.yml
2
+
3
+ Style/Documentation:
4
+ Enabled: false
5
+
6
+ Metrics/LineLength:
7
+ Max: 120
data/.rubocop_todo.yml ADDED
@@ -0,0 +1,40 @@
1
+ # This configuration was generated by `rubocop --auto-gen-config`
2
+ # on 2015-10-29 12:58:41 +0000 using RuboCop version 0.32.0.
3
+ # The point is for the user to remove these configuration records
4
+ # one by one as the offenses are removed from the code base.
5
+ # Note that changes in the inspected code, or installation of new
6
+ # versions of RuboCop, may require this file to be generated again.
7
+
8
+ # Offense count: 2
9
+ # Cop supports --auto-correct.
10
+ # Configuration parameters: EnforcedStyle, SupportedStyles.
11
+ Style/EmptyLinesAroundBlockBody:
12
+ Enabled: false
13
+
14
+ # Offense count: 2
15
+ # Cop supports --auto-correct.
16
+ # Configuration parameters: EnforcedStyle, SupportedStyles.
17
+ Style/EmptyLinesAroundModuleBody:
18
+ Enabled: false
19
+
20
+ # Offense count: 1
21
+ # Configuration parameters: Exclude.
22
+ Style/FileName:
23
+ Enabled: false
24
+
25
+ # Offense count: 1
26
+ # Cop supports --auto-correct.
27
+ Style/SingleSpaceBeforeFirstArg:
28
+ Enabled: false
29
+
30
+ # Offense count: 3
31
+ # Cop supports --auto-correct.
32
+ # Configuration parameters: EnforcedStyleForMultiline, SupportedStyles.
33
+ Style/TrailingComma:
34
+ Enabled: false
35
+
36
+ # Offense count: 6
37
+ # Cop supports --auto-correct.
38
+ # Configuration parameters: WordRegex.
39
+ Style/WordArray:
40
+ MinSize: 2
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ rvm:
2
+ - 1.9.3
3
+ - 2.1.2
4
+ - 2.2.3
5
+ gemfile:
6
+ - Gemfile
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'http://rubygems.org'
2
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,75 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ filterable-by (0.4.0)
5
+ activerecord
6
+ activesupport
7
+
8
+ GEM
9
+ remote: http://rubygems.org/
10
+ specs:
11
+ activemodel (4.2.4)
12
+ activesupport (= 4.2.4)
13
+ builder (~> 3.1)
14
+ activerecord (4.2.4)
15
+ activemodel (= 4.2.4)
16
+ activesupport (= 4.2.4)
17
+ arel (~> 6.0)
18
+ activesupport (4.2.4)
19
+ i18n (~> 0.7)
20
+ json (~> 1.7, >= 1.7.7)
21
+ minitest (~> 5.1)
22
+ thread_safe (~> 0.3, >= 0.3.4)
23
+ tzinfo (~> 1.1)
24
+ arel (6.0.3)
25
+ ast (2.0.0)
26
+ astrolabe (1.3.0)
27
+ parser (>= 2.2.0.pre.3, < 3.0)
28
+ builder (3.2.2)
29
+ diff-lcs (1.2.5)
30
+ i18n (0.7.0)
31
+ json (1.8.3)
32
+ minitest (5.8.2)
33
+ parser (2.2.2.5)
34
+ ast (>= 1.1, < 3.0)
35
+ powerpack (0.1.1)
36
+ rainbow (2.0.0)
37
+ rake (10.4.2)
38
+ rspec (3.3.0)
39
+ rspec-core (~> 3.3.0)
40
+ rspec-expectations (~> 3.3.0)
41
+ rspec-mocks (~> 3.3.0)
42
+ rspec-core (3.3.2)
43
+ rspec-support (~> 3.3.0)
44
+ rspec-expectations (3.3.1)
45
+ diff-lcs (>= 1.2.0, < 2.0)
46
+ rspec-support (~> 3.3.0)
47
+ rspec-mocks (3.3.2)
48
+ diff-lcs (>= 1.2.0, < 2.0)
49
+ rspec-support (~> 3.3.0)
50
+ rspec-support (3.3.0)
51
+ rubocop (0.32.0)
52
+ astrolabe (~> 1.3)
53
+ parser (>= 2.2.2.5, < 3.0)
54
+ powerpack (~> 0.1)
55
+ rainbow (>= 1.99.1, < 3.0)
56
+ ruby-progressbar (~> 1.4)
57
+ ruby-progressbar (1.7.5)
58
+ sqlite3 (1.3.10)
59
+ thread_safe (0.3.5)
60
+ tzinfo (1.2.2)
61
+ thread_safe (~> 0.1)
62
+
63
+ PLATFORMS
64
+ ruby
65
+
66
+ DEPENDENCIES
67
+ bundler
68
+ filterable-by!
69
+ rake
70
+ rspec
71
+ rubocop
72
+ sqlite3
73
+
74
+ BUNDLED WITH
75
+ 1.10.6
data/README.md ADDED
@@ -0,0 +1,62 @@
1
+ # Filterable By
2
+
3
+ ActiveRecord plugin to parse e.g. a `filter` query parameter apply scopes. Useful for [JSON-API][jsonapi] compatibility.
4
+
5
+ [jsonapi]: http://jsonapi.org/format/#fetching-filtering
6
+
7
+ ## Installation
8
+
9
+ Add `gem 'filterable-by'` to your Gemfile.
10
+
11
+ ## Usage
12
+
13
+ ```ruby
14
+ class Comment < ActiveRecord::Base
15
+ belongs_to :post
16
+
17
+ filterable_by :post_id, :user_id
18
+ filterable_by :post_author_id do |scope, value|
19
+ scope.joins(:posts).where(:"posts.author_id" => value)
20
+ end
21
+ end
22
+
23
+ Comment.filter_by(params[:filter]) # => ActiveRecord::Relation
24
+ ```
25
+
26
+ Simple use cases:
27
+
28
+ ```ruby
29
+ Comment.filter_by({ "post_id" => "1" })
30
+ # => WHERE post_id = 1
31
+
32
+ Comment.filter_by({ "user_id" => "2", "ignored" => "3" })
33
+ # => WHERE user_id = 2
34
+
35
+ Comment.filter_by({ "post_author_id" => "5" })
36
+ # => JOINS posts ON posts.id = comments.post_id WHERE posts.author_id = 5
37
+ ```
38
+
39
+ ## LICENCE
40
+
41
+ ```
42
+ Copyright (c) 2015 Black Square Media
43
+
44
+ Permission is hereby granted, free of charge, to any person obtaining
45
+ a copy of this software and associated documentation files (the
46
+ "Software"), to deal in the Software without restriction, including
47
+ without limitation the rights to use, copy, modify, merge, publish,
48
+ distribute, sublicense, and/or sell copies of the Software, and to
49
+ permit persons to whom the Software is furnished to do so, subject to
50
+ the following conditions:
51
+
52
+ The above copyright notice and this permission notice shall be
53
+ included in all copies or substantial portions of the Software.
54
+
55
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
56
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
57
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
58
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
59
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
60
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
61
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
62
+ ```
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require 'bundler/setup'
2
+ require 'bundler/gem_tasks'
3
+ require 'rspec/core/rake_task'
4
+ require 'rubocop/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+ RuboCop::RakeTask.new(:rubocop)
8
+
9
+ task default: [:spec, :rubocop]
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ Gem::Specification.new do |s|
3
+ s.name = 'filterable-by'
4
+ s.version = '0.4.0'
5
+ s.authors = ['Dimitrij Denissenko']
6
+ s.email = ['dimitrij@blacksquaremedia.com']
7
+ s.summary = 'Generate white-listed filter scopes from URL parameter values'
8
+ s.description = 'ActiveRecord plugin'
9
+ s.homepage = 'https://github.com/bsm/filterable-by'
10
+ s.license = 'MIT'
11
+
12
+ s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^spec/}) }
13
+ s.test_files = `git ls-files -z -- spec/*`.split("\x0")
14
+ s.require_paths = ['lib']
15
+ s.required_ruby_version = '>= 1.9.3'
16
+
17
+ s.add_dependency 'activesupport'
18
+ s.add_dependency 'activerecord'
19
+
20
+ s.add_development_dependency 'bundler'
21
+ s.add_development_dependency 'rake'
22
+ s.add_development_dependency 'rspec'
23
+ s.add_development_dependency 'rubocop'
24
+ s.add_development_dependency 'sqlite3'
25
+ end
@@ -0,0 +1 @@
1
+ require 'filterable_by'
@@ -0,0 +1,45 @@
1
+ require 'active_record'
2
+ require 'active_support/concern'
3
+ require 'set'
4
+
5
+ module ActiveRecord
6
+ module FilterableByHelper
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ class_attribute :_filterable_by_scope_options, instance_accessor: false
11
+ self._filterable_by_scope_options = {}
12
+ end
13
+
14
+ module ClassMethods
15
+
16
+ def filterable_by(*names, &block)
17
+ names.each do |name|
18
+ _filterable_by_scope_options[name.to_s] = block || ->(scope, v) { scope.where(name.to_sym => v) }
19
+ end
20
+ end
21
+
22
+ # @param [Hash] hash the filter params
23
+ # @return [ActiveRecord::Relation] the scoped relation
24
+ def filter_by(hash)
25
+ scope = all
26
+ return scope unless hash.is_a?(Hash)
27
+
28
+ _filterable_by_scope_options.each do |name, block|
29
+ next unless hash.key?(name)
30
+
31
+ value = hash[name]
32
+ next unless (value.is_a?(String) && value.present?) || value.is_a?(Numeric)
33
+
34
+ scope = block.call(scope, value)
35
+ end
36
+ scope
37
+ end
38
+
39
+ end
40
+ end
41
+
42
+ class Base
43
+ include FilterableByHelper
44
+ end
45
+ end
@@ -0,0 +1,46 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe ActiveRecord::FilterableByHelper do
4
+
5
+ let(:alice) { AUTHORS[:alice] }
6
+ let(:bob) { AUTHORS[:bob] }
7
+
8
+ let(:apost) { POSTS[:alices] }
9
+ let(:bpost) { POSTS[:bobs] }
10
+
11
+ it 'should have config' do
12
+ expect(Comment._filterable_by_scope_options.size).to eq(3)
13
+ end
14
+
15
+ it 'should ignore bad inputs' do
16
+ expect(Comment.filter_by(nil).count).to eq(4)
17
+ expect(Comment.filter_by({}).count).to eq(4)
18
+
19
+ expect(Comment.filter_by('author_id' => '').count).to eq(4)
20
+ expect(Comment.filter_by('author_id' => []).count).to eq(4)
21
+ end
22
+
23
+ it 'should generate simple scopes' do
24
+ expect(Comment.filter_by('author_id' => alice.id).pluck(:title)).to match_array(['AA', 'AB'])
25
+ expect(Comment.filter_by('author_id' => bob.id).pluck(:title)).to match_array(['BA', 'BB'])
26
+
27
+ expect(Comment.filter_by('post_id' => apost.id).pluck(:title)).to match_array(['AA', 'BA'])
28
+ expect(Comment.filter_by('post_id' => bpost.id).pluck(:title)).to match_array(['AB', 'BB'])
29
+
30
+ expect(Comment.filter_by('post_author_id' => alice.id).pluck(:title)).to match_array(['AA', 'BA'])
31
+ expect(Comment.filter_by('post_author_id' => bob.id).pluck(:title)).to match_array(['AB', 'BB'])
32
+ end
33
+
34
+ it 'should generate combined scopes' do
35
+ expect(Comment.filter_by('author_id' => alice.id, 'post_id' => apost.id).pluck(:title)).to match_array(['AA'])
36
+ expect(Comment.filter_by('author_id' => alice.id, 'post_id' => bpost.id).pluck(:title)).to match_array(['AB'])
37
+ expect(Comment.filter_by('author_id' => bob.id, 'post_id' => apost.id).pluck(:title)).to match_array(['BA'])
38
+ expect(Comment.filter_by('author_id' => bob.id, 'post_id' => bpost.id).pluck(:title)).to match_array(['BB'])
39
+ end
40
+
41
+ it 'should combine with other scopes' do
42
+ scope = Comment.where(author_id: alice.id).filter_by('post_id' => apost.id)
43
+ expect(scope.pluck(:title)).to match_array(['AA'])
44
+ end
45
+
46
+ end
@@ -0,0 +1,53 @@
1
+ ENV['RACK_ENV'] ||= 'test'
2
+ require 'filterable-by'
3
+ require 'rspec'
4
+
5
+ ActiveRecord::Base.configurations['test'] = { 'adapter' => 'sqlite3', 'database' => ':memory:' }
6
+ ActiveRecord::Base.establish_connection :test
7
+
8
+ ActiveRecord::Base.connection.instance_eval do
9
+ create_table :authors do |_|
10
+ end
11
+ create_table :posts do |t|
12
+ t.integer :author_id, null: false
13
+ end
14
+ create_table :comments do |t|
15
+ t.string :title, null: false
16
+ t.integer :post_id, null: false
17
+ t.integer :author_id, null: false
18
+ end
19
+ end
20
+
21
+ class Author < ActiveRecord::Base
22
+ end
23
+
24
+ class Post < ActiveRecord::Base
25
+ belongs_to :author
26
+ end
27
+
28
+ class Comment < ActiveRecord::Base
29
+ belongs_to :author
30
+ belongs_to :post
31
+
32
+ filterable_by :post_id, :author_id
33
+ filterable_by :post_author_id do |scope, value|
34
+ scope.joins(:post).where(Post.arel_table[:author_id].eq(value))
35
+ end
36
+ end
37
+
38
+ AUTHORS = {
39
+ alice: Author.create!,
40
+ bob: Author.create!,
41
+ }
42
+
43
+ POSTS = {
44
+ alices: Post.create!(author_id: AUTHORS[:alice].id),
45
+ bobs: Post.create!(author_id: AUTHORS[:bob].id),
46
+ }
47
+
48
+ COMMENTS = {
49
+ alice_on_alice: Comment.create!(title: 'AA', post_id: POSTS[:alices].id, author_id: AUTHORS[:alice].id),
50
+ bob_on_alice: Comment.create!(title: 'BA', post_id: POSTS[:alices].id, author_id: AUTHORS[:bob].id),
51
+ alice_on_bob: Comment.create!(title: 'AB', post_id: POSTS[:bobs].id, author_id: AUTHORS[:alice].id),
52
+ bob_on_bob: Comment.create!(title: 'BB', post_id: POSTS[:bobs].id, author_id: AUTHORS[:bob].id),
53
+ }
metadata ADDED
@@ -0,0 +1,157 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: filterable-by
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.0
5
+ platform: ruby
6
+ authors:
7
+ - Dimitrij Denissenko
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-10-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activerecord
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
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'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: sqlite3
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ description: ActiveRecord plugin
112
+ email:
113
+ - dimitrij@blacksquaremedia.com
114
+ executables: []
115
+ extensions: []
116
+ extra_rdoc_files: []
117
+ files:
118
+ - ".gitignore"
119
+ - ".rubocop.yml"
120
+ - ".rubocop_todo.yml"
121
+ - ".travis.yml"
122
+ - Gemfile
123
+ - Gemfile.lock
124
+ - README.md
125
+ - Rakefile
126
+ - filterable-by.gemspec
127
+ - lib/filterable-by.rb
128
+ - lib/filterable_by.rb
129
+ - spec/filterable_by_spec.rb
130
+ - spec/spec_helper.rb
131
+ homepage: https://github.com/bsm/filterable-by
132
+ licenses:
133
+ - MIT
134
+ metadata: {}
135
+ post_install_message:
136
+ rdoc_options: []
137
+ require_paths:
138
+ - lib
139
+ required_ruby_version: !ruby/object:Gem::Requirement
140
+ requirements:
141
+ - - ">="
142
+ - !ruby/object:Gem::Version
143
+ version: 1.9.3
144
+ required_rubygems_version: !ruby/object:Gem::Requirement
145
+ requirements:
146
+ - - ">="
147
+ - !ruby/object:Gem::Version
148
+ version: '0'
149
+ requirements: []
150
+ rubyforge_project:
151
+ rubygems_version: 2.4.7
152
+ signing_key:
153
+ specification_version: 4
154
+ summary: Generate white-listed filter scopes from URL parameter values
155
+ test_files:
156
+ - spec/filterable_by_spec.rb
157
+ - spec/spec_helper.rb