porolog 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.
- checksums.yaml +7 -0
- data/README.md +119 -0
- data/Rakefile +40 -0
- data/bin/porolog +11 -0
- data/lib/porolog/error.rb +12 -0
- data/lib/porolog/scope.rb +68 -0
- data/lib/porolog.rb +16 -0
- data/test/porolog/scope_test.rb +270 -0
- data/test/test_helper.rb +21 -0
- metadata +54 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 255c18b94a31335781ec500cbec763b710def39e9b1c9e331b573e51b18da80e
|
4
|
+
data.tar.gz: d676680c7eea7d2fd3df0b3708fd7f09977ed92472616cdf44b3db2eede37688
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2f5018c1e104af58e1decd95f1d55647c6c51c7eb8bf38e3720feb4438d3c52fa24d9cfab000e5251d8160b2980d74234e56dd2c3f1a8991334e98276e519b44
|
7
|
+
data.tar.gz: 1b4e60c16c278e5c4f3f276545fc0c1e330b895f01c916f053217dd72df1f5f198c3b8feed2a3c5460e1de3f3238da3b5d481e0405de272ee28142361d6adb88
|
data/README.md
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
# porolog
|
2
|
+
|
3
|
+
Plain Old Ruby Objects Prolog
|
4
|
+
|
5
|
+
## Introduction
|
6
|
+
|
7
|
+
porolog is a Prolog implementation using plain old Ruby objects with the aim that
|
8
|
+
logic queries can be called within a regular Ruby program. The goal was not to
|
9
|
+
implement a Prolog interpreter that is just implement in Ruby, but rather that
|
10
|
+
a logic engine could be embedded in a larger program.
|
11
|
+
|
12
|
+
The goal was to implement a minimal logic engine in the style of Prolog where
|
13
|
+
Ruby objects could be passed in and Ruby objects were passed back.
|
14
|
+
|
15
|
+
## Installation
|
16
|
+
|
17
|
+
gem install porolog
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
### Basic Usage
|
22
|
+
|
23
|
+
require 'porolog'
|
24
|
+
|
25
|
+
prime = Predicate.new :prime
|
26
|
+
|
27
|
+
prime.(2).fact!
|
28
|
+
prime.(3).fact!
|
29
|
+
prime.(5).fact!
|
30
|
+
prime.(7).fact!
|
31
|
+
|
32
|
+
solutions = prime.(:X).solve
|
33
|
+
|
34
|
+
solutions.each do |solution|
|
35
|
+
puts "#{solution[:X]} is prime"
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
### Common Usage
|
40
|
+
|
41
|
+
require 'porolog'
|
42
|
+
|
43
|
+
include Porolog
|
44
|
+
|
45
|
+
predicate :prime
|
46
|
+
|
47
|
+
prime(2).fact!
|
48
|
+
prime(3).fact!
|
49
|
+
prime(5).fact!
|
50
|
+
prime(7).fact!
|
51
|
+
|
52
|
+
solutions = prime(:X).solve
|
53
|
+
|
54
|
+
solutions.each do |solution|
|
55
|
+
puts "#{solution[:X]} is prime"
|
56
|
+
end
|
57
|
+
|
58
|
+
### Scope Usage
|
59
|
+
|
60
|
+
The Scope class enables you to have multiple logic programs embedded in the same
|
61
|
+
Ruby program. A Scope object defines a scope for the predicates of a logic programme.
|
62
|
+
|
63
|
+
require 'porolog'
|
64
|
+
|
65
|
+
# -- Prime Numbers Predicate --
|
66
|
+
prime = prime1 = Predicate.new :prime, :numbers
|
67
|
+
|
68
|
+
prime.(2).fact!
|
69
|
+
prime.(3).fact!
|
70
|
+
prime.(5).fact!
|
71
|
+
prime.(7).fact!
|
72
|
+
prime.(11).fact!
|
73
|
+
|
74
|
+
# -- Pump Predicate --
|
75
|
+
prime = prime2 = Predicate.new :prime, :pumps
|
76
|
+
|
77
|
+
prime.('pump A').fact!
|
78
|
+
prime.('pump B').fact!
|
79
|
+
prime.('pump C').fact!
|
80
|
+
prime.('pump D').fact!
|
81
|
+
|
82
|
+
assert_equal [:default,:numbers,:pumps], Scope.scopes
|
83
|
+
|
84
|
+
assert_scope Scope[:default], :default, []
|
85
|
+
assert_scope Scope[:numbers], :first, [prime1]
|
86
|
+
assert_scope Scope[:pumps], :second, [prime2]
|
87
|
+
|
88
|
+
assert_equal :prime, prime1.name
|
89
|
+
assert_equal :prime, prime2.name
|
90
|
+
|
91
|
+
solutions = [
|
92
|
+
{ X: 2 },
|
93
|
+
{ X: 3 },
|
94
|
+
{ X: 5 },
|
95
|
+
{ X: 7 },
|
96
|
+
{ X: 11 },
|
97
|
+
]
|
98
|
+
assert_equal solutions, prime1.(:X).solve
|
99
|
+
|
100
|
+
solutions = [
|
101
|
+
{ X: 'pump A' },
|
102
|
+
{ X: 'pump B' },
|
103
|
+
{ X: 'pump C' },
|
104
|
+
{ X: 'pump D' },
|
105
|
+
]
|
106
|
+
assert_equal solutions, prime2.(:X).solve
|
107
|
+
|
108
|
+
|
109
|
+
## Testing
|
110
|
+
|
111
|
+
rake test
|
112
|
+
|
113
|
+
or
|
114
|
+
|
115
|
+
rake scope_test
|
116
|
+
|
117
|
+
## Author
|
118
|
+
|
119
|
+
Luis Esteban MSc MTeach
|
data/Rakefile
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
#
|
2
|
+
# Rakefile - Porolog Rakefile
|
3
|
+
#
|
4
|
+
# 2 May 2018
|
5
|
+
#
|
6
|
+
|
7
|
+
require 'rake/testtask'
|
8
|
+
|
9
|
+
|
10
|
+
# -- Run All Tests Task --
|
11
|
+
Rake::TestTask.new do |task|
|
12
|
+
task.libs << 'test'
|
13
|
+
task.pattern = 'test/porolog/*_test.rb'
|
14
|
+
end
|
15
|
+
|
16
|
+
# -- Create Separate Test Tasks --
|
17
|
+
Dir['test/porolog/*_test.rb'].each do |test_file|
|
18
|
+
name = File.basename(test_file, '.rb')
|
19
|
+
Rake::TestTask.new(name) do |task|
|
20
|
+
task.verbose = true
|
21
|
+
task.options = '--verbose'
|
22
|
+
task.libs << 'test'
|
23
|
+
task.pattern = test_file
|
24
|
+
task.warning = nil
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# -- Tasks --
|
29
|
+
task default: :test
|
30
|
+
|
31
|
+
desc 'Help'
|
32
|
+
task :help do
|
33
|
+
puts <<-EOF
|
34
|
+
Porolog is a Ruby library.
|
35
|
+
See README.md for more information.
|
36
|
+
Run
|
37
|
+
rake -T
|
38
|
+
for other tasks.
|
39
|
+
EOF
|
40
|
+
end
|
data/bin/porolog
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
#
|
2
|
+
# lib/porolog/scope.rb - Plain Old Ruby Objects Prolog Engine -- Scope
|
3
|
+
#
|
4
|
+
# Luis Esteban 2 May 2018
|
5
|
+
# created
|
6
|
+
#
|
7
|
+
|
8
|
+
# == Porolog::Scope ==
|
9
|
+
#
|
10
|
+
# A Porolog::Scope is a container for Porolog::Predicates.
|
11
|
+
# Its purpose is to allow a Ruby program to contain multiple Prolog programs
|
12
|
+
# without the Prolog programs interfering with each other.
|
13
|
+
#
|
14
|
+
|
15
|
+
module Porolog
|
16
|
+
|
17
|
+
class Scope
|
18
|
+
|
19
|
+
class ScopeError < PorologError ; end
|
20
|
+
class NotPredicateError < ScopeError ; end
|
21
|
+
|
22
|
+
attr_reader :name
|
23
|
+
|
24
|
+
@@scopes = {}
|
25
|
+
|
26
|
+
def self.new(name)
|
27
|
+
@@scopes[name] || super
|
28
|
+
end
|
29
|
+
|
30
|
+
def initialize(name)
|
31
|
+
@name = name
|
32
|
+
@predicates = {}
|
33
|
+
|
34
|
+
@@scopes[@name] = self
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.reset
|
38
|
+
@@scopes = {}
|
39
|
+
new(:default)
|
40
|
+
end
|
41
|
+
|
42
|
+
reset
|
43
|
+
|
44
|
+
def self.[](name)
|
45
|
+
@@scopes[name]
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.scopes
|
49
|
+
@@scopes.keys
|
50
|
+
end
|
51
|
+
|
52
|
+
def [](name)
|
53
|
+
@predicates[name.to_sym]
|
54
|
+
end
|
55
|
+
|
56
|
+
def []=(name,predicate)
|
57
|
+
# TODO: Uncomment when Porolog::Predicate has been added.
|
58
|
+
#raise NotPredicateError.new("#{predicate.inspect} is not a Predicate") unless predicate.is_a?(Predicate)
|
59
|
+
@predicates[name.to_sym] = predicate
|
60
|
+
end
|
61
|
+
|
62
|
+
def predicates
|
63
|
+
@predicates.values
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
data/lib/porolog.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#
|
2
|
+
# lib/porolog.rb - Plain Old Ruby Objects Prolog Engine
|
3
|
+
#
|
4
|
+
# Luis Esteban 2 May 2018
|
5
|
+
# created
|
6
|
+
#
|
7
|
+
|
8
|
+
module Porolog
|
9
|
+
|
10
|
+
VERSION = '0.0.1'
|
11
|
+
VERSION_DATE = '2018-05-02'
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
require_relative 'porolog/error'
|
16
|
+
require_relative 'porolog/scope'
|
@@ -0,0 +1,270 @@
|
|
1
|
+
#
|
2
|
+
# test/porolog/scope_test.rb - Test Suite for Porolog::Scope
|
3
|
+
#
|
4
|
+
# Luis Esteban 2 May 2018
|
5
|
+
# created
|
6
|
+
#
|
7
|
+
|
8
|
+
require_relative '../test_helper'
|
9
|
+
|
10
|
+
describe 'Porolog' do
|
11
|
+
|
12
|
+
before(:all) do
|
13
|
+
reset
|
14
|
+
end
|
15
|
+
|
16
|
+
describe 'Scope' do
|
17
|
+
|
18
|
+
it 'should already declare the default scope' do
|
19
|
+
assert_equal 1, Scope.scopes.size
|
20
|
+
assert_equal [:default], Scope.scopes
|
21
|
+
|
22
|
+
assert_scope Scope[:default], :default, []
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should allow predicates with the same name to coexist in different scopes' do
|
26
|
+
skip 'until Predicate added'
|
27
|
+
|
28
|
+
prime = prime1 = Predicate.new :prime, :first
|
29
|
+
|
30
|
+
prime.(2).fact!
|
31
|
+
prime.(3).fact!
|
32
|
+
prime.(5).fact!
|
33
|
+
prime.(7).fact!
|
34
|
+
prime.(11).fact!
|
35
|
+
|
36
|
+
prime = prime2 = Predicate.new :prime, :second
|
37
|
+
|
38
|
+
prime.('pump A').fact!
|
39
|
+
prime.('pump B').fact!
|
40
|
+
prime.('pump C').fact!
|
41
|
+
prime.('pump D').fact!
|
42
|
+
|
43
|
+
assert_equal [:default,:first,:second], Scope.scopes
|
44
|
+
assert_scope Scope[:default], :default, []
|
45
|
+
assert_scope Scope[:first], :first, [prime1]
|
46
|
+
assert_scope Scope[:second], :second, [prime2]
|
47
|
+
|
48
|
+
assert_equal :prime, prime1.name
|
49
|
+
assert_equal :prime, prime2.name
|
50
|
+
|
51
|
+
solutions = [
|
52
|
+
{ X: 2 },
|
53
|
+
{ X: 3 },
|
54
|
+
{ X: 5 },
|
55
|
+
{ X: 7 },
|
56
|
+
{ X: 11 },
|
57
|
+
]
|
58
|
+
assert_equal solutions, prime1.(:X).solve
|
59
|
+
|
60
|
+
solutions = [
|
61
|
+
{ X: 'pump A' },
|
62
|
+
{ X: 'pump B' },
|
63
|
+
{ X: 'pump C' },
|
64
|
+
{ X: 'pump D' },
|
65
|
+
]
|
66
|
+
assert_equal solutions, prime2.(:X).solve
|
67
|
+
end
|
68
|
+
|
69
|
+
describe '.scopes' do
|
70
|
+
|
71
|
+
it 'should return the names of all registered scopes' do
|
72
|
+
Scope.new :alpha
|
73
|
+
Scope.new :bravo
|
74
|
+
Scope.new :carly
|
75
|
+
|
76
|
+
assert_equal 4, Scope.scopes.size
|
77
|
+
assert_equal [:default, :alpha, :bravo, :carly], Scope.scopes
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
describe '.reset' do
|
83
|
+
|
84
|
+
it 'should clear all scopes' do
|
85
|
+
skip 'until Predicate added'
|
86
|
+
|
87
|
+
delta = Predicate.new :delta
|
88
|
+
|
89
|
+
Scope.new :alpha
|
90
|
+
Scope.new :bravo
|
91
|
+
Scope.new :carly
|
92
|
+
|
93
|
+
assert_equal 4, Scope.scopes.size
|
94
|
+
assert_equal [:default, :alpha, :bravo, :carly], Scope.scopes
|
95
|
+
|
96
|
+
assert_scope Scope[:default], :default, [delta]
|
97
|
+
assert_scope Scope[:alpha], :alpha, []
|
98
|
+
assert_scope Scope[:bravo], :bravo, []
|
99
|
+
assert_scope Scope[:carly], :carly, []
|
100
|
+
|
101
|
+
Scope.reset
|
102
|
+
|
103
|
+
assert_equal 1, Scope.scopes.size
|
104
|
+
assert_equal [:default], Scope.scopes
|
105
|
+
|
106
|
+
assert_scope Scope[:default], :default, []
|
107
|
+
|
108
|
+
assert_nil Scope[:alpha]
|
109
|
+
assert_nil Scope[:bravo]
|
110
|
+
assert_nil Scope[:carly]
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'should clear all scopes and start with a default scope when reset' do
|
114
|
+
skip 'until Predicate added'
|
115
|
+
|
116
|
+
test_predicate0 = Predicate.new 'test_predicate0'
|
117
|
+
test_predicate11 = Predicate.new 'test_predicate11', :scope1
|
118
|
+
test_predicate12 = Predicate.new 'test_predicate12', :scope1
|
119
|
+
test_predicate13 = Predicate.new 'test_predicate13', :scope1
|
120
|
+
test_predicate21 = Predicate.new 'test_predicate21', :scope2
|
121
|
+
test_predicate22 = Predicate.new 'test_predicate22', :scope2
|
122
|
+
test_predicate23 = Predicate.new 'test_predicate23', :scope2
|
123
|
+
|
124
|
+
assert_equal [:default, :scope1, :scope2], Scope.scopes
|
125
|
+
assert_equal [test_predicate0], Scope[:default].predicates
|
126
|
+
assert_equal [test_predicate11, test_predicate12, test_predicate13], Scope[:scope1 ].predicates
|
127
|
+
assert_equal [test_predicate21, test_predicate22, test_predicate23], Scope[:scope2 ].predicates
|
128
|
+
|
129
|
+
Scope.reset
|
130
|
+
|
131
|
+
test_predicate3 = Predicate.new 'test_predicate3'
|
132
|
+
|
133
|
+
assert_equal [:default], Scope.scopes
|
134
|
+
assert_equal [test_predicate3], Scope[:default].predicates
|
135
|
+
assert_nil Scope[:scope1 ]
|
136
|
+
assert_nil Scope[:scope2 ]
|
137
|
+
end
|
138
|
+
|
139
|
+
end
|
140
|
+
|
141
|
+
describe '.[]' do
|
142
|
+
|
143
|
+
it 'should provide access to a scope by name' do
|
144
|
+
alpha = Scope.new :alpha
|
145
|
+
bravo = Scope.new :bravo
|
146
|
+
carly = Scope.new :carly
|
147
|
+
|
148
|
+
assert_equal alpha, Scope[:alpha]
|
149
|
+
assert_equal bravo, Scope[:bravo]
|
150
|
+
assert_equal carly, Scope[:carly]
|
151
|
+
assert_nil Scope[:delta]
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
155
|
+
|
156
|
+
describe '.new' do
|
157
|
+
|
158
|
+
it 'should not create duplicate scopes (with the same name)' do
|
159
|
+
Scope.new :duplicate
|
160
|
+
Scope.new :duplicate
|
161
|
+
Scope.new :duplicate
|
162
|
+
Scope.new :duplicate
|
163
|
+
|
164
|
+
assert_equal 2, Scope.scopes.size
|
165
|
+
assert_equal 1, Scope.scopes.count(:duplicate)
|
166
|
+
assert_equal [:default, :duplicate], Scope.scopes
|
167
|
+
end
|
168
|
+
|
169
|
+
end
|
170
|
+
|
171
|
+
describe '#initialize' do
|
172
|
+
|
173
|
+
it 'should keep track of created scopes' do
|
174
|
+
Scope.new :alpha
|
175
|
+
Scope.new :bravo
|
176
|
+
Scope.new :carly
|
177
|
+
|
178
|
+
assert_equal [:default,:alpha,:bravo,:carly], Scope.scopes
|
179
|
+
end
|
180
|
+
|
181
|
+
it 'should create scopes with no predicates' do
|
182
|
+
scope = Scope.new('test_scope_name')
|
183
|
+
|
184
|
+
assert_respond_to scope, :predicates
|
185
|
+
assert_equal [], scope.predicates
|
186
|
+
end
|
187
|
+
|
188
|
+
end
|
189
|
+
|
190
|
+
describe '#name' do
|
191
|
+
|
192
|
+
it 'should create scopes with a name attribute' do
|
193
|
+
scope = Scope.new('test_scope_name')
|
194
|
+
|
195
|
+
assert_respond_to scope, :name
|
196
|
+
assert_equal 'test_scope_name', scope.name
|
197
|
+
end
|
198
|
+
|
199
|
+
end
|
200
|
+
|
201
|
+
describe '#predicates' do
|
202
|
+
|
203
|
+
it 'should provide access to all the predicates of a scope' do
|
204
|
+
skip 'until Predicate added'
|
205
|
+
|
206
|
+
test_predicate1 = Predicate.new 'test_predicate1', :test
|
207
|
+
test_predicate2 = Predicate.new 'test_predicate2', :test
|
208
|
+
|
209
|
+
assert_respond_to Scope[:test], :predicates
|
210
|
+
assert_equal [test_predicate1,test_predicate2], Scope[:test].predicates
|
211
|
+
end
|
212
|
+
|
213
|
+
end
|
214
|
+
|
215
|
+
describe '#[]' do
|
216
|
+
|
217
|
+
it 'should provide access to a predicate of a scope by its name' do
|
218
|
+
skip 'until Predicate added'
|
219
|
+
|
220
|
+
test_predicate3 = Predicate.new 'test_predicate3', :test
|
221
|
+
test_predicate4 = Predicate.new 'test_predicate4', :test
|
222
|
+
|
223
|
+
assert_includes Scope[:test].predicates, test_predicate3
|
224
|
+
|
225
|
+
assert_instance_of Class, Scope
|
226
|
+
assert_instance_of Scope, Scope[:test]
|
227
|
+
assert_instance_of Predicate, Scope[:test][:test_predicate3]
|
228
|
+
|
229
|
+
assert_equal test_predicate3, Scope[:test][:test_predicate3]
|
230
|
+
assert_equal test_predicate3, Scope[:test]['test_predicate3']
|
231
|
+
assert_equal test_predicate4, Scope[:test]['test_predicate4']
|
232
|
+
end
|
233
|
+
|
234
|
+
end
|
235
|
+
|
236
|
+
describe '#[]=' do
|
237
|
+
|
238
|
+
it 'should raise an error when trying to put anthing but a Predicate in a Scope' do
|
239
|
+
skip 'until Predicate added'
|
240
|
+
|
241
|
+
error = assert_raises(Porolog::Scope::NotPredicateError){
|
242
|
+
scope = Scope.new :scope
|
243
|
+
|
244
|
+
scope[:predicate] = 'predicate'
|
245
|
+
}
|
246
|
+
assert_equal error.message, '"predicate" is not a Predicate'
|
247
|
+
end
|
248
|
+
|
249
|
+
it 'should allow an existing predicate to be assigned to a scope' do
|
250
|
+
skip 'until Predicate added'
|
251
|
+
|
252
|
+
test_predicate = Predicate.new 'test_predicate', :test
|
253
|
+
|
254
|
+
scope = Scope.new :scope
|
255
|
+
|
256
|
+
scope[:predicate] = test_predicate
|
257
|
+
|
258
|
+
assert_equal 3, Scope.scopes.size
|
259
|
+
assert_equal [:default, :test, :scope], Scope.scopes
|
260
|
+
|
261
|
+
assert_scope Scope[:default], :default, []
|
262
|
+
assert_scope Scope[:test], :test, [test_predicate]
|
263
|
+
assert_scope Scope[:scope], :scope, [test_predicate]
|
264
|
+
end
|
265
|
+
|
266
|
+
end
|
267
|
+
|
268
|
+
end
|
269
|
+
|
270
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
#
|
2
|
+
# test/test_helper.rb - Test Suite Helper for Porolog
|
3
|
+
#
|
4
|
+
# Luis Esteban 2 May 2018
|
5
|
+
# created
|
6
|
+
#
|
7
|
+
|
8
|
+
require 'minitest/autorun'
|
9
|
+
require 'porolog'
|
10
|
+
|
11
|
+
include Porolog
|
12
|
+
|
13
|
+
def reset
|
14
|
+
Scope.reset
|
15
|
+
end
|
16
|
+
|
17
|
+
def assert_scope(scope, name, predicates)
|
18
|
+
assert_instance_of Scope, scope
|
19
|
+
assert_equal name, scope.name
|
20
|
+
assert_equal predicates, scope.predicates
|
21
|
+
end
|
metadata
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: porolog
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Luis Esteban
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-05-02 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Implements a Prolog inference engine using Plain Old Ruby Objects
|
14
|
+
email:
|
15
|
+
- luis.esteban.consulting@gmail.com
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- README.md
|
21
|
+
- Rakefile
|
22
|
+
- bin/porolog
|
23
|
+
- lib/porolog.rb
|
24
|
+
- lib/porolog/error.rb
|
25
|
+
- lib/porolog/scope.rb
|
26
|
+
- test/porolog/scope_test.rb
|
27
|
+
- test/test_helper.rb
|
28
|
+
homepage: http://rubygems.org/gems/porolog
|
29
|
+
licenses:
|
30
|
+
- Unlicense
|
31
|
+
metadata: {}
|
32
|
+
post_install_message:
|
33
|
+
rdoc_options: []
|
34
|
+
require_paths:
|
35
|
+
- lib
|
36
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
requirements: []
|
47
|
+
rubyforge_project:
|
48
|
+
rubygems_version: 2.7.6
|
49
|
+
signing_key:
|
50
|
+
specification_version: 4
|
51
|
+
summary: Prolog using Plain Old Ruby Objects
|
52
|
+
test_files:
|
53
|
+
- test/test_helper.rb
|
54
|
+
- test/porolog/scope_test.rb
|