activerecord-like 0.0.1
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.
- data/.gitignore +18 -0
- data/Gemfile +5 -0
- data/LICENSE.txt +22 -0
- data/README.md +38 -0
- data/Rakefile +13 -0
- data/activerecord-like.gemspec +22 -0
- data/lib/active_record/like.rb +25 -0
- data/lib/active_record/like/version.rb +5 -0
- data/test/helper.rb +17 -0
- data/test/integration/like_test.rb +48 -0
- data/test/integration/not_like_test.rb +49 -0
- data/test/unit/like_test.rb +28 -0
- data/test/unit/not_like_test.rb +29 -0
- metadata +115 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 René van den Berg
|
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/README.md
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# ActiveRecord::Like
|
2
|
+
|
3
|
+
An Active Record Plugin that allows chaining a more DSL-style 'like' or 'not-like' query to an ActiveRecord::Base#where. Requires Rails 4 beta or higher.
|
4
|
+
|
5
|
+
This plugin has been salvaged from the stellar work done by @amatsuda and @claudiob. Most of the code was previously in Active Record master, but was subsequently removed due to, amongst other, scope creep.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
gem 'activerecord-like'
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install activerecord-like
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
Given a class Post and the WhereChain work in Active Record, this plugin allows code like:
|
24
|
+
|
25
|
+
Post.where.like(title: "%rails%")
|
26
|
+
|
27
|
+
and
|
28
|
+
|
29
|
+
Post.where.not_like(title: "%rails%")
|
30
|
+
|
31
|
+
## Contributing
|
32
|
+
|
33
|
+
1. Fork it
|
34
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
35
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
36
|
+
4. Make sure the tests run. I will not merge commits that change code without testing the new code.
|
37
|
+
5. Push to the branch (`git push origin my-new-feature`)
|
38
|
+
6. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
require 'bundler/gem_tasks'
|
3
|
+
require 'rake/testtask'
|
4
|
+
|
5
|
+
Rake::TestTask.new do |t|
|
6
|
+
t.libs = ["test"]
|
7
|
+
t.pattern = "test/**/*_test.rb"
|
8
|
+
|
9
|
+
# also warn about bad practices and such
|
10
|
+
t.ruby_opts = ['-w']
|
11
|
+
end
|
12
|
+
|
13
|
+
task :default => :test
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/active_record/like/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.name = "activerecord-like"
|
6
|
+
gem.version = ActiveRecord::Like::VERSION
|
7
|
+
gem.authors = ["René van den Berg"]
|
8
|
+
gem.email = ["rene.vandenberg@ogd.nl"]
|
9
|
+
gem.description = %q{An ActiveRecord plugin providing a higher-level abstraction for SQL 'LIKE' queries}
|
10
|
+
gem.summary = %q{ActiveRecord::Like provides ActiveRecord::Base with where.like(attribute: string)-style extensions. This functionality was, at one point, included in Rails-master, but subsequently removed. Since the feature seemed to be in some demand, I thought I'd try my hand at building an ActiveRecord plugin}
|
11
|
+
gem.homepage = "http://github.com/ReneB/activerecord-like"
|
12
|
+
|
13
|
+
gem.files = `git ls-files`.split($/)
|
14
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
15
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
16
|
+
gem.require_paths = ["lib"]
|
17
|
+
|
18
|
+
gem.add_development_dependency "minitest", ">= 3"
|
19
|
+
gem.add_development_dependency "sqlite3", "~> 1.3"
|
20
|
+
|
21
|
+
gem.add_dependency "activerecord", ">= 4.0.0"
|
22
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require "active_record"
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module QueryMethods
|
5
|
+
module Like
|
6
|
+
def like(opts, *rest)
|
7
|
+
@scope.tap do |s|
|
8
|
+
s.where_values += s.send(:build_where, opts, *rest).map do |r|
|
9
|
+
Arel::Nodes::Matches.new(r.left, r.right)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def not_like(opts, *rest)
|
15
|
+
@scope.tap do |s|
|
16
|
+
s.where_values += s.send(:build_where, opts, *rest).map do |r|
|
17
|
+
Arel::Nodes::DoesNotMatch.new(r.left, r.right)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
ActiveRecord::QueryMethods::WhereChain.send(:include, ActiveRecord::QueryMethods::Like)
|
data/test/helper.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'minitest/spec'
|
3
|
+
require 'minitest/autorun'
|
4
|
+
require 'active_record/like'
|
5
|
+
|
6
|
+
ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
|
7
|
+
|
8
|
+
ActiveRecord::Schema.verbose = false
|
9
|
+
ActiveRecord::Schema.define do
|
10
|
+
create_table :posts do |t|
|
11
|
+
t.string :title
|
12
|
+
t.string :category
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class Post < ActiveRecord::Base
|
17
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require File.expand_path('../../helper', __FILE__)
|
2
|
+
|
3
|
+
describe ActiveRecord::QueryMethods::WhereChain do
|
4
|
+
describe :like do
|
5
|
+
before do
|
6
|
+
Post.create(id: 1, title: 'We need some content to test with')
|
7
|
+
Post.create(id: 2, title: 'I really like DSLs - see what I did there?')
|
8
|
+
end
|
9
|
+
|
10
|
+
after do
|
11
|
+
Post.delete_all
|
12
|
+
end
|
13
|
+
|
14
|
+
it "finds records with attributes matching the criteria" do
|
15
|
+
Post.where.like(title: '%there?').map(&:id).must_include 2
|
16
|
+
end
|
17
|
+
|
18
|
+
it "is case-insensitive" do
|
19
|
+
search_term = "%dsls"
|
20
|
+
|
21
|
+
lowercase_posts = Post.where.like(title: search_term)
|
22
|
+
uppercase_posts = Post.where.like(title: search_term.upcase)
|
23
|
+
|
24
|
+
lowercase_posts.map(&:id).must_equal(uppercase_posts.map(&:id))
|
25
|
+
end
|
26
|
+
|
27
|
+
it "does not find records with attributes not matching the criteria" do
|
28
|
+
Post.where.like(title: '%this title is not used anywhere%').map(&:id).wont_include 2
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "security-related behavior" do
|
32
|
+
before do
|
33
|
+
@user_input = "unused%' OR 1=1); --"
|
34
|
+
end
|
35
|
+
|
36
|
+
# This test is only here to provide the contrast for the test below
|
37
|
+
# Interpolating input strings into LIKE queries is an all-too-common
|
38
|
+
# mistake that is prevented by the syntax this plugin provides
|
39
|
+
it "is possible to inject SQL into literal query strings" do
|
40
|
+
Post.where("title LIKE '%#{@user_input}%'").count.must_equal(2)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "prevents SQL injection" do
|
44
|
+
Post.where.like(title: @user_input).count.must_equal(0)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require File.expand_path('../../helper', __FILE__)
|
2
|
+
|
3
|
+
describe ActiveRecord::QueryMethods::WhereChain do
|
4
|
+
describe :not_like do
|
5
|
+
before do
|
6
|
+
Post.create(id: 1, title: 'We need some content to test with')
|
7
|
+
Post.create(id: 2, title: 'I really like DSLs - see what I did there?')
|
8
|
+
end
|
9
|
+
|
10
|
+
after do
|
11
|
+
Post.delete_all
|
12
|
+
end
|
13
|
+
|
14
|
+
it "finds records with attributes not matching the criteria" do
|
15
|
+
Post.where.not_like(title: '%there?').map(&:id).wont_include 2
|
16
|
+
end
|
17
|
+
|
18
|
+
it "is case-insensitive" do
|
19
|
+
search_term = "%dsls"
|
20
|
+
|
21
|
+
lowercase_posts = Post.where.not_like(title: search_term)
|
22
|
+
uppercase_posts = Post.where.not_like(title: search_term.upcase)
|
23
|
+
|
24
|
+
lowercase_posts.map(&:id).must_equal(uppercase_posts.map(&:id))
|
25
|
+
end
|
26
|
+
|
27
|
+
it "does not find records with attributes matching the criteria" do
|
28
|
+
Post.where.not_like(title: '%this title is not used anywhere%').map(&:id).must_include 2
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "security-related behavior" do
|
32
|
+
before do
|
33
|
+
@user_input = "unused%' OR 1=1); --"
|
34
|
+
end
|
35
|
+
|
36
|
+
# This test is only here to provide the contrast for the test below
|
37
|
+
# Interpolating input strings into LIKE queries is an all-too-common
|
38
|
+
# mistake that is prevented by the syntax this plugin provides
|
39
|
+
it "is possible to inject SQL into literal query strings" do
|
40
|
+
Post.where("title NOT LIKE '%#{@user_input}%'").count.must_equal(2)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "prevents SQL injection" do
|
44
|
+
Post.where.not_like(title: @user_input).count.must_equal(2)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require File.expand_path('../../helper', __FILE__)
|
2
|
+
|
3
|
+
describe ActiveRecord::QueryMethods::WhereChain do
|
4
|
+
describe :like do
|
5
|
+
it "creates an Arel Matches node in the relation" do
|
6
|
+
relation = Post.where.like(title: '')
|
7
|
+
|
8
|
+
relation.where_values.first.must_be_instance_of(Arel::Nodes::Matches)
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "the Arel Node" do
|
12
|
+
before do
|
13
|
+
@attribute = "title"
|
14
|
+
@value = '%value%'
|
15
|
+
|
16
|
+
@relation_specifier = Post.where.like(@attribute => @value).where_values.first
|
17
|
+
end
|
18
|
+
|
19
|
+
it "has the attribute as the left operand" do
|
20
|
+
@relation_specifier.left.name.must_equal @attribute
|
21
|
+
end
|
22
|
+
|
23
|
+
it "has the value as the right operand" do
|
24
|
+
@relation_specifier.right.must_equal @value
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require File.expand_path('../../helper', __FILE__)
|
2
|
+
|
3
|
+
describe ActiveRecord::QueryMethods::WhereChain do
|
4
|
+
describe :not_like do
|
5
|
+
it "creates an Arel DoesNotMatch node in the relation" do
|
6
|
+
relation = Post.where.not_like(title: '')
|
7
|
+
|
8
|
+
relation.where_values.first.must_be_instance_of(Arel::Nodes::DoesNotMatch)
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "the Arel Node" do
|
12
|
+
before do
|
13
|
+
@attribute = "title"
|
14
|
+
@value = '%value%'
|
15
|
+
|
16
|
+
@relation_specifier = Post.where.not_like(@attribute => @value).where_values.first
|
17
|
+
end
|
18
|
+
|
19
|
+
it "has the attribute as the left operand" do
|
20
|
+
@relation_specifier.left.name.must_equal @attribute
|
21
|
+
end
|
22
|
+
|
23
|
+
it "has the value as the right operand" do
|
24
|
+
@relation_specifier.right.must_equal @value
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
metadata
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: activerecord-like
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- René van den Berg
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-06-26 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: minitest
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '3'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '3'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: sqlite3
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '1.3'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '1.3'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: activerecord
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 4.0.0
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 4.0.0
|
62
|
+
description: An ActiveRecord plugin providing a higher-level abstraction for SQL 'LIKE'
|
63
|
+
queries
|
64
|
+
email:
|
65
|
+
- rene.vandenberg@ogd.nl
|
66
|
+
executables: []
|
67
|
+
extensions: []
|
68
|
+
extra_rdoc_files: []
|
69
|
+
files:
|
70
|
+
- .gitignore
|
71
|
+
- Gemfile
|
72
|
+
- LICENSE.txt
|
73
|
+
- README.md
|
74
|
+
- Rakefile
|
75
|
+
- activerecord-like.gemspec
|
76
|
+
- lib/active_record/like.rb
|
77
|
+
- lib/active_record/like/version.rb
|
78
|
+
- test/helper.rb
|
79
|
+
- test/integration/like_test.rb
|
80
|
+
- test/integration/not_like_test.rb
|
81
|
+
- test/unit/like_test.rb
|
82
|
+
- test/unit/not_like_test.rb
|
83
|
+
homepage: http://github.com/ReneB/activerecord-like
|
84
|
+
licenses: []
|
85
|
+
post_install_message:
|
86
|
+
rdoc_options: []
|
87
|
+
require_paths:
|
88
|
+
- lib
|
89
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
90
|
+
none: false
|
91
|
+
requirements:
|
92
|
+
- - ! '>='
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
96
|
+
none: false
|
97
|
+
requirements:
|
98
|
+
- - ! '>='
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: '0'
|
101
|
+
requirements: []
|
102
|
+
rubyforge_project:
|
103
|
+
rubygems_version: 1.8.24
|
104
|
+
signing_key:
|
105
|
+
specification_version: 3
|
106
|
+
summary: ! 'ActiveRecord::Like provides ActiveRecord::Base with where.like(attribute:
|
107
|
+
string)-style extensions. This functionality was, at one point, included in Rails-master,
|
108
|
+
but subsequently removed. Since the feature seemed to be in some demand, I thought
|
109
|
+
I''d try my hand at building an ActiveRecord plugin'
|
110
|
+
test_files:
|
111
|
+
- test/helper.rb
|
112
|
+
- test/integration/like_test.rb
|
113
|
+
- test/integration/not_like_test.rb
|
114
|
+
- test/unit/like_test.rb
|
115
|
+
- test/unit/not_like_test.rb
|