filterable-by 0.4.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 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