relata 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +3 -0
- data/MIT-LICENSE +20 -0
- data/README.markdown +43 -0
- data/Rakefile +45 -0
- data/init.rb +7 -0
- data/install.rb +1 -0
- data/lib/filtered_relation.rb +3 -0
- data/lib/relata/dsl/conditions.rb +45 -0
- data/lib/relata/dsl/constraints.rb +62 -0
- data/lib/relata/dsl/custom_relation.rb +26 -0
- data/lib/relata/dsl/field_search.rb +85 -0
- data/lib/relata/dsl/missed_builder.rb +28 -0
- data/lib/relata/dsl/querys/fields.rb +25 -0
- data/lib/relata/dsl/querys/multiple.rb +8 -0
- data/lib/relata/dsl/querys/simple.rb +11 -0
- data/lib/relata/dsl.rb +41 -0
- data/lib/relata/filter.rb +46 -0
- data/lib/relata/related_query_methods.rb +44 -0
- data/test/dsl_test.rb +187 -0
- data/test/filtered_relation_test.rb +75 -0
- data/test/schema.rb +48 -0
- data/test/test.sqlite3 +0 -0
- data/test/test_helper.rb +3 -0
- data/test.sqlite3 +0 -0
- data/uninstall.rb +1 -0
- metadata +91 -0
data/Gemfile
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 [name of plugin creator]
|
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.markdown
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
#FilteredRelation#
|
2
|
+
|
3
|
+
Making dynamic filters easier with a nice ActiveRecord DSL.
|
4
|
+
|
5
|
+
## Filter Example ##
|
6
|
+
|
7
|
+
Create dynamic filters with just onde method.
|
8
|
+
|
9
|
+
<% form_tag :action => 'filter' do %>
|
10
|
+
Title: <%= text_field_tag 'post[title]' %><br />
|
11
|
+
Only with Comments?
|
12
|
+
<%= select("post", "comments", options_for_select({ "false" => "", "true" => "true" })) %> %>
|
13
|
+
|
14
|
+
def filter
|
15
|
+
@posts = Post.filtered_relation(params[:post]).all
|
16
|
+
end
|
17
|
+
|
18
|
+
Create more advanced relations.
|
19
|
+
|
20
|
+
posts = Post.filtered_relation(:comments => true).where(:user_id => 4).limit(3).order("id ASC")
|
21
|
+
|
22
|
+
posts.each do |post|
|
23
|
+
# records
|
24
|
+
end
|
25
|
+
|
26
|
+
## DSL API ##
|
27
|
+
|
28
|
+
Post.where(:body).like?("%caelum%")
|
29
|
+
|
30
|
+
Post.where(:comments).count.exists?
|
31
|
+
|
32
|
+
Post.where(:comments).count.gt(2)
|
33
|
+
|
34
|
+
Post.where(:comments).count.lt(2)
|
35
|
+
|
36
|
+
Post.where(:comments).description.like?("%filtered%")
|
37
|
+
|
38
|
+
Post.where(:comments).subject.like?("%filtered%")
|
39
|
+
|
40
|
+
Post.where { comments >= 2 }
|
41
|
+
|
42
|
+
Post.where { published_at.between(2.years.ago, 6.months.ago) }
|
43
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
require 'rake/gempackagetask'
|
5
|
+
|
6
|
+
desc 'Default: run unit tests.'
|
7
|
+
task :default => :test
|
8
|
+
|
9
|
+
desc 'Test the relata plugin.'
|
10
|
+
Rake::TestTask.new(:test) do |t|
|
11
|
+
t.libs << 'lib'
|
12
|
+
t.libs << 'test'
|
13
|
+
t.pattern = 'test/**/*_test.rb'
|
14
|
+
t.verbose = true
|
15
|
+
end
|
16
|
+
|
17
|
+
desc 'Generate documentation for the relata plugin.'
|
18
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
19
|
+
rdoc.rdoc_dir = 'rdoc'
|
20
|
+
rdoc.title = 'relata'
|
21
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
22
|
+
rdoc.rdoc_files.include('README.markdown')
|
23
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
24
|
+
end
|
25
|
+
|
26
|
+
PKG_FILES = FileList[ '[a-zA-Z]*', 'generators/**/*', 'lib/**/*', 'rails/**/*', 'tasks/**/*', 'test/**/*' ]
|
27
|
+
|
28
|
+
spec = Gem::Specification.new do |s|
|
29
|
+
s.name = "relata"
|
30
|
+
s.version = "0.0.3"
|
31
|
+
s.author = "Anderson Leite, Guilherme Silveira"
|
32
|
+
s.email = "anderson.leite@caelum.com.br"
|
33
|
+
s.homepage = "http://github.com/caelum/relata"
|
34
|
+
s.platform = Gem::Platform::RUBY
|
35
|
+
s.summary = "Helps poking around with relationships when using ARel"
|
36
|
+
s.files = PKG_FILES.to_a
|
37
|
+
s.require_path = "lib"
|
38
|
+
s.has_rdoc = false
|
39
|
+
s.extra_rdoc_files = ["README.markdown"]
|
40
|
+
end
|
41
|
+
|
42
|
+
desc 'Turn this plugin into a gem.'
|
43
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
44
|
+
pkg.gem_spec = spec
|
45
|
+
end
|
data/init.rb
ADDED
data/install.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# Install hook code here
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# A custom set of conditions that can be applied
|
2
|
+
# to a query
|
3
|
+
module Relata::Dsl::Conditions
|
4
|
+
|
5
|
+
def eq(value)
|
6
|
+
add_filter("= #{value}")
|
7
|
+
end
|
8
|
+
|
9
|
+
def ge(value)
|
10
|
+
add_filter(">= #{value}")
|
11
|
+
end
|
12
|
+
|
13
|
+
def greater_or_equals(value)
|
14
|
+
ge(value)
|
15
|
+
end
|
16
|
+
|
17
|
+
def gt(value)
|
18
|
+
add_filter("> #{value}")
|
19
|
+
end
|
20
|
+
|
21
|
+
def greater_than(value)
|
22
|
+
gt(value)
|
23
|
+
end
|
24
|
+
|
25
|
+
def le(value)
|
26
|
+
add_filter("<= #{value}")
|
27
|
+
end
|
28
|
+
|
29
|
+
def lt(value)
|
30
|
+
add_filter("< #{value}")
|
31
|
+
end
|
32
|
+
|
33
|
+
def lesser_than(value)
|
34
|
+
lt value
|
35
|
+
end
|
36
|
+
|
37
|
+
# whether this relation has at least one element.
|
38
|
+
def exists?
|
39
|
+
if @relation_search.nil?
|
40
|
+
count.exists?
|
41
|
+
else
|
42
|
+
add_filter("> 0")
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Relata::Dsl::Constraints
|
2
|
+
|
3
|
+
def length
|
4
|
+
@relation_search = LengthManager
|
5
|
+
self
|
6
|
+
end
|
7
|
+
|
8
|
+
class LengthManager
|
9
|
+
def self.condition(field, *args)
|
10
|
+
"len(field)"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def count
|
15
|
+
@select_fields << "COUNT(#{@current_field}.id) AS count"
|
16
|
+
@groups << "#{table_name}.id"
|
17
|
+
@relation_search = 'count'
|
18
|
+
self
|
19
|
+
end
|
20
|
+
|
21
|
+
def like?(value)
|
22
|
+
query.where("#{@current_field} like ?", [value])
|
23
|
+
end
|
24
|
+
|
25
|
+
# def between(first, second)
|
26
|
+
# @relation_search = SimpleRangeCondition
|
27
|
+
# self
|
28
|
+
# # query.where("#{@current_field} like ?", [value])
|
29
|
+
# # add_filter("> #{first}").add_filter("< #{second}")
|
30
|
+
# end
|
31
|
+
|
32
|
+
class SimpleCondition
|
33
|
+
def self.condition(field, *args)
|
34
|
+
"#{field}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class SimpleRangeCondition
|
39
|
+
def self.condition(field, *args)
|
40
|
+
"#{field} > #{args[0]} AND #{field} < #{args[1]}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class RangeManager
|
45
|
+
def self.select_fields(facet)
|
46
|
+
"COUNT(#{facet}.id) AS count"
|
47
|
+
end
|
48
|
+
def self.having(expectation)
|
49
|
+
"count #{expectation}"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class CountManager
|
54
|
+
def self.select_fields(facet)
|
55
|
+
"COUNT(#{facet}.id) AS count"
|
56
|
+
end
|
57
|
+
def self.having(expectation)
|
58
|
+
"count #{expectation}"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# defines helper methods to deal with custom relation
|
2
|
+
module Relata::Dsl::CustomRelation
|
3
|
+
include Relata::Dsl::Conditions
|
4
|
+
include Relata::Dsl::Constraints
|
5
|
+
|
6
|
+
def using(record, field)
|
7
|
+
@record = record
|
8
|
+
@current_field = field
|
9
|
+
@start_field = field
|
10
|
+
@select_fields = ["#{table_name}.*"]
|
11
|
+
@groups = []
|
12
|
+
if relates_to_many?
|
13
|
+
self.extend MultipleQuery
|
14
|
+
self.extend ModelFields
|
15
|
+
else
|
16
|
+
self.extend SimpleQuery
|
17
|
+
end
|
18
|
+
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
def relates_to_many?
|
23
|
+
@record.reflect_on_association @current_field.to_sym
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# a relation search in a specific field
|
2
|
+
class Relata::Dsl::FieldSearch
|
3
|
+
|
4
|
+
def initialize(rel, field)
|
5
|
+
@rel = rel
|
6
|
+
@field = field
|
7
|
+
end
|
8
|
+
|
9
|
+
def ==(value)
|
10
|
+
@rel.where("#{@field} == ?", value)
|
11
|
+
end
|
12
|
+
|
13
|
+
def >=(value)
|
14
|
+
@rel.where("#{@field} >= ?", value)
|
15
|
+
end
|
16
|
+
|
17
|
+
def <=(value)
|
18
|
+
@rel.where("#{@field} <= ?", value)
|
19
|
+
end
|
20
|
+
|
21
|
+
def >(value)
|
22
|
+
@rel.where("#{@field} > ?", value)
|
23
|
+
end
|
24
|
+
|
25
|
+
def <(value)
|
26
|
+
@rel.where("#{@field} < ?", value)
|
27
|
+
end
|
28
|
+
|
29
|
+
def like?(value)
|
30
|
+
@rel.where(@field).like?(value)
|
31
|
+
end
|
32
|
+
|
33
|
+
def between(first, second)
|
34
|
+
@rel.where("#{@field} > ? and #{@field} < ?", first, second)
|
35
|
+
end
|
36
|
+
|
37
|
+
def length
|
38
|
+
@field = "length(#{@field})"
|
39
|
+
self
|
40
|
+
end
|
41
|
+
|
42
|
+
def custom(*args)
|
43
|
+
comparison = args.shift
|
44
|
+
@rel.where("#{@field} #{comparison}", args)
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
class Relata::Dsl::FieldSearchMany
|
50
|
+
|
51
|
+
def initialize(rel, field)
|
52
|
+
@rel = rel
|
53
|
+
@field = field
|
54
|
+
end
|
55
|
+
|
56
|
+
def ==(value)
|
57
|
+
@rel.where(@field).count.eq(value)
|
58
|
+
end
|
59
|
+
|
60
|
+
def >=(value)
|
61
|
+
@rel.where(@field).count.ge(value)
|
62
|
+
end
|
63
|
+
|
64
|
+
def <=(value)
|
65
|
+
@rel.where(@field).count.le(value)
|
66
|
+
end
|
67
|
+
|
68
|
+
def >(value)
|
69
|
+
@rel.where(@field).count.gt(value)
|
70
|
+
end
|
71
|
+
|
72
|
+
def <(value)
|
73
|
+
@rel.where(@field).count.lt(value)
|
74
|
+
end
|
75
|
+
|
76
|
+
def exists?
|
77
|
+
@rel.where(@field).exists?
|
78
|
+
end
|
79
|
+
|
80
|
+
def custom(*args)
|
81
|
+
comparison = args.shift
|
82
|
+
@rel.where("#{@field} #{comparison}", args)
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# a builder ready to collect which field you want to search
|
2
|
+
class Relata::Dsl::MissedBuilder
|
3
|
+
|
4
|
+
def initialize(rel)
|
5
|
+
@rel = rel
|
6
|
+
end
|
7
|
+
|
8
|
+
def method_missing(field, *args)
|
9
|
+
relation = @rel.scoped
|
10
|
+
relation.extend Relata::Dsl::CustomRelation
|
11
|
+
relation.using(@rel, field)
|
12
|
+
|
13
|
+
if relation.relates_to_many?
|
14
|
+
type = Relata::Dsl::FieldSearchMany
|
15
|
+
else
|
16
|
+
type = Relata::Dsl::FieldSearch
|
17
|
+
end
|
18
|
+
|
19
|
+
instance = type.new(relation, field)
|
20
|
+
|
21
|
+
if args.size != 0
|
22
|
+
instance = instance.custom(*args)
|
23
|
+
end
|
24
|
+
|
25
|
+
instance
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module ModelFields
|
2
|
+
|
3
|
+
def self.extended(base)
|
4
|
+
|
5
|
+
base.reflect_on_all_associations.each do |r|
|
6
|
+
@fields = r.klass.columns.map do |c|
|
7
|
+
c.name if [:string, :text].include? c.type
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
@fields.each do |field|
|
12
|
+
include_method field if field != nil
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
def self.include_method(field)
|
19
|
+
define_method field do
|
20
|
+
@current_field = "#{@current_field}.#{field}"
|
21
|
+
self
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
module MultipleQuery
|
2
|
+
def query
|
3
|
+
preload(@start_field).select(@select_fields.join ',').from("#{table_name}, #{@start_field}").where("#{table_name}.id = #{@start_field}.post_id")
|
4
|
+
end
|
5
|
+
def add_filter expectation
|
6
|
+
query.group(@groups.first).having("#{@relation_search} #{expectation}")
|
7
|
+
end
|
8
|
+
end
|
data/lib/relata/dsl.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
# in case you did not require the entire relata plugin
|
2
|
+
module Relata
|
3
|
+
module Dsl
|
4
|
+
end
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'relata/dsl/conditions'
|
8
|
+
require 'relata/dsl/constraints'
|
9
|
+
require 'relata/dsl/custom_relation'
|
10
|
+
require 'relata/dsl/field_search'
|
11
|
+
require 'relata/dsl/missed_builder'
|
12
|
+
require 'relata/dsl/querys/multiple'
|
13
|
+
require 'relata/dsl/querys/simple'
|
14
|
+
require 'relata/dsl/querys/fields'
|
15
|
+
|
16
|
+
module Relata::Dsl::Relation
|
17
|
+
|
18
|
+
# extended where clause that allows symbol and custom dsl lookup
|
19
|
+
#
|
20
|
+
# examples:
|
21
|
+
# where(:body).like?("%guilherme%")
|
22
|
+
# where { body.like?("%guilherme%")
|
23
|
+
#
|
24
|
+
# While the last will delegate to the MissedBuilder component
|
25
|
+
# the symbol based query will delegate query builder to CustomRelation.
|
26
|
+
def where(*args, &block)
|
27
|
+
if args.size==0 && block
|
28
|
+
Relata::Dsl::MissedBuilder.new(self).instance_eval(&block)
|
29
|
+
elsif args.size==1 && args[0].is_a?(Symbol)
|
30
|
+
relation = scoped
|
31
|
+
relation.extend Relata::Dsl::CustomRelation
|
32
|
+
relation.using(self, args[0])
|
33
|
+
else
|
34
|
+
super(*args, &block)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class ActiveRecord::Relation
|
40
|
+
include Relata::Dsl::Relation
|
41
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'filtered_relation/related_query_methods'
|
2
|
+
ActiveRecord::Base.send :include, RelatedQueryMethods
|
3
|
+
|
4
|
+
module FilteredRelation
|
5
|
+
|
6
|
+
class ::ActiveRecord::Base
|
7
|
+
|
8
|
+
def self.filtered_relation(params)
|
9
|
+
relation = scoped
|
10
|
+
|
11
|
+
columns = self.columns.map do |c|
|
12
|
+
c.name if [:string, :text].include? c.type
|
13
|
+
end
|
14
|
+
|
15
|
+
params.each do |facet, value|
|
16
|
+
if self.reflect_on_association facet.to_sym
|
17
|
+
relation = send("filter_by_has_many", facet, value, relation)
|
18
|
+
elsif columns.include? facet.to_s
|
19
|
+
relation = send("filter_by_exact", facet, value, relation)
|
20
|
+
else
|
21
|
+
relation = send("filter_by_#{facet}", value, relation)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
relation
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.related_to(params)
|
29
|
+
relation = scoped
|
30
|
+
|
31
|
+
params.each do |facet, value|
|
32
|
+
relation = send("filter_by_related", facet, value, relation)
|
33
|
+
end
|
34
|
+
relation
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.date_between(params)
|
38
|
+
relation = scoped
|
39
|
+
relation = send("filter_by_date_between", params, relation)
|
40
|
+
relation
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module RelatedQueryMethods
|
2
|
+
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
|
9
|
+
def filter_by_has_many(facet, value, relation)
|
10
|
+
|
11
|
+
table_name = self.table_name
|
12
|
+
|
13
|
+
if !value.empty?
|
14
|
+
relation.preload(facet).select("#{table_name}.*, COUNT(#{facet}.id) AS count").from("#{table_name}, #{facet}").where("#{table_name}.id = #{facet}.post_id").group("#{table_name}.id").having("count > 0")
|
15
|
+
else
|
16
|
+
relation
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def filter_by_related(facet, value, relation)
|
21
|
+
if value
|
22
|
+
relation.preload(facet).select("posts.*, COUNT(#{facet}.id) AS comment_count").from("posts, #{facet}").group("posts.id").having("comment_count > 0")
|
23
|
+
else
|
24
|
+
relation
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def filter_by_exact(facet, value, relation)
|
29
|
+
!value.empty? ? relation.where(facet => value) : relation
|
30
|
+
end
|
31
|
+
|
32
|
+
def filter_by_published_at(value, relation)
|
33
|
+
value ? relation.where("published_at < ?", 1.month.ago) : relation
|
34
|
+
end
|
35
|
+
|
36
|
+
def filter_by_date_between(params, relation)
|
37
|
+
relation.where("published_at < ?", params[:before])
|
38
|
+
relation.where("published_at > ?", params[:after])
|
39
|
+
relation
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
data/test/dsl_test.rb
ADDED
@@ -0,0 +1,187 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__)) + "/test_helper"
|
2
|
+
require 'schema'
|
3
|
+
require 'relata/dsl'
|
4
|
+
|
5
|
+
class DSLTest < ActiveSupport::TestCase
|
6
|
+
|
7
|
+
setup do
|
8
|
+
setup_db
|
9
|
+
@caelum = Post.create :body => "CaelumObjects training and inovation"
|
10
|
+
@guilherme = Post.create :body => "Guilherme Silveira"
|
11
|
+
end
|
12
|
+
|
13
|
+
test "given an attribute and expectation, gives the results" do
|
14
|
+
posts = Post.where(:body).like?("%caelum%").all
|
15
|
+
assert_equal @caelum, posts[0]
|
16
|
+
assert_equal 1, posts.size
|
17
|
+
|
18
|
+
posts = Post.where { body.like? "%caelum%" }
|
19
|
+
assert_equal @caelum, posts[0]
|
20
|
+
assert_equal 1, posts.size
|
21
|
+
end
|
22
|
+
|
23
|
+
test "given an attribute and constraint expectation, gives the results" do
|
24
|
+
posts = Post.where { body.length < 22 }
|
25
|
+
assert_equal @guilherme, posts[0]
|
26
|
+
assert_equal 1, posts.size
|
27
|
+
end
|
28
|
+
|
29
|
+
test "exists posts with comments" do
|
30
|
+
@caelum.update_attributes(:comments => [Comment.create])
|
31
|
+
posts = Post.where(:comments).count.exists?
|
32
|
+
assert_equal @caelum, posts[0]
|
33
|
+
assert_equal 1, posts.size
|
34
|
+
end
|
35
|
+
|
36
|
+
test "exists posts with comments can be shortcuted with exists?" do
|
37
|
+
@caelum.update_attributes(:comments => [Comment.create])
|
38
|
+
posts = Post.where(:comments).exists?
|
39
|
+
assert_equal @caelum, posts[0]
|
40
|
+
assert_equal 1, posts.size
|
41
|
+
|
42
|
+
posts = Post.where { comments.exists? }
|
43
|
+
assert_equal @caelum, posts[0]
|
44
|
+
assert_equal 1, posts.size
|
45
|
+
end
|
46
|
+
|
47
|
+
test "exists posts with more than 2 comments" do
|
48
|
+
@caelum.update_attributes(:comments => [Comment.create, Comment.create, Comment.create])
|
49
|
+
posts = Post.where(:comments).count.gt(2)
|
50
|
+
assert_equal @caelum, posts.first
|
51
|
+
assert_equal 3, posts.first.comments.size
|
52
|
+
end
|
53
|
+
|
54
|
+
test "exists posts with less than 2 comments" do
|
55
|
+
@caelum.update_attributes(:comments => [Comment.create])
|
56
|
+
posts = Post.where(:comments).count.lt(2)
|
57
|
+
assert_equal @caelum, posts.first
|
58
|
+
assert_equal 1, posts.first.comments.size
|
59
|
+
end
|
60
|
+
|
61
|
+
test "exists posts with more than or equals 2 comments" do
|
62
|
+
@caelum.update_attributes(:comments => [Comment.create, Comment.create])
|
63
|
+
@guilherme.update_attributes(:comments => [Comment.create, Comment.create, Comment.create])
|
64
|
+
|
65
|
+
posts = Post.where(:comments).count.ge(2).all
|
66
|
+
assert_equal 2, posts.size
|
67
|
+
assert_equal @caelum, posts[0]
|
68
|
+
assert_equal @guilherme, posts[1]
|
69
|
+
end
|
70
|
+
|
71
|
+
test "dsl query supports first" do
|
72
|
+
@caelum.update_attributes(:comments => [Comment.create, Comment.create])
|
73
|
+
@guilherme.update_attributes(:comments => [Comment.create, Comment.create, Comment.create])
|
74
|
+
|
75
|
+
posts = Post.where(:comments).count.ge(2).first
|
76
|
+
assert_equal @caelum, posts
|
77
|
+
end
|
78
|
+
|
79
|
+
# pending
|
80
|
+
# test "all post which commits has some description" do
|
81
|
+
# comment = Comment.create :description => "dsl test"
|
82
|
+
# @caelum.update_attributes :comments => [comment]
|
83
|
+
#
|
84
|
+
# posts = Post.where(:comments).description.like?("%dsl test%")
|
85
|
+
# assert_equal @caelum, posts[0]
|
86
|
+
# assert_equal 1, posts.size
|
87
|
+
#
|
88
|
+
# end
|
89
|
+
|
90
|
+
test "exists posts using strict extended methods" do
|
91
|
+
@caelum.update_attributes(:comments => [Comment.create, Comment.create])
|
92
|
+
@guilherme.update_attributes(:comments => [Comment.create, Comment.create, Comment.create])
|
93
|
+
posts = Post.where { comments >= 2 }
|
94
|
+
assert_equal @caelum, posts[0]
|
95
|
+
assert_equal 2, posts.size
|
96
|
+
assert_equal @guilherme, posts[1]
|
97
|
+
end
|
98
|
+
|
99
|
+
test "strict block supports first" do
|
100
|
+
@caelum.update_attributes(:comments => [Comment.create, Comment.create])
|
101
|
+
@guilherme.update_attributes(:comments => [Comment.create, Comment.create, Comment.create])
|
102
|
+
post = Post.where { comments >= 2 }.first
|
103
|
+
assert_equal @caelum, post
|
104
|
+
end
|
105
|
+
|
106
|
+
test "exists posts using range expectations" do
|
107
|
+
@caelum.update_attributes :published_at => 1.year.ago
|
108
|
+
|
109
|
+
posts = Post.where { published_at.between(2.years.ago, 6.months.ago) }
|
110
|
+
assert_equal @caelum, posts[0]
|
111
|
+
assert_equal 1, posts.size
|
112
|
+
end
|
113
|
+
|
114
|
+
# pending
|
115
|
+
# test "all post which commits has some subject" do
|
116
|
+
# comment = Comment.create :subject => "dsl subject"
|
117
|
+
# @caelum.update_attributes :comments => [comment]
|
118
|
+
# posts = Post.where(:comments).subject.like?("%dsl subject%")
|
119
|
+
# assert_equal @caelum, posts[0]
|
120
|
+
# assert_equal 1, posts.size
|
121
|
+
# end
|
122
|
+
|
123
|
+
test "accepts two conditions inline" do
|
124
|
+
@caelum.update_attributes :published_at => 1.year.ago
|
125
|
+
@guilherme.update_attributes :published_at => 1.year.ago
|
126
|
+
|
127
|
+
posts = Post.where {
|
128
|
+
published_at.between(2.years.ago, 6.months.ago)
|
129
|
+
body.like?("%lum%")
|
130
|
+
}
|
131
|
+
assert_equal @caelum, posts[0]
|
132
|
+
assert_equal 1, posts.size
|
133
|
+
end
|
134
|
+
|
135
|
+
test "supports two conditions in dsl mixing everything together" do
|
136
|
+
@caelum.update_attributes(:comments => [Comment.create, Comment.create])
|
137
|
+
@guilherme.update_attributes(:comments => [Comment.create, Comment.create, Comment.create])
|
138
|
+
posts = Post.where { comments >= 1 }.where(:body).like?("%lum%")
|
139
|
+
assert_equal @caelum, posts[0]
|
140
|
+
assert_equal 1, posts.size
|
141
|
+
end
|
142
|
+
|
143
|
+
test "supports == with relation count" do
|
144
|
+
@caelum.update_attributes(:comments => [Comment.create, Comment.create])
|
145
|
+
posts = Post.where { comments == 2 }
|
146
|
+
assert_equal @caelum, posts[0]
|
147
|
+
assert_equal 1, posts.size
|
148
|
+
end
|
149
|
+
|
150
|
+
test "supports == with simple field" do
|
151
|
+
@caelum.update_attributes(:comments => [Comment.create, Comment.create])
|
152
|
+
posts = Post.where { body == "CaelumObjects training and inovation" }
|
153
|
+
assert_equal @caelum, posts[0]
|
154
|
+
assert_equal 1, posts.size
|
155
|
+
end
|
156
|
+
|
157
|
+
test "accepts two conditions one after the other" do
|
158
|
+
@caelum.update_attributes :published_at => 1.year.ago
|
159
|
+
@guilherme.update_attributes :published_at => 1.year.ago
|
160
|
+
|
161
|
+
posts = Post.where { published_at.between(2.years.ago, 6.months.ago) }
|
162
|
+
assert_equal @caelum, posts[0]
|
163
|
+
assert_equal 2, posts.size
|
164
|
+
posts = posts.where { body.like?("%lum%") }
|
165
|
+
assert_equal @caelum, posts[0]
|
166
|
+
assert_equal 1, posts.size
|
167
|
+
end
|
168
|
+
|
169
|
+
test "accepts any custom condition" do
|
170
|
+
posts = Post.where { body "like ?", "%lum%" }
|
171
|
+
assert_equal @caelum, posts[0]
|
172
|
+
assert_equal 1, posts.size
|
173
|
+
end
|
174
|
+
|
175
|
+
# test "second level relation in a dsl" do
|
176
|
+
# comment = Comment.create :description => "dsl test"
|
177
|
+
# @caelum.update_attributes :comments => [comment]
|
178
|
+
#
|
179
|
+
# posts = Post.where{ comments.description.like?("%dsl test%") }
|
180
|
+
# assert_equal @caelum, posts[0]
|
181
|
+
# assert_equal 1, posts.size
|
182
|
+
#
|
183
|
+
# end
|
184
|
+
|
185
|
+
# Author.where(:posts).comments.count.gt(2)
|
186
|
+
|
187
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__)) + "/test_helper"
|
2
|
+
|
3
|
+
require 'filtered_relation/filter'
|
4
|
+
require 'schema'
|
5
|
+
|
6
|
+
class FilteredRelationTest < ActiveSupport::TestCase
|
7
|
+
setup do
|
8
|
+
setup_db
|
9
|
+
create_posts
|
10
|
+
end
|
11
|
+
|
12
|
+
test "given no values to filtered_relation, gives us all records" do
|
13
|
+
assert_equal Post.all, Post.filtered_relation({}).all
|
14
|
+
end
|
15
|
+
|
16
|
+
test "given a content and comment filter, gives us filtered records - generic" do
|
17
|
+
@base.update_attributes(:content => "picture", :comments => [Comment.create])
|
18
|
+
assert_equal @base, Post.filtered_relation(:content => "picture", :comments => 'true').first
|
19
|
+
end
|
20
|
+
|
21
|
+
test "given a content and comment filter, gives us filtered records" do
|
22
|
+
@base.update_attributes(:content => "picture", :comments => [Comment.create])
|
23
|
+
assert_equal @base, Post.filtered_relation(:content => "picture").related_to(:comments => true).first
|
24
|
+
end
|
25
|
+
|
26
|
+
test "given a date and comment filter, gives us filtered records" do
|
27
|
+
@base.update_attributes(:published_at => 2.years.ago, :comments => [Comment.create])
|
28
|
+
assert_equal @base, Post.filtered_relation(:published_at => true).related_to(:comments => true).first
|
29
|
+
end
|
30
|
+
|
31
|
+
test "given a date and content filter, gives us filtered records" do
|
32
|
+
@base.update_attribute(:published_at, 2.years.ago)
|
33
|
+
@base.update_attribute(:content, "picture")
|
34
|
+
record = Post.filtered_relation(:published_at => true, :content => "picture").first
|
35
|
+
|
36
|
+
assert_equal @base, record
|
37
|
+
end
|
38
|
+
|
39
|
+
test "given two dates, gives us filtered records between this date" do
|
40
|
+
assert_equal @base, Post.date_between(:before => 1.year.ago, :after => Time.now).first
|
41
|
+
end
|
42
|
+
|
43
|
+
test "return a post with same title" do
|
44
|
+
@base.update_attributes(:title => "Post Title")
|
45
|
+
assert_equal @base, Post.filtered_relation(:title => "Post Title").first
|
46
|
+
end
|
47
|
+
|
48
|
+
test "return a post with same title and body" do
|
49
|
+
@base.update_attributes(:title => "Post Title", :body => "Ruby")
|
50
|
+
assert_equal @base, Post.filtered_relation(:title => "Post Title", :body => "Ruby").first
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
def create_posts
|
55
|
+
valid_attributes = {
|
56
|
+
:body => "Hello.",
|
57
|
+
:title => "Hi!",
|
58
|
+
:content => "text",
|
59
|
+
:user_id => 1,
|
60
|
+
:published_at => Time.now
|
61
|
+
}
|
62
|
+
|
63
|
+
@base = Post.create(valid_attributes)
|
64
|
+
@quote = Post.create(valid_attributes.merge(:content => "quote"))
|
65
|
+
@number2 = Post.create(valid_attributes.merge(:user_id => 2))
|
66
|
+
@old = Post.create(valid_attributes.merge(:published_at => 1.year.ago))
|
67
|
+
end
|
68
|
+
|
69
|
+
teardown do
|
70
|
+
ActiveRecord::Base.connection.tables.each do |table|
|
71
|
+
ActiveRecord::Base.connection.drop_table(table)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
data/test/schema.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
|
3
|
+
ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => "test.sqlite3")
|
4
|
+
|
5
|
+
def setup_db
|
6
|
+
|
7
|
+
ActiveRecord::Base.connection.tables.each do |table|
|
8
|
+
ActiveRecord::Base.connection.drop_table(table)
|
9
|
+
end
|
10
|
+
|
11
|
+
ActiveRecord::Schema.define(:version => 1) do
|
12
|
+
|
13
|
+
create_table :posts do |t|
|
14
|
+
t.string :body
|
15
|
+
t.string :title
|
16
|
+
t.text :content
|
17
|
+
t.integer :user_id
|
18
|
+
t.datetime :published_at
|
19
|
+
t.timestamps
|
20
|
+
end
|
21
|
+
|
22
|
+
create_table :users do |t|
|
23
|
+
t.timestamps
|
24
|
+
end
|
25
|
+
|
26
|
+
create_table :comments do |t|
|
27
|
+
t.text :subject
|
28
|
+
t.text :description
|
29
|
+
t.integer :post_id
|
30
|
+
t.timestamps
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class Post < ActiveRecord::Base
|
37
|
+
belongs_to :user
|
38
|
+
has_many :comments
|
39
|
+
end
|
40
|
+
|
41
|
+
class User < ActiveRecord::Base
|
42
|
+
has_many :posts
|
43
|
+
end
|
44
|
+
|
45
|
+
class Comment < ActiveRecord::Base
|
46
|
+
belongs_to :post
|
47
|
+
end
|
48
|
+
|
data/test/test.sqlite3
ADDED
Binary file
|
data/test/test_helper.rb
ADDED
data/test.sqlite3
ADDED
Binary file
|
data/uninstall.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# Uninstall hook code here
|
metadata
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: relata
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 25
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 3
|
10
|
+
version: 0.0.3
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Anderson Leite, Guilherme Silveira
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-10-22 00:00:00 -02:00
|
19
|
+
default_executable:
|
20
|
+
dependencies: []
|
21
|
+
|
22
|
+
description:
|
23
|
+
email: anderson.leite@caelum.com.br
|
24
|
+
executables: []
|
25
|
+
|
26
|
+
extensions: []
|
27
|
+
|
28
|
+
extra_rdoc_files:
|
29
|
+
- README.markdown
|
30
|
+
files:
|
31
|
+
- Gemfile
|
32
|
+
- init.rb
|
33
|
+
- install.rb
|
34
|
+
- MIT-LICENSE
|
35
|
+
- Rakefile
|
36
|
+
- README.markdown
|
37
|
+
- test.sqlite3
|
38
|
+
- uninstall.rb
|
39
|
+
- lib/filtered_relation.rb
|
40
|
+
- lib/relata/dsl/conditions.rb
|
41
|
+
- lib/relata/dsl/constraints.rb
|
42
|
+
- lib/relata/dsl/custom_relation.rb
|
43
|
+
- lib/relata/dsl/field_search.rb
|
44
|
+
- lib/relata/dsl/missed_builder.rb
|
45
|
+
- lib/relata/dsl/querys/fields.rb
|
46
|
+
- lib/relata/dsl/querys/multiple.rb
|
47
|
+
- lib/relata/dsl/querys/simple.rb
|
48
|
+
- lib/relata/dsl.rb
|
49
|
+
- lib/relata/filter.rb
|
50
|
+
- lib/relata/related_query_methods.rb
|
51
|
+
- test/dsl_test.rb
|
52
|
+
- test/filtered_relation_test.rb
|
53
|
+
- test/schema.rb
|
54
|
+
- test/test.sqlite3
|
55
|
+
- test/test_helper.rb
|
56
|
+
has_rdoc: false
|
57
|
+
homepage: http://github.com/caelum/relata
|
58
|
+
licenses: []
|
59
|
+
|
60
|
+
post_install_message:
|
61
|
+
rdoc_options: []
|
62
|
+
|
63
|
+
require_paths:
|
64
|
+
- lib
|
65
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
66
|
+
none: false
|
67
|
+
requirements:
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
hash: 3
|
71
|
+
segments:
|
72
|
+
- 0
|
73
|
+
version: "0"
|
74
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
75
|
+
none: false
|
76
|
+
requirements:
|
77
|
+
- - ">="
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
hash: 3
|
80
|
+
segments:
|
81
|
+
- 0
|
82
|
+
version: "0"
|
83
|
+
requirements: []
|
84
|
+
|
85
|
+
rubyforge_project:
|
86
|
+
rubygems_version: 1.3.7
|
87
|
+
signing_key:
|
88
|
+
specification_version: 3
|
89
|
+
summary: Helps poking around with relationships when using ARel
|
90
|
+
test_files: []
|
91
|
+
|