wherex 1.0.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.
data/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ # rcov generated
2
+ coverage
3
+
4
+ # rdoc generated
5
+ rdoc
6
+
7
+ # yard generated
8
+ doc
9
+ .yardoc
10
+
11
+ # bundler
12
+ .bundle
13
+
14
+ # jeweler generated
15
+ pkg
16
+
17
+ # ignore Gemfile.lock because it is rewritten by the tests
18
+ Gemfile.lock
19
+
data/Gemfile ADDED
@@ -0,0 +1,28 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ gem 'arel', '~> 2.0'
5
+ gem 'activerecord', '~> 3.0'
6
+
7
+ case ENV["RAILS_DB"]
8
+ when "postgres"
9
+ gem 'pg'
10
+ when "mysql"
11
+ gem 'mysql2'
12
+ else
13
+ gem 'sqlite3'
14
+ end
15
+
16
+ # Add dependencies to develop your gem here.
17
+ # Include everything needed to run rake, tests, features, etc.
18
+ group :development do
19
+ gem "bundler", "~> 1.0.0"
20
+ gem "jeweler", "~> 1.5.2"
21
+ gem "rcov", ">= 0"
22
+ end
23
+
24
+ group :test do
25
+ gem 'redgreen'
26
+ gem 'turn'
27
+ end
28
+
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Jason King
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,106 @@
1
+ # wherex
2
+
3
+ Regexp support to ActiveRecord finders.
4
+
5
+ ## Howto?
6
+
7
+ In your Gemfile
8
+
9
+ gem 'wherex'
10
+
11
+ Then run `bundle install`
12
+
13
+ ## What the?
14
+
15
+ # find users in 93, 94 and 95 (5 digit) zipcodes (new style finder)
16
+ User.where :zipcode => /^9[345][0-9]{3}$/
17
+
18
+ # find students with invalid characters in their names (old style finder)
19
+ Student.all :conditions => { :name => /[^a-zA-Z ]/ }
20
+
21
+ # find products with a complex code structure (dynamic finders)
22
+ Product.find_by_code /^[NRW][^-]+-[456]/
23
+
24
+ ## Limitations?
25
+
26
+ ### POSIX Only (mostly)
27
+
28
+ Most DBs only support POSIX regexps!
29
+
30
+ Let me repeat? POSIX only! So, some examples of things to know:
31
+
32
+ * `^` and `$` have their POSIX meanings, so beginning and end of the whole string, not of each line within the string. Also `\A` and `\Z` have no special meaning.
33
+ * Convenience character classes like `\w \W \d \D \s \S`
34
+ * Capturing parens won't break the RE, but there's no capturing (so you can't use what you captured)
35
+ * No extended patterns, so `(?i:foo)` won't work, nor will `(?:bar)` and nor will `(?i)foo`
36
+ * No look-around assertions work, so no `foo(?!bar)`
37
+
38
+ ### SQLite is very slow
39
+
40
+ SQLite's implementation of REGEXP is actually a callback to a user defined
41
+ function, which this gem provides in Ruby. This is **really cool™** because
42
+ SQLite users get to use full Ruby regexps (ie. **none** of the limitations above
43
+ apply) but it comes at a cost.
44
+
45
+ On a relatively small data set (say 1000 rows) my MySQL testing ran through all
46
+ records in 2.3ms whereas SQLite took 84.6ms (because it has to call out to Ruby
47
+ for each row tested).
48
+
49
+ So, don't be surprised if SQLite falls in a heap, and I'd recommend just
50
+ abandoning this idea for any production usage.
51
+
52
+ ### Only SQLite, MySQL and PostgreSQL supported out of the box
53
+
54
+ You can add your own support for any other DB that supports regexp. I know that
55
+ Oracle has a regexp function, and I believe that MSSQL has a regexp XP (a Perl
56
+ RE one actually).
57
+
58
+ Here's how I'd add in Oracle support:
59
+
60
+ # config/initializers/wherex.rb
61
+ module Wherex
62
+ module OracleEnhancedAdapter
63
+ def regexp left, right
64
+ "REGEXP_LIKE( #{left}, #{right} )"
65
+ end
66
+
67
+ def regexp_not left, right
68
+ "NOT #{regexp left, right}"
69
+ end
70
+ end
71
+ end
72
+
73
+ That's it (and the 'NOT' is only necessary if you use meta_where or something
74
+ that enables negative Arel statements to be generated). You just have to make
75
+ sure it is named correctly (needs to be the same as your actual adapter, so if
76
+ you're using `SomeOtherOracleAdapter` then you need to name your module that
77
+ too).
78
+
79
+ Wherex will pick this module up, and add it into the right place. Just see
80
+ `lib/wherex/adapters.rb` for more examples.
81
+
82
+ ## Testing
83
+
84
+ There are test suites for all three database engines that ship with Rails, the
85
+ default is SQLite and requires no preconfiguration. So you can just clone the
86
+ repo and run `rake` and it will run the tests against SQLite
87
+
88
+ If you want to run the tests against MySQL or PostgreSQL then you will first
89
+ need to create a `wherex_test` database in your local machine. Then you will
90
+ need to provide the `RAILS_DB` environment variable to rake, eg:
91
+
92
+ RAILS_DB=mysql rake
93
+
94
+ …or…
95
+
96
+ RAILS_DB=postgres rake
97
+
98
+ These will use a default user of `root` for MySQL and `postgres` for PostgreSQL.
99
+ If you want to use different usernames or passwords then take a look in the
100
+ `config/database.yml` file and either provide the appropriate environment
101
+ variables, or edit the file itself.
102
+
103
+ ## Copyright
104
+
105
+ Copyright © 2011 Jason King. See LICENSE.txt for further details.
106
+
data/Rakefile ADDED
@@ -0,0 +1,31 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'rake'
11
+
12
+ spec = Gem::Specification.load( File.join( File.dirname(__FILE__), 'wherex.gemspec' ) )
13
+
14
+ $:.push File.expand_path("../lib", __FILE__)
15
+ require "wherex/version"
16
+
17
+ require 'rake/testtask'
18
+ Rake::TestTask.new(:test) do |test|
19
+ test.libs << 'lib' << 'test'
20
+ test.pattern = 'test/**/test_*.rb'
21
+ test.verbose = true
22
+ end
23
+
24
+ require 'rcov/rcovtask'
25
+ Rcov::RcovTask.new do |test|
26
+ test.libs << 'test'
27
+ test.pattern = 'test/**/test_*.rb'
28
+ test.verbose = true
29
+ end
30
+
31
+ task :default => :test
@@ -0,0 +1,35 @@
1
+ module Wherex
2
+ module AbstractAdapter
3
+
4
+ def regexp_operator
5
+ "REGEXP"
6
+ end
7
+
8
+ def regexp_not_operator
9
+ "NOT #{regexp_operator}"
10
+ end
11
+
12
+ def regexp left, right
13
+ "#{left} #{regexp_operator} #{right}"
14
+ end
15
+
16
+ def regexp_not left, right
17
+ "#{left} #{regexp_not_operator} #{right}"
18
+ end
19
+
20
+ def regexp_quote str
21
+ quote str
22
+ end
23
+ end
24
+
25
+ module PostgreSQLAdapter
26
+
27
+ def regexp_operator
28
+ "~"
29
+ end
30
+
31
+ def regexp_not_operator
32
+ "!~"
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,35 @@
1
+ module Wherex
2
+ module ActiveRecord
3
+ def self.extended base
4
+ base.class_eval do
5
+ class << self
6
+ alias_method_chain :establish_connection, :wherex
7
+ end
8
+ end
9
+ end
10
+
11
+ def establish_connection_with_wherex spec = nil
12
+ establish_connection_without_wherex spec
13
+
14
+ require 'wherex/adapters'
15
+ ::ActiveRecord::ConnectionAdapters::AbstractAdapter.send :include, AbstractAdapter
16
+
17
+ adapter = ::ActiveRecord::Base.connection.class
18
+ my_adapter_name = adapter.to_s.demodulize
19
+
20
+ begin
21
+ my_adapter = "Wherex::#{my_adapter_name}".constantize
22
+ adapter.send :include, my_adapter
23
+ rescue NameError => e
24
+ end
25
+
26
+ if defined? ::ActiveRecord::ConnectionAdapters::SQLiteAdapter
27
+ ::ActiveRecord::Base.connection.raw_connection.create_function( "regexp", 2 ) do |context, pattern, string|
28
+ if string.present?
29
+ context.result = 1 if string.match pattern
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,3 @@
1
+ module Wherex
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,33 @@
1
+ module Wherex
2
+ module Visitor
3
+
4
+ def self.included base
5
+ base.class_eval do
6
+ alias_method_chain :visit_Arel_Nodes_Equality, :wherex
7
+ alias_method_chain :visit_Arel_Nodes_NotEqual, :wherex
8
+ end
9
+ end
10
+
11
+ def visit_Arel_Nodes_Equality_with_wherex o
12
+ right = o.right
13
+ if right.present? and right.is_a? Regexp
14
+ @connection.regexp visit(o.left), visit(right)
15
+ else
16
+ visit_Arel_Nodes_Equality_without_wherex o
17
+ end
18
+ end
19
+
20
+ def visit_Arel_Nodes_NotEqual_with_wherex o
21
+ right = o.right
22
+ if right.present? and right.is_a? Regexp
23
+ @connection.regexp_not visit(o.left), visit(right)
24
+ else
25
+ visit_Arel_Nodes_NotEqual_without_wherex o
26
+ end
27
+ end
28
+
29
+ def visit_Regexp o; @connection.regexp_quote(o.source) end
30
+
31
+ end
32
+ end
33
+
data/lib/wherex.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'arel'
2
+ require 'active_record'
3
+ require 'active_support/core_ext'
4
+
5
+ require 'wherex/visitor'
6
+ require 'wherex/connection'
7
+ module Wherex
8
+ ::Arel::Visitors::ToSql.send :include, Visitor
9
+ ::ActiveRecord::Base.send :extend, ActiveRecord
10
+ end
@@ -0,0 +1,2 @@
1
+ class Product < ActiveRecord::Base
2
+ end
@@ -0,0 +1,2 @@
1
+ class Student < ActiveRecord::Base
2
+ end
@@ -0,0 +1,2 @@
1
+ class User < ActiveRecord::Base
2
+ end
@@ -0,0 +1,22 @@
1
+ test:
2
+ postgres:
3
+ adapter: postgresql
4
+ encoding: unicode
5
+ database: wherex_test
6
+ pool: 5
7
+ username: <%= ENV['POSTGRESQL_USERNAME'] || 'postgres' %>
8
+ password: <%= ENV['POSTGRESQL_PASSWORD'] || '' %>
9
+ mysql:
10
+ adapter: mysql2
11
+ encoding: utf8
12
+ reconnect: false
13
+ database: wherex_test
14
+ pool: 5
15
+ username: <%= ENV['MYSQL_USERNAME'] || 'root' %>
16
+ password: <%= ENV['MYSQL_PASSWORD'] || '' %>
17
+ socket: <%= ENV['MYSQL_SOCKET'] || '/tmp/mysql.sock' %>
18
+ sqlite:
19
+ adapter: sqlite3
20
+ database: test/db/test.sqlite3
21
+ pool: 5
22
+ timeout: 5000
Binary file
@@ -0,0 +1,15 @@
1
+ ---
2
+ one:
3
+ code: N123-4394-2344
4
+
5
+ two:
6
+ code: P123-4394-2234
7
+
8
+ three:
9
+ code: N343-899434-32434
10
+
11
+ four:
12
+ code: 1N34-4543-3434
13
+
14
+ five:
15
+ code: N2343-32343-4444
@@ -0,0 +1,19 @@
1
+ ActiveRecord::Schema.define do
2
+
3
+ create_table "users", :force => true do |t|
4
+ t.string "zipcode"
5
+ t.timestamps
6
+ end
7
+
8
+ create_table "students", :force => true do |t|
9
+ t.string "name"
10
+ t.timestamps
11
+ end
12
+
13
+ create_table "products", :force => true do |t|
14
+ t.string "code"
15
+ t.timestamps
16
+ end
17
+ end
18
+
19
+
@@ -0,0 +1,12 @@
1
+ ---
2
+ one:
3
+ name: Jimbo Jones
4
+
5
+ two:
6
+ name: Jóse Guervo
7
+
8
+ three:
9
+ name: Henri D'Havelan
10
+
11
+ four:
12
+ name: foo_bar
@@ -0,0 +1,27 @@
1
+ ---
2
+ one:
3
+ zipcode: 92345
4
+
5
+ two:
6
+ zipcode: 93456
7
+
8
+ three:
9
+ zipcode: 94567
10
+
11
+ four:
12
+ zipcode: 95678
13
+
14
+ five:
15
+ zipcode: 95abc
16
+
17
+ six:
18
+ zipcode: 9512
19
+
20
+ seven:
21
+ zipcode: 96789
22
+
23
+ eight:
24
+ zipcode: 194123
25
+
26
+ nine:
27
+ zipcode: 12345
data/test/helper.rb ADDED
@@ -0,0 +1,44 @@
1
+ ENV['RAILS_DB'] = ( ENV['RAILS_DB'] || 'sqlite' ).downcase
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ Bundler.setup
6
+ require 'test/unit'
7
+ begin; require 'redgreen'; rescue LoadError; end
8
+ begin; require 'turn'; rescue LoadError; end
9
+
10
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
11
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
12
+
13
+ require 'wherex'
14
+ require 'active_record/fixtures'
15
+
16
+ ROOT_PATH = File.expand_path( File.join( File.dirname(__FILE__) ))
17
+ FIXTURES_PATH = File.join(File.dirname(__FILE__), 'fixtures')
18
+
19
+ config = YAML::load(ERB.new(IO.read(File.join( ROOT_PATH, 'config', 'database.yml'))).result)['test'][ENV['RAILS_DB']]
20
+
21
+ ActiveRecord::Base.establish_connection config
22
+
23
+ dep = defined?(ActiveSupport::Dependencies) ? ActiveSupport::Dependencies : ::Dependencies
24
+ dep.autoload_paths.unshift File.join( ROOT_PATH, 'app/models' )
25
+
26
+ dep.autoload_paths.unshift FIXTURES_PATH
27
+
28
+ ActiveRecord::Base.silence do
29
+ ActiveRecord::Migration.verbose = false
30
+ load File.join(FIXTURES_PATH, 'schema.rb')
31
+ end
32
+
33
+ Fixtures.create_fixtures(FIXTURES_PATH, ActiveRecord::Base.connection.tables)
34
+
35
+ class Test::Unit::TestCase
36
+ def method_missing method, *args
37
+ candidate = method.to_s.classify
38
+ begin
39
+ candidate.constantize.find( Fixtures.identify args[0] )
40
+ rescue NameError
41
+ raise NameError.new "undefined local variable or method `#{method}`"
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,28 @@
1
+ require 'helper'
2
+
3
+ class TestWherex < Test::Unit::TestCase
4
+ def test_user_example
5
+ assert u = User.where( :zipcode => /^9[345][0-9]{3}$/ )
6
+ assert_equal 3, u.count
7
+ [:two, :three, :four ].each do |id|
8
+ assert u.include?( users(id) )
9
+ end
10
+ end
11
+
12
+ def test_product_example
13
+ assert p = Product.find_by_code( /^[NRW][^-]+-[456]/ )
14
+ assert_equal products(:one), p
15
+
16
+ assert p = Product.find_all_by_code( /^[NRW][^-]+-[456]/ )
17
+ assert_equal 1, p.count
18
+ assert_equal products(:one), p.first
19
+ end
20
+
21
+ def test_student_example
22
+ assert s = Student.all( :conditions => { :name => /[^a-zA-Z ]/ } )
23
+ assert_equal 3, s.count
24
+ [ :two, :three, :four ].each do |id|
25
+ assert s.include?( students(id) )
26
+ end
27
+ end
28
+ end
data/wherex.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ $:.push File.expand_path("../lib", __FILE__)
2
+ require "wherex/version"
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "wherex"
6
+ s.version = Wherex::VERSION
7
+ s.platform = Gem::Platform::RUBY
8
+ s.authors = ["Jason King"]
9
+ s.email = ["jk@handle.it"]
10
+ s.homepage = "http://github.com/smathy/wherex"
11
+ s.license = "MIT"
12
+ s.summary = %q{Regexp for ActiveRecord finders}
13
+ s.description = %q{This gem allows you to pass a Regexp as the value for any finder in ActiveRecord - Rails3 only}
14
+
15
+ s.rubyforge_project = "wherex"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+
22
+ s.add_dependency 'arel', '~> 2.0'
23
+ s.add_dependency 'activerecord', '~> 3.0'
24
+ end
metadata ADDED
@@ -0,0 +1,126 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: wherex
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease:
6
+ segments:
7
+ - 1
8
+ - 0
9
+ - 0
10
+ version: 1.0.0
11
+ platform: ruby
12
+ authors:
13
+ - Jason King
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-04-11 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: arel
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ~>
27
+ - !ruby/object:Gem::Version
28
+ hash: 3
29
+ segments:
30
+ - 2
31
+ - 0
32
+ version: "2.0"
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: activerecord
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ hash: 7
44
+ segments:
45
+ - 3
46
+ - 0
47
+ version: "3.0"
48
+ type: :runtime
49
+ version_requirements: *id002
50
+ description: This gem allows you to pass a Regexp as the value for any finder in ActiveRecord - Rails3 only
51
+ email:
52
+ - jk@handle.it
53
+ executables: []
54
+
55
+ extensions: []
56
+
57
+ extra_rdoc_files: []
58
+
59
+ files:
60
+ - .gitignore
61
+ - Gemfile
62
+ - LICENSE.txt
63
+ - README.md
64
+ - Rakefile
65
+ - lib/wherex.rb
66
+ - lib/wherex/adapters.rb
67
+ - lib/wherex/connection.rb
68
+ - lib/wherex/version.rb
69
+ - lib/wherex/visitor.rb
70
+ - test/app/models/product.rb
71
+ - test/app/models/student.rb
72
+ - test/app/models/user.rb
73
+ - test/config/database.yml
74
+ - test/db/test.sqlite3
75
+ - test/fixtures/products.yml
76
+ - test/fixtures/schema.rb
77
+ - test/fixtures/students.yml
78
+ - test/fixtures/users.yml
79
+ - test/helper.rb
80
+ - test/test_wherex.rb
81
+ - wherex.gemspec
82
+ homepage: http://github.com/smathy/wherex
83
+ licenses:
84
+ - MIT
85
+ post_install_message:
86
+ rdoc_options: []
87
+
88
+ require_paths:
89
+ - lib
90
+ required_ruby_version: !ruby/object:Gem::Requirement
91
+ none: false
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ hash: 3
96
+ segments:
97
+ - 0
98
+ version: "0"
99
+ required_rubygems_version: !ruby/object:Gem::Requirement
100
+ none: false
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ hash: 3
105
+ segments:
106
+ - 0
107
+ version: "0"
108
+ requirements: []
109
+
110
+ rubyforge_project: wherex
111
+ rubygems_version: 1.7.2
112
+ signing_key:
113
+ specification_version: 3
114
+ summary: Regexp for ActiveRecord finders
115
+ test_files:
116
+ - test/app/models/product.rb
117
+ - test/app/models/student.rb
118
+ - test/app/models/user.rb
119
+ - test/config/database.yml
120
+ - test/db/test.sqlite3
121
+ - test/fixtures/products.yml
122
+ - test/fixtures/schema.rb
123
+ - test/fixtures/students.yml
124
+ - test/fixtures/users.yml
125
+ - test/helper.rb
126
+ - test/test_wherex.rb