pragmatic_context 0.0.2 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Guardfile +4 -0
- data/README.md +77 -19
- data/lib/pragmatic_context/contextualizable.rb +23 -4
- data/lib/pragmatic_context/version.rb +1 -1
- data/pragmatic_context.gemspec +1 -0
- data/spec/lib/pragmatic_context/contextualizable_spec.rb +42 -5
- metadata +17 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 40b3f7414a092b590f39f53530c841de2eb6856b
|
4
|
+
data.tar.gz: c2690d5363e6ebe4f97d8ff1bf6f74eda15c9d0c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4c73f9b9644b5cba0eec15b9057fa38d89a951cfbed66e1e20e6e87fbc3ab30140e1e7cf18930f780b2dd9dd320aaec424a941a23e99d3de6e6a3808b583e171
|
7
|
+
data.tar.gz: fdd45f04bd8b81024c96dc441a2b8539d59c77ec32e4a9cbb296a7f3a80a8c21eb6944d7e05d577608f3de4ac88005634c98fbc1cd088ab989ed3d5b3a03681b
|
data/Guardfile
ADDED
data/README.md
CHANGED
@@ -9,7 +9,7 @@ get your models talking JSON-LD.
|
|
9
9
|
I'm glad you asked. [JSON-LD](http://json-ld.org/) is a lightweight JSON format
|
10
10
|
for expressing data within a context (also known as 'Linked Data'). Essentially
|
11
11
|
what this means is that it lets you say that the thing your data model calls
|
12
|
-
a 'last_name'
|
12
|
+
a 'last_name' (and what someone else may call 'family_name', or 'surname', or 'apellido') are all embodiments of the same abstract idea of a [Family
|
13
13
|
Name](http://xmlns.com/foaf/spec/#term_familyName). JSON-LD is similar in spirit
|
14
14
|
(though [vastly different in
|
15
15
|
execution](http://manu.sporny.org/2014/json-ld-origins-2/)) to
|
@@ -116,11 +116,15 @@ it like so:
|
|
116
116
|
contextualize :email, :as => 'http://xmlns.com/foaf/0.1/mbox'
|
117
117
|
end
|
118
118
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
the following JSON(-LD!) serialization:
|
119
|
+
This gives Pragmatic Context everything it needs to be able to create JSON-LD
|
120
|
+
representations of your model objects. Getting at these representations is
|
121
|
+
discussed in the following section.
|
123
122
|
|
123
|
+
### Getting at a JSON-LD representation
|
124
|
+
|
125
|
+
The `Contextualizable` mixin adds a `as_jsonld` method that returns a Hash
|
126
|
+
representation of your object, ready for serialization. Calling this method on
|
127
|
+
the `Person` object described above will produce the following JSON-LD document:
|
124
128
|
|
125
129
|
{
|
126
130
|
"@context": {
|
@@ -133,6 +137,74 @@ the following JSON(-LD!) serialization:
|
|
133
137
|
"email": "mat@geeky.net"
|
134
138
|
}
|
135
139
|
|
140
|
+
A couple of things to note about this implementation:
|
141
|
+
|
142
|
+
* `as_jsonld` will only serialize fields that have matching `contextualize`
|
143
|
+
statements. This is in keeping with JSON-LD's convention of ignoring fields in
|
144
|
+
a document which do not have a context defined.
|
145
|
+
* The produced JSON-LD document embeds the context directly in the document,
|
146
|
+
within the `@context` field.
|
147
|
+
* For each field present in your object's `as_json` method *that has a matching
|
148
|
+
`contextualize` statement*, `as_jsonld` will:
|
149
|
+
* If the field is a primitive value (string, number, boolean, nil), its
|
150
|
+
value will be copied directly from the output of `as_json`
|
151
|
+
* If the field is a nested object which includes `Contextualizable`, it is
|
152
|
+
recursively converted to JSON-LD by calling its `as_jsonld` method
|
153
|
+
* If the field is a Hash, its keys are copied over to the parent document's
|
154
|
+
representation, namespaced as per the field's `contextualize` statement.
|
155
|
+
More info on this case is given below.
|
156
|
+
|
157
|
+
#### Hash subdocuments and namespacing
|
158
|
+
|
159
|
+
As dicsussed above, subdocuments which are also `Contextualizable` are
|
160
|
+
automatically recused into and serialized into the output of `as_jsonld`. This
|
161
|
+
behaviour should be pretty straightforward to understand and make use of, but
|
162
|
+
doesn't clearly cover the case where you may want to refer to concepts in
|
163
|
+
separate vocabularies with similar names. In this case, Pragmatic Context
|
164
|
+
allows for namespacing based on nested hashes.
|
165
|
+
|
166
|
+
*This is an advanced use case and probably only useful if you know that you need
|
167
|
+
it. If this doesn't make sense to you, feel free to ignore it.*
|
168
|
+
|
169
|
+
Nested hashes which have a corresponding `contextualize` statement are copied into the
|
170
|
+
parent document within a namespace. This may seem confusing at first, but is
|
171
|
+
done principally to realize use cases such as the following:
|
172
|
+
|
173
|
+
class CreativeWork
|
174
|
+
include Mongoid::Document
|
175
|
+
include PragmaticContext::Contextualizable
|
176
|
+
|
177
|
+
field :dc, type: Hash
|
178
|
+
field :mods, type: Hash
|
179
|
+
|
180
|
+
contextualize :dc, :as => 'http://purl.org/dc/terms/'
|
181
|
+
contextualize :mods, :as => 'http://www.loc.gov/mods/rdf/v1#'
|
182
|
+
end
|
183
|
+
|
184
|
+
This allows for both dc['title'] and mods['title'] to be set to independent
|
185
|
+
values, and for the resulting document to serialize out as something like:
|
186
|
+
|
187
|
+
{
|
188
|
+
"@context": {
|
189
|
+
"dc": "http://purl.org/dc/terms/",
|
190
|
+
"mods": "http://www.loc.gov/mods/rdf/v1#',
|
191
|
+
},
|
192
|
+
"dc:title": "The value of dc['title']",
|
193
|
+
"mods:title": "The value of mods['title']"
|
194
|
+
}
|
195
|
+
|
196
|
+
### Custom serializers
|
197
|
+
|
198
|
+
If you want to refer to your JSON-LD context by URI (see Example 4 in the [JSON
|
199
|
+
spec](http://www.w3.org/TR/json-ld) for more info), or if you want to use your
|
200
|
+
own serializer to customize the JSON-LD representaions you produce, you'll want
|
201
|
+
to make use of the `Contextualizable` mixin's `context` method. `context`
|
202
|
+
returns a Hash object ready to be serialized into the `@context` field of the
|
203
|
+
output object. By wiring this up in whatever serializer your application uses,
|
204
|
+
you can easily extend custom serializations to output
|
205
|
+
valid JSON-LD.
|
206
|
+
|
207
|
+
|
136
208
|
### Dynamic documents & more complicated cases
|
137
209
|
|
138
210
|
There are cases (especially using Monogid's dynamic fields feature) where the
|
@@ -159,20 +231,6 @@ configured like so:
|
|
159
231
|
|
160
232
|
Examples of this are forthcoming.
|
161
233
|
|
162
|
-
## Known Issues
|
163
|
-
|
164
|
-
* `@type` values aren't yet handled in any real way. This is next up on my queue
|
165
|
-
and should be done very soon.
|
166
|
-
* The above example is purposely short on serialization specifics. I'm trying to
|
167
|
-
be accommodating of various serializers and I'm still figuring out the best
|
168
|
-
way to do this. Hopefully we'll be able to make the process of adding
|
169
|
-
a `@context` automatic (or close to it) for common cases. Suggestions for this
|
170
|
-
point are very welcome.
|
171
|
-
* Further to the above, there are many (most, even) use cases where the included
|
172
|
-
`@context` field should simply be a URI pointing to a stand-alone
|
173
|
-
represntation of the object's context. I'm not sure how to flexibly realize
|
174
|
-
this yet. Again, suggestions are very welcome.
|
175
|
-
|
176
234
|
## Contributing
|
177
235
|
|
178
236
|
1. Fork it ( http://github.com/mtrudel/pragmatic_context/fork )
|
@@ -3,8 +3,6 @@ require 'pragmatic_context/default_contextualizer'
|
|
3
3
|
module PragmaticContext
|
4
4
|
module Contextualizable
|
5
5
|
def Contextualizable.included(base)
|
6
|
-
base.class_eval do
|
7
|
-
end
|
8
6
|
base.extend ClassMethods
|
9
7
|
end
|
10
8
|
|
@@ -30,7 +28,28 @@ module PragmaticContext
|
|
30
28
|
end
|
31
29
|
|
32
30
|
def as_jsonld(opts = nil)
|
33
|
-
|
31
|
+
# We iterate over terms_with_context because we want to look at our own
|
32
|
+
# fields (and not the fields in 'as_json') to case on their class. In the
|
33
|
+
# case where we want to serialize directly, we rely on the field value as
|
34
|
+
# sourced from as_json
|
35
|
+
terms_with_context = self.class.contextualizer.definitions_for_terms(terms).keys
|
36
|
+
json_results = as_json(opts).slice(*terms_with_context)
|
37
|
+
results = {}
|
38
|
+
terms_with_context.each do |term|
|
39
|
+
# Don't use idiomatic case here since Mongoid relations return proxies
|
40
|
+
# that fail the Contextualizable test
|
41
|
+
value = self.send(term)
|
42
|
+
if (value.is_a? Contextualizable)
|
43
|
+
results[term] = self.send(term).as_jsonld
|
44
|
+
elsif (value.is_a? Hash)
|
45
|
+
self.send(term).each do |key, value|
|
46
|
+
results["#{term}:#{key}"] = value
|
47
|
+
end
|
48
|
+
else
|
49
|
+
results[term] = json_results[term]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
results.merge("@context" => context)
|
34
53
|
end
|
35
54
|
|
36
55
|
def context
|
@@ -45,7 +64,7 @@ module PragmaticContext
|
|
45
64
|
private
|
46
65
|
|
47
66
|
def terms
|
48
|
-
|
67
|
+
as_json.keys
|
49
68
|
end
|
50
69
|
end
|
51
70
|
end
|
data/pragmatic_context.gemspec
CHANGED
@@ -21,6 +21,7 @@ Gem::Specification.new do |spec|
|
|
21
21
|
spec.add_development_dependency "bundler", "~> 1.5"
|
22
22
|
spec.add_development_dependency "rake"
|
23
23
|
spec.add_development_dependency "rspec", "~> 2.6"
|
24
|
+
spec.add_development_dependency "guard-rspec"
|
24
25
|
|
25
26
|
spec.add_dependency "activesupport"
|
26
27
|
spec.add_dependency "activemodel"
|
@@ -5,10 +5,10 @@ class Stub
|
|
5
5
|
include ActiveModel::Serializers::JSON
|
6
6
|
include PragmaticContext::Contextualizable
|
7
7
|
|
8
|
-
attr_accessor :bacon
|
8
|
+
attr_accessor :bacon, :ham
|
9
9
|
|
10
10
|
def attributes
|
11
|
-
{ "bacon" => bacon }
|
11
|
+
{ "bacon" => bacon, "ham" => ham }
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
@@ -57,12 +57,47 @@ describe PragmaticContext::Contextualizable do
|
|
57
57
|
end
|
58
58
|
|
59
59
|
describe 'as_jsonld' do
|
60
|
-
it 'should respond with
|
60
|
+
it 'should respond with only contextualized terms plus their context' do
|
61
61
|
@contextualizer.stub(:definitions_for_terms) do |terms|
|
62
62
|
{ 'bacon' => { "@id" => "http://bacon.yum" } }.slice(*terms)
|
63
63
|
end
|
64
64
|
subject.bacon = 'crispy'
|
65
|
-
subject.
|
65
|
+
subject.ham = 'honey'
|
66
|
+
subject.as_jsonld.should == { "bacon" => "crispy", "@context" => subject.context }
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'should recurse into Contextualizable subobjects' do
|
70
|
+
@contextualizer.stub(:definitions_for_terms) do |terms|
|
71
|
+
{ 'bacon' => { "@id" => "http://bacon.yum" },
|
72
|
+
'ham' => { "@id" => "http://ham.yum" } }.slice(*terms)
|
73
|
+
end
|
74
|
+
subject.bacon = 'crispy'
|
75
|
+
subject.ham = Stub.new
|
76
|
+
subject.ham.bacon = 'nested bacon'
|
77
|
+
subject.ham.ham = 'nested ham'
|
78
|
+
subject.as_jsonld.should == {
|
79
|
+
"@context" => subject.context,
|
80
|
+
"bacon" => "crispy",
|
81
|
+
"ham" => {
|
82
|
+
"@context" => subject.ham.context,
|
83
|
+
"bacon" => "nested bacon",
|
84
|
+
"ham" => "nested ham"
|
85
|
+
}
|
86
|
+
}
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'should compact sub-hashes into namespaced properties on self' do
|
90
|
+
@contextualizer.stub(:definitions_for_terms) do |terms|
|
91
|
+
{ 'bacon' => { "@id" => "http://bacon.yum" },
|
92
|
+
'ham' => { "@id" => "http://ham.yum" } }.slice(*terms)
|
93
|
+
end
|
94
|
+
subject.bacon = 'crispy'
|
95
|
+
subject.ham = { 'bacon' => 'nested bacon' }
|
96
|
+
subject.as_jsonld.should == {
|
97
|
+
"@context" => subject.context,
|
98
|
+
"bacon" => "crispy",
|
99
|
+
"ham:bacon" => "nested bacon"
|
100
|
+
}
|
66
101
|
end
|
67
102
|
end
|
68
103
|
|
@@ -72,14 +107,16 @@ describe PragmaticContext::Contextualizable do
|
|
72
107
|
{ 'bacon' => { "@id" => "http://bacon.yum" } }.slice(*terms)
|
73
108
|
end
|
74
109
|
subject.bacon = 'crispy'
|
110
|
+
subject.ham = 'honey'
|
75
111
|
subject.context['bacon']['@id'].should == 'http://bacon.yum'
|
112
|
+
subject.context['ham'].should == nil
|
76
113
|
end
|
77
114
|
end
|
78
115
|
|
79
116
|
describe 'uncontextualized_terms' do
|
80
117
|
it 'should include terms which are not contextualized by the configured Contextualizer' do
|
81
118
|
@contextualizer.stub(:definitions_for_terms) { {} }
|
82
|
-
subject.uncontextualized_terms.should == ['bacon']
|
119
|
+
subject.uncontextualized_terms.should == ['bacon', 'ham']
|
83
120
|
end
|
84
121
|
end
|
85
122
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pragmatic_context
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mat Trudel
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-04-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -52,6 +52,20 @@ dependencies:
|
|
52
52
|
- - ~>
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '2.6'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: guard-rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
70
|
name: activesupport
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -90,6 +104,7 @@ files:
|
|
90
104
|
- .gitignore
|
91
105
|
- .rspec
|
92
106
|
- Gemfile
|
107
|
+
- Guardfile
|
93
108
|
- LICENSE.txt
|
94
109
|
- README.md
|
95
110
|
- Rakefile
|
@@ -127,4 +142,3 @@ summary: JSON-LD from a JSON perspective
|
|
127
142
|
test_files:
|
128
143
|
- spec/lib/pragmatic_context/contextualizable_spec.rb
|
129
144
|
- spec/lib/pragmatic_context/default_contextualizer_spec.rb
|
130
|
-
has_rdoc:
|