set_builder 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/MIT-LICENSE +20 -0
- data/README.md +35 -0
- data/Rakefile +14 -0
- data/assets/javascripts/array.js +100 -0
- data/assets/javascripts/set_builder.js +415 -0
- data/init.rb +1 -0
- data/install.rb +1 -0
- data/lib/set_builder.rb +58 -0
- data/lib/set_builder/constraint.rb +67 -0
- data/lib/set_builder/modifier.rb +134 -0
- data/lib/set_builder/modifier/adverb.rb +11 -0
- data/lib/set_builder/modifier/base.rb +105 -0
- data/lib/set_builder/modifier/verb.rb +24 -0
- data/lib/set_builder/modifier_collection.rb +41 -0
- data/lib/set_builder/modifiers.rb +3 -0
- data/lib/set_builder/modifiers/date_modifier.rb +83 -0
- data/lib/set_builder/modifiers/number_modifier.rb +48 -0
- data/lib/set_builder/modifiers/string_modifier.rb +81 -0
- data/lib/set_builder/query_builders/string.rb +0 -0
- data/lib/set_builder/set.rb +81 -0
- data/lib/set_builder/trait.rb +74 -0
- data/lib/set_builder/traits.rb +42 -0
- data/lib/set_builder/value_map.rb +62 -0
- data/lib/set_builder/version.rb +3 -0
- data/set_builder.gemspec +25 -0
- data/spec/commands/example_command.rb +19 -0
- data/spec/dom.html +24 -0
- data/spec/lib/images/bg.png +0 -0
- data/spec/lib/images/hr.png +0 -0
- data/spec/lib/images/loading.gif +0 -0
- data/spec/lib/images/sprites.bg.png +0 -0
- data/spec/lib/images/sprites.png +0 -0
- data/spec/lib/images/vr.png +0 -0
- data/spec/lib/jspec.css +149 -0
- data/spec/lib/jspec.growl.js +115 -0
- data/spec/lib/jspec.jquery.js +88 -0
- data/spec/lib/jspec.js +1908 -0
- data/spec/lib/jspec.nodejs.js +18 -0
- data/spec/lib/jspec.shell.js +39 -0
- data/spec/lib/jspec.timers.js +154 -0
- data/spec/lib/jspec.xhr.js +210 -0
- data/spec/node.js +10 -0
- data/spec/rhino.js +10 -0
- data/spec/server.html +18 -0
- data/spec/server.rb +4 -0
- data/spec/unit/array.spec.js +82 -0
- data/spec/unit/set_builder.spec.js +166 -0
- data/spec/unit/spec.helper.js +0 -0
- data/test/date_modifier_test.rb +13 -0
- data/test/inflector_test.rb +27 -0
- data/test/modifier_test.rb +90 -0
- data/test/set_test.rb +96 -0
- data/test/test_helper.rb +79 -0
- data/test/trait_test.rb +49 -0
- data/test/traits_test.rb +41 -0
- data/test/value_map_test.rb +30 -0
- data/uninstall.rb +1 -0
- metadata +191 -0
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'set_builder/modifier/adverb'
|
2
|
+
|
3
|
+
|
4
|
+
module SetBuilder
|
5
|
+
module Modifiers
|
6
|
+
class DateModifier < Modifier::Adverb
|
7
|
+
|
8
|
+
|
9
|
+
|
10
|
+
def self.operators
|
11
|
+
{
|
12
|
+
:ever => [],
|
13
|
+
:before => [:date],
|
14
|
+
:after => [:date],
|
15
|
+
:on => [:date],
|
16
|
+
:during_month => [:month],
|
17
|
+
:during_year => [:year],
|
18
|
+
:in_the_last => [:number, :period],
|
19
|
+
:between => [:date, :date]
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
|
25
|
+
def build_conditions_for(selector)
|
26
|
+
case operator
|
27
|
+
when :ever
|
28
|
+
"#{selector} IS NOT NULL"
|
29
|
+
when :before
|
30
|
+
"#{selector}<'#{format_value(get_date)}'"
|
31
|
+
when :after
|
32
|
+
"#{selector}>'#{format_value(get_date)}'"
|
33
|
+
when :on
|
34
|
+
"#{selector}='#{format_value(get_date)}'"
|
35
|
+
when :during_month
|
36
|
+
"extract(month from #{selector})=#{values[0].to_i}"
|
37
|
+
when :during_year
|
38
|
+
year = values[0].to_i
|
39
|
+
return "TRUE" if year <= 0
|
40
|
+
"#{selector} BETWEEN '#{year}-01-01' AND '#{year}-12-31'"
|
41
|
+
when :in_the_last
|
42
|
+
"#{selector}>='#{format_value(get_date)}'"
|
43
|
+
when :between
|
44
|
+
"#{selector} BETWEEN '#{format_value(Date.parse values[0])}' AND '#{format_value(Date.parse values[1])}'"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
|
50
|
+
protected
|
51
|
+
|
52
|
+
|
53
|
+
|
54
|
+
def get_date
|
55
|
+
case operator
|
56
|
+
when :in_the_last
|
57
|
+
case values[1]
|
58
|
+
when "years", "year"
|
59
|
+
values[0].to_i.years.ago
|
60
|
+
when "months", "month"
|
61
|
+
values[0].to_i.months.ago
|
62
|
+
when "weeks", "week"
|
63
|
+
values[0].to_i.weeks.ago
|
64
|
+
when "days", "day"
|
65
|
+
values[0].to_i.days.ago
|
66
|
+
end
|
67
|
+
else
|
68
|
+
Date.parse values[0]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
|
74
|
+
def format_value(date)
|
75
|
+
date = [Date.new(1,1,1), date].max # constrain dates to A.D.
|
76
|
+
date.strftime('%Y-%m-%d')
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'set_builder/modifier/verb'
|
2
|
+
|
3
|
+
|
4
|
+
module SetBuilder
|
5
|
+
module Modifiers
|
6
|
+
class NumberModifier < Modifier::Verb
|
7
|
+
|
8
|
+
|
9
|
+
|
10
|
+
def self.operators
|
11
|
+
{
|
12
|
+
:is => [:number],
|
13
|
+
:is_less_than => [:number],
|
14
|
+
:is_greater_than => [:number],
|
15
|
+
:is_between => [:number, :number]
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
|
21
|
+
def build_conditions_for(selector)
|
22
|
+
case operator
|
23
|
+
when :is
|
24
|
+
["#{selector}=?", format_value]
|
25
|
+
when :is_less_than
|
26
|
+
["#{selector}<?", format_value]
|
27
|
+
when :is_greater_than
|
28
|
+
["#{selector}>?", format_value]
|
29
|
+
when :is_between
|
30
|
+
["#{selector}>=? AND #{selector}<=?", values[0], values[1]]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
|
39
|
+
|
40
|
+
def format_value
|
41
|
+
values[0]
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'set_builder/modifier/verb'
|
2
|
+
|
3
|
+
|
4
|
+
module SetBuilder
|
5
|
+
module Modifiers
|
6
|
+
class StringModifier < Modifier::Verb
|
7
|
+
|
8
|
+
|
9
|
+
|
10
|
+
def self.operators
|
11
|
+
{
|
12
|
+
:contains => [:string],
|
13
|
+
:does_not_contain => [:string],
|
14
|
+
:begins_with => [:string],
|
15
|
+
:does_not_begin_with => [:string],
|
16
|
+
:ends_with => [:string],
|
17
|
+
:does_not_end_with => [:string],
|
18
|
+
:is => [:string],
|
19
|
+
:is_not => [:string]
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
|
25
|
+
def self.negate(operator)
|
26
|
+
case operator
|
27
|
+
when :contains
|
28
|
+
"does not contain"
|
29
|
+
when :begins_with
|
30
|
+
"does not begin with"
|
31
|
+
when :ends_with
|
32
|
+
"does not end with"
|
33
|
+
when :is
|
34
|
+
"is not"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
|
40
|
+
def build_conditions_for(selector, operator=nil)
|
41
|
+
operator ||= self.operator
|
42
|
+
case operator
|
43
|
+
|
44
|
+
when :does_not_contain
|
45
|
+
negate(selector, :contains)
|
46
|
+
when :does_not_begin_with
|
47
|
+
negate(selector, :begins_with)
|
48
|
+
when :does_not_end_with
|
49
|
+
negate(selector, :ends_with)
|
50
|
+
when :is_not
|
51
|
+
negate(selector, :is)
|
52
|
+
|
53
|
+
when :is
|
54
|
+
["#{selector}=?", values[0]]
|
55
|
+
else
|
56
|
+
["#{selector} LIKE ?", get_like_value_for_operator]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
|
65
|
+
|
66
|
+
def get_like_value_for_operator
|
67
|
+
case operator
|
68
|
+
when :contains
|
69
|
+
"%#{values[0]}%"
|
70
|
+
when :begins_with
|
71
|
+
"#{values[0]}%"
|
72
|
+
when :ends_with
|
73
|
+
"%#{values[0]}"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
File without changes
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module SetBuilder
|
2
|
+
class Set
|
3
|
+
|
4
|
+
|
5
|
+
|
6
|
+
def initialize(model_or_scope, raw_data)
|
7
|
+
@model, @scope = get_model_and_scope(model_or_scope)
|
8
|
+
@set = raw_data
|
9
|
+
end
|
10
|
+
|
11
|
+
|
12
|
+
|
13
|
+
attr_reader :model
|
14
|
+
|
15
|
+
|
16
|
+
|
17
|
+
def constraints
|
18
|
+
@constraints ||= get_constraints
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
|
23
|
+
#
|
24
|
+
# Returns true if all of the constraints in this set are valid
|
25
|
+
#
|
26
|
+
def valid?
|
27
|
+
constraints.all?(&:valid?)
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
|
32
|
+
#
|
33
|
+
# Describes this set in natural language
|
34
|
+
#
|
35
|
+
def to_s
|
36
|
+
constraints.to_sentence
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
|
41
|
+
#
|
42
|
+
# Returns an instance of ActiveRecord::NamedScope::Scope
|
43
|
+
# which can fetch the objects which belong to this set
|
44
|
+
#
|
45
|
+
def perform
|
46
|
+
constraints.inject(@scope) {|scope, constraint| constraint.perform(scope)}
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
|
54
|
+
|
55
|
+
def get_constraints
|
56
|
+
@set.inject([]) do |constraints, line|
|
57
|
+
negate, trait_name, args = false, line.first.to_s, line[1..-1]
|
58
|
+
trait_name, negate = trait_name[1..-1], true if (trait_name[0..0] == "!")
|
59
|
+
trait = model.traits[trait_name]
|
60
|
+
raise("\"#{trait_name}\" is not a trait for #{model}") unless trait
|
61
|
+
constraints << trait.apply(*args).negate(negate)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
|
67
|
+
# !todo: this can be overriden or factored out to allow SetBuilder
|
68
|
+
# to be used with other ORMs like DataMapper
|
69
|
+
def get_model_and_scope(model_or_scope)
|
70
|
+
if defined?(ActiveRecord::NamedScope::Scope) && model_or_scope.is_a?(ActiveRecord::NamedScope::Scope)
|
71
|
+
[model_or_scope.proxy_scope, model_or_scope]
|
72
|
+
else
|
73
|
+
# [model_or_scope, model_or_scope.scoped({})]
|
74
|
+
[model_or_scope, model_or_scope.scoped]
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'set_builder/constraint'
|
2
|
+
require 'set_builder/modifier'
|
3
|
+
|
4
|
+
|
5
|
+
module SetBuilder
|
6
|
+
class Trait
|
7
|
+
|
8
|
+
|
9
|
+
|
10
|
+
def initialize(name, part_of_speech, *args, &block)
|
11
|
+
case name
|
12
|
+
when Hash
|
13
|
+
@name, @direct_object_type = name.first[0].to_s, name.first[1]
|
14
|
+
else
|
15
|
+
@name = name.to_s
|
16
|
+
end
|
17
|
+
@part_of_speech, @block = part_of_speech, block
|
18
|
+
@modifiers = (args||[]).collect {|modifier| Modifier[modifier]}
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
|
23
|
+
attr_reader :name, :part_of_speech, :modifiers, :direct_object_type
|
24
|
+
|
25
|
+
|
26
|
+
|
27
|
+
def requires_direct_object?
|
28
|
+
!@direct_object_type.nil?
|
29
|
+
end
|
30
|
+
alias :direct_object_required? :requires_direct_object?
|
31
|
+
|
32
|
+
|
33
|
+
|
34
|
+
def noun?
|
35
|
+
(self.part_of_speech == :noun)
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
|
40
|
+
def to_s(negative=false)
|
41
|
+
case part_of_speech
|
42
|
+
when :active
|
43
|
+
negative ? "who have not #{name}" : "who #{name}"
|
44
|
+
when :perfect
|
45
|
+
negative ? "who have not #{name}" : "who have #{name}"
|
46
|
+
when :passive
|
47
|
+
negative ? "who were not #{name}" : "who were #{name}"
|
48
|
+
when :reflexive
|
49
|
+
negative ? "who are not #{name}" : "who are #{name}"
|
50
|
+
when :noun
|
51
|
+
"whose #{name}"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
|
57
|
+
def to_json
|
58
|
+
array = []
|
59
|
+
array << (requires_direct_object? ? [name, @direct_object_type] : name)
|
60
|
+
array << part_of_speech
|
61
|
+
array << modifiers.collect{|klass| Modifier.name(klass)} unless modifiers.empty?
|
62
|
+
array.to_json
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
|
67
|
+
def apply(*args)
|
68
|
+
SetBuilder::Constraint.new(self, *args, &@block)
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'set_builder/trait'
|
2
|
+
require 'set_builder/modifier_collection'
|
3
|
+
|
4
|
+
|
5
|
+
module SetBuilder
|
6
|
+
class Traits < Array
|
7
|
+
|
8
|
+
|
9
|
+
|
10
|
+
def [](index)
|
11
|
+
case index
|
12
|
+
when Symbol, String
|
13
|
+
index = index.to_s
|
14
|
+
self.find {|trait| trait.name == index}
|
15
|
+
else
|
16
|
+
super
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
def to_json
|
23
|
+
"[#{collect(&:to_json).join(",")}]"
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
|
28
|
+
def modifiers
|
29
|
+
# !nb: not sure why inject was failing but it was modifying trait.modifiers!
|
30
|
+
@modifiers = ModifierCollection.new
|
31
|
+
each do |trait|
|
32
|
+
trait.modifiers.each do |modifier|
|
33
|
+
@modifiers << modifier unless @modifiers.member?(modifier)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
@modifiers
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module SetBuilder
|
2
|
+
module ValueMap
|
3
|
+
|
4
|
+
|
5
|
+
|
6
|
+
@registered_value_maps = {}
|
7
|
+
|
8
|
+
|
9
|
+
|
10
|
+
def self.registered?(name)
|
11
|
+
name = name.to_sym
|
12
|
+
@registered_value_maps.key?(name)
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
|
17
|
+
def self.to_s(name, value)
|
18
|
+
name = name.to_sym
|
19
|
+
map = @registered_value_maps[name]
|
20
|
+
if map
|
21
|
+
pair = map.find {|pair| pair[0] == value}
|
22
|
+
pair ? pair[1].to_s : "(unknown)"
|
23
|
+
else
|
24
|
+
value.to_s
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
|
30
|
+
def self.for(name)
|
31
|
+
name = name.to_sym
|
32
|
+
@registered_value_maps[name] || raise(ArgumentError, "A value map has not been registered for #{value}")
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
|
37
|
+
def self.register_collection(name, collection, name_method = :name, id_method = :id)
|
38
|
+
map = collection.map {|i| [i.send(id_method).to_s, i.send(name_method)]}
|
39
|
+
register(name, map)
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
|
44
|
+
def self.register(name, map)
|
45
|
+
raise "map is expected to be an array of pairs" unless map.is_a?(Array)
|
46
|
+
name = name.to_sym
|
47
|
+
@registered_value_maps[name] = map
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
|
52
|
+
def self.to_json
|
53
|
+
@registered_value_maps.to_json
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
|
62
|
+
end
|