set_builder 2.0.0.beta2 → 2.0.0.beta3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +6 -6
  3. data/Rakefile +2 -2
  4. data/init.rb +1 -1
  5. data/lib/assets/javascripts/set_builder.js +122 -125
  6. data/lib/set_builder/constraint.rb +66 -50
  7. data/lib/set_builder/errors/trait_not_found.rb +3 -0
  8. data/lib/set_builder/modifier/adverb.rb +4 -4
  9. data/lib/set_builder/modifier/base.rb +74 -67
  10. data/lib/set_builder/modifier/verb.rb +1 -16
  11. data/lib/set_builder/modifier.rb +49 -49
  12. data/lib/set_builder/modifier_collection.rb +16 -16
  13. data/lib/set_builder/modifiers/date_preposition.rb +6 -48
  14. data/lib/set_builder/modifiers/number_preposition.rb +4 -25
  15. data/lib/set_builder/modifiers/string_preposition.rb +9 -43
  16. data/lib/set_builder/modifiers.rb +3 -3
  17. data/lib/set_builder/set.rb +48 -8
  18. data/lib/set_builder/trait.rb +75 -49
  19. data/lib/set_builder/traits.rb +13 -4
  20. data/lib/set_builder/value_map.rb +30 -30
  21. data/lib/set_builder/version.rb +1 -1
  22. data/lib/set_builder.rb +8 -7
  23. data/set_builder.gemspec +4 -2
  24. data/spec/commands/example_command.rb +2 -2
  25. data/spec/lib/jspec.css +4 -4
  26. data/spec/lib/jspec.growl.js +11 -11
  27. data/spec/lib/jspec.jquery.js +14 -14
  28. data/spec/lib/jspec.js +210 -210
  29. data/spec/lib/jspec.nodejs.js +2 -2
  30. data/spec/lib/jspec.shell.js +11 -11
  31. data/spec/lib/jspec.timers.js +23 -23
  32. data/spec/rhino.js +1 -1
  33. data/spec/server.rb +1 -1
  34. data/spec/unit/array.spec.js +9 -9
  35. data/spec/unit/set_builder.spec.js +71 -82
  36. data/spec/unit/spec.helper.js +1 -0
  37. data/test/constraint_test.rb +84 -0
  38. data/test/date_preposition_test.rb +17 -7
  39. data/test/modifier_test.rb +26 -1
  40. data/test/set_test.rb +51 -28
  41. data/test/string_preposition_test.rb +10 -9
  42. data/test/test_helper.rb +17 -15
  43. data/test/trait_test.rb +49 -30
  44. data/test/traits_test.rb +35 -30
  45. data/test/value_map_test.rb +1 -1
  46. metadata +37 -15
  47. data/lib/set_builder/query_builders/string.rb +0 -0
  48. data/test/inflector_test.rb +0 -27
@@ -4,9 +4,11 @@ module SetBuilder
4
4
 
5
5
 
6
6
  def initialize(traits, scope, raw_data)
7
+ traits = Traits.new(traits) if traits.is_a?(Array)
8
+ raise ArgumentError, "Expected traits to be an instance of SetBuilder::Traits" unless traits.is_a?(Traits)
7
9
  @traits = traits
8
10
  @scope = scope
9
- @set = raw_data
11
+ @set = self.class.normalize(raw_data)
10
12
  end
11
13
 
12
14
 
@@ -25,7 +27,14 @@ module SetBuilder
25
27
  # Returns true if all of the constraints in this set are valid
26
28
  #
27
29
  def valid?
28
- constraints.all?(&:valid?)
30
+ errors.none?
31
+ end
32
+
33
+ def errors
34
+ @errors ||= constraints.each_with_object({}) do |constraint, hash|
35
+ errors = constraint.errors
36
+ hash[constraint.trait.name] = errors if errors.any?
37
+ end
29
38
  end
30
39
 
31
40
 
@@ -37,6 +46,13 @@ module SetBuilder
37
46
  constraints.to_sentence
38
47
  end
39
48
 
49
+ #
50
+ # Exports this set in raw data
51
+ #
52
+ def to_a
53
+ set
54
+ end
55
+
40
56
 
41
57
 
42
58
  #
@@ -49,19 +65,43 @@ module SetBuilder
49
65
 
50
66
 
51
67
 
52
- private
68
+ def self.normalize(constraints)
69
+ hash_to_array(constraints).map do |constraint|
70
+ constraint = constraint.symbolize_keys
71
+ if constraint[:modifiers]
72
+ constraint[:modifiers] = hash_to_array(constraint[:modifiers]).map do |modifier|
73
+ modifier = modifier.symbolize_keys
74
+ modifier[:values] = hash_to_array(modifier[:values]) if modifier[:values]
75
+ modifier
76
+ end
77
+ end
78
+ constraint[:enums] = hash_to_array(constraint[:enums]) if constraint[:enums]
79
+ constraint
80
+ end
81
+ end
53
82
 
54
83
 
55
84
 
85
+ private
56
86
  attr_reader :set
57
87
 
88
+
89
+
90
+ def self.hash_to_array(hash)
91
+ return hash.values if hash.is_a?(Hash)
92
+ Array(hash)
93
+ end
94
+
95
+
96
+
58
97
  def get_constraints
59
- set.inject([]) do |constraints, line|
60
- negate, trait_name, args = false, line.first.to_s, line[1..-1]
61
- trait_name, negate = trait_name[1..-1], true if (trait_name[0..0] == "!")
98
+ set.map do |constraint|
99
+ trait_name = constraint.fetch(:trait) do
100
+ raise ArgumentError, ":trait is missing from the given constraint`"
101
+ end
62
102
  trait = traits[trait_name]
63
- raise("\"#{trait_name}\" is not a trait in `traits`") unless trait
64
- constraints << trait.apply(*args).negate(negate)
103
+ raise SetBuilder::TraitNotFound, "\"#{trait_name}\" is not a trait in `traits`" unless trait
104
+ trait.apply(constraint)
65
105
  end
66
106
  end
67
107
 
@@ -1,76 +1,102 @@
1
- require 'set_builder/constraint'
2
- require 'set_builder/modifier'
1
+ require "set_builder/constraint"
2
+ require "set_builder/modifier"
3
3
 
4
4
 
5
5
  module SetBuilder
6
6
  class Trait
7
-
8
- attr_reader :name, :parsed_expression, :part_of_speech, :modifiers, :direct_object_type
9
-
10
- LEXER = {
11
- name: /("[^"]+")/,
12
- direct_object_type: /(:\w+)/,
13
- negative: /(\[[^\]]+\])/,
14
- modifier: /(<\w+>)/
15
- }.freeze
16
-
17
- def initialize(trait_expression, &block)
18
- @parsed_expression = parse(trait_expression)
19
- @name, @direct_object_type = find(:name), find(:direct_object_type)
7
+ attr_reader :expression, :tokens, :name, :modifiers, :direct_object_type, :enums
8
+
9
+
10
+
11
+ def initialize(expression, &block)
12
+ @expression = expression.to_s.strip
13
+ @tokens = parse(expression)
20
14
  @block = block
21
- @modifiers = find_all(:modifier).map { |modifier| Modifier[modifier] }
15
+
16
+ names = find_all(:name)
17
+ raise ArgumentError, "A trait must be defined with a name" if names.none?
18
+ raise ArgumentError, "A trait must be defined with only one name" if names.length > 1
19
+ @name = names[0]
20
+
21
+ direct_object_types = find_all(:direct_object_type)
22
+ raise ArgumentError, "A trait may define only one direct object" if direct_object_types.length > 1
23
+ @direct_object_type = direct_object_types[0].to_sym if direct_object_types[0]
24
+
25
+ @enums = find_all(:enum)
26
+ raise ArgumentError, "An enum must define more than one option" if enums.any? { |options| options.length < 2 }
27
+
28
+ @modifiers = find_all(:modifier).map { |modifier_type| Modifier[modifier_type] }
22
29
  end
23
-
30
+
31
+
32
+
24
33
  def requires_direct_object?
25
34
  !@direct_object_type.nil?
26
35
  end
27
36
  alias :direct_object_required? :requires_direct_object?
28
-
29
- def negative?
30
- find(:negative)
37
+
38
+
39
+
40
+ def to_s
41
+ expression
31
42
  end
32
43
 
33
- def to_s(negative=false)
34
- parsed_expression.reject do |token, _|
35
- [:modifier, :direct_object_type].include?(token) || (!negative && token == :negative)
36
- end.map do |token, value|
37
- token == :name ? name : value
38
- end.join(" ")
44
+ def as_json(*)
45
+ tokens.map { |(token, value)| [token.to_s, value] }
46
+ end
47
+
48
+ def apply(constraint)
49
+ SetBuilder::Constraint.new(self, constraint, &@block)
39
50
  end
40
-
51
+
52
+
53
+
54
+ private
55
+
56
+
57
+
41
58
  def find_all(token)
42
- parsed_expression.select { |(_token, _)| _token == token }.map { |(_, value)| value }
59
+ tokens.select { |(_token, _)| _token == token }.map { |(_, value)| value }
43
60
  end
44
-
61
+
45
62
  def find(token)
46
63
  find_all(token).first
47
64
  end
48
-
49
- def as_json(*)
50
- parsed_expression.map { |(token, value)| [token.to_s, value] }
51
- end
52
65
 
53
- def apply(*args)
54
- SetBuilder::Constraint.new(self, *args, &@block)
55
- end
56
-
57
- private
58
-
59
- def parse(trait_definition)
60
- regex = Regexp.union(LEXER.values)
61
- trait_definition.split(regex).map do |lexeme|
62
- [token_for(lexeme), value_for(lexeme)] unless lexeme.strip.empty?
63
- end.compact
66
+
67
+
68
+ def parse(expression)
69
+ tokenizer = Regexp.union(LEXER.values)
70
+ expression.split(tokenizer).each_with_object([]) do |lexeme, output|
71
+ next if lexeme.empty?
72
+ token = token_for(lexeme)
73
+ output.push [token, value_for(token, lexeme)]
74
+ end
64
75
  end
65
-
76
+
66
77
  def token_for(lexeme)
67
78
  LEXER.each { |token, pattern| return token if pattern.match(lexeme) }
68
- return :string
79
+ :string
69
80
  end
70
-
71
- def value_for(lexeme)
72
- lexeme.to_s.strip.gsub(/[<>"\[\]:]/, "")
81
+
82
+ def value_for(token, lexeme)
83
+ case token
84
+ when :name then lexeme[1...-1]
85
+ when :enum then lexeme[1...-1].split("|")
86
+ when :modifier then lexeme[1...-1]
87
+ when :direct_object_type then lexeme[1..-1]
88
+ else lexeme
89
+ end
73
90
  end
74
91
 
92
+ LEXER = {
93
+ name: /("[^"]+")/,
94
+ direct_object_type: /(:[\w\-\.]+)/,
95
+ enum: /(\[[^\]]+\])/,
96
+ modifier: /(<\w+>)/
97
+ }.freeze
98
+
99
+
100
+
75
101
  end
76
102
  end
@@ -1,7 +1,7 @@
1
- require 'set_builder/trait'
2
- require 'set_builder/trait_builder'
3
- require 'set_builder/modifier_collection'
4
- require 'delegate'
1
+ require "set_builder/trait"
2
+ require "set_builder/trait_builder"
3
+ require "set_builder/modifier_collection"
4
+ require "delegate"
5
5
 
6
6
 
7
7
  module SetBuilder
@@ -51,6 +51,15 @@ module SetBuilder
51
51
  self.class.new(self.__getobj__.concat other_traits.__getobj__)
52
52
  end
53
53
 
54
+ def sort_by(&block)
55
+ self.class.new(self.__getobj__.sort_by(&block))
56
+ end
57
+
58
+ def <<(trait)
59
+ super
60
+ self
61
+ end
62
+
54
63
 
55
64
 
56
65
  def modifiers
@@ -1,19 +1,19 @@
1
1
  module SetBuilder
2
2
  module ValueMap
3
-
4
-
5
-
3
+
4
+
5
+
6
6
  @registered_value_maps = {}
7
-
8
-
9
-
7
+
8
+
9
+
10
10
  def self.registered?(name)
11
11
  name = name.to_sym
12
12
  @registered_value_maps.key?(name)
13
13
  end
14
-
15
-
16
-
14
+
15
+
16
+
17
17
  def self.to_s(name, value)
18
18
  if value.is_a?(Array)
19
19
  values = value.map { |value| to_s(name, value) }
@@ -23,8 +23,8 @@ module SetBuilder
23
23
  when 2 then return "#{values.first} or #{values.last}"
24
24
  else return "#{values[0..-2].join(', ')}, or #{values.last}"
25
25
  end
26
- end
27
-
26
+ end
27
+
28
28
  name = name.to_sym
29
29
  map = @registered_value_maps[name]
30
30
  if map
@@ -34,43 +34,43 @@ module SetBuilder
34
34
  value.to_s
35
35
  end
36
36
  end
37
-
38
-
39
-
37
+
38
+
39
+
40
40
  def self.for(name)
41
41
  name = name.to_sym
42
42
  @registered_value_maps[name] || raise(ArgumentError, "A value map has not been registered for #{value}")
43
43
  end
44
-
45
-
46
-
44
+
45
+
46
+
47
47
  def self.register_collection(name, collection, name_method = :name, id_method = :id)
48
48
  map = collection.map { |i| [i.send(id_method).to_s, i.send(name_method)] }
49
49
  register(name, map)
50
50
  end
51
-
52
-
53
-
51
+
52
+
53
+
54
54
  def self.register(name, map)
55
55
  raise "map is expected to be an array of pairs" unless map.is_a?(Array)
56
56
  name = name.to_sym
57
57
  @registered_value_maps[name] = map
58
58
  end
59
-
60
-
61
-
59
+
60
+
61
+
62
62
  def self.as_json(options={})
63
63
  @registered_value_maps.dup
64
64
  end
65
-
65
+
66
66
  def self.to_json
67
67
  @registered_value_maps.to_json
68
68
  end
69
-
70
-
71
-
69
+
70
+
71
+
72
72
  end
73
-
74
-
75
-
73
+
74
+
75
+
76
76
  end
@@ -1,3 +1,3 @@
1
1
  module SetBuilder
2
- VERSION = "2.0.0.beta2"
2
+ VERSION = "2.0.0.beta3"
3
3
  end
data/lib/set_builder.rb CHANGED
@@ -1,10 +1,11 @@
1
- require 'active_record'
2
- require 'active_support/core_ext'
3
- require 'set_builder/traits'
4
- require 'set_builder/modifiers'
5
- require 'set_builder/value_map'
6
- require 'set_builder/set'
7
- require 'set_builder/engine'
1
+ require "active_record"
2
+ require "active_support/core_ext"
3
+ require "set_builder/traits"
4
+ require "set_builder/errors/trait_not_found"
5
+ require "set_builder/modifiers"
6
+ require "set_builder/value_map"
7
+ require "set_builder/set"
8
+ require "set_builder/engine"
8
9
 
9
10
 
10
11
  module SetBuilder
data/set_builder.gemspec CHANGED
@@ -1,7 +1,7 @@
1
1
  # coding: utf-8
2
2
  lib = File.expand_path('../lib', __FILE__)
3
3
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'set_builder/version'
4
+ require "set_builder/version"
5
5
 
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = "set_builder"
@@ -18,11 +18,13 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
- spec.add_dependency "rails", ">= 3.1", "< 4.2"
21
+ spec.add_dependency "rails", "~> 4.2.0"
22
22
  spec.add_dependency "arel"
23
23
  spec.add_development_dependency "bundler", "~> 1.3"
24
24
  spec.add_development_dependency "rake"
25
25
  spec.add_development_dependency "jspec"
26
26
  spec.add_development_dependency "pry"
27
+ spec.add_development_dependency "shoulda-context"
27
28
  spec.add_development_dependency "minitest-reporters"
29
+ spec.add_development_dependency "timecop"
28
30
  end
@@ -15,5 +15,5 @@
15
15
  # # options.bar
16
16
  # # options.__hash__[:foo]
17
17
  # # options.__hash__[:bar]
18
- # end
19
- # end
18
+ # end
19
+ # end
data/spec/lib/jspec.css CHANGED
@@ -90,7 +90,7 @@ body.jspec {
90
90
  margin: 0;
91
91
  padding: 0 0 5px 25px;
92
92
  }
93
- #jspec-report tr.even:hover + tr.body,
93
+ #jspec-report tr.even:hover + tr.body,
94
94
  #jspec-report tr.odd:hover + tr.body {
95
95
  display: block;
96
96
  }
@@ -101,7 +101,7 @@ body.jspec {
101
101
  font-weight: normal;
102
102
  color: #7B8D9B;
103
103
  }
104
- #jspec-report tr.even:hover,
104
+ #jspec-report tr.even:hover,
105
105
  #jspec-report tr.odd:hover {
106
106
  text-shadow: 1px 1px 1px #fff;
107
107
  background: #F2F5F7;
@@ -130,7 +130,7 @@ body.jspec {
130
130
  color: #1a1a1a;
131
131
  }
132
132
  #jspec-report tr.description:first-child td {
133
- border-top: none;
133
+ border-top: none;
134
134
  }
135
135
  #jspec-report .assertion {
136
136
  display: block;
@@ -146,4 +146,4 @@ body.jspec {
146
146
  }
147
147
  .jspec-sandbox {
148
148
  display: none;
149
- }
149
+ }
@@ -2,13 +2,13 @@
2
2
  // JSpec - Growl - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
3
3
 
4
4
  ;(function(){
5
-
5
+
6
6
  Growl = {
7
-
7
+
8
8
  // --- Version
9
-
9
+
10
10
  version: '1.0.0',
11
-
11
+
12
12
  /**
13
13
  * Execute the given _cmd_, returning an array of lines from stdout.
14
14
  *
@@ -28,11 +28,11 @@
28
28
  var stream = new DataInputStream(proccess.getInputStream())
29
29
  while (line = stream.readLine())
30
30
  lines.push(line + '')
31
- stream.close()
31
+ stream.close()
32
32
  }
33
33
  return lines
34
34
  },
35
-
35
+
36
36
  /**
37
37
  * Return the extension of the given _path_ or null.
38
38
  *
@@ -40,9 +40,9 @@
40
40
  * @return {string}
41
41
  * @api private
42
42
  */
43
-
43
+
44
44
  extname: function(path) {
45
- return path.lastIndexOf('.') != -1 ?
45
+ return path.lastIndexOf('.') != -1 ?
46
46
  path.slice(path.lastIndexOf('.') + 1, path.length) :
47
47
  null
48
48
  },
@@ -102,7 +102,7 @@
102
102
  this.exec.apply(this, args)
103
103
  }
104
104
  }
105
-
105
+
106
106
  JSpec.include({
107
107
  name: 'Growl',
108
108
  reporting: function(options){
@@ -111,5 +111,5 @@
111
111
  else Growl.notify('passed ' + stats.passes + ' assertions', { title: 'JSpec' })
112
112
  }
113
113
  })
114
-
115
- })()
114
+
115
+ })()
@@ -5,15 +5,15 @@ JSpec
5
5
  .requires('jQuery', 'when using jspec.jquery.js')
6
6
  .include({
7
7
  name: 'jQuery',
8
-
8
+
9
9
  // --- Initialize
10
-
10
+
11
11
  init : function() {
12
12
  jQuery.ajaxSetup({ async: false })
13
13
  },
14
-
14
+
15
15
  // --- Utilities
16
-
16
+
17
17
  utilities : {
18
18
  element: jQuery,
19
19
  elements: jQuery,
@@ -21,10 +21,10 @@ JSpec
21
21
  return jQuery('<div class="sandbox"></div>')
22
22
  }
23
23
  },
24
-
24
+
25
25
  // --- Matchers
26
-
27
- matchers : {
26
+
27
+ matchers : {
28
28
  have_tag : "jQuery(expected, actual).length === 1",
29
29
  have_one : "alias have_tag",
30
30
  have_tags : "jQuery(expected, actual).length > 1",
@@ -36,18 +36,18 @@ JSpec
36
36
  have_value : "jQuery(actual).val() === expected",
37
37
  be_enabled : "!jQuery(actual).attr('disabled')",
38
38
  have_class : "jQuery(actual).hasClass(expected)",
39
- be_animated : "jQuery(actual).queue().length > 0",
40
-
39
+ be_animated : "jQuery(actual).queue().length > 0",
40
+
41
41
  be_visible : function(actual) {
42
42
  return jQuery(actual).css('display') != 'none' &&
43
43
  jQuery(actual).css('visibility') != 'hidden' &&
44
44
  jQuery(actual).attr('type') != 'hidden'
45
45
  },
46
-
46
+
47
47
  be_hidden : function(actual) {
48
48
  return !JSpec.does(actual, 'be_visible')
49
49
  },
50
-
50
+
51
51
  have_classes : function(actual) {
52
52
  return !JSpec.any(JSpec.toArray(arguments, 1), function(arg){
53
53
  return !JSpec.does(actual, 'have_class', arg)
@@ -58,7 +58,7 @@ JSpec
58
58
  return value ? jQuery(actual).attr(attr) == value:
59
59
  jQuery(actual).attr(attr)
60
60
  },
61
-
61
+
62
62
  have_event_handlers : function(actual, expected) {
63
63
  if (jQuery(actual).data('events') && jQuery(actual).data('events').hasOwnProperty(expected)) {
64
64
  return true;
@@ -73,11 +73,11 @@ JSpec
73
73
  }
74
74
  return false;
75
75
  },
76
-
76
+
77
77
  'be disabled selected checked' : function(attr) {
78
78
  return 'jQuery(actual).attr("' + attr + '")'
79
79
  },
80
-
80
+
81
81
  'have type id title alt href src sel rev name target' : function(attr) {
82
82
  return function(actual, value) {
83
83
  return JSpec.does(actual, 'have_attr', attr, value)