aska 0.0.2 → 0.0.6
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/CHANGELOG +9 -2
- data/Manifest +2 -0
- data/README +12 -0
- data/Rakefile +72 -0
- data/aska.gemspec +80 -24
- data/lib/aska.rb +71 -26
- data/lib/object.rb +6 -0
- data/spec/rules_spec.rb +61 -13
- metadata +31 -13
data/CHANGELOG
CHANGED
@@ -1,2 +1,9 @@
|
|
1
|
-
v0.0.
|
2
|
-
v0.0.
|
1
|
+
v0.0.5 * Rewrite of adding rules method
|
2
|
+
v0.0.4 * Added lookup methods
|
3
|
+
* Added supported methods
|
4
|
+
* Updated README
|
5
|
+
* Renamed all the attr_accessors and namespaced them inside aska
|
6
|
+
v0.0.3 * Changed the methodology of storing the rules
|
7
|
+
* Removed evaluation
|
8
|
+
v0.0.2 * Added buckets of rules
|
9
|
+
v0.0.1 * First write
|
data/Manifest
CHANGED
data/README
CHANGED
@@ -25,13 +25,25 @@
|
|
25
25
|
|
26
26
|
@car.rules_valid?(:names)
|
27
27
|
|
28
|
+
Currently, the methods that are supported are: < > == => =< to check the rules against
|
29
|
+
|
30
|
+
Aska will try to call the method name given if the method exists on the object, or it will revert to a self-created attribute (named: METHODNAME_aska, if you are curious). This way it does not overwrite any methods on accident and you have access to the data.
|
31
|
+
|
32
|
+
This rewrite does not use eval on each check.
|
33
|
+
|
28
34
|
If they do all match, then the rules_valid? will return true, otherwise it will return false
|
29
35
|
|
36
|
+
You can check if a set of rules exist with: are_rules?(:rules)
|
37
|
+
You can check look up the rules manually with: look_up_rules(:rules)
|
38
|
+
|
30
39
|
== INSTALL:
|
31
40
|
|
32
41
|
gem install aska
|
33
42
|
|
34
43
|
== ROADMAP
|
44
|
+
* v0.0.4 - Namespace rules
|
45
|
+
* v0.0.3 - Remove eval from the evaluation of rules
|
46
|
+
* v0.0.2 - Add buckets for rules
|
35
47
|
* v0.0.1 - First release
|
36
48
|
|
37
49
|
== LICENSE:
|
data/Rakefile
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
begin
|
2
|
+
require 'echoe'
|
3
|
+
Echoe.new("aska") do |p|
|
4
|
+
p.author = "Ari Lerner"
|
5
|
+
p.email = "ari.lerner@citrusbyte.com"
|
6
|
+
p.summary = "The basics of an expert system"
|
7
|
+
p.url = "http://blog.citrusbyte.com"
|
8
|
+
p.dependencies = []
|
9
|
+
p.version = "0.0.6"
|
10
|
+
p.install_message =<<-EOM
|
11
|
+
|
12
|
+
Aska - Expert system basics
|
13
|
+
|
14
|
+
See blog.citrusbyte.com for more details
|
15
|
+
*** Ari Lerner @ <ari.lerner@citrusbyte.com> ***
|
16
|
+
|
17
|
+
EOM
|
18
|
+
p.include_rakefile = true
|
19
|
+
end
|
20
|
+
rescue Exception => e
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
namespace(:pkg) do
|
25
|
+
## Rake task to create/update a .manifest file in your project, as well as update *.gemspec
|
26
|
+
desc %{Update ".manifest" with the latest list of project filenames. Respect\
|
27
|
+
.gitignore by excluding everything that git ignores. Update `files` and\
|
28
|
+
`test_files` arrays in "*.gemspec" file if it's present.}
|
29
|
+
task :manifest do
|
30
|
+
list = Dir['**/*'].sort
|
31
|
+
spec_file = Dir['*.gemspec'].first
|
32
|
+
list -= [spec_file] if spec_file
|
33
|
+
|
34
|
+
File.read('.gitignore').each_line do |glob|
|
35
|
+
glob = glob.chomp.sub(/^\//, '')
|
36
|
+
list -= Dir[glob]
|
37
|
+
list -= Dir["#{glob}/**/*"] if File.directory?(glob) and !File.symlink?(glob)
|
38
|
+
puts "excluding #{glob}"
|
39
|
+
end
|
40
|
+
|
41
|
+
if spec_file
|
42
|
+
spec = File.read spec_file
|
43
|
+
spec.gsub! /^(\s* s.(test_)?files \s* = \s* )( \[ [^\]]* \] | %w\( [^)]* \) )/mx do
|
44
|
+
assignment = $1
|
45
|
+
bunch = $2 ? list.grep(/^test\//) : list
|
46
|
+
'%s%%w(%s)' % [assignment, bunch.join(' ')]
|
47
|
+
end
|
48
|
+
|
49
|
+
File.open(spec_file, 'w') {|f| f << spec }
|
50
|
+
end
|
51
|
+
File.open('Manifest', 'w') {|f| f << list.join("\n") }
|
52
|
+
end
|
53
|
+
desc "Build gemspec for github"
|
54
|
+
task :gemspec => :manifest do
|
55
|
+
require "yaml"
|
56
|
+
`rake manifest gem`
|
57
|
+
data = YAML.load(open("aska.gemspec").read).to_ruby
|
58
|
+
File.open("aska.gemspec", "w+") {|f| f << data }
|
59
|
+
end
|
60
|
+
desc "Update gemspec with the time"
|
61
|
+
task :gemspec_update => :gemspec do
|
62
|
+
end
|
63
|
+
desc "Get ready to release the gem"
|
64
|
+
task :prerelease => :gemspec_update do
|
65
|
+
`git add .`
|
66
|
+
`git ci -a -m "Updated gemspec for github"`
|
67
|
+
end
|
68
|
+
desc "Release them gem to the gem server"
|
69
|
+
task :release => :prerelease do
|
70
|
+
`git push origin master`
|
71
|
+
end
|
72
|
+
end
|
data/aska.gemspec
CHANGED
@@ -1,31 +1,87 @@
|
|
1
1
|
|
2
|
-
# Gem::Specification for Aska-0.0.
|
2
|
+
# Gem::Specification for Aska-0.0.6
|
3
3
|
# Originally generated by Echoe
|
4
4
|
|
5
|
-
Gem::Specification
|
6
|
-
|
7
|
-
|
5
|
+
--- !ruby/object:Gem::Specification
|
6
|
+
name: aska
|
7
|
+
version: !ruby/object:Gem::Version
|
8
|
+
version: 0.0.6
|
9
|
+
platform: ruby
|
10
|
+
authors:
|
11
|
+
- Ari Lerner
|
12
|
+
autorequire:
|
13
|
+
bindir: bin
|
8
14
|
|
9
|
-
|
15
|
+
date: 2008-10-31 00:00:00 -07:00
|
16
|
+
default_executable:
|
17
|
+
dependencies:
|
18
|
+
- !ruby/object:Gem::Dependency
|
19
|
+
name: echoe
|
20
|
+
type: :development
|
21
|
+
version_requirement:
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: "0"
|
27
|
+
version:
|
28
|
+
description: The basics of an expert system
|
29
|
+
email: ari.lerner@citrusbyte.com
|
30
|
+
executables: []
|
10
31
|
|
11
|
-
|
12
|
-
s.authors = [""]
|
13
|
-
s.date = %q{2008-05-28}
|
14
|
-
s.description = %q{}
|
15
|
-
s.email = %q{}
|
16
|
-
s.extra_rdoc_files = ["CHANGELOG", "lib/aska.rb", "LICENSE", "README"]
|
17
|
-
s.files = ["aska.gemspec", "CHANGELOG", "lib/aska.rb", "LICENSE", "Manifest", "README", "spec/rules_spec.rb", "spec/spec_helper.rb"]
|
18
|
-
s.has_rdoc = true
|
19
|
-
s.homepage = %q{}
|
20
|
-
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Aska", "--main", "README"]
|
21
|
-
s.require_paths = ["lib"]
|
22
|
-
s.rubyforge_project = %q{aska}
|
23
|
-
s.rubygems_version = %q{1.0.1}
|
24
|
-
s.summary = %q{}
|
25
|
-
end
|
32
|
+
extensions: []
|
26
33
|
|
34
|
+
extra_rdoc_files:
|
35
|
+
- CHANGELOG
|
36
|
+
- lib/aska.rb
|
37
|
+
- lib/object.rb
|
38
|
+
- LICENSE
|
39
|
+
- README
|
40
|
+
files:
|
41
|
+
- aska.gemspec
|
42
|
+
- CHANGELOG
|
43
|
+
- lib/aska.rb
|
44
|
+
- lib/object.rb
|
45
|
+
- LICENSE
|
46
|
+
- Manifest
|
47
|
+
- Rakefile
|
48
|
+
- README
|
49
|
+
- spec/rules_spec.rb
|
50
|
+
- spec/spec_helper.rb
|
51
|
+
has_rdoc: true
|
52
|
+
homepage: http://blog.citrusbyte.com
|
53
|
+
post_install_message: |+
|
54
|
+
|
55
|
+
Aska - Expert system basics
|
56
|
+
|
57
|
+
See blog.citrusbyte.com for more details
|
58
|
+
*** Ari Lerner @ <ari.lerner@citrusbyte.com> ***
|
59
|
+
|
60
|
+
rdoc_options:
|
61
|
+
- --line-numbers
|
62
|
+
- --inline-source
|
63
|
+
- --title
|
64
|
+
- Aska
|
65
|
+
- --main
|
66
|
+
- README
|
67
|
+
require_paths:
|
68
|
+
- lib
|
69
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: "0"
|
74
|
+
version:
|
75
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
76
|
+
requirements:
|
77
|
+
- - "="
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: "1.2"
|
80
|
+
version:
|
81
|
+
requirements: []
|
27
82
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
83
|
+
rubyforge_project: aska
|
84
|
+
rubygems_version: 1.2.0
|
85
|
+
specification_version: 2
|
86
|
+
summary: The basics of an expert system
|
87
|
+
test_files: []
|
data/lib/aska.rb
CHANGED
@@ -1,23 +1,44 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
=begin rdoc
|
2
|
+
Aska
|
3
|
+
=end
|
4
|
+
require "#{File.dirname(__FILE__)}/object"
|
5
5
|
module Aska
|
6
6
|
module ClassMethods
|
7
|
-
def rules(name=:rules,
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
7
|
+
def rules(name=:rules, arr=[])
|
8
|
+
returning look_up_rules(name) do |rs|
|
9
|
+
arr.each do |line|
|
10
|
+
k = line[/(.+)[=\\\<\>](.*)/, 1].gsub(/\s+/, '')
|
11
|
+
v = line[/(.+)[=\\<>](.*)/, 2].gsub(/\s+/, '')
|
12
|
+
m = line[/[=\\<>]/, 0].gsub(/\s+/, '')
|
13
|
+
|
14
|
+
create_instance_variable(k)
|
15
|
+
rs << {k => [m, v]}
|
16
|
+
end
|
17
|
+
self.send(:define_method, name) do
|
18
|
+
look_up_rules(name)
|
19
|
+
end
|
13
20
|
end
|
14
21
|
end
|
22
|
+
def create_instance_variable(k)
|
23
|
+
aska_attr_accessors << k.to_sym unless aska_attr_accessors.include?(k.to_sym)
|
24
|
+
attr_reader k.to_sym unless respond_to?("#{k}".to_sym)
|
25
|
+
attr_writer k.to_sym unless respond_to?("#{k}=".to_sym)
|
26
|
+
end
|
15
27
|
def look_up_rules(name)
|
16
|
-
defined_rules[
|
28
|
+
defined_rules[name.to_sym] ||= []
|
29
|
+
end
|
30
|
+
def are_rules?(name)
|
31
|
+
!look_up_rules(name).empty?
|
32
|
+
end
|
33
|
+
def aska_attr_accessors
|
34
|
+
@@aska_attr_accessors ||= []
|
17
35
|
end
|
18
36
|
def defined_rules
|
19
37
|
@defined_rules ||= {}
|
20
38
|
end
|
39
|
+
def aska_named(name)
|
40
|
+
"#{name}_aska"
|
41
|
+
end
|
21
42
|
end
|
22
43
|
|
23
44
|
module InstanceMethods
|
@@ -25,31 +46,55 @@ module Aska
|
|
25
46
|
@rules ||= self.class.defined_rules
|
26
47
|
end
|
27
48
|
def valid_rules?(name=:rules)
|
28
|
-
self.class.look_up_rules(name).
|
29
|
-
|
49
|
+
self.class.look_up_rules(name).reject {|rule| valid_rule?(rule) }.empty?
|
50
|
+
end
|
51
|
+
def __aska_aska_stuff(m)
|
52
|
+
if respond_to?(m.to_sym)
|
53
|
+
self.send(m.to_sym)
|
54
|
+
else
|
55
|
+
m
|
30
56
|
end
|
31
|
-
return true
|
32
57
|
end
|
33
|
-
def valid_rule?(rule
|
34
|
-
return false unless rule # Can't apply a rule that is nil, can we?
|
58
|
+
def valid_rule?(rule)
|
35
59
|
rule.each do |key,value|
|
36
60
|
begin
|
37
|
-
|
61
|
+
# puts "#{aska(key)} #{value[0].to_sym} #{get_var(value[1])} (#{attr_accessor?(value[1])})"
|
62
|
+
return __aska_aska_stuff(key).send(value[0].to_sym, __aska_get_var(value[1]))
|
38
63
|
rescue Exception => e
|
39
64
|
return false
|
40
65
|
end
|
41
66
|
end
|
42
67
|
end
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
68
|
+
# Get the variable from the class
|
69
|
+
# If it's defined as an attr_accessor, we know it has been defined as a rule
|
70
|
+
# Otherwise, if we are passing it as a
|
71
|
+
def __aska_get_var(name)
|
72
|
+
# attr_accessor?(name) ? aska(name) :
|
73
|
+
(supported_method?(name) ? name.to_sym : name.to_f)
|
74
|
+
end
|
75
|
+
def __aska_aska(name)
|
76
|
+
self.class.aska_named(name)
|
77
|
+
end
|
78
|
+
def attr_accessor?(name)
|
79
|
+
self.class.aska_attr_accessors.include?(name.to_sym)
|
80
|
+
end
|
81
|
+
def supported_method?(meth)
|
82
|
+
%w(< > == => =<).include?("#{meth}")
|
52
83
|
end
|
84
|
+
|
85
|
+
def look_up_rules(r);self.class.look_up_rules(r);end
|
86
|
+
def are_rules?(r);self.class.are_rules?(r);end
|
87
|
+
|
88
|
+
# def method_missing(m, *args, &block)
|
89
|
+
# if self.class.defined_rules.has_key?(m.to_sym)
|
90
|
+
# self.class.send(:define_method, m) do
|
91
|
+
# self.class.look_up_rules(m)
|
92
|
+
# end
|
93
|
+
# self.send m
|
94
|
+
# else
|
95
|
+
# super
|
96
|
+
# end
|
97
|
+
# end
|
53
98
|
end
|
54
99
|
|
55
100
|
def self.included(receiver)
|
data/lib/object.rb
ADDED
data/spec/rules_spec.rb
CHANGED
@@ -2,12 +2,11 @@ require File.dirname(__FILE__) + '/spec_helper'
|
|
2
2
|
|
3
3
|
class Car
|
4
4
|
include Aska
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
EOR
|
5
|
+
rules :names, [
|
6
|
+
"x > 0",
|
7
|
+
"y > 0",
|
8
|
+
"x > y"
|
9
|
+
]
|
11
10
|
end
|
12
11
|
describe "Rules" do
|
13
12
|
before(:each) do
|
@@ -19,19 +18,63 @@ describe "Rules" do
|
|
19
18
|
it "should be able to look up the rules based on the name into an array" do
|
20
19
|
@car.names.class.should == Array
|
21
20
|
end
|
22
|
-
it "should be able to
|
23
|
-
@car.names.
|
24
|
-
|
25
|
-
|
21
|
+
it "should be able to say that rules are defined when they are defined" do
|
22
|
+
@car.names.should_not be_nil
|
23
|
+
end
|
24
|
+
it "should be able tos ay that rules are not defined when they are not defined" do
|
25
|
+
@car.look_up_rules(:cars_and_wheels).should be_empty
|
26
|
+
end
|
27
|
+
it "should be able to say if the rules are not rules" do
|
28
|
+
@car.are_rules?(:cars_and_wheels).should be_false
|
29
|
+
end
|
30
|
+
it "should be able to say that rules are rules" do
|
31
|
+
@car.are_rules?(:names).should be_true
|
32
|
+
end
|
33
|
+
describe "parsing" do
|
34
|
+
it "should be able to parse the x > 0 into an array" do
|
35
|
+
@car.names.include?({"x"=>[">","0"]}).should == true
|
36
|
+
end
|
37
|
+
it "should be able to parse y > 0 into an array" do
|
38
|
+
@car.names.include?({"y"=>[">","0"]}).should == true
|
39
|
+
end
|
40
|
+
it "should be able to parse x > y into the hash" do
|
41
|
+
@car.names.include?({"x"=>[">","y"]}).should == true
|
42
|
+
end
|
43
|
+
it "should have 3 rules" do
|
44
|
+
@car.names.size.should == 3
|
45
|
+
end
|
46
|
+
it "should be able to look up the names as a rule" do
|
47
|
+
Car.look_up_rules(:names).should == [{"x"=>[">", "0"]}, {"y"=>[">", "0"]}, {"x"=>[">", "y"]}]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
# it "should use x if available instead of x_aska" do
|
51
|
+
# @car.x = 6
|
52
|
+
# @car.get_var(:x).class.should == @car.x_aska.class
|
53
|
+
# end
|
54
|
+
# it "should be able to get the variable associated with the instance and return it" do
|
55
|
+
# @car.x_aska = 4
|
56
|
+
# @car.get_var(:x).class.should == @car.x_aska.class
|
57
|
+
# end
|
58
|
+
it "should be able to get a number with the instance and return it as a float" do
|
59
|
+
@car.__aska_get_var(4).class.should == Float
|
60
|
+
end
|
61
|
+
it "should be able to get the method it's applying as a method symbol" do
|
62
|
+
@car.__aska_get_var(:<).class.should == Symbol
|
63
|
+
end
|
64
|
+
it "should be able to get the method as a symbol" do
|
65
|
+
@car.__aska_get_var("<").class.should == Symbol
|
66
|
+
end
|
67
|
+
it "should be able to retrieve the value of the rule when checking if it's valid" do
|
68
|
+
@car.x = 10
|
69
|
+
@car.valid_rule?({:x => [:==, 10]}).should == true
|
26
70
|
end
|
27
71
|
it "should be able to apply the rules and say that they are not met when they aren't" do
|
28
|
-
@car.x = 0
|
29
72
|
@car.valid_rules?(:names).should == false
|
30
73
|
end
|
31
74
|
it "should be able to apply the rules and say they aren't valid when they aren't all met" do
|
32
75
|
@car.x = 5
|
33
76
|
@car.y = 10
|
34
|
-
@car.valid_rules?(:names).should ==
|
77
|
+
@car.valid_rules?(:names).should == true
|
35
78
|
end
|
36
79
|
it "should be able to apply the rules and say they aren't valid when they aren't all met" do
|
37
80
|
@car.x = 5
|
@@ -40,7 +83,12 @@ describe "Rules" do
|
|
40
83
|
end
|
41
84
|
it "should be able to apply the rules and say that they are in fact valid" do
|
42
85
|
@car.x = 10
|
43
|
-
@car.y = 5
|
86
|
+
@car.y = 5
|
44
87
|
@car.valid_rules?(:names).should == true
|
45
88
|
end
|
89
|
+
it "should have the rules in an array of hashes" do
|
90
|
+
@car.names.each do |n|
|
91
|
+
|
92
|
+
end
|
93
|
+
end
|
46
94
|
end
|
metadata
CHANGED
@@ -1,20 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: aska
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
-
|
7
|
+
- Ari Lerner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-
|
12
|
+
date: 2008-10-31 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
|
-
dependencies:
|
15
|
-
|
16
|
-
|
17
|
-
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: echoe
|
17
|
+
type: :development
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
description: The basics of an expert system
|
26
|
+
email: ari.lerner@citrusbyte.com
|
18
27
|
executables: []
|
19
28
|
|
20
29
|
extensions: []
|
@@ -22,20 +31,29 @@ extensions: []
|
|
22
31
|
extra_rdoc_files:
|
23
32
|
- CHANGELOG
|
24
33
|
- lib/aska.rb
|
34
|
+
- lib/object.rb
|
25
35
|
- LICENSE
|
26
36
|
- README
|
27
37
|
files:
|
28
38
|
- aska.gemspec
|
29
39
|
- CHANGELOG
|
30
40
|
- lib/aska.rb
|
41
|
+
- lib/object.rb
|
31
42
|
- LICENSE
|
32
43
|
- Manifest
|
44
|
+
- Rakefile
|
33
45
|
- README
|
34
46
|
- spec/rules_spec.rb
|
35
47
|
- spec/spec_helper.rb
|
36
48
|
has_rdoc: true
|
37
|
-
homepage:
|
38
|
-
post_install_message:
|
49
|
+
homepage: http://blog.citrusbyte.com
|
50
|
+
post_install_message: |+
|
51
|
+
|
52
|
+
Aska - Expert system basics
|
53
|
+
|
54
|
+
See blog.citrusbyte.com for more details
|
55
|
+
*** Ari Lerner @ <ari.lerner@citrusbyte.com> ***
|
56
|
+
|
39
57
|
rdoc_options:
|
40
58
|
- --line-numbers
|
41
59
|
- --inline-source
|
@@ -53,16 +71,16 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
53
71
|
version:
|
54
72
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
55
73
|
requirements:
|
56
|
-
- - "
|
74
|
+
- - "="
|
57
75
|
- !ruby/object:Gem::Version
|
58
|
-
version: "
|
76
|
+
version: "1.2"
|
59
77
|
version:
|
60
78
|
requirements: []
|
61
79
|
|
62
80
|
rubyforge_project: aska
|
63
|
-
rubygems_version: 1.0
|
81
|
+
rubygems_version: 1.2.0
|
64
82
|
signing_key:
|
65
83
|
specification_version: 2
|
66
|
-
summary:
|
84
|
+
summary: The basics of an expert system
|
67
85
|
test_files: []
|
68
86
|
|