duck_chain 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 +4 -0
- data/CHANGELOG.rdoc +2 -0
- data/Gemfile +4 -0
- data/LICENSE +21 -0
- data/README.rdoc +85 -0
- data/Rakefile +13 -0
- data/duck_chain.gemspec +25 -0
- data/lib/duck_chain/active_record_extensions.rb +32 -0
- data/lib/duck_chain/toolset.rb +99 -0
- data/lib/duck_chain/version.rb +3 -0
- data/lib/duck_chain.rb +6 -0
- metadata +95 -0
data/.gitignore
ADDED
data/CHANGELOG.rdoc
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Copyright (c) 2011 Tony Drake
|
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.
|
21
|
+
|
data/README.rdoc
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
= duck_chain
|
2
|
+
|
3
|
+
== Intro
|
4
|
+
This gem's purpose is to generate basic chainable search methods on models in Rails 3 similar to searchlogic's dynamic named scopes.
|
5
|
+
|
6
|
+
duck_chain's original purpose is to help with the transition from Rails 2 to 3. A lot of projects use searchlogic, and while metasearch
|
7
|
+
is a fine replacement, it doesn't provide the dynamic named scopes (ie Post.title_equals("foobar").content_like("I like pie"). To help with the
|
8
|
+
transition, duck_chain extends ActiveRecord and creates extra chainable model methods for attributes you specifiy which act similar to the basic
|
9
|
+
searchlogic named scopes. The methods simply return Arel where cluases so you can chain them with other methods.
|
10
|
+
|
11
|
+
Only the basic single table methods are supported (eq, not_equal, gte, like, etc.). Meaning you can't do Post.post_comments_title_eq("This blog sux").
|
12
|
+
Legacy code like that should be converted into an Arel where method, metasearch search method, or whatever you decide to go with for Rails 3. Again,
|
13
|
+
the gem was built to help with the transition and provide some of the basic search methods people using searchlogic are used to in Rails 3. Plus, I personally like typing out
|
14
|
+
Post.created_at_gte(Time.now).content_like("I like shorts. They're comfortable and easy to wear.") more than Post.where(['created_at >= ?', Time.now]).where("content LIKE '%I like shorts. They're comfortable and easy to wear.%'").
|
15
|
+
Just seems a little clearer this way. :)
|
16
|
+
|
17
|
+
== Setup
|
18
|
+
Include the gem in your GemFile like any other gem.
|
19
|
+
|
20
|
+
== Usage
|
21
|
+
Consider the following model...
|
22
|
+
class Post < ActiveRecord::Base
|
23
|
+
# Fields:
|
24
|
+
# id:integer
|
25
|
+
# title:string
|
26
|
+
# content:text
|
27
|
+
# url:string
|
28
|
+
# comment_counter:integer
|
29
|
+
# created_at:datetime
|
30
|
+
# updated_at:datetime
|
31
|
+
end
|
32
|
+
|
33
|
+
==== Use all search methods for selected attributes
|
34
|
+
To have duck_chain bind to the title, content, and url attributes of the model...
|
35
|
+
class Post < ActiveRecord::Base
|
36
|
+
duck_chain :content, 'title', :url
|
37
|
+
# Note you can use symbols or strings in your list of attributes
|
38
|
+
end
|
39
|
+
|
40
|
+
Calling duck_chain creates a bunch of chainable methods for the Post model for the content, title, and url attributes...
|
41
|
+
|
42
|
+
Example new methods related to the title attribute:
|
43
|
+
|
44
|
+
<i>The following <b>'eq'</b> methods can accept a string, array, or range</i>
|
45
|
+
* title_eq
|
46
|
+
* title_equals
|
47
|
+
* title_is
|
48
|
+
* title_in
|
49
|
+
* title_neq
|
50
|
+
* title_not_equal
|
51
|
+
* title_is_not
|
52
|
+
* title_does_not_equal
|
53
|
+
* title_not_in
|
54
|
+
<i>The following <b>'like'</b> methods should only be passed a string</i>
|
55
|
+
* title_like
|
56
|
+
* title_begins_with
|
57
|
+
* title_ends_with
|
58
|
+
* title_not_like
|
59
|
+
* title_not_begin_with
|
60
|
+
* title_not_end_with
|
61
|
+
<i>The following <b>'range'</b> methods should only be passed a string</i>
|
62
|
+
* title_gt
|
63
|
+
* title_greater_than
|
64
|
+
* title_gte
|
65
|
+
* title_greater_than_or_equal_to
|
66
|
+
* title_lt
|
67
|
+
* title_less_than
|
68
|
+
* title_lte
|
69
|
+
* title_less_than_or_equal_to
|
70
|
+
|
71
|
+
Then, you can just use the methods in your code like you did with searchlogic...
|
72
|
+
Post.title_eq("Foobar").where(...).content_like("Blah")
|
73
|
+
|
74
|
+
==== Use only certain search methods for selected attributes
|
75
|
+
There might be some cases where you'd rather just only bind the 'eq' methods to an attribute rather than all of them...
|
76
|
+
class Post < ActiveRecord::Base
|
77
|
+
# Will only create the _eq, _is, _neq, _in, etc. methods
|
78
|
+
duck_chain_eq :content, 'title', :url
|
79
|
+
end
|
80
|
+
|
81
|
+
You can also create only the 'like' and 'range' methods in a similar fasion...
|
82
|
+
class Post < ActiveRecord::Base
|
83
|
+
duck_chain_like :content
|
84
|
+
duck_chain_range :created_at
|
85
|
+
end
|
data/Rakefile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
desc 'Generate RDoc documentation for the will_paginate plugin.'
|
5
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
6
|
+
rdoc.rdoc_files.include('README.rdoc', 'LICENSE', 'CHANGELOG.rdoc').include('lib/**/*.rb').exclude('lib/duck_chain/version.rb')
|
7
|
+
|
8
|
+
rdoc.main = "README.rdoc" # page to start on
|
9
|
+
rdoc.title = "duck_chain documentation"
|
10
|
+
|
11
|
+
rdoc.rdoc_dir = 'doc' # rdoc output folder
|
12
|
+
rdoc.options << '--inline-source' << '--charset=UTF-8'
|
13
|
+
end
|
data/duck_chain.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "duck_chain/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "duck_chain"
|
7
|
+
s.version = DuckChain::VERSION
|
8
|
+
|
9
|
+
s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version=
|
10
|
+
|
11
|
+
s.authors = ["Tony Drake"]
|
12
|
+
s.email = ["t27duck@gmail.com"]
|
13
|
+
# s.homepage = ""
|
14
|
+
s.summary = %q{Because I like chaining easy to read search methods with ActiveRecord thankyouverymuch}
|
15
|
+
s.description = %q{A simple Rails 3 Gem that extends ActiveRecord. Let's you specify model attributes to dynamically create search methods similar to searchlogic's dynamic named scopes. It can be used to help Rails 2 projects with searchlogic migrate to Rails 3 with metasearch a little easier.}
|
16
|
+
|
17
|
+
s.rubyforge_project = "duck_chain"
|
18
|
+
|
19
|
+
s.files = `git ls-files`.split("\n")
|
20
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
21
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
22
|
+
s.require_paths = ["lib"]
|
23
|
+
|
24
|
+
s.add_dependency(%q<activerecord>, [">= 3.0.0"])
|
25
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module DuckChain
|
2
|
+
|
3
|
+
# Methods that extend ActiveRecord and allows duck_chain to work
|
4
|
+
module ActiveRecordExtensions
|
5
|
+
|
6
|
+
# The generic call which will generate all methods for the model
|
7
|
+
def duck_chain(*model_attributes)
|
8
|
+
Toolset.create_eq_methods(model_attributes)
|
9
|
+
Toolset.create_like_methods(model_attributes)
|
10
|
+
Toolset.create_range_methods(model_attributes)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Takes the list of model attributes calls the create method from DuckChain::Toolset
|
14
|
+
# to generate the 'eq' methods
|
15
|
+
def duck_chain_eq(*model_attributes)
|
16
|
+
Toolset.create_eq_methods(model_attributes)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Takes the list of model attributes calls the create method from DuckChain::Toolset
|
20
|
+
# to generate the 'list' methods
|
21
|
+
def duck_chain_like(*model_attributes)
|
22
|
+
Toolset.create_like_methods(model_attributes)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Takes the list of model attributes calls the create method from DuckChain::Toolset
|
26
|
+
# to generate the 'range' methods
|
27
|
+
def duck_chain_range(*model_attributes)
|
28
|
+
Toolset.create_range_methods(model_attributes)
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module DuckChain
|
2
|
+
module Toolset
|
3
|
+
# Generic helper method that ensures whatever is passed into it is an array.
|
4
|
+
# If it's not, make it one.
|
5
|
+
def self.set_as_array(value)
|
6
|
+
value.is_a?(Array) ? value : [value]
|
7
|
+
end
|
8
|
+
|
9
|
+
# Creates 'eq' class methods for the model
|
10
|
+
# Ex:: Post.title_eq("Why do my feet smell?")
|
11
|
+
# Methods created include...
|
12
|
+
# * eq
|
13
|
+
# * equals
|
14
|
+
# * is
|
15
|
+
# * in
|
16
|
+
# * neq
|
17
|
+
# * not_equal
|
18
|
+
# * is_not
|
19
|
+
# * does_not_equal
|
20
|
+
# * not_in
|
21
|
+
# A string, array, or a range may be passed into these created methods.
|
22
|
+
# Passing an array or range will result in a IN clause in the resulting SQL.
|
23
|
+
def self.create_eq_methods(model_attributes)
|
24
|
+
Toolset.set_as_array(model_attributes).each do |m|
|
25
|
+
['eq','equals','is','in'].each do |suffix|
|
26
|
+
self.class.send(:define_method, "#{m}_#{suffix}", Proc.new{ |search_term|
|
27
|
+
if search_term.is_a?(Array) || search_term.is_a?(Range)
|
28
|
+
where(["#{m} IN (?)", search_term])
|
29
|
+
else
|
30
|
+
where(["#{m} = ?", search_term])
|
31
|
+
end
|
32
|
+
}
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
36
|
+
['neq','not_equal','is_not','does_not_equal','not_in'].each do |suffix|
|
37
|
+
self.class.send(:define_method, "#{m}_#{suffix}", Proc.new{ |search_term|
|
38
|
+
if search_term.is_a?(Array) || search_term.is_a?(Range)
|
39
|
+
where(["? NOT IN (?)", m, search_term])
|
40
|
+
else
|
41
|
+
where(["? != ?", m, search_term])
|
42
|
+
end
|
43
|
+
}
|
44
|
+
)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Creates 'like' class methods for the model
|
50
|
+
# Ex:: Post.title_like("Foo")
|
51
|
+
# Methods created include...
|
52
|
+
# * like
|
53
|
+
# * begins_with
|
54
|
+
# * ends_with
|
55
|
+
# * not_like
|
56
|
+
# * not_begin_with
|
57
|
+
# * not_end_with
|
58
|
+
def self.create_like_methods(model_attributes)
|
59
|
+
Toolset.set_as_array(model_attributes).each do |m|
|
60
|
+
self.class.send(:define_method, "#{m}_like", Proc.new{ |search_term| where(["? LIKE (?)", m, "%#{search_term}%"])} )
|
61
|
+
self.class.send(:define_method, "#{m}_begins_with", Proc.new{ |search_term| where(["? LIKE (?)", m, "#{search_term}%"])} )
|
62
|
+
self.class.send(:define_method, "#{m}_ends_with", Proc.new{ |search_term| where(["? LIKE (?)", m, "%#{search_term}"])} )
|
63
|
+
|
64
|
+
self.class.send(:define_method, "#{m}_not_like", Proc.new{ |search_term| where(["? NOT LIKE (?)", m, "%#{search_term}%"])} )
|
65
|
+
self.class.send(:define_method, "#{m}_not_begin_with", Proc.new{ |search_term| where(["? NOT LIKE (?)", m, "#{search_term}%"])} )
|
66
|
+
self.class.send(:define_method, "#{m}_not_end_with", Proc.new{ |search_term| where(["? NOT LIKE (?)", m, "%#{search_term}"])} )
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# Creates 'range' class methods for the model
|
71
|
+
# Ex:: Post.comment_counter_gt(5)
|
72
|
+
# Methods created include...
|
73
|
+
# * gt
|
74
|
+
# * greater_than
|
75
|
+
# * gte
|
76
|
+
# * greater_than_or_equal_to
|
77
|
+
# * lt
|
78
|
+
# * less_then
|
79
|
+
# * lte
|
80
|
+
# * less_than_or_equal_to
|
81
|
+
def self.create_range_methods(model_attributes)
|
82
|
+
Toolset.set_as_array(model_attributes).each do |m|
|
83
|
+
['gt','greater_than'].each do |suffix|
|
84
|
+
self.class.send(:define_method, "#{m}_#{suffix}", Proc.new{ |search_term| where(['? > ?', m, search_term])} )
|
85
|
+
end
|
86
|
+
['gte','greater_than_or_equal_to'].each do |suffix|
|
87
|
+
self.class.send(:define_method, "#{m}_#{suffix}", Proc.new{ |search_term| where(['? >= ?', m, search_term])} )
|
88
|
+
end
|
89
|
+
['lt','less_than'].each do |suffix|
|
90
|
+
self.class.send(:define_method, "#{m}_#{suffix}", Proc.new{ |search_term| where(['? < ?', m, search_term])} )
|
91
|
+
end
|
92
|
+
['lte','less_than_or_equal_to'].each do |suffix|
|
93
|
+
self.class.send(:define_method, "#{m}_#{suffix}", Proc.new{ |search_term| where(['? <= ?', m, search_term])} )
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
end
|
data/lib/duck_chain.rb
ADDED
metadata
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: duck_chain
|
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
|
+
- Tony Drake
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-06-14 00:00:00 -04:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: activerecord
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 7
|
30
|
+
segments:
|
31
|
+
- 3
|
32
|
+
- 0
|
33
|
+
- 0
|
34
|
+
version: 3.0.0
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id001
|
37
|
+
description: A simple Rails 3 Gem that extends ActiveRecord. Let's you specify model attributes to dynamically create search methods similar to searchlogic's dynamic named scopes. It can be used to help Rails 2 projects with searchlogic migrate to Rails 3 with metasearch a little easier.
|
38
|
+
email:
|
39
|
+
- t27duck@gmail.com
|
40
|
+
executables: []
|
41
|
+
|
42
|
+
extensions: []
|
43
|
+
|
44
|
+
extra_rdoc_files: []
|
45
|
+
|
46
|
+
files:
|
47
|
+
- .gitignore
|
48
|
+
- CHANGELOG.rdoc
|
49
|
+
- Gemfile
|
50
|
+
- LICENSE
|
51
|
+
- README.rdoc
|
52
|
+
- Rakefile
|
53
|
+
- duck_chain.gemspec
|
54
|
+
- lib/duck_chain.rb
|
55
|
+
- lib/duck_chain/active_record_extensions.rb
|
56
|
+
- lib/duck_chain/toolset.rb
|
57
|
+
- lib/duck_chain/version.rb
|
58
|
+
has_rdoc: true
|
59
|
+
homepage:
|
60
|
+
licenses: []
|
61
|
+
|
62
|
+
post_install_message:
|
63
|
+
rdoc_options: []
|
64
|
+
|
65
|
+
require_paths:
|
66
|
+
- lib
|
67
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
68
|
+
none: false
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
hash: 3
|
73
|
+
segments:
|
74
|
+
- 0
|
75
|
+
version: "0"
|
76
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
77
|
+
none: false
|
78
|
+
requirements:
|
79
|
+
- - ">"
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
hash: 25
|
82
|
+
segments:
|
83
|
+
- 1
|
84
|
+
- 3
|
85
|
+
- 1
|
86
|
+
version: 1.3.1
|
87
|
+
requirements: []
|
88
|
+
|
89
|
+
rubyforge_project: duck_chain
|
90
|
+
rubygems_version: 1.6.2
|
91
|
+
signing_key:
|
92
|
+
specification_version: 3
|
93
|
+
summary: Because I like chaining easy to read search methods with ActiveRecord thankyouverymuch
|
94
|
+
test_files: []
|
95
|
+
|