relata 0.0.3
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/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
|
+
|