de 0.0.1

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,5 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ nbproject*
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm 1.8.7@de
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in de.gemspec
4
+ gemspec
data/README ADDED
@@ -0,0 +1,28 @@
1
+
2
+ De (Dynamic Expression) module provides means to build and evaluate
3
+ dynamic expression of arbitrary complecity and operands/operators nature.
4
+
5
+ Expression is considered as tree consisted of operands and operators.
6
+ Operater must have child nodes. Operand is terminal node.
7
+
8
+ Tree example:
9
+ (a + b) * c + d
10
+
11
+ - +
12
+ --- *
13
+ ----- +
14
+ ------- a
15
+ ------- b
16
+ --- d
17
+
18
+ Here "+" and "*" are operators,
19
+ "a", "b" and "d" are operands
20
+
21
+ Expression tree structure is built with the help of <rubytree> gem
22
+ (https://rubygems.org/gems/rubytree)
23
+
24
+ In addition to basic classes of Expression, Operand and Operator
25
+ module provides some extensions for different nature expressions:
26
+ - Boolean expressions
27
+ - Sunspot Solr search expressions
28
+
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/de.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "de/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "de"
7
+ s.version = De::VERSION
8
+ s.authors = ["Olga Gorun"]
9
+ s.email = ["ogorun@quicklizard.com"]
10
+ s.homepage = ""
11
+ s.summary = %q{Dynamic Expression}
12
+ s.description = %q{De (Dynamic Expression) module provides means to build and evaluate
13
+ dynamic expression of arbitrary complecity and operands/operators nature}
14
+
15
+ s.rubyforge_project = "de"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+
22
+ s.add_dependency 'rubytree', '~> 0.8.1'
23
+ s.add_dependency 'activesupport', '~> 2.3.10'
24
+ s.add_development_dependency 'rack-test'
25
+ end
26
+
@@ -0,0 +1,46 @@
1
+ #
2
+ # De::Boolean::Operand class
3
+ #
4
+ # De::Boolean module provides means for dynamic logical expression building/validation/evaluation
5
+ # It is built as extension of De module
6
+ #
7
+ # Boolean expression example:
8
+ #
9
+ # true AND (false OR true)
10
+ #
11
+
12
+ module De
13
+ module Boolean
14
+
15
+ # Marker module included to all module classes
16
+ # to be able to check that class is inside module
17
+ module Bn; end
18
+
19
+ #
20
+ # Class representing Boolean operand
21
+ #
22
+ class Operand < De::Operand
23
+ include Bn
24
+
25
+ #
26
+ # Checks content is boolean value
27
+ #
28
+ # === Output
29
+ #
30
+ # true|false
31
+ #
32
+ def valid?
33
+ content.is_a?(TrueClass) || content.is_a?(FalseClass)
34
+ end
35
+
36
+ #
37
+ # Returns value stored in content field if it is valid
38
+ # or raises exception
39
+ #
40
+ def evaluate
41
+ super
42
+ content
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,134 @@
1
+ #
2
+ # De::Boolean::Operator class
3
+ #
4
+ # De::Boolean module provides means for dynamic logical expression building/validation/evaluation
5
+ # It is built as extension of De module
6
+ #
7
+ # Boolean expression example:
8
+ #
9
+ # true AND (false OR true)
10
+ #
11
+ #
12
+
13
+ require 'de/symmetric_operator'
14
+
15
+ module De
16
+ module Boolean
17
+
18
+ # Marker module included to all module classes
19
+ # to be able to check that class is inside module
20
+ module Bn; end
21
+
22
+ #
23
+ # Class representing Boolean operator
24
+ # Base abstract class for concrete operators AND, OR, NOT
25
+ #
26
+ class BooleanOperator < Operator
27
+ include Bn
28
+ include De::SymmetricOperator
29
+
30
+ #
31
+ # Constructor. Prevents direct BooleanOperator class objects creation
32
+ #
33
+ # === Input
34
+ #
35
+ # name<String>
36
+ # operands<Array>:: (optional) array of Operand objects.
37
+ # If given they are added as children to current operator
38
+ #
39
+ def initialize(name, operands = nil)
40
+ raise Error::AbstractClassObjectCreationError if instance_of? BooleanOperator
41
+ super(name, operands)
42
+ end
43
+
44
+ #
45
+ # Adds boolean operator or operand as a child to boolean operator
46
+ #
47
+ def <<(obj)
48
+ raise Error::TypeError unless obj.is_a?(De::Boolean::Bn)
49
+ super(obj)
50
+ end
51
+
52
+ end
53
+
54
+ #
55
+ # Class representing boolean AND operator
56
+ #
57
+ class And < BooleanOperator
58
+
59
+ #
60
+ # Creates And object.
61
+ # Name includes word 'AND' with random number to avoid TreeNode problem
62
+ # when trying to add children with the same name to a node
63
+ #
64
+ # === Input
65
+ #
66
+ # operands<Array>:: (optional) array of Operand objects.
67
+ # If given they are added as children to current operator
68
+ #
69
+ def initialize(operands = nil)
70
+ super("AND-#{rand(1000)}", operands)
71
+ end
72
+
73
+ def evaluate
74
+ super
75
+ children.inject(true) {|result, el| result && el.evaluate}
76
+ end
77
+ end
78
+
79
+ #
80
+ # Class representing boolean OR operator
81
+ #
82
+ class Or < BooleanOperator
83
+
84
+ #
85
+ # Creates Or object.
86
+ # Name includes word 'AND' with random number to avoid TreeNode problem
87
+ # when trying to add children with the same name to a node
88
+ #
89
+ # === Input
90
+ #
91
+ # operands<Array>:: (optional) array of Operand objects.
92
+ # If given they are added as children to current operator
93
+ #
94
+ def initialize(operands = nil)
95
+ super("OR-#{rand(1000)}", operands)
96
+ end
97
+
98
+ def evaluate
99
+ super
100
+ children.inject(false) {|result, el| result || el.evaluate}
101
+ end
102
+ end
103
+
104
+ #
105
+ # Class representing boolean NOT operator
106
+ #
107
+ class Not < BooleanOperator
108
+
109
+ #
110
+ # Creates Not object.
111
+ # Name includes word 'AND' with random number to avoid TreeNode problem
112
+ # when trying to add children with the same name to a node
113
+ #
114
+ # === Input
115
+ #
116
+ # operand<De::Boolean::Operand>:: (optional) Operand object.
117
+ # If given it is added as child to current operator
118
+ #
119
+ def initialize(operand = nil)
120
+ super("NOT-#{rand(1000)}", operand ? [operand] : nil)
121
+ end
122
+
123
+ def <<(obj)
124
+ raise Error::ArgumentNumerError if has_children?
125
+ super(obj)
126
+ end
127
+
128
+ def evaluate
129
+ super
130
+ !first_child.evaluate
131
+ end
132
+ end
133
+ end
134
+ end
data/lib/de/boolean.rb ADDED
@@ -0,0 +1,2 @@
1
+ require 'de/boolean/operand'
2
+ require 'de/boolean/operator'
data/lib/de/de.rb ADDED
@@ -0,0 +1,210 @@
1
+ #
2
+ # De (Dynamic Expression) module provides means to build and evaluate
3
+ # dynamic expression of arbitrary complecity and operands/operators nature.
4
+ #
5
+ # Expression is considered as tree consisted of operands and operators.
6
+ # Operater must have child nodes. Operand is terminal node.
7
+ #
8
+ # Tree example:
9
+ # (a + b) * c + d
10
+ #
11
+ # - +
12
+ # --- *
13
+ # ----- +
14
+ # ------- a
15
+ # ------- b
16
+ # --- d
17
+ #
18
+ # Here "+" and "*" are operators,
19
+ # "a", "b" and "d" are operands
20
+ #
21
+ # Expression tree structure is built with the help of <rubytree> gem
22
+ # (https://rubygems.org/gems/rubytree)
23
+ #
24
+ # In addition to basic classes of Expression, Operand and Operator
25
+ # module provides some extensions for different nature expressions:
26
+ # - Boolean expressions
27
+ # - Sunspot Solr search expressions
28
+ #
29
+
30
+ require 'tree'
31
+ require 'de/error'
32
+
33
+ module De
34
+
35
+ #
36
+ # Base class of De module
37
+ # Represents tree of dynamic expression consisted of operators and operands
38
+ # Provides means for expression tree building, validation and evaluation
39
+ #
40
+ class Expression < Tree::TreeNode
41
+
42
+ #
43
+ # Adds operator or operand as a child to Expression
44
+ #
45
+ # === Input
46
+ #
47
+ # obj<Operator|Operand>:: object to be added as child one
48
+ #
49
+ # === Output
50
+ #
51
+ # <Operator|Operand>:: child object
52
+ #
53
+ def <<(obj)
54
+ raise Error::TypeError unless (obj.is_a?(Operator) or obj.is_a?(Operand))
55
+ super(obj)
56
+ end
57
+
58
+ #
59
+ # Checks expression validity
60
+ # Must be overriden by extending class
61
+ #
62
+ # === Input
63
+ #
64
+ # no input
65
+ #
66
+ # === Output
67
+ #
68
+ # true|false
69
+ #
70
+ def valid?
71
+ raise Error::MethodShouldBeOverridenByExtendingClassError
72
+ end
73
+
74
+ #
75
+ # Evaluates expression in case it is valid
76
+ # otherwise raises exception
77
+ # If expression valid returns true
78
+ # Must be overriden in extending class to do some usefull evaluation
79
+ #
80
+ # === Input
81
+ #
82
+ # no input
83
+ #
84
+ # === Output
85
+ #
86
+ # true
87
+ #
88
+ def evaluate
89
+ raise Error::InvalidExpressionError unless valid?
90
+ true
91
+ end
92
+
93
+ #
94
+ # Override eql? and hash for consistent behaviour in hashes and arrays
95
+ #
96
+
97
+ def eql?(obj)
98
+ self == obj
99
+ end
100
+
101
+ def hash
102
+ @content.hash
103
+ end
104
+
105
+ end
106
+
107
+ #
108
+ # Class representing operator concept -
109
+ # kind of expression that has children
110
+ #
111
+ # Class can be considered abstract.
112
+ # Its object creation is prevented by constructor.
113
+ # Extending classes must be implemented
114
+ # in order to work with this concept
115
+ #
116
+ class Operator < Expression
117
+
118
+ #
119
+ # Constructor. Prevents this class objects direct creation
120
+ #
121
+ # === Input
122
+ #
123
+ # name<String>
124
+ # operands<Array>:: (optional) array of Operand objects.
125
+ # If given they are added as children to current operator
126
+ #
127
+ def initialize(name, operands = nil)
128
+ raise Error::AbstractClassObjectCreationError if instance_of? Operator
129
+ super(name)
130
+
131
+ unless operands.nil?
132
+ raise Error::TypeError unless operands.is_a?(Array)
133
+ operands.each { |operand| self << operand }
134
+ end
135
+ end
136
+
137
+ #
138
+ # Checks expression validity
139
+ # Operator is valid if it has children and every one from them is valid
140
+ #
141
+ # === Input
142
+ #
143
+ # no input
144
+ #
145
+ # === Output
146
+ #
147
+ # true|false
148
+ #
149
+ def valid?
150
+ has_children? and children.inject(true) { |result, el| result and el.valid? }
151
+ end
152
+
153
+ #
154
+ # Equal operator override
155
+ # This basic implementation checks objects class, children number equality
156
+ # and children equality. Operands order is important
157
+ #
158
+ def ==(obj)
159
+ self.class.name == obj.class.name && children == obj.children
160
+ end
161
+
162
+ #
163
+ # Define hash function to get equal results for operators from the same class and with the equal children
164
+ #
165
+ def hash
166
+ [self.class.name, children.map { |el| el.hash}].hash
167
+ end
168
+ end
169
+
170
+ #
171
+ # Class representing operand concept -
172
+ # kind of expression without children.
173
+ #
174
+ # Class can be considered abstract.
175
+ # Its object creation is prevented by constructor.
176
+ # Extending classes must be implemented
177
+ # in order to work with this concept
178
+ #
179
+ class Operand < Expression
180
+ #
181
+ # Constructor. Prevents this class objects direct creation
182
+ #
183
+ # === Input
184
+ #
185
+ # name<String>
186
+ # content<Object>:: arbitrary content. Its interpritation is done by extending classes
187
+ #
188
+ def initialize(name, content)
189
+ raise Error::AbstractClassObjectCreationError if instance_of? Operand
190
+ super(name, content)
191
+ end
192
+
193
+ #
194
+ # Adding children to operand is prevented (raises exception)
195
+ #
196
+ def <<(obj)
197
+ raise Error::TypeError
198
+ end
199
+
200
+ #
201
+ # Evaluator must be overriden by extending classes (raises exception)
202
+ #
203
+ def evaluate
204
+ raise Error::MethodShouldBeOverridenByExtendingClassError if instance_of? Operand
205
+ super
206
+ end
207
+ end
208
+
209
+ end
210
+
data/lib/de/error.rb ADDED
@@ -0,0 +1,6 @@
1
+ module De
2
+ module Error
3
+ exceptions = %w[TypeError ArgumentNumerError InvalidExpressionError AbstractClassObjectCreationError MethodShouldBeOverridenByExtendingClassError]
4
+ exceptions.each { |e| const_set(e, Class.new(StandardError)) }
5
+ end
6
+ end