set_builder 1.0.2
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.
- 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
|