ruva 1.0.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/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .project
4
+ .bundle
5
+ .config
6
+ .yardoc
7
+ .DS_store
8
+ Gemfile.lock
9
+ InstalledFiles
10
+ _yardoc
11
+ coverage
12
+ doc/
13
+ lib/bundler/man
14
+ pkg
15
+ rdoc
16
+ spec/reports
17
+ test/tmp
18
+ test/version_tmp
19
+ tmp
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.8.7
4
+ - 1.9.2
5
+ - 1.9.3
6
+ - ruby-head
7
+ script: bundle exec rspec spec
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in ruva.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Samuel Mueller
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,143 @@
1
+ # Ruva
2
+
3
+ [![Build Status](https://secure.travis-ci.org/ssmm/ruva.png)](http://travis-ci.org/ssmm/ruva)
4
+ [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/ssmm/ruva)
5
+
6
+ Ruva is a simple utility to write conditions in a human readable manner.
7
+
8
+ Assume you have an object called `person` with an attribute `age = 23`:
9
+
10
+ ```ruby
11
+ person = OpenStruct.new
12
+ person.age = 23
13
+ ```
14
+
15
+ You write
16
+
17
+ ```ruby
18
+ person.if "age is 23" do
19
+ stuff
20
+ end
21
+ ```
22
+
23
+ You can also define more complex conditions:
24
+
25
+ any
26
+ name matches samuel
27
+ all
28
+ city matches zurich
29
+ profession matches /application engineer/
30
+ age is greater than or equal to 18
31
+
32
+ In plain ruby, the above condition translates to
33
+
34
+ ```ruby
35
+ (person.name =~ /samuel/) ||
36
+ (person.city =~ /zurich/ && person.profession =~ /application engineer/ && person.age >= 18)
37
+ ```
38
+
39
+ # Details
40
+
41
+ ## Inline definition
42
+
43
+ ### One liners
44
+
45
+ As in the example above you can define a short condition on one line:
46
+
47
+ ```ruby
48
+ person.if "name matches daniel or jonas" do
49
+ stuff
50
+ end
51
+ ```
52
+
53
+ You can also define an else statement:
54
+
55
+ ```ruby
56
+ person.if("name matches daniel or jonas") do
57
+ stuff
58
+ end.else do
59
+ other_stuff
60
+ end
61
+ ```
62
+
63
+ Note that you have to put the condition into brackets in this case.
64
+
65
+ ### Multiline
66
+
67
+ You may define larger conditions like this:
68
+
69
+ ```ruby
70
+ person.if "
71
+ any
72
+ name matches lisa or simon or samuel
73
+ age is between 18 and 28
74
+ city matches zurich or /st. gallen/
75
+ " do
76
+ stuff
77
+ end
78
+ ```
79
+
80
+ The above definition says, that the condition is met if any of the three conditions defined under the
81
+ `any` keyword meet.
82
+
83
+ Currently supported are the keywords `all`, `any` and `none`.
84
+
85
+ ## Separate definition
86
+
87
+ You can define the same condition in a file with ".ruva" as extension:
88
+
89
+ any
90
+ name matches lisa or simon or samuel
91
+ age is between 18 and 28
92
+ city matches zurich or /st. gallen/
93
+
94
+ Then you load it:
95
+
96
+ ```ruby
97
+ condition = Ruva.read "your_condition"
98
+ ```
99
+
100
+ At this point, ruva loads the condition into some kind of hirarchy, according to the
101
+ [Specification pattern](http://en.wikipedia.org/wiki/Specification_pattern), and you can invoke its
102
+ `evaluate` method to test any object you want against that condition:
103
+
104
+ ```ruby
105
+ result = condition.evaluate(your_input)
106
+ ```
107
+
108
+ ## Expressions
109
+
110
+ #### is
111
+
112
+ age is 20
113
+
114
+ Acts the same as the `==` operator.
115
+
116
+ #### is between
117
+
118
+ age is between 20 and 30
119
+
120
+ Acts the same as [`between?`](http://www.ruby-doc.org/core-1.9.3/Comparable.html#method-i-between-3F).
121
+
122
+ #### is greater than (or equal to) / is less than (or equal to)
123
+
124
+ age is greater than 18
125
+ age is less than 18
126
+ age is greater than or equal to 18
127
+ age is less than or equal to 18
128
+
129
+ Acts the same as the `<, >, <=, >=` operators
130
+
131
+ #### matches
132
+
133
+ city matches zurich
134
+ city matches /new york/
135
+
136
+ Acts the same as the `=~` operator.
137
+
138
+ #### or / and
139
+
140
+ You can use `ors` and `ands` where it makes sense. For Example:
141
+
142
+ name matches lisa or simon or samuel
143
+ city matches z and u and r
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
data/lib/ruva.rb ADDED
@@ -0,0 +1,73 @@
1
+ require 'indentation-parser'
2
+
3
+ require "ruva/version"
4
+ require "ruva/expression"
5
+
6
+ class Object
7
+ def if condition
8
+ raise unless block_given?
9
+ spec = Ruva.interpret(Ruva.normalize_indentation condition)
10
+ result = spec.evaluate self
11
+ yield if result.satisfied
12
+ Ruva::Else.new result.satisfied
13
+ end
14
+ end
15
+
16
+ module Ruva
17
+
18
+ def self.read file
19
+ interpret IO.read("#{file}.ruva")
20
+ end
21
+
22
+ def self.interpret text
23
+
24
+ parser = IndentationParser.new do |p|
25
+ p.on /^all$/ do |parent, source, captures|
26
+ node = AndSpec.new
27
+ parent.append node
28
+ node
29
+ end
30
+
31
+ p.on /^any$/ do |parent, source, captures|
32
+ node = OrSpec.new
33
+ parent.append node
34
+ node
35
+ end
36
+
37
+ p.on /^none$/ do |parent, source, captures|
38
+ node = NotSpec.new
39
+ parent.append node
40
+ node
41
+ end
42
+
43
+ p.else do |parent, source|
44
+ node = RuvaExpression.parse(source)
45
+ parent.append node
46
+ node
47
+ end
48
+ end
49
+
50
+ spec = parser.read(text, AndSpec.new)
51
+ spec.value
52
+ end
53
+
54
+ def self.normalize_indentation str
55
+ if str.index(/^[ ]*$\n/) == 0
56
+ str.sub!(/^[ ]*$\n/, "")
57
+ end
58
+ captures = /^\A([ ]*)/.match str
59
+ first_indentation = captures[1]
60
+ str.gsub!(/^#{first_indentation}/, "")
61
+ str
62
+ end
63
+
64
+ class Else
65
+ def initialize satisfied
66
+ @satisfied = satisfied
67
+ end
68
+ def else
69
+ raise unless block_given?
70
+ yield unless @satisfied
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,17 @@
1
+ require 'citrus'
2
+ require 'ostruct'
3
+ require 'ruva/specification'
4
+ require 'ruva/expression/expression_leaf_spec'
5
+ require 'ruva/expression/equal_greater_less'
6
+ require 'ruva/expression/identifier_comparable'
7
+ require 'ruva/expression/is_between'
8
+ require 'ruva/expression/matches'
9
+
10
+ Citrus.require 'ruva/expression/ruva_expression'
11
+
12
+ module RuvaExpression
13
+ def self.parse expression
14
+ result = super expression
15
+ result.value
16
+ end
17
+ end
@@ -0,0 +1,61 @@
1
+ class GreaterSpec < ExpressionLeafSpec
2
+ def compare (comparable, value) value > comparable end
3
+ def satisfied_text() "greater than" end
4
+ def unsatisfied_text() "less than" end
5
+ end
6
+
7
+ class LessSpec < ExpressionLeafSpec
8
+ def compare (comparable, value) value < comparable end
9
+ def satisfied_text() "less than" end
10
+ def unsatisfied_text() "greater than" end
11
+ end
12
+
13
+ class GreaterEqualSpec < ExpressionLeafSpec
14
+ def compare (comparable, value) value >= comparable end
15
+ def satisfied_text() "greater than or equal to" end
16
+ def unsatisfied_text() "less than" end
17
+ end
18
+
19
+ class LessEqualSpec < ExpressionLeafSpec
20
+ def compare (comparable, value) value <= comparable end
21
+ def satisfied_text() "less than or equal to" end
22
+ def unsatisfied_text() "greater than" end
23
+ end
24
+
25
+ module EqualGreaterLess
26
+ def value
27
+ spec = create_spec
28
+ spec.set_validator { |value, *args|
29
+ compare @comparable, value
30
+ }
31
+ spec.set_reporting { |satisfied, value, *args|
32
+ comparable_string = @comparable.to_s
33
+ create_report_string "is #{satisfied_text} #{comparable_string}", "is #{unsatisfied_text} #{comparable_string}", satisfied, value
34
+ }
35
+ spec
36
+ end
37
+ end
38
+
39
+ module Greater include EqualGreaterLess
40
+ def create_spec
41
+ GreaterSpec.new(identifier.value, comparable.value)
42
+ end
43
+ end
44
+
45
+ module Less include EqualGreaterLess
46
+ def create_spec
47
+ LessSpec.new(identifier.value, comparable.value)
48
+ end
49
+ end
50
+
51
+ module GreaterOrEqual include EqualGreaterLess
52
+ def create_spec
53
+ GreaterEqualSpec.new(identifier.value, comparable.value)
54
+ end
55
+ end
56
+
57
+ module LessOrEqual include EqualGreaterLess
58
+ def create_spec
59
+ LessEqualSpec.new(identifier.value, comparable.value)
60
+ end
61
+ end
@@ -0,0 +1,52 @@
1
+ class ExpressionLeafSpec < LeafSpec
2
+ def get_value *args
3
+ if (args.size == 1)
4
+ obj = args[0]
5
+ if (obj.is_a? Hash)
6
+ get_hash_value @name.split(".").reverse, obj
7
+ else
8
+ get_object_value @name.split(".").reverse, obj
9
+ end
10
+ else
11
+ raise "Unexpected input"
12
+ end
13
+ end
14
+
15
+ def get_hash_value key_array, hash
16
+ if (key_array.size > 1)
17
+ obj = hash[key_array.pop.to_sym]
18
+ if (obj.is_a?(Hash))
19
+ get_hash_value(key_array, obj)
20
+ else
21
+ get_object_value(key_array, obj)
22
+ end
23
+ else
24
+ hash[key_array.pop.to_sym]
25
+ end
26
+ end
27
+
28
+ def get_object_value key_array, obj
29
+ if (key_array.size > 1)
30
+ obj = obj.send key_array.pop
31
+ if (obj.is_a?(Hash))
32
+ get_hash_value(key_array, obj)
33
+ else
34
+ get_object_value(key_array, obj)
35
+ end
36
+ else
37
+ obj.send key_array.pop
38
+ end
39
+ end
40
+
41
+ def create_typed_value value
42
+ case value
43
+ when Numeric then value
44
+ when String then "'#{value}'"
45
+ else value
46
+ end
47
+ end
48
+
49
+ def create_report_string positive, negative, satisfied, value
50
+ @name.to_s + " #{create_typed_value value} " + (satisfied ? positive : negative)
51
+ end
52
+ end
@@ -0,0 +1,53 @@
1
+ module Identifier
2
+ def value
3
+ to_s.strip
4
+ end
5
+ end
6
+
7
+ module Comparable
8
+ def value
9
+ to_s
10
+ end
11
+ end
12
+
13
+ module LastComparable
14
+ def value
15
+ Array.new << comparable
16
+ end
17
+ end
18
+
19
+ module IntComparable
20
+ def value
21
+ to_i
22
+ end
23
+ end
24
+
25
+ module FloatComparable
26
+ def value
27
+ to_f
28
+ end
29
+ end
30
+
31
+ module DateComparable
32
+ def value
33
+ Date.parse to_s
34
+ end
35
+ end
36
+
37
+ module RegexComparable
38
+ def value
39
+ Regexp.new regex.value
40
+ end
41
+ end
42
+
43
+ module TextComparable
44
+ def value
45
+ return to_s.strip
46
+ end
47
+ end
48
+
49
+ module Comparables
50
+ def value
51
+ subresult.value << comparable
52
+ end
53
+ end
@@ -0,0 +1,49 @@
1
+ def create_is_spec identifier, comparable
2
+ spec = ExpressionLeafSpec.new(identifier.value, comparable.value)
3
+ spec.set_validator { |value, *args|
4
+ @comparable == value
5
+ }
6
+ spec.set_reporting { |satisfied, value, *args|
7
+ comparable_string = @comparable.to_s
8
+ create_report_string "is #{comparable_string}", "is not #{comparable_string}", satisfied, value
9
+ }
10
+ spec
11
+ end
12
+
13
+ module Is
14
+ def value
15
+ create_is_spec identifier, comparable
16
+ end
17
+ end
18
+
19
+ module ManyIs
20
+ def value
21
+ spec = create_spec
22
+ comparables.value.reverse.each do |comparable|
23
+ new_spec = create_is_spec identifier, comparable
24
+ spec.append(new_spec)
25
+ end
26
+ spec
27
+ end
28
+ end
29
+
30
+ module OrIs include ManyIs
31
+ def create_spec
32
+ OrSpec.new
33
+ end
34
+ end
35
+
36
+ module IsBetween
37
+ def value
38
+ comparables = [from.value, to.value]
39
+ spec = ExpressionLeafSpec.new(identifier.value, comparables)
40
+ spec.set_validator { |value, *args|
41
+ value.between? @comparable[0], @comparable[1]
42
+ }
43
+ spec.set_reporting { |satisfied, value, *args|
44
+ comparable_string = "#{@comparable[0]} and #{@comparable[1]}"
45
+ create_report_string "is between #{comparable_string}", "is not between #{comparable_string}", satisfied, value
46
+ }
47
+ spec
48
+ end
49
+ end
@@ -0,0 +1,42 @@
1
+ def create_matches_spec identifier, comparable
2
+ comparable = comparable.value
3
+ regex = comparable.is_a?(Regexp) ? comparable : Regexp.new(comparable)
4
+ spec = ExpressionLeafSpec.new(identifier.value, regex)
5
+ spec.set_validator { |value, *args|
6
+ (@comparable =~ value) != nil
7
+ }
8
+ spec.set_reporting { |satisfied, value, *args|
9
+ comparable_string = "/#{@comparable.source}/"
10
+ create_report_string "matches #{comparable_string}", "does not match #{comparable_string}", satisfied, value
11
+ }
12
+ spec
13
+ end
14
+
15
+ module Matches
16
+ def value
17
+ create_matches_spec identifier, comparable
18
+ end
19
+ end
20
+
21
+ module ManyMatches
22
+ def value
23
+ spec = create_spec
24
+ comparables.value.reverse.each do |comparable|
25
+ new_spec = create_matches_spec identifier, comparable
26
+ spec.append(new_spec)
27
+ end
28
+ spec
29
+ end
30
+ end
31
+
32
+ module AndMatches include ManyMatches
33
+ def create_spec
34
+ AndSpec.new
35
+ end
36
+ end
37
+
38
+ module OrMatches include ManyMatches
39
+ def create_spec
40
+ OrSpec.new
41
+ end
42
+ end
@@ -0,0 +1,112 @@
1
+ grammar RuvaExpression
2
+
3
+ rule root
4
+ (identifier matches comparables:(and_comparables)) <AndMatches>
5
+ |
6
+ (identifier matches comparables:(or_comparables)) <OrMatches>
7
+ |
8
+ (identifier matches comparable) <Matches>
9
+ |
10
+ (identifier is between from:(comparable) and to:(comparable)) <IsBetween>
11
+ |
12
+ (identifier is greater than or equal to comparable) <GreaterOrEqual>
13
+ |
14
+ (identifier is less than or equal to comparable) <LessOrEqual>
15
+ |
16
+ (identifier is greater than comparable) <Greater>
17
+ |
18
+ (identifier is less than comparable) <Less>
19
+ |
20
+ (identifier is comparables:(or_comparables)) <OrIs>
21
+ |
22
+ (identifier is comparable) <Is>
23
+ end
24
+
25
+ rule matches
26
+ 'matches' space
27
+ end
28
+
29
+ rule is
30
+ 'is' space
31
+ end
32
+
33
+ rule between
34
+ 'between' space
35
+ end
36
+
37
+ rule greater
38
+ 'greater' space
39
+ end
40
+
41
+ rule less
42
+ 'less' space
43
+ end
44
+
45
+ rule than
46
+ 'than' space
47
+ end
48
+
49
+ rule equal
50
+ 'equal' space
51
+ end
52
+
53
+ rule to
54
+ 'to' space
55
+ end
56
+
57
+ rule and
58
+ 'and' space
59
+ end
60
+
61
+ rule or
62
+ 'or' space
63
+ end
64
+
65
+ rule identifier
66
+ ([a-zA-Z.]+ space) <Identifier>
67
+ end
68
+
69
+ rule comparable
70
+ (number_comparable | date_comparable | regex_comparable | text_comparable)
71
+ end
72
+
73
+ rule last_comparable
74
+ (comparable '') <LastComparable>
75
+ end
76
+
77
+ rule number_comparable
78
+ (int_comparable | float_comparable)
79
+ end
80
+
81
+ rule int_comparable
82
+ ([0-9]+ space) <IntComparable>
83
+ end
84
+
85
+ rule float_comparable
86
+ ([0-9]* '.' [0-9]+ space) <FloatComparable>
87
+ end
88
+
89
+ rule date_comparable
90
+ ([0-9]2*2 '.' [0-9]2*2 '.' [0-9]4*4 space) <DateComparable>
91
+ end
92
+
93
+ rule regex_comparable
94
+ ('/' regex:((!'/'!'\\' .) | '\\/' | '\\\\')* '/' space) <RegexComparable>
95
+ end
96
+
97
+ rule text_comparable
98
+ ([^ ]+ space) <TextComparable>
99
+ end
100
+
101
+ rule and_comparables
102
+ (comparable and subresult:(and_comparables | last_comparable)) <Comparables>
103
+ end
104
+
105
+ rule or_comparables
106
+ (comparable or subresult:(or_comparables | last_comparable)) <Comparables>
107
+ end
108
+
109
+ rule space
110
+ [ \t]+ | /$/
111
+ end
112
+ end
@@ -0,0 +1 @@
1
+ require 'ruva/specification/specification'
@@ -0,0 +1,100 @@
1
+ class Spec
2
+ def initialize
3
+ @specs = Array.new
4
+ end
5
+
6
+ def append spec
7
+ @specs << spec
8
+ spec
9
+ end
10
+
11
+ def create_subreport spec, *args
12
+ if spec.is_a? Spec
13
+ spec.evaluate *args
14
+ else
15
+ result = spec.evaluate *args
16
+ message = spec.report result, *args
17
+ SpecReport.new result, message
18
+ end
19
+ end
20
+
21
+ def evaluate *args
22
+ passed = initial_condition
23
+ report = SpecReport.new false, name
24
+ @specs.each do |spec|
25
+ subreport = create_subreport spec, *args
26
+ report.add_subreport subreport
27
+ if condition_change? subreport.satisfied
28
+ passed = !initial_condition
29
+ end
30
+ end
31
+ report.satisfied = passed
32
+ report
33
+ end
34
+ end
35
+
36
+ class AndSpec < Spec
37
+ def initial_condition() return true end
38
+ def name() "all" end
39
+ def condition_change?(satisfied) !satisfied end
40
+ end
41
+
42
+ class OrSpec < Spec
43
+ def initial_condition() return false end
44
+ def name() "any" end
45
+ def condition_change?(satisfied) satisfied end
46
+ end
47
+
48
+ class NotSpec < Spec
49
+ def initial_condition() return true end
50
+ def name() "none" end
51
+ def condition_change?(satisfied) satisfied end
52
+ end
53
+
54
+ class LeafSpec
55
+ def initialize name, args
56
+ @name = name
57
+ @comparable = args
58
+ end
59
+
60
+ def set_validator &block
61
+ @validator = block
62
+ end
63
+
64
+ def set_reporting &block
65
+ @reporting = block
66
+ end
67
+
68
+ def evaluate *args
69
+ instance_exec(get_value(*args), *args, &@validator)
70
+ end
71
+
72
+ def report satisfied, *args
73
+ instance_exec(satisfied, get_value(*args), *args, &@reporting)
74
+ end
75
+ end
76
+
77
+ class SpecReport
78
+
79
+ attr_accessor :satisfied, :name, :input
80
+
81
+ def initialize satisfied, message
82
+ @satisfied = satisfied
83
+ @message = message
84
+ @subreports = Array.new
85
+ end
86
+
87
+ def add_subreport subreport
88
+ @subreports << subreport
89
+ end
90
+
91
+ def to_s s = ''
92
+ met = @satisfied ? "[+] " : "[-] "
93
+ output = s + met + @message
94
+ output += "\n"
95
+ @subreports.each do |subreport|
96
+ output += subreport.to_s(s + ' ')
97
+ end
98
+ output
99
+ end
100
+ end
@@ -0,0 +1,3 @@
1
+ module Ruva
2
+ VERSION = "1.0.0"
3
+ end
data/ruva.gemspec ADDED
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/ruva/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.name = "ruva"
6
+ gem.version = Ruva::VERSION
7
+
8
+ gem.authors = ["Samuel Mueller"]
9
+ gem.email = ["mueller.samu@gmail.com"]
10
+ gem.description = %q{Ruva is a simple utility to write conditions in a human readable manner.}
11
+ gem.summary = gem.description
12
+ gem.homepage = "https://github.com/ssmm/ruva"
13
+
14
+ gem.files = `git ls-files`.split($\)
15
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
16
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
17
+ gem.require_paths = ["lib"]
18
+
19
+ gem.add_dependency "citrus", "~> 2.4.1"
20
+ gem.add_dependency "indentation-parser"
21
+
22
+ gem.add_development_dependency "rspec", "~> 2.6"
23
+ end
@@ -0,0 +1,7 @@
1
+ all
2
+ age is 23
3
+ name matches samuel
4
+ profession matches /application engineer/
5
+ any
6
+ city matches zurich
7
+ hobby matches guitar
@@ -0,0 +1,85 @@
1
+ require 'ruva/expression'
2
+ require 'helpers/expression_helper'
3
+
4
+ RSpec.configure do |c|
5
+ c.include ExpressionHelper
6
+ end
7
+
8
+ describe Ruva do
9
+
10
+ before :all do
11
+
12
+ end
13
+
14
+ describe "greater/less/equal expressions" do
15
+ it "handles numerics" do
16
+ test_expression "age is 23", {:age => 23}, true
17
+ test_expression "age is 23", {:age => 24}, false
18
+
19
+ test_expression "age is greater than 23", {:age => 24}, true
20
+ test_expression "age is greater than 23", {:age => 23}, false
21
+
22
+ test_expression "age is less than 23", {:age => 22}, true
23
+ test_expression "age is less than 23", {:age => 23}, false
24
+
25
+ test_expression "age is greater than or equal to 23", {:age => 23}, true
26
+ test_expression "age is greater than or equal to 23", {:age => 22}, false
27
+
28
+ test_expression "age is less than or equal to 23", {:age => 23}, true
29
+ test_expression "age is less than or equal to 23", {:age => 24}, false
30
+ end
31
+
32
+ it "handles strings" do
33
+ test_expression "name is samuel", {:name => "samuel"}, true
34
+ test_expression "name is samuel", {:name => "melchior"}, false
35
+
36
+ test_expression "name is greater than samuel", {:name => "simon"}, true
37
+ test_expression "name is greater than samuel", {:name => "samantha"}, false
38
+
39
+ test_expression "name is less than samuel", {:name => "lisa"}, true
40
+ test_expression "name is less than samuel", {:name => "samuel l jackson"}, false
41
+
42
+ test_expression "name is greater than or equal to samuel", {:name => "samuel"}, true
43
+ test_expression "name is greater than or equal to samuel", {:name => "samantha"}, false
44
+
45
+ test_expression "name is less than or equal to samuel", {:name => "samuel"}, true
46
+ test_expression "name is less than or equal to samuel", {:name => "samuel l jackson"}, false
47
+ end
48
+
49
+ it "handes dates" do
50
+ #TODO!!
51
+ end
52
+ end
53
+
54
+ describe "between expressions" do
55
+ it "handles numerics" do
56
+ test_expression "age is between 18 and 28", {:age => 23}, true
57
+
58
+ test_expression "age is between 18 and 28", {:age => 28}, true
59
+ test_expression "age is between 18 and 28", {:age => 18}, true
60
+
61
+ test_expression "age is between 18 and 28", {:age => 50}, false
62
+ test_expression "age is between 18 and 28", {:age => 17}, false
63
+ end
64
+ end
65
+
66
+ describe "matches expressions" do
67
+ it "handles strings" do
68
+ test_expression "name matches sam", {:name => "samuel"}, true
69
+ test_expression "name matches am", {:name => "samuel"}, true
70
+ test_expression "name matches uel", {:name => "samuel"}, true
71
+
72
+ test_expression "name matches xyz", {:name => "samuel"}, false
73
+
74
+ test_expression "name matches /^samuel$/", {:name => "samuel"}, true
75
+ test_expression "name matches /^samuel$/", {:name => "samuel "}, false
76
+
77
+ test_expression "name matches /daniel|jonas/", {:name => "daniel"}, true
78
+ test_expression "name matches /daniel|jonas/", {:name => "jonas"}, true
79
+
80
+ test_expression 'name matches /regex\/with escaping/', {:name => "regex/with escaping"}, true
81
+ test_expression "name matches /regex\\/with escaping/", {:name => "regex/with escaping"}, true
82
+ end
83
+ end
84
+
85
+ end
@@ -0,0 +1,7 @@
1
+ module ExpressionHelper
2
+ def test_expression expression, input, expected
3
+ spec = RuvaExpression.parse(expression)
4
+ result = spec.evaluate input
5
+ result.should be expected
6
+ end
7
+ end
@@ -0,0 +1,73 @@
1
+ require 'ruva'
2
+ require 'ostruct'
3
+
4
+ describe Ruva do
5
+ describe "the 'if' syntax" do
6
+
7
+ before :all do
8
+ @person = OpenStruct.new
9
+ @person.age = 23
10
+ @person.city = "zurich"
11
+ @person.name = "samuel"
12
+ end
13
+
14
+ it "handles one liners" do
15
+ #person.if "age is 23 or higher" <-- TODO: support this
16
+ executed = false
17
+ @person.if "age is 23" do
18
+ executed = true
19
+ end
20
+ executed.should be true
21
+
22
+ executed = false
23
+ @person.if("age is 23") {
24
+ executed = true
25
+ }.else {
26
+ true.should be false
27
+ }
28
+ executed.should be true
29
+
30
+ executed = false
31
+ @person.if("age is 23") do
32
+ executed = true
33
+ end.else do
34
+ true.should be false
35
+ end
36
+ executed.should be true
37
+
38
+ executed = true
39
+ @person.if("age is 24") {
40
+ true.should be false
41
+ }.else {
42
+ executed = false
43
+ }
44
+ executed.should be false
45
+ end
46
+
47
+ it "handles multi line conditions" do
48
+
49
+ executed = false
50
+ @person.if "
51
+ age is 23
52
+ city is zurich
53
+ name is samuel
54
+ " do
55
+ executed = true
56
+ end
57
+ executed.should be true
58
+
59
+ executed = true
60
+ @person.if("
61
+ age is 24
62
+ city is zurich
63
+ name is samuel
64
+ ") {
65
+ true.should be false
66
+ }.else {
67
+ executed = false
68
+ }
69
+ executed.should be false
70
+
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,22 @@
1
+ require 'ruva'
2
+
3
+ describe Ruva do
4
+ it "normalizes indentation" do
5
+ indented_text = "
6
+ this
7
+ text
8
+ is
9
+ indented
10
+ "
11
+ indented_text = Ruva.normalize_indentation indented_text
12
+
13
+ expected =
14
+ "this
15
+ text
16
+ is
17
+ indented
18
+ "
19
+
20
+ indented_text.should eq expected
21
+ end
22
+ end
data/spec/ruva_spec.rb ADDED
@@ -0,0 +1,20 @@
1
+ require 'ruva'
2
+ require 'ostruct'
3
+
4
+ describe Ruva do
5
+ it "reads .ruva files" do
6
+
7
+ spec = Ruva.read "spec/conditions/simple_condition"
8
+
9
+ input = OpenStruct.new
10
+ input.age = 23
11
+ input.name = "samuel"
12
+ input.profession = "application engineer"
13
+ input.city = "zurich"
14
+
15
+ result = spec.evaluate(input)
16
+
17
+ result.satisfied.should be true
18
+ #puts result
19
+ end
20
+ end
metadata ADDED
@@ -0,0 +1,123 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ruva
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Samuel Mueller
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-07-21 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: citrus
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 2.4.1
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 2.4.1
30
+ - !ruby/object:Gem::Dependency
31
+ name: indentation-parser
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: '2.6'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '2.6'
62
+ description: Ruva is a simple utility to write conditions in a human readable manner.
63
+ email:
64
+ - mueller.samu@gmail.com
65
+ executables: []
66
+ extensions: []
67
+ extra_rdoc_files: []
68
+ files:
69
+ - .gitignore
70
+ - .travis.yml
71
+ - Gemfile
72
+ - LICENSE
73
+ - README.md
74
+ - Rakefile
75
+ - lib/ruva.rb
76
+ - lib/ruva/expression.rb
77
+ - lib/ruva/expression/equal_greater_less.rb
78
+ - lib/ruva/expression/expression_leaf_spec.rb
79
+ - lib/ruva/expression/identifier_comparable.rb
80
+ - lib/ruva/expression/is_between.rb
81
+ - lib/ruva/expression/matches.rb
82
+ - lib/ruva/expression/ruva_expression.citrus
83
+ - lib/ruva/specification.rb
84
+ - lib/ruva/specification/specification.rb
85
+ - lib/ruva/version.rb
86
+ - ruva.gemspec
87
+ - spec/conditions/simple_condition.ruva
88
+ - spec/expression_spec.rb
89
+ - spec/helpers/expression_helper.rb
90
+ - spec/inline_ruva_spec.rb
91
+ - spec/normalize_indentation_spec.rb
92
+ - spec/ruva_spec.rb
93
+ homepage: https://github.com/ssmm/ruva
94
+ licenses: []
95
+ post_install_message:
96
+ rdoc_options: []
97
+ require_paths:
98
+ - lib
99
+ required_ruby_version: !ruby/object:Gem::Requirement
100
+ none: false
101
+ requirements:
102
+ - - ! '>='
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ required_rubygems_version: !ruby/object:Gem::Requirement
106
+ none: false
107
+ requirements:
108
+ - - ! '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ requirements: []
112
+ rubyforge_project:
113
+ rubygems_version: 1.8.24
114
+ signing_key:
115
+ specification_version: 3
116
+ summary: Ruva is a simple utility to write conditions in a human readable manner.
117
+ test_files:
118
+ - spec/conditions/simple_condition.ruva
119
+ - spec/expression_spec.rb
120
+ - spec/helpers/expression_helper.rb
121
+ - spec/inline_ruva_spec.rb
122
+ - spec/normalize_indentation_spec.rb
123
+ - spec/ruva_spec.rb