set_builder 2.0.0.beta2 → 2.0.0.beta3

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.
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)