sakuramochi 0.2.1 → 0.5.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/.rspec +1 -1
- data/.travis.yml +3 -0
- data/Gemfile +8 -8
- data/README.md +26 -6
- data/Rakefile +2 -2
- data/VERSION +1 -1
- data/lib/sakuramochi.rb +3 -0
- data/lib/sakuramochi/condition.rb +139 -0
- data/lib/sakuramochi/predicate_builder.rb +12 -7
- data/lib/sakuramochi/relation.rb +58 -0
- data/sakuramochi.gemspec +29 -23
- data/spec/sakuramochi/condition_spec.rb +108 -0
- data/spec/sakuramochi/predicate_builder_spec.rb +15 -36
- data/spec/sakuramochi/predicate_spec.rb +1 -0
- data/spec/sakuramochi/relation_spec.rb +29 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/support/matcher.rb +5 -0
- data/spec/support/schema.rb +63 -0
- metadata +38 -32
data/.rspec
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
--color
|
2
|
-
--format
|
2
|
+
--format d
|
data/.travis.yml
ADDED
data/Gemfile
CHANGED
@@ -1,13 +1,13 @@
|
|
1
|
-
source
|
1
|
+
source 'http://rubygems.org'
|
2
2
|
|
3
|
-
gem
|
4
|
-
gem
|
3
|
+
gem 'activesupport', '~> 3.0'
|
4
|
+
gem 'activerecord', '~> 3.0'
|
5
5
|
|
6
6
|
group :development do
|
7
|
-
gem
|
8
|
-
gem
|
9
|
-
gem
|
10
|
-
gem
|
11
|
-
gem
|
7
|
+
gem 'sqlite3'
|
8
|
+
gem 'rspec'
|
9
|
+
gem 'rdoc'
|
10
|
+
gem 'bundler'
|
11
|
+
gem 'jeweler'
|
12
12
|
end
|
13
13
|
|
data/README.md
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# Sakuramochi
|
2
2
|
|
3
|
-
|
3
|
+
[](http://travis-ci.org/mashiro/sakuramochi)
|
4
|
+
|
5
|
+
Sakuramochi is a minimal extensions for Active Record 3.
|
4
6
|
|
5
7
|
This is similar to the ar-extensions gem.
|
6
8
|
|
@@ -15,13 +17,13 @@ gem 'sakuramochi'
|
|
15
17
|
|
16
18
|
```ruby
|
17
19
|
User.where(:name_contains => 'ai')
|
18
|
-
# => "SELECT
|
20
|
+
# => "SELECT "users".* FROM "users" WHERE ("users"."name" LIKE '%ai%')"
|
19
21
|
|
20
22
|
User.where(:name_contains_any => ['ru', 'ai'])
|
21
|
-
# => "SELECT
|
23
|
+
# => "SELECT "users".* FROM "users" WHERE (("users"."name" LIKE '%ru%' OR "users"."name" LIKE '%ai%'))"
|
22
24
|
|
23
25
|
User.where(:name_contains_all => ['ru', 'ai'])
|
24
|
-
# => "SELECT
|
26
|
+
# => "SELECT "users".* FROM "users" WHERE (("users"."name" LIKE '%ru%' AND "users"."name" LIKE '%ai%'))"
|
25
27
|
```
|
26
28
|
|
27
29
|
## Predicates
|
@@ -36,6 +38,24 @@ User.where(:name_contains_all => ['ru', 'ai'])
|
|
36
38
|
* lt
|
37
39
|
* lte, lteq
|
38
40
|
|
41
|
+
## Conditions
|
42
|
+
```
|
43
|
+
expression = term {("and"|"or") term}
|
44
|
+
term = ["not"] factor
|
45
|
+
factor = array | hash | "(" expression ")"
|
46
|
+
```
|
47
|
+
|
48
|
+
``` ruby
|
49
|
+
User.where([{:name_contains => 'harune'}, :and, {:name_contains => 'aira'}])
|
50
|
+
# => "SELECT "users".* FROM "users" WHERE (("users"."name" LIKE '%harune%') AND ("users"."name" LIKE '%aira%'))"
|
51
|
+
|
52
|
+
User.where([:not, {:name_end_with => "mion"}])
|
53
|
+
# => SELECT "users".* FROM "users" WHERE (NOT (("users"."name" LIKE '%mion')))
|
54
|
+
|
55
|
+
User.where([:not, :'(', {:name_contains => 'aira'}, :or, {:name_contains => 'rizumu'}, :')', :and, {:age_gte => 14}])
|
56
|
+
# => SELECT "users".* FROM "users" WHERE (NOT ((("users"."name" LIKE '%aira%') OR ("users"."name" LIKE '%rizumu%'))) AND ("users"."age" >= 14))
|
57
|
+
```
|
58
|
+
|
39
59
|
## Configration
|
40
60
|
|
41
61
|
```ruby
|
@@ -55,10 +75,10 @@ Sakuramochi.configure do |config|
|
|
55
75
|
end
|
56
76
|
|
57
77
|
User.where(:name_eq_amamiya => 'rizumu')
|
58
|
-
# => "SELECT
|
78
|
+
# => "SELECT "users".* FROM "users" WHERE "users"."name" = 'amamiya rizumu'"
|
59
79
|
|
60
80
|
User.where(:name_surrounds_with => ["ama", "umu"])
|
61
|
-
# => "SELECT
|
81
|
+
# => "SELECT "users".* FROM "users" WHERE ("users"."name" LIKE 'ama%umu')"
|
62
82
|
```
|
63
83
|
|
64
84
|
## Copyright
|
data/Rakefile
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.5.0
|
data/lib/sakuramochi.rb
CHANGED
@@ -4,7 +4,10 @@ require 'active_record'
|
|
4
4
|
require 'sakuramochi/config'
|
5
5
|
require 'sakuramochi/predicate'
|
6
6
|
require 'sakuramochi/predicate_builder'
|
7
|
+
require 'sakuramochi/condition'
|
8
|
+
require 'sakuramochi/relation'
|
7
9
|
|
8
10
|
ActiveSupport.on_load(:active_record) do
|
11
|
+
ActiveRecord::Relation.send(:include, Sakuramochi::Relation)
|
9
12
|
ActiveRecord::PredicateBuilder.send(:include, Sakuramochi::PredicateBuilder)
|
10
13
|
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
module Sakuramochi
|
2
|
+
module Condition
|
3
|
+
class UnexpectedError < StandardError; end
|
4
|
+
class SyntaxError < StandardError; end
|
5
|
+
|
6
|
+
module Nodes
|
7
|
+
class Node
|
8
|
+
end
|
9
|
+
|
10
|
+
class Expression < Node
|
11
|
+
attr_reader :operator, :left, :right
|
12
|
+
def initialize(operator, left, right)
|
13
|
+
@operator = operator
|
14
|
+
@left = left
|
15
|
+
@right = right
|
16
|
+
end
|
17
|
+
|
18
|
+
def inspect
|
19
|
+
"(#{@operator} #{@left.inspect} #{@right.inspect})"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class Term < Node
|
24
|
+
attr_reader :operator, :value
|
25
|
+
def initialize(operator, value)
|
26
|
+
@operator = operator
|
27
|
+
@value = value
|
28
|
+
end
|
29
|
+
|
30
|
+
def inspect
|
31
|
+
"(#{@operator} #{@value.inspect})"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class Factor < Node
|
36
|
+
attr_reader :value
|
37
|
+
def initialize(value)
|
38
|
+
@value = value
|
39
|
+
end
|
40
|
+
|
41
|
+
def inspect
|
42
|
+
"#{@value}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class Group < Node
|
47
|
+
attr_reader :expression
|
48
|
+
def initialize(expression)
|
49
|
+
@expression = expression
|
50
|
+
end
|
51
|
+
|
52
|
+
def inspect
|
53
|
+
"(#{@expression.inspect})"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# expression = term {("and"|"or") term}
|
59
|
+
# term = ["not"] factor
|
60
|
+
# factor = array | hash | "(" expression ")"
|
61
|
+
class Parser
|
62
|
+
EXPRESSIONS = ['and', 'or']
|
63
|
+
TERMS = ['not']
|
64
|
+
PARENTHESES = {'(' => ')'}
|
65
|
+
|
66
|
+
def initialize(tokens)
|
67
|
+
@tokens = tokens.dup
|
68
|
+
@tokens = @tokens.split(/\s+/) if @tokens.is_a?(String) # debug
|
69
|
+
end
|
70
|
+
|
71
|
+
def parse
|
72
|
+
expression
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def self.indifferenct_key(token)
|
78
|
+
if token.is_a?(String) || token.is_a?(Symbol)
|
79
|
+
token.to_s.downcase
|
80
|
+
else
|
81
|
+
token
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def peek
|
86
|
+
token = @tokens.first
|
87
|
+
self.class.indifferenct_key token
|
88
|
+
end
|
89
|
+
|
90
|
+
def shift
|
91
|
+
token = @tokens.shift
|
92
|
+
self.class.indifferenct_key token
|
93
|
+
end
|
94
|
+
|
95
|
+
def accept(*args)
|
96
|
+
if args.include?(peek)
|
97
|
+
shift
|
98
|
+
else
|
99
|
+
nil
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def expect(*args)
|
104
|
+
if token = accept(*args)
|
105
|
+
token
|
106
|
+
else
|
107
|
+
raise UnexpectedError
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def expression
|
112
|
+
node = term
|
113
|
+
while token = accept(*EXPRESSIONS)
|
114
|
+
node = Nodes::Expression.new(token, node, term)
|
115
|
+
end
|
116
|
+
node
|
117
|
+
end
|
118
|
+
|
119
|
+
def term
|
120
|
+
if token = accept(*TERMS)
|
121
|
+
Nodes::Term.new(token, factor)
|
122
|
+
else
|
123
|
+
factor
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def factor
|
128
|
+
if token = accept(*PARENTHESES.keys)
|
129
|
+
node = expression
|
130
|
+
expect PARENTHESES[token]
|
131
|
+
Nodes::Group.new(node)
|
132
|
+
else
|
133
|
+
Nodes::Factor.new(shift)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
139
|
+
end
|
@@ -19,7 +19,7 @@ module Sakuramochi
|
|
19
19
|
|
20
20
|
if value.is_a?(Hash)
|
21
21
|
table = Arel::Table.new(column, engine)
|
22
|
-
|
22
|
+
build_from_hash_with_predicate(engine, value, table)
|
23
23
|
else
|
24
24
|
column = column.to_s
|
25
25
|
|
@@ -42,6 +42,8 @@ module Sakuramochi
|
|
42
42
|
predicates.flatten.compact
|
43
43
|
end
|
44
44
|
|
45
|
+
private
|
46
|
+
|
45
47
|
def build_attribute_with_predicate(attribute, value, predicate)
|
46
48
|
if predicate.validate(value)
|
47
49
|
if predicate.converter
|
@@ -61,21 +63,24 @@ module Sakuramochi
|
|
61
63
|
value = value.select(value.klass.arel_table[value.klass.primary_key]) if value.select_values.empty?
|
62
64
|
attribute.in(value.arel.ast)
|
63
65
|
when Array, ActiveRecord::Associations::CollectionProxy
|
64
|
-
|
65
|
-
|
66
|
-
}
|
66
|
+
when Array, ActiveRecord::Associations::CollectionProxy
|
67
|
+
values = value.to_a.map {|x| x.is_a?(ActiveRecord::Base) ? x.id : x}
|
68
|
+
ranges, values = values.partition {|v| v.is_a?(Range) || v.is_a?(Arel::Relation)}
|
69
|
+
|
70
|
+
array_predicates = ranges.map {|range| attribute.in(range)}
|
67
71
|
|
68
72
|
if values.include?(nil)
|
69
73
|
values = values.compact
|
70
74
|
if values.empty?
|
71
|
-
attribute.eq
|
75
|
+
array_predicates << attribute.eq(nil)
|
72
76
|
else
|
73
|
-
attribute.in(values.compact).or
|
77
|
+
array_predicates << attribute.in(values.compact).or(attribute.eq(nil))
|
74
78
|
end
|
75
79
|
else
|
76
|
-
attribute.in(values)
|
80
|
+
array_predicates << attribute.in(values)
|
77
81
|
end
|
78
82
|
|
83
|
+
array_predicates.inject {|composite, predicate| composite.or(predicate)}
|
79
84
|
when Range, Arel::Relation
|
80
85
|
attribute.in(value)
|
81
86
|
when ActiveRecord::Base
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
require 'sakuramochi/condition'
|
3
|
+
|
4
|
+
module Sakuramochi
|
5
|
+
module Relation
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
alias :build_where_without_condition :build_where
|
10
|
+
alias :build_where :build_where_with_condition
|
11
|
+
end
|
12
|
+
|
13
|
+
module InstanceMethods
|
14
|
+
def collapse_conditions(node, other)
|
15
|
+
case node
|
16
|
+
when Sakuramochi::Condition::Nodes::Expression
|
17
|
+
case node.operator.to_s
|
18
|
+
when 'and'
|
19
|
+
left = collapse_conditions(node.left, other)
|
20
|
+
right = collapse_conditions(node.right, other)
|
21
|
+
Arel::Nodes::And.new([left, right])
|
22
|
+
when 'or'
|
23
|
+
left = collapse_conditions(node.left, other)
|
24
|
+
right = collapse_conditions(node.right, other)
|
25
|
+
Arel::Nodes::Or.new(left, right)
|
26
|
+
end
|
27
|
+
|
28
|
+
when Sakuramochi::Condition::Nodes::Term
|
29
|
+
case node.operator.to_s
|
30
|
+
when 'not'
|
31
|
+
right = collapse_conditions(node.value, other)
|
32
|
+
Arel::Nodes::Not.new(right)
|
33
|
+
end
|
34
|
+
|
35
|
+
when Sakuramochi::Condition::Nodes::Factor
|
36
|
+
wheres = build_where_without_condition(node.value, other)
|
37
|
+
arel = table.from(table)
|
38
|
+
collapse_wheres(arel, (wheres - ['']).uniq)
|
39
|
+
arel.constraints.inject { |left, right| left.and(right) }
|
40
|
+
|
41
|
+
when Sakuramochi::Condition::Nodes::Group
|
42
|
+
expression = collapse_conditions(node.expression, other)
|
43
|
+
Arel::Nodes::Grouping.new(expression)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def build_where_with_condition(opts, other = [])
|
48
|
+
if opts.is_a?(Array) && !opts.first.is_a?(String)
|
49
|
+
ast = Sakuramochi::Condition::Parser.new(opts.dup).parse
|
50
|
+
[collapse_conditions(ast, other)]
|
51
|
+
else
|
52
|
+
build_where_without_condition(opts, other)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
data/sakuramochi.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "sakuramochi"
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.5.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["mashiro"]
|
12
|
-
s.date = "2011-
|
12
|
+
s.date = "2011-11-28"
|
13
13
|
s.description = "Minimal extensions for active record 3"
|
14
14
|
s.email = "mail@mashiro.org"
|
15
15
|
s.extra_rdoc_files = [
|
@@ -19,20 +19,26 @@ Gem::Specification.new do |s|
|
|
19
19
|
s.files = [
|
20
20
|
".document",
|
21
21
|
".rspec",
|
22
|
+
".travis.yml",
|
22
23
|
"Gemfile",
|
23
24
|
"LICENSE.txt",
|
24
25
|
"README.md",
|
25
26
|
"Rakefile",
|
26
27
|
"VERSION",
|
27
28
|
"lib/sakuramochi.rb",
|
29
|
+
"lib/sakuramochi/condition.rb",
|
28
30
|
"lib/sakuramochi/config.rb",
|
29
31
|
"lib/sakuramochi/predicate.rb",
|
30
32
|
"lib/sakuramochi/predicate_builder.rb",
|
33
|
+
"lib/sakuramochi/relation.rb",
|
31
34
|
"sakuramochi.gemspec",
|
35
|
+
"spec/sakuramochi/condition_spec.rb",
|
32
36
|
"spec/sakuramochi/config_spec.rb",
|
33
37
|
"spec/sakuramochi/predicate_builder_spec.rb",
|
34
38
|
"spec/sakuramochi/predicate_spec.rb",
|
39
|
+
"spec/sakuramochi/relation_spec.rb",
|
35
40
|
"spec/spec_helper.rb",
|
41
|
+
"spec/support/matcher.rb",
|
36
42
|
"spec/support/schema.rb"
|
37
43
|
]
|
38
44
|
s.homepage = "http://github.com/mashiro/sakuramochi"
|
@@ -45,30 +51,30 @@ Gem::Specification.new do |s|
|
|
45
51
|
s.specification_version = 3
|
46
52
|
|
47
53
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
48
|
-
s.add_runtime_dependency(%q<activesupport>, ["
|
49
|
-
s.add_runtime_dependency(%q<activerecord>, ["
|
50
|
-
s.add_development_dependency(%q<sqlite3>, ["
|
51
|
-
s.add_development_dependency(%q<rspec>, ["
|
52
|
-
s.add_development_dependency(%q<
|
53
|
-
s.add_development_dependency(%q<
|
54
|
-
s.add_development_dependency(%q<
|
54
|
+
s.add_runtime_dependency(%q<activesupport>, ["~> 3.0"])
|
55
|
+
s.add_runtime_dependency(%q<activerecord>, ["~> 3.0"])
|
56
|
+
s.add_development_dependency(%q<sqlite3>, [">= 0"])
|
57
|
+
s.add_development_dependency(%q<rspec>, [">= 0"])
|
58
|
+
s.add_development_dependency(%q<rdoc>, [">= 0"])
|
59
|
+
s.add_development_dependency(%q<bundler>, [">= 0"])
|
60
|
+
s.add_development_dependency(%q<jeweler>, [">= 0"])
|
55
61
|
else
|
56
|
-
s.add_dependency(%q<activesupport>, ["
|
57
|
-
s.add_dependency(%q<activerecord>, ["
|
58
|
-
s.add_dependency(%q<sqlite3>, ["
|
59
|
-
s.add_dependency(%q<rspec>, ["
|
60
|
-
s.add_dependency(%q<
|
61
|
-
s.add_dependency(%q<
|
62
|
-
s.add_dependency(%q<
|
62
|
+
s.add_dependency(%q<activesupport>, ["~> 3.0"])
|
63
|
+
s.add_dependency(%q<activerecord>, ["~> 3.0"])
|
64
|
+
s.add_dependency(%q<sqlite3>, [">= 0"])
|
65
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
66
|
+
s.add_dependency(%q<rdoc>, [">= 0"])
|
67
|
+
s.add_dependency(%q<bundler>, [">= 0"])
|
68
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
63
69
|
end
|
64
70
|
else
|
65
|
-
s.add_dependency(%q<activesupport>, ["
|
66
|
-
s.add_dependency(%q<activerecord>, ["
|
67
|
-
s.add_dependency(%q<sqlite3>, ["
|
68
|
-
s.add_dependency(%q<rspec>, ["
|
69
|
-
s.add_dependency(%q<
|
70
|
-
s.add_dependency(%q<
|
71
|
-
s.add_dependency(%q<
|
71
|
+
s.add_dependency(%q<activesupport>, ["~> 3.0"])
|
72
|
+
s.add_dependency(%q<activerecord>, ["~> 3.0"])
|
73
|
+
s.add_dependency(%q<sqlite3>, [">= 0"])
|
74
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
75
|
+
s.add_dependency(%q<rdoc>, [">= 0"])
|
76
|
+
s.add_dependency(%q<bundler>, [">= 0"])
|
77
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
72
78
|
end
|
73
79
|
end
|
74
80
|
|
@@ -0,0 +1,108 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
shared_examples_for 'expression node' do |operator, left, right, &block|
|
5
|
+
it { should be_instance_of Sakuramochi::Condition::Nodes::Expression }
|
6
|
+
its(:operator) { should eq operator }
|
7
|
+
its(:left) { should be_instance_of left }
|
8
|
+
its(:right) { should be_instance_of right }
|
9
|
+
end
|
10
|
+
|
11
|
+
shared_examples_for 'term node' do |operator, value|
|
12
|
+
it { should be_instance_of Sakuramochi::Condition::Nodes::Term }
|
13
|
+
its(:operator) { should eq operator }
|
14
|
+
its(:value) { should be_instance_of value }
|
15
|
+
end
|
16
|
+
|
17
|
+
shared_examples_for 'factor node' do |value|
|
18
|
+
it { should be_instance_of Sakuramochi::Condition::Nodes::Factor }
|
19
|
+
its(:value) { should eq value }
|
20
|
+
end
|
21
|
+
|
22
|
+
shared_examples_for 'group node' do |expression|
|
23
|
+
it { should be_instance_of Sakuramochi::Condition::Nodes::Group }
|
24
|
+
its(:expression) { should be_instance_of expression }
|
25
|
+
end
|
26
|
+
|
27
|
+
include Sakuramochi::Condition::Nodes
|
28
|
+
|
29
|
+
describe Sakuramochi::Condition::Parser do
|
30
|
+
context '1 and 2' do
|
31
|
+
before { @node = Sakuramochi::Condition::Parser.new('1 and 2').parse }
|
32
|
+
subject { @node }
|
33
|
+
|
34
|
+
it_should_behave_like 'expression node', 'and', Factor, Factor
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'not 1 or not 2' do
|
38
|
+
before { @node = Sakuramochi::Condition::Parser.new('not 1 or not 2').parse }
|
39
|
+
subject { @node }
|
40
|
+
it_should_behave_like 'expression node', 'or', Term, Term
|
41
|
+
|
42
|
+
context 'left' do
|
43
|
+
before { @node = @node.left }
|
44
|
+
it_should_behave_like 'term node', 'not', Factor
|
45
|
+
|
46
|
+
context 'value' do
|
47
|
+
before { @node = @node.value }
|
48
|
+
it_should_behave_like 'factor node', '1'
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'right' do
|
53
|
+
before { @node = @node.right }
|
54
|
+
it_should_behave_like 'term node', 'not', Factor
|
55
|
+
|
56
|
+
context 'value' do
|
57
|
+
before { @node = @node.value }
|
58
|
+
it_should_behave_like 'factor node', '2'
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context '( 3 and not ( ( 4 ) or 5 ) )' do
|
64
|
+
before { @node = Sakuramochi::Condition::Parser.new('( 3 and not ( ( 4 ) or 5 ) )').parse }
|
65
|
+
subject { @node }
|
66
|
+
it_should_behave_like 'group node', Expression
|
67
|
+
|
68
|
+
context 'expression' do
|
69
|
+
before { @node = @node.expression }
|
70
|
+
it_should_behave_like 'expression node', 'and', Factor, Term
|
71
|
+
|
72
|
+
context 'left' do
|
73
|
+
before { @node = @node.left }
|
74
|
+
it_should_behave_like 'factor node', '3'
|
75
|
+
end
|
76
|
+
|
77
|
+
context 'right' do
|
78
|
+
before { @node = @node.right }
|
79
|
+
it_should_behave_like 'term node', 'not', Group
|
80
|
+
|
81
|
+
context 'value' do
|
82
|
+
before { @node = @node.value }
|
83
|
+
it_should_behave_like 'group node', Expression
|
84
|
+
|
85
|
+
context 'expression' do
|
86
|
+
before { @node = @node.expression }
|
87
|
+
it_should_behave_like 'expression node', 'or', Group, Factor
|
88
|
+
|
89
|
+
context 'left' do
|
90
|
+
before { @node = @node.left }
|
91
|
+
it_should_behave_like 'group node', Factor
|
92
|
+
|
93
|
+
context 'expression' do
|
94
|
+
before { @node = @node.expression }
|
95
|
+
it_should_behave_like 'factor node', '4'
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
context 'right' do
|
100
|
+
before { @node = @node.right }
|
101
|
+
it_should_behave_like 'factor node', '5'
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -1,28 +1,7 @@
|
|
1
|
+
# encoding: utf-8
|
1
2
|
require 'spec_helper'
|
2
3
|
|
3
|
-
RSpec::Matchers.define :be_match_all do |expected|
|
4
|
-
match do |actuals|
|
5
|
-
actuals.all? { |actual| !!(actual =~ expected) }
|
6
|
-
end
|
7
|
-
end
|
8
|
-
|
9
4
|
describe Sakuramochi::PredicateBuilder do
|
10
|
-
before(:all) do
|
11
|
-
@aira = User.create! :name => 'harune aira'
|
12
|
-
['fresh fruit basket', 'munekyun taiken', 'heartful splash',
|
13
|
-
'lovely rainbow', 'hirahira hiraku koi no hana', 'crystal splash'
|
14
|
-
].each do |jump|
|
15
|
-
Status.create! :text => jump, :user => @aira
|
16
|
-
end
|
17
|
-
|
18
|
-
@rizumu = User.create! :name => 'amamiya rizumu'
|
19
|
-
['heartful splash', 'colorful choco parade', 'fun fun heart dive',
|
20
|
-
'stardust shower', 'happy macaron spin', 'pop\'n candy rocket'
|
21
|
-
].each do |jump|
|
22
|
-
Status.create! :text => jump, :user => @rizumu
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
5
|
describe 'matches' do
|
27
6
|
before do
|
28
7
|
Sakuramochi.configure do |config|
|
@@ -40,43 +19,43 @@ describe Sakuramochi::PredicateBuilder do
|
|
40
19
|
|
41
20
|
context 'contains' do
|
42
21
|
let(:key) { :text_contains }
|
43
|
-
context 'with "
|
44
|
-
let(:value) { '
|
22
|
+
context 'with "マカロン"' do
|
23
|
+
let(:value) { 'マカロン' }
|
45
24
|
|
46
25
|
it_should_behave_like :never_match
|
47
26
|
it { should have(1).items }
|
48
|
-
it { subject.map(&:text).should be_match_all
|
27
|
+
it { subject.map(&:text).should be_match_all /マカロン/ }
|
49
28
|
end
|
50
29
|
|
51
|
-
context 'with "
|
52
|
-
let(:value) { '
|
30
|
+
context 'with "スプラッシュ"' do
|
31
|
+
let(:value) { 'スプラッシュ' }
|
53
32
|
|
54
33
|
it_should_behave_like :never_match
|
55
34
|
it { should have(3).items }
|
56
|
-
it { subject.map(&:text).should be_match_all
|
35
|
+
it { subject.map(&:text).should be_match_all /スプラッシュ/ }
|
57
36
|
end
|
58
37
|
end
|
59
38
|
|
60
39
|
context 'contains_any' do
|
61
40
|
let(:key) { :text_contains_any }
|
62
|
-
context 'with ["
|
63
|
-
let(:value) { ['
|
41
|
+
context 'with ["ハート", "スプラッシュ"]' do
|
42
|
+
let(:value) { ['ハート', 'スプラッシュ'] }
|
64
43
|
|
65
44
|
it_should_behave_like :never_match
|
66
|
-
it { should have(
|
67
|
-
it { subject.map(&:text).should be_match_all /(
|
45
|
+
it { should have(7).items }
|
46
|
+
it { subject.map(&:text).should be_match_all /(ハート|スプラッシュ)/ }
|
68
47
|
end
|
69
48
|
end
|
70
49
|
|
71
50
|
context 'contains_all' do
|
72
51
|
let(:key) { :text_contains_all }
|
73
|
-
context 'with ["
|
74
|
-
let(:value) { ['
|
52
|
+
context 'with ["ハート", "スプラッシュ"]' do
|
53
|
+
let(:value) { ['ハート', 'スプラッシュ'] }
|
75
54
|
|
76
55
|
it_should_behave_like :never_match
|
77
56
|
it { should have(2).items }
|
78
|
-
it { subject.map(&:text).should be_match_all
|
79
|
-
it { subject.map(&:text).should be_match_all
|
57
|
+
it { subject.map(&:text).should be_match_all /ハート/ }
|
58
|
+
it { subject.map(&:text).should be_match_all /スプラッシュ/ }
|
80
59
|
end
|
81
60
|
end
|
82
61
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe Sakuramochi::Relation do
|
5
|
+
describe '#build_where_with_condition' do
|
6
|
+
describe 'not' do
|
7
|
+
before { @users = User.where([:not, {:name_contains => 'あいら'}]) }
|
8
|
+
subject { @users }
|
9
|
+
|
10
|
+
it { subject.map(&:name).should_not be_match_all /あいら/ }
|
11
|
+
end
|
12
|
+
|
13
|
+
describe 'and' do
|
14
|
+
before { @users = User.where([{:name_contains => '春音'}, :and, {:name_contains => 'あいら'}]) }
|
15
|
+
subject { @users }
|
16
|
+
|
17
|
+
it { subject.map(&:name).should be_match_all /春音/ }
|
18
|
+
it { subject.map(&:name).should be_match_all /あいら/ }
|
19
|
+
it { subject.map(&:name).should_not be_match_all /うる|える/ }
|
20
|
+
end
|
21
|
+
|
22
|
+
describe 'or' do
|
23
|
+
before { @users = User.where([{:name_contains => 'あいら'}, :or, {:name_contains => 'りずむ'}]) }
|
24
|
+
subject { @users }
|
25
|
+
|
26
|
+
it { subject.map(&:name).should_not be_match_all /みおん/ }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/spec/spec_helper.rb
CHANGED
data/spec/support/schema.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# encoding: utf-8
|
1
2
|
require 'active_record'
|
2
3
|
|
3
4
|
ActiveRecord::Base.configurations = {'test' => {:adapter => 'sqlite3', :database => ':memory:'}}
|
@@ -23,4 +24,66 @@ class CreateTestTables < ActiveRecord::Migration
|
|
23
24
|
t.string :text
|
24
25
|
end
|
25
26
|
end
|
27
|
+
|
28
|
+
def self.seed
|
29
|
+
items = {
|
30
|
+
'春音あいら' => [
|
31
|
+
'フレッシュフルーツバスケット',
|
32
|
+
'ハートフルスプラッシュ',
|
33
|
+
'カラフルチョコパレード',
|
34
|
+
'胸キュン体験',
|
35
|
+
'スターダストシャワー',
|
36
|
+
'ラブリーレインボー',
|
37
|
+
'ヒラヒラヒラク恋の花',
|
38
|
+
'ドレミファスライダー',
|
39
|
+
'クリスタルスプラッシュ',
|
40
|
+
'ポップンキャンディロケット',
|
41
|
+
'チカラ合わせて!ワンダースイーツShow',
|
42
|
+
'ココロ重ねて!ハートアーチファンタジー',
|
43
|
+
'フライハイチアガール',
|
44
|
+
'チアフルHip Hop Win!',
|
45
|
+
'ドキドキハロウィンナイト',
|
46
|
+
'ビタミンガーデンサンシャイン',
|
47
|
+
],
|
48
|
+
'天宮りずむ' => [
|
49
|
+
'ハートフルスプラッシュ',
|
50
|
+
'カラフルチョコパレード',
|
51
|
+
'FUNFUNハートダイブ',
|
52
|
+
'スターダストシャワー',
|
53
|
+
'ハッピーマカロンスピン',
|
54
|
+
'ドレミファスライダー',
|
55
|
+
'ポップンキャンディロケット',
|
56
|
+
'ドルフィンビーナル',
|
57
|
+
'チカラ合わせて!ワンダースイーツShow',
|
58
|
+
'ココロ重ねて!ハートアーチファンタジー',
|
59
|
+
'フライハイチアガール',
|
60
|
+
'チアフルHip Hop Win!',
|
61
|
+
'ドキドキハロウィンナイト',
|
62
|
+
'ビタミンガーデンサンシャイン',
|
63
|
+
],
|
64
|
+
'高峰みおん' => [
|
65
|
+
'スターダストシャワー',
|
66
|
+
'ヒラヒラヒラク恋の花',
|
67
|
+
'ドレミファスライダー',
|
68
|
+
'ゴールデンスターマジック',
|
69
|
+
'ときめきメモリーリーフ',
|
70
|
+
'はちみつキッス',
|
71
|
+
'ココロ重ねて!ハートアーチファンタジー',
|
72
|
+
'フライハイチアガール',
|
73
|
+
'チアフルHip Hop Win!',
|
74
|
+
'ドキドキハロウィンナイト',
|
75
|
+
'ビタミンガーデンサンシャイン',
|
76
|
+
],
|
77
|
+
'春音うる' => [],
|
78
|
+
'春音える' => [],
|
79
|
+
}
|
80
|
+
|
81
|
+
items.each do |name, jumps|
|
82
|
+
User.create! :name => name do |user|
|
83
|
+
jumps.each do |jump|
|
84
|
+
Status.create! :user => user, :text => jump
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
26
89
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sakuramochi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,77 +9,77 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-
|
12
|
+
date: 2011-11-28 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|
16
|
-
requirement: &
|
16
|
+
requirement: &21741100 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
|
-
- -
|
19
|
+
- - ~>
|
20
20
|
- !ruby/object:Gem::Version
|
21
|
-
version: 3.0
|
21
|
+
version: '3.0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *21741100
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: activerecord
|
27
|
-
requirement: &
|
27
|
+
requirement: &21740560 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
|
-
- -
|
30
|
+
- - ~>
|
31
31
|
- !ruby/object:Gem::Version
|
32
|
-
version: 3.0
|
32
|
+
version: '3.0'
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *21740560
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: sqlite3
|
38
|
-
requirement: &
|
38
|
+
requirement: &21736720 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
|
-
- -
|
41
|
+
- - ! '>='
|
42
42
|
- !ruby/object:Gem::Version
|
43
|
-
version:
|
43
|
+
version: '0'
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *21736720
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: rspec
|
49
|
-
requirement: &
|
49
|
+
requirement: &21736180 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
|
-
- -
|
52
|
+
- - ! '>='
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
54
|
+
version: '0'
|
55
55
|
type: :development
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *21736180
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
|
-
name:
|
60
|
-
requirement: &
|
59
|
+
name: rdoc
|
60
|
+
requirement: &21735640 !ruby/object:Gem::Requirement
|
61
61
|
none: false
|
62
62
|
requirements:
|
63
|
-
- -
|
63
|
+
- - ! '>='
|
64
64
|
- !ruby/object:Gem::Version
|
65
|
-
version:
|
65
|
+
version: '0'
|
66
66
|
type: :development
|
67
67
|
prerelease: false
|
68
|
-
version_requirements: *
|
68
|
+
version_requirements: *21735640
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
71
|
-
requirement: &
|
70
|
+
name: bundler
|
71
|
+
requirement: &21735140 !ruby/object:Gem::Requirement
|
72
72
|
none: false
|
73
73
|
requirements:
|
74
|
-
- -
|
74
|
+
- - ! '>='
|
75
75
|
- !ruby/object:Gem::Version
|
76
|
-
version:
|
76
|
+
version: '0'
|
77
77
|
type: :development
|
78
78
|
prerelease: false
|
79
|
-
version_requirements: *
|
79
|
+
version_requirements: *21735140
|
80
80
|
- !ruby/object:Gem::Dependency
|
81
|
-
name:
|
82
|
-
requirement: &
|
81
|
+
name: jeweler
|
82
|
+
requirement: &21734540 !ruby/object:Gem::Requirement
|
83
83
|
none: false
|
84
84
|
requirements:
|
85
85
|
- - ! '>='
|
@@ -87,7 +87,7 @@ dependencies:
|
|
87
87
|
version: '0'
|
88
88
|
type: :development
|
89
89
|
prerelease: false
|
90
|
-
version_requirements: *
|
90
|
+
version_requirements: *21734540
|
91
91
|
description: Minimal extensions for active record 3
|
92
92
|
email: mail@mashiro.org
|
93
93
|
executables: []
|
@@ -98,20 +98,26 @@ extra_rdoc_files:
|
|
98
98
|
files:
|
99
99
|
- .document
|
100
100
|
- .rspec
|
101
|
+
- .travis.yml
|
101
102
|
- Gemfile
|
102
103
|
- LICENSE.txt
|
103
104
|
- README.md
|
104
105
|
- Rakefile
|
105
106
|
- VERSION
|
106
107
|
- lib/sakuramochi.rb
|
108
|
+
- lib/sakuramochi/condition.rb
|
107
109
|
- lib/sakuramochi/config.rb
|
108
110
|
- lib/sakuramochi/predicate.rb
|
109
111
|
- lib/sakuramochi/predicate_builder.rb
|
112
|
+
- lib/sakuramochi/relation.rb
|
110
113
|
- sakuramochi.gemspec
|
114
|
+
- spec/sakuramochi/condition_spec.rb
|
111
115
|
- spec/sakuramochi/config_spec.rb
|
112
116
|
- spec/sakuramochi/predicate_builder_spec.rb
|
113
117
|
- spec/sakuramochi/predicate_spec.rb
|
118
|
+
- spec/sakuramochi/relation_spec.rb
|
114
119
|
- spec/spec_helper.rb
|
120
|
+
- spec/support/matcher.rb
|
115
121
|
- spec/support/schema.rb
|
116
122
|
homepage: http://github.com/mashiro/sakuramochi
|
117
123
|
licenses:
|
@@ -128,7 +134,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
128
134
|
version: '0'
|
129
135
|
segments:
|
130
136
|
- 0
|
131
|
-
hash:
|
137
|
+
hash: 493135335537982085
|
132
138
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
133
139
|
none: false
|
134
140
|
requirements:
|