gearbox 0.1.10 → 0.1.17
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +9 -27
- data/VERSION +1 -1
- data/gearbox.gemspec +4 -2
- data/lib/gearbox.rb +1 -0
- data/lib/gearbox/mixins/active_model_implementation.rb +1 -0
- data/lib/gearbox/mixins/queryable_implementation.rb +14 -0
- data/lib/gearbox/mixins/resource.rb +3 -7
- data/lib/gearbox/mixins/semantic_accessors.rb +6 -1
- data/lib/pry_utilities.rb +41 -5
- data/spec/gearbox/mixins/queryable_implementation_spec.rb +36 -0
- data/spec/gearbox/mixins/resource_spec.rb +0 -17
- data/spec/gearbox/mixins/semantic_accessors_spec.rb +24 -0
- data/spec/spec_helper.rb +1 -0
- metadata +5 -3
data/README.md
CHANGED
@@ -5,7 +5,7 @@ The purpose of this project is to make it easier for me to curate semantic graph
|
|
5
5
|
|
6
6
|
To get to wherever I am now, I've been playing with semantic models for a while. I started by demonstrating working code from various corners of my imagination. After a while, I thought I had enough to create a useful gem. In the past, I've worked with [Spira](https://github.com/bhuga/spira) and examples given by [Gregg Kellogg](http://greggkellogg.net/). These have been very useful in forming ideas.
|
7
7
|
|
8
|
-
As I worked on this code, I decided to go to [Spira](https://github.com/bhuga/spira) to see how similar the two gems are. I was surprised how similar they actually are. The similarities are:
|
8
|
+
As I worked on this code, I decided to go to [Spira](https://github.com/bhuga/spira) to see how similar the two gems are. I've read Ben's code a dozen times before, but this time I was surprised how similar they actually are. The similarities are:
|
9
9
|
|
10
10
|
* both are ORMs for semantic data, written in Ruby
|
11
11
|
* support for properties/attributes
|
@@ -13,43 +13,25 @@ As I worked on this code, I decided to go to [Spira](https://github.com/bhuga/sp
|
|
13
13
|
* data validations
|
14
14
|
* value types
|
15
15
|
|
16
|
-
However, Gearbox is quite a bit different. It came from a different place. I've been trying to embrace the nature of semantic models
|
16
|
+
However, Gearbox is quite a bit different. It came from a different place. I've been trying to embrace the nature of semantic models (or find a way to express my imagination with semantic tools). The major differences, I imagine are:
|
17
17
|
|
18
18
|
* support for SPARQL-based scopes and finder methods
|
19
19
|
* better support for working with various SPARQL end points
|
20
20
|
* creating and maintaining full-text indices
|
21
21
|
* object factory from triples
|
22
22
|
|
23
|
-
Development Workflow for Semantic Data
|
24
|
-
======================================
|
25
|
-
|
26
|
-
For a typical domain, I test-drive some models to define the nature of the data. These are custom-built to support the user scenarios and behavior an application is built to serve. For semantic models, I'm going for something different. The relationships between resources are much more dynamic. It's tough to build an application on dynamic domain models.
|
27
|
-
|
28
|
-
To work with this, I'd rather optimize for different things. Instead of trying to canonize the data, concretize the interaction. Given a head/buffer/file full of data, what's the easiest way to save it? Which values have to be recorded? Which values can be inferred or classified offline?
|
29
|
-
|
30
|
-
I think that there's going to be an evolution of the data graph. I'm looking to start with the mundane, and hope to be able to create a broad view from the details collected. Possibly, I will have alternative broad views, such as a full view of the topic or a chronological account of the topic as it transpires. From here, I want to look for insightful information: inferences that show the nature of our subject.
|
31
|
-
|
32
|
-
That's the goal.
|
33
|
-
|
34
|
-
Practically, the work changes over time. We start with defining the attributes and associations that are needed. This is the mundane. We then start to qualify the data by writing validations. Probably, there will be overlapping models to reflect the interaction. For example, browsing email might have a cursory recording of the people involved. Looking up Twitter information might have a more-specific user model. Facebook, the same. We're building a summary of the users, or whatever we're studying. From here, we might be able to use various analytical methods to classify and enhance the models, to clarify the story we are able to gather.
|
35
|
-
|
36
|
-
As you can see, this is a very different process than something we might do with typical relational data. I don't think there is much that we do with semantic data that can't be done with relational data, it's just that the technologies are optimized for different purposes.
|
37
|
-
|
38
23
|
Practical Example
|
39
24
|
=================
|
40
25
|
|
41
|
-
It would be good to offer a practical example. I'll get to these in a bit.
|
26
|
+
It would be good to offer a practical example. I'll get to these in a bit. Probably these will start appearing around version 0.2 or 0.3. They probably won't be very exciting until around version 0.4.
|
42
27
|
|
43
|
-
|
44
|
-
|
28
|
+
Road Ahead
|
29
|
+
==========
|
45
30
|
|
46
|
-
* bring in the
|
47
|
-
* implement mutability
|
48
|
-
*
|
49
|
-
*
|
50
|
-
* implement named scopes
|
51
|
-
* implement some sort of repository access
|
52
|
-
* use Gearbox in the examples (were written to concretize an example in my head)
|
31
|
+
* bring in the associations and type system (related, believe it or not)
|
32
|
+
* implement mutability (version 0.2)
|
33
|
+
* workflows with SPIN and full-text indexing (version 0.3)
|
34
|
+
* exploration utilities (version 0.4)
|
53
35
|
|
54
36
|
Contributing to Gearbox
|
55
37
|
=======================
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.17
|
data/gearbox.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "gearbox"
|
8
|
-
s.version = "0.1.
|
8
|
+
s.version = "0.1.17"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["David Richards"]
|
12
|
-
s.date = "2012-04-
|
12
|
+
s.date = "2012-04-05"
|
13
13
|
s.description = "A SPARQL-driven modeling toolset for semantic models."
|
14
14
|
s.email = "davidlamontrichards@gmail.com"
|
15
15
|
s.executables = ["gearbox"]
|
@@ -34,6 +34,7 @@ Gem::Specification.new do |s|
|
|
34
34
|
"lib/gearbox/attribute_collection.rb",
|
35
35
|
"lib/gearbox/mixins/active_model_implementation.rb",
|
36
36
|
"lib/gearbox/mixins/ad_hoc_properties.rb",
|
37
|
+
"lib/gearbox/mixins/queryable_implementation.rb",
|
37
38
|
"lib/gearbox/mixins/resource.rb",
|
38
39
|
"lib/gearbox/mixins/semantic_accessors.rb",
|
39
40
|
"lib/gearbox/mixins/subject_methods.rb",
|
@@ -64,6 +65,7 @@ Gem::Specification.new do |s|
|
|
64
65
|
"spec/gearbox/attribute_spec.rb",
|
65
66
|
"spec/gearbox/mixins/active_model_implementation_spec.rb",
|
66
67
|
"spec/gearbox/mixins/ad_hoc_properties_spec.rb",
|
68
|
+
"spec/gearbox/mixins/queryable_implementation_spec.rb",
|
67
69
|
"spec/gearbox/mixins/resource_spec.rb",
|
68
70
|
"spec/gearbox/mixins/semantic_accessors_spec.rb",
|
69
71
|
"spec/gearbox/mixins/subject_methods_spec.rb",
|
data/lib/gearbox.rb
CHANGED
@@ -129,6 +129,7 @@ module Gearbox
|
|
129
129
|
autoload :AdHocProperties, path('mixins/ad_hoc_properties')
|
130
130
|
autoload :ActiveModelImplementation, path('mixins/active_model_implementation')
|
131
131
|
autoload :AttributeMethods, path('mixins/attribute_methods')
|
132
|
+
autoload :QueryableImplementation, path('mixins/queryable_implementation')
|
132
133
|
autoload :Resource, path('mixins/resource')
|
133
134
|
autoload :SemanticAccessors, path('mixins/semantic_accessors')
|
134
135
|
autoload :SubjectMethods, path('mixins/subject_methods')
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Gearbox
|
2
|
+
module QueryableImplementation
|
3
|
+
def self.included(base)
|
4
|
+
base.send :include, RDF::Queryable
|
5
|
+
end
|
6
|
+
|
7
|
+
# Depends on RDF::Queryable, SemanticAccessors and SubjectMethods
|
8
|
+
def each(opts={}, &block)
|
9
|
+
attribute_definitions.map{|name, attribute| attribute.to_rdf(self, opts)}.each(&block)
|
10
|
+
end
|
11
|
+
alias :each_statement :each
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
@@ -13,16 +13,12 @@ module Gearbox
|
|
13
13
|
# This is more for knowledge base discovery or throw-away models
|
14
14
|
# So there's a new approach on the horizon of my imagination.
|
15
15
|
# base.send :include, AdHocProperties
|
16
|
+
|
17
|
+
base.send :include, ActiveModelImplementation
|
16
18
|
base.send :include, SubjectMethods
|
17
19
|
base.send :include, SemanticAccessors
|
18
|
-
base.send :include,
|
20
|
+
base.send :include, QueryableImplementation
|
19
21
|
base.send :include, RDF::Mutable
|
20
|
-
base.send :include, RDF::Queryable
|
21
|
-
end
|
22
|
-
|
23
|
-
# Depends on RDF::Queryable, SemanticAccessors and SubjectMethods
|
24
|
-
def each(opts={}, &block)
|
25
|
-
attribute_definitions.map{|name, attribute| attribute.to_rdf(self, opts)}.each(&block)
|
26
22
|
end
|
27
23
|
|
28
24
|
def inspect
|
@@ -23,11 +23,15 @@ module Gearbox
|
|
23
23
|
end
|
24
24
|
|
25
25
|
define_method("#{name}=") do |value|
|
26
|
+
@previously_changed = changes
|
26
27
|
attribute_definitions[name] ||= Attribute.new(opts)
|
27
|
-
attribute_definitions[name].
|
28
|
+
old_value = attribute_definitions[name].get
|
29
|
+
new_value = attribute_definitions[name].set(value)
|
30
|
+
name_will_change! unless new_value == old_value
|
28
31
|
end
|
29
32
|
|
30
33
|
attributes[name] = opts
|
34
|
+
define_attribute_method(name)
|
31
35
|
end
|
32
36
|
end
|
33
37
|
|
@@ -37,6 +41,7 @@ module Gearbox
|
|
37
41
|
super
|
38
42
|
assert_defaults
|
39
43
|
assert_options(opts)
|
44
|
+
@changed_attributes.clear if @changed_attributes
|
40
45
|
end
|
41
46
|
|
42
47
|
def attributes
|
data/lib/pry_utilities.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# Useful for a Pry session.
|
2
2
|
|
3
3
|
require 'forwardable'
|
4
|
+
require 'fileutils'
|
4
5
|
|
5
6
|
include Gearbox
|
6
7
|
|
@@ -17,17 +18,49 @@ class Utilities
|
|
17
18
|
alias :update_model :write_model
|
18
19
|
alias :build_model :write_model
|
19
20
|
|
21
|
+
def edit_models
|
22
|
+
raise "Directory does not exist" unless File.exist?(model_directory)
|
23
|
+
raise "ENV['EDITOR'] not set" unless ENV['EDITOR']
|
24
|
+
`#{ENV['EDITOR']} #{model_directory}`
|
25
|
+
puts "The models are open in your editor, but you will need to load them again after completing your work."
|
26
|
+
true
|
27
|
+
end
|
28
|
+
|
20
29
|
def load_model(name)
|
21
30
|
raise "Directory does not exist" unless File.exist?(model_directory)
|
22
31
|
filename = File.join(model_directory, "#{name}.rb")
|
23
32
|
load filename
|
24
33
|
end
|
34
|
+
|
35
|
+
def load_models
|
36
|
+
Dir.glob("#{model_directory}/*.rb").map { |filename| load filename}
|
37
|
+
end
|
25
38
|
|
39
|
+
def user_directory
|
40
|
+
File.expand_path("~/.gearbox")
|
41
|
+
end
|
42
|
+
|
43
|
+
def user_model_directory
|
44
|
+
@user_model_directory ||= File.join(user_directory, "models")
|
45
|
+
end
|
46
|
+
|
26
47
|
def model_directory
|
27
|
-
@model_directory
|
48
|
+
return @model_directory if @model_directory
|
49
|
+
if File.exists?(user_directory)
|
50
|
+
FileUtils.mkdir(user_model_directory) unless File.exists?(user_model_directory)
|
51
|
+
@model_directory = user_model_directory
|
52
|
+
elsif File.exists?("/tmp")
|
53
|
+
@model_directory = File.expand_path("/tmp") if File.exists?("/tmp")
|
54
|
+
end
|
55
|
+
@model_directory
|
28
56
|
end
|
29
57
|
attr_writer :model_directory
|
30
58
|
|
59
|
+
def list_models
|
60
|
+
Dir.glob("#{model_directory}/*.rb").map { |file| File.basename(file).split('.')[0..-2].join('.').to_sym}
|
61
|
+
true
|
62
|
+
end
|
63
|
+
|
31
64
|
def tmp_directory
|
32
65
|
@tmp_directory ||= "/tmp"
|
33
66
|
end
|
@@ -59,13 +92,16 @@ end
|
|
59
92
|
@utilities = Utilities.new
|
60
93
|
extend Forwardable
|
61
94
|
def_delegators :@utilities,
|
62
|
-
:write_model,
|
63
|
-
:update_model,
|
64
95
|
:build_model,
|
96
|
+
:edit_models,
|
97
|
+
:get_note,
|
98
|
+
:list_models,
|
99
|
+
:load_model,
|
100
|
+
:load_models,
|
65
101
|
:model_directory,
|
66
102
|
:model_directory=,
|
67
103
|
:tmp_directory,
|
68
104
|
:tmp_directory=,
|
69
|
-
:
|
70
|
-
:
|
105
|
+
:update_model,
|
106
|
+
:write_model
|
71
107
|
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require_relative '../../spec_helper'
|
2
|
+
|
3
|
+
include Gearbox
|
4
|
+
|
5
|
+
describe QueryableImplementation do
|
6
|
+
|
7
|
+
before do
|
8
|
+
@class = Class.new do
|
9
|
+
include Gearbox::Resource
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
subject { @class.new }
|
14
|
+
|
15
|
+
it "uses RDF::Queryable" do
|
16
|
+
@class = Class.new {include Gearbox::QueryableImplementation}
|
17
|
+
@class.included_modules.must_include RDF::Queryable
|
18
|
+
end
|
19
|
+
|
20
|
+
it "has each defined on the model" do
|
21
|
+
subject.respond_to?(:each).must_equal true
|
22
|
+
end
|
23
|
+
|
24
|
+
it "produces RDF statements from the enumeration" do
|
25
|
+
@class.attribute :name, :predicate => RDF::FOAF.name
|
26
|
+
subject = @class.new(:name => "George")
|
27
|
+
subject.to_a.must_equal([RDF::Statement.new(subject.subject, RDF::FOAF.name, RDF::Literal.new("George"))])
|
28
|
+
end
|
29
|
+
|
30
|
+
it "aliases each_statement to each" do
|
31
|
+
@class.attribute :name, :predicate => RDF::FOAF.name
|
32
|
+
subject = @class.new(:name => "George")
|
33
|
+
subject.each_statement.to_a.must_equal([RDF::Statement.new(subject.subject, RDF::FOAF.name, RDF::Literal.new("George"))])
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
@@ -35,23 +35,6 @@ describe Resource do
|
|
35
35
|
@class.included_modules.must_include RDF::Mutable
|
36
36
|
end
|
37
37
|
|
38
|
-
it "uses RDF::Queryable" do
|
39
|
-
@class.included_modules.must_include RDF::Queryable
|
40
|
-
end
|
41
|
-
|
42
|
-
describe "Enumerable integration" do
|
43
|
-
it "has each defined on the model" do
|
44
|
-
subject.respond_to?(:each).must_equal true
|
45
|
-
end
|
46
|
-
|
47
|
-
it "produces RDF statements from the enumeration" do
|
48
|
-
@class.attribute :name, :predicate => RDF::FOAF.name
|
49
|
-
subject = @class.new(:name => "George")
|
50
|
-
subject.to_a.must_equal([RDF::Statement.new(subject.subject, RDF::FOAF.name, RDF::Literal.new("George"))])
|
51
|
-
end
|
52
|
-
|
53
|
-
end
|
54
|
-
|
55
38
|
describe "Load order" do
|
56
39
|
# Unfortunately, the load order is important here.
|
57
40
|
# There is only one example right now: id.
|
@@ -10,6 +10,7 @@ describe SemanticAccessors do
|
|
10
10
|
@class = Class.new do
|
11
11
|
def self.name; 'demo_class'; end
|
12
12
|
include SemanticAccessors
|
13
|
+
include ActiveModelImplementation
|
13
14
|
end
|
14
15
|
end
|
15
16
|
|
@@ -33,6 +34,18 @@ describe SemanticAccessors do
|
|
33
34
|
@class.attribute(:name, :predicate => RDF::FOAF.name)
|
34
35
|
@class.attributes[:name].must_equal({:name => :name, :predicate => RDF::FOAF.name})
|
35
36
|
end
|
37
|
+
|
38
|
+
it "uses define_attribute_method to tell ActiveModel about the attribute" do
|
39
|
+
def @class.define_attribute_method(name)
|
40
|
+
@attribute_methods_defined ||= []
|
41
|
+
@attribute_methods_defined << name
|
42
|
+
end
|
43
|
+
def @class.attribute_methods_defined
|
44
|
+
@attribute_methods_defined
|
45
|
+
end
|
46
|
+
@class.attribute(:name, :predicate => RDF::FOAF.name)
|
47
|
+
@class.attribute_methods_defined.must_equal([:name])
|
48
|
+
end
|
36
49
|
end
|
37
50
|
end # "Class methods"
|
38
51
|
|
@@ -40,6 +53,7 @@ describe SemanticAccessors do
|
|
40
53
|
before do
|
41
54
|
@class = Class.new do
|
42
55
|
include SemanticAccessors
|
56
|
+
include ActiveModelImplementation
|
43
57
|
attribute :name, :predicate => RDF::FOAF.name
|
44
58
|
end
|
45
59
|
end
|
@@ -51,6 +65,16 @@ describe SemanticAccessors do
|
|
51
65
|
subject.name.must_equal "George"
|
52
66
|
end
|
53
67
|
|
68
|
+
it "marks the attribute as dirty when changed" do
|
69
|
+
subject.name = "George"
|
70
|
+
subject.changed_attributes.must_equal({"name" => "George"})
|
71
|
+
end
|
72
|
+
|
73
|
+
it "does not mark the attributes as dirty after initialization" do
|
74
|
+
subject = @class.new(:name => "George")
|
75
|
+
subject.changed?.must_equal false
|
76
|
+
end
|
77
|
+
|
54
78
|
it "supports the attribute data types" do
|
55
79
|
# In fact, it supports the full attribute system, this is just a quick check.
|
56
80
|
@class.attribute :birthday, :predicate => RDF::FOAF.birthday, :datatype => RDF::XSD.date
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gearbox
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.17
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-04-
|
12
|
+
date: 2012-04-05 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: linkeddata
|
@@ -276,6 +276,7 @@ files:
|
|
276
276
|
- lib/gearbox/attribute_collection.rb
|
277
277
|
- lib/gearbox/mixins/active_model_implementation.rb
|
278
278
|
- lib/gearbox/mixins/ad_hoc_properties.rb
|
279
|
+
- lib/gearbox/mixins/queryable_implementation.rb
|
279
280
|
- lib/gearbox/mixins/resource.rb
|
280
281
|
- lib/gearbox/mixins/semantic_accessors.rb
|
281
282
|
- lib/gearbox/mixins/subject_methods.rb
|
@@ -306,6 +307,7 @@ files:
|
|
306
307
|
- spec/gearbox/attribute_spec.rb
|
307
308
|
- spec/gearbox/mixins/active_model_implementation_spec.rb
|
308
309
|
- spec/gearbox/mixins/ad_hoc_properties_spec.rb
|
310
|
+
- spec/gearbox/mixins/queryable_implementation_spec.rb
|
309
311
|
- spec/gearbox/mixins/resource_spec.rb
|
310
312
|
- spec/gearbox/mixins/semantic_accessors_spec.rb
|
311
313
|
- spec/gearbox/mixins/subject_methods_spec.rb
|
@@ -327,7 +329,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
327
329
|
version: '0'
|
328
330
|
segments:
|
329
331
|
- 0
|
330
|
-
hash:
|
332
|
+
hash: 1172307424994719689
|
331
333
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
332
334
|
none: false
|
333
335
|
requirements:
|