kartograph 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rspec +2 -0
- data/README.md +92 -1
- data/examples/domains.rb +54 -0
- data/examples/representation_for.rb +24 -0
- data/kartograph.gemspec +1 -0
- data/lib/kartograph.rb +9 -1
- data/lib/kartograph/artist.rb +22 -0
- data/lib/kartograph/dsl.rb +58 -0
- data/lib/kartograph/map.rb +32 -0
- data/lib/kartograph/property.rb +43 -0
- data/lib/kartograph/property_collection.rb +20 -0
- data/lib/kartograph/root_key.rb +19 -0
- data/lib/kartograph/sculptor.rb +26 -0
- data/lib/kartograph/version.rb +1 -1
- data/spec/lib/kartograph/artist_spec.rb +70 -0
- data/spec/lib/kartograph/dsl_spec.rb +125 -0
- data/spec/lib/kartograph/map_spec.rb +46 -0
- data/spec/lib/kartograph/property_collection_spec.rb +15 -0
- data/spec/lib/kartograph/property_spec.rb +155 -0
- data/spec/lib/kartograph/root_key_spec.rb +33 -0
- data/spec/lib/kartograph/sculptor_spec.rb +76 -0
- data/spec/spec_helper.rb +66 -0
- data/spec/support/dsl_contexts.rb +22 -0
- data/spec/support/dummy_comment.rb +2 -0
- data/spec/support/dummy_user.rb +2 -0
- metadata +49 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d0486e484ee7a91e233645567afcf28f2ff25d6b
|
4
|
+
data.tar.gz: 0b6232dbd15c7a27a90f89b00652abdc4912a789
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 389d7c025aad932d0f22e2d5b3fa1d03d907bd5af1cb06d916549af5caadfc10c47ee975ef3a0d5c4c9de6ea093bfc039bf2b9dccd8e34345ca5a9f5a6c0d994
|
7
|
+
data.tar.gz: aeff8daa983de66a69c76a7c73e995d91ce28b977c2749c7eba257ecda38294ba6d4653eb0f16c03f8bcac860003cd4b072dc44cecd1e25d1cb265d7a1f6092a
|
data/.rspec
ADDED
data/README.md
CHANGED
@@ -18,7 +18,98 @@ Or install it yourself as:
|
|
18
18
|
|
19
19
|
## Usage
|
20
20
|
|
21
|
-
|
21
|
+
Kartograph makes it easy to generate and convert JSON. It's intention is to be used for API clients.
|
22
|
+
|
23
|
+
For example, if you have an object that you would like to convert to JSON for a create request to an API. You would have something similar to this:
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
class UserMapping
|
27
|
+
include Kartograph::DSL
|
28
|
+
|
29
|
+
kartograph do
|
30
|
+
mapping User # The object we're mapping
|
31
|
+
|
32
|
+
property :name, scopes: [:create, :update]
|
33
|
+
property :id, scopes: [:read]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
user = User.new(name: 'Bobby Tables')
|
38
|
+
json_for_create = UserMapping.representation_for(:create, user)
|
39
|
+
```
|
40
|
+
|
41
|
+
Some API's will give you the created resource back as JSON as well on a successful create. For that, you may do something like this:
|
42
|
+
|
43
|
+
```ruby
|
44
|
+
response = HTTPClient.post("http://something.com/api/users", body: json_for_create)
|
45
|
+
created_user = UserMapping.extract_single(response.body, :read)
|
46
|
+
```
|
47
|
+
|
48
|
+
Most API's will have a way of retrieving an entire resource collection. For this you can instruct Kartograph to convert a collection.
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
response = HTTPClient.get("http://something.com/api/users")
|
52
|
+
users = UserMapping.extract_collection(response.body, :read)
|
53
|
+
# => [ User, User, User ]
|
54
|
+
```
|
55
|
+
|
56
|
+
### Getting Harder
|
57
|
+
|
58
|
+
Sometimes resources will nest other properties under a key. Kartograph can handle this as well.
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
class UserMapping
|
62
|
+
include Kartograph::DSL
|
63
|
+
|
64
|
+
kartograph do
|
65
|
+
mapping User # The object we're mapping
|
66
|
+
|
67
|
+
property :name, scopes: [:read]
|
68
|
+
|
69
|
+
property :comments do
|
70
|
+
mapping Comment # The nested object we're mapping
|
71
|
+
|
72
|
+
property :text, scopes: [:read]
|
73
|
+
property :author, scopes: [:read]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
```
|
78
|
+
|
79
|
+
Just like the previous examples, when you serialize this. It will include the comment block for the scope defined.
|
80
|
+
|
81
|
+
### Root Keys
|
82
|
+
|
83
|
+
Kartograph can also handle the event of root keys in response bodies. For example, if you receive a response with:
|
84
|
+
|
85
|
+
```json
|
86
|
+
{ "user": { "id": 123 } }
|
87
|
+
```
|
88
|
+
|
89
|
+
You could define a mapping like this:
|
90
|
+
|
91
|
+
|
92
|
+
```ruby
|
93
|
+
class UserMapping
|
94
|
+
include Kartograph::DSL
|
95
|
+
|
96
|
+
kartograph do
|
97
|
+
mapping User
|
98
|
+
root_key singular: 'user', plural: 'users' scopes: [:read]
|
99
|
+
property :id, scopes: [:read]
|
100
|
+
end
|
101
|
+
end
|
102
|
+
```
|
103
|
+
|
104
|
+
This means that when you call the same thing:
|
105
|
+
|
106
|
+
```ruby
|
107
|
+
response = HTTPClient.get("http://something.com/api/users")
|
108
|
+
users = UserMapping.extract_collection(response.body, :read)
|
109
|
+
```
|
110
|
+
|
111
|
+
It will look for the root key before trying to deserialize the JSON response.
|
112
|
+
The advantage of this is it will only use the root key if there is a scope defined for it.
|
22
113
|
|
23
114
|
## Contributing
|
24
115
|
|
data/examples/domains.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'kartograph'
|
2
|
+
|
3
|
+
json = '{
|
4
|
+
"domains": [
|
5
|
+
{
|
6
|
+
"name": "example.com",
|
7
|
+
"ttl": 1800,
|
8
|
+
"zone_file": "Example zone file text..."
|
9
|
+
}
|
10
|
+
],
|
11
|
+
"meta": {
|
12
|
+
"total": 1
|
13
|
+
}
|
14
|
+
}'
|
15
|
+
|
16
|
+
class Domain
|
17
|
+
attr_accessor :name, :ttl, :zone_file
|
18
|
+
end
|
19
|
+
|
20
|
+
class MetaInformation
|
21
|
+
attr_accessor :total
|
22
|
+
end
|
23
|
+
|
24
|
+
class DomainMapper
|
25
|
+
include Kartograph::DSL
|
26
|
+
|
27
|
+
kartograph do
|
28
|
+
mapping Domain
|
29
|
+
root_key singular: 'domain', plural: 'domains', scopes: [:read]
|
30
|
+
|
31
|
+
property :name, scopes: [:read]
|
32
|
+
property :ttl, scopes: [:read]
|
33
|
+
property :zone_file, scopes: [:read]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class MetaInformationMapper
|
38
|
+
include Kartograph::DSL
|
39
|
+
|
40
|
+
kartograph do
|
41
|
+
mapping MetaInformation
|
42
|
+
|
43
|
+
root_key singular: 'meta', scopes: [:read]
|
44
|
+
property :total, scopes: [:read]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
domains = DomainMapper.extract_collection(json, :read)
|
49
|
+
meta = MetaInformationMapper.extract_single(json, :read)
|
50
|
+
|
51
|
+
puts "Total Domains: #{domains.size}"
|
52
|
+
puts domains.map(&:name)
|
53
|
+
puts
|
54
|
+
puts "Total pages: #{meta.total}"
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class Domain
|
2
|
+
attr_accessor :name, :ttl, :zone_file
|
3
|
+
end
|
4
|
+
|
5
|
+
class DomainMapper
|
6
|
+
include Kartograph::DSL
|
7
|
+
|
8
|
+
kartograph do
|
9
|
+
mapping Domain
|
10
|
+
root_key singular: 'domain', plural: 'domains', scopes: [:read]
|
11
|
+
|
12
|
+
property :name, scopes: [:read, :create]
|
13
|
+
property :ttl, scopes: [:read, :create]
|
14
|
+
property :zone_file, scopes: [:read]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
domain = Domain.new
|
19
|
+
domain.name = 'example.com'
|
20
|
+
domain.ttl = 3600
|
21
|
+
domain.zone_file = "this wont be represented for create"
|
22
|
+
|
23
|
+
puts DomainMapper.representation_for(:create, domain)
|
24
|
+
#=> {"name":"example.com","ttl":3600}
|
data/kartograph.gemspec
CHANGED
data/lib/kartograph.rb
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
require "kartograph/version"
|
2
|
+
require 'json'
|
2
3
|
|
3
4
|
module Kartograph
|
4
|
-
|
5
|
+
autoload :DSL, 'kartograph/dsl'
|
6
|
+
autoload :Map, 'kartograph/map'
|
7
|
+
autoload :Property, 'kartograph/property'
|
8
|
+
autoload :PropertyCollection, 'kartograph/property_collection'
|
9
|
+
autoload :RootKey, 'kartograph/root_key'
|
10
|
+
|
11
|
+
autoload :Artist, 'kartograph/artist'
|
12
|
+
autoload :Sculptor, 'kartograph/sculptor'
|
5
13
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Kartograph
|
2
|
+
class Artist
|
3
|
+
attr_reader :object, :map
|
4
|
+
|
5
|
+
def initialize(object, map)
|
6
|
+
@object = object
|
7
|
+
@map = map
|
8
|
+
end
|
9
|
+
|
10
|
+
def properties
|
11
|
+
map.properties
|
12
|
+
end
|
13
|
+
|
14
|
+
def draw(scope = nil)
|
15
|
+
scoped_properties = scope ? properties.filter_by_scope(scope) : properties
|
16
|
+
scoped_properties.each_with_object({}) do |property, mapped|
|
17
|
+
raise ArgumentError, "#{object} does not respond to #{property.name}, so we can't map it" unless object.respond_to?(property.name)
|
18
|
+
mapped[property.name] = property.value_for(object, scope)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Kartograph
|
2
|
+
module DSL
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def kartograph(&block)
|
9
|
+
@kartograph_map ||= Map.new
|
10
|
+
|
11
|
+
block.arity > 0 ? block.call(@kartograph_map) : @kartograph_map.instance_eval(&block)
|
12
|
+
end
|
13
|
+
|
14
|
+
def representation_for(scope, object, dumper = JSON)
|
15
|
+
drawed = Artist.new(object, @kartograph_map).draw(scope)
|
16
|
+
|
17
|
+
retrieve_root_key(scope, :singular) do |root_key|
|
18
|
+
# Reassign drawed if a root key exists
|
19
|
+
drawed = { root_key => drawed }
|
20
|
+
end
|
21
|
+
|
22
|
+
dumper.dump(drawed)
|
23
|
+
end
|
24
|
+
|
25
|
+
def extract_single(content, scope, loader = JSON)
|
26
|
+
loaded = loader.load(content)
|
27
|
+
|
28
|
+
retrieve_root_key(scope, :singular) do |root_key|
|
29
|
+
# Reassign loaded if a root key exists
|
30
|
+
loaded = loaded[root_key]
|
31
|
+
end
|
32
|
+
|
33
|
+
Sculptor.new(loaded, @kartograph_map).sculpt(scope)
|
34
|
+
end
|
35
|
+
|
36
|
+
def extract_collection(content, scope, loader = JSON)
|
37
|
+
loaded = loader.load(content)
|
38
|
+
|
39
|
+
retrieve_root_key(scope, :plural) do |root_key|
|
40
|
+
# Reassign loaded if a root key exists
|
41
|
+
loaded = loaded[root_key]
|
42
|
+
end
|
43
|
+
|
44
|
+
loaded.map do |object|
|
45
|
+
Sculptor.new(object, @kartograph_map).sculpt(scope)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def retrieve_root_key(scope, type, &block)
|
52
|
+
if root_key = @kartograph_map.root_key_for(scope, type)
|
53
|
+
yield root_key
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Kartograph
|
2
|
+
class Map
|
3
|
+
def property(*args, &block)
|
4
|
+
properties << Property.new(*args, &block)
|
5
|
+
end
|
6
|
+
|
7
|
+
def properties
|
8
|
+
@properties ||= PropertyCollection.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def root_keys
|
12
|
+
@root_keys ||= []
|
13
|
+
end
|
14
|
+
|
15
|
+
def mapping(klass = nil)
|
16
|
+
@mapping = klass if klass
|
17
|
+
@mapping
|
18
|
+
end
|
19
|
+
|
20
|
+
def root_key(options)
|
21
|
+
root_keys << RootKey.new(options)
|
22
|
+
end
|
23
|
+
|
24
|
+
def root_key_for(scope, type)
|
25
|
+
return unless %i(singular plural).include?(type)
|
26
|
+
|
27
|
+
if (root_key = root_keys.select {|rk| rk.scopes.include?(scope) }[0])
|
28
|
+
root_key.send(type)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Kartograph
|
2
|
+
class Property
|
3
|
+
attr_reader :name, :options, :map
|
4
|
+
|
5
|
+
def initialize(name, options = {}, &block)
|
6
|
+
@name = name
|
7
|
+
@options = options
|
8
|
+
|
9
|
+
if block_given?
|
10
|
+
@map ||= Map.new
|
11
|
+
block.arity > 0 ? block.call(map) : map.instance_eval(&block)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def value_for(object, scope = nil)
|
16
|
+
value = object.send(name)
|
17
|
+
map ? artist_value(value, scope) : value
|
18
|
+
end
|
19
|
+
|
20
|
+
def value_from(object, scope = nil)
|
21
|
+
value = object.has_key?(name) ? object[name] : object[name.to_s]
|
22
|
+
map ? sculpt_value(value, scope) : value
|
23
|
+
end
|
24
|
+
|
25
|
+
def scopes
|
26
|
+
options[:scopes] || []
|
27
|
+
end
|
28
|
+
|
29
|
+
def plural?
|
30
|
+
!!options[:plural]
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def sculpt_value(value, scope)
|
36
|
+
plural? ? value.map {|v| Sculptor.new(v, map).sculpt(scope) } : Sculptor.new(value, map).sculpt(scope)
|
37
|
+
end
|
38
|
+
|
39
|
+
def artist_value(value, scope)
|
40
|
+
plural? ? value.map {|v| Artist.new(v, map).draw(scope) } : Artist.new(value, map).draw(scope)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module Kartograph
|
4
|
+
class PropertyCollection
|
5
|
+
# Make this collection quack like an array
|
6
|
+
# http://words.steveklabnik.com/beware-subclassing-ruby-core-classes
|
7
|
+
extend Forwardable
|
8
|
+
def_delegators :@collection, *(Array.instance_methods - Object.instance_methods)
|
9
|
+
|
10
|
+
def initialize(*)
|
11
|
+
@collection = []
|
12
|
+
end
|
13
|
+
|
14
|
+
def filter_by_scope(scope)
|
15
|
+
select do |property|
|
16
|
+
property.scopes.include?(scope)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Kartograph
|
2
|
+
class RootKey
|
3
|
+
attr_reader :options
|
4
|
+
|
5
|
+
def initialize(options = {})
|
6
|
+
@options = options
|
7
|
+
end
|
8
|
+
|
9
|
+
def scopes
|
10
|
+
options[:scopes] || []
|
11
|
+
end
|
12
|
+
|
13
|
+
%i(singular plural).each do |method|
|
14
|
+
define_method(method) do
|
15
|
+
options[method]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Kartograph
|
2
|
+
class Sculptor
|
3
|
+
attr_reader :object, :map
|
4
|
+
|
5
|
+
def initialize(object, map)
|
6
|
+
@object = object
|
7
|
+
@map = map
|
8
|
+
end
|
9
|
+
|
10
|
+
def properties
|
11
|
+
map.properties
|
12
|
+
end
|
13
|
+
|
14
|
+
def sculpt(scope = nil)
|
15
|
+
# Initializing the object we're coercing so we can set attributes on it
|
16
|
+
coerced = map.mapping.new
|
17
|
+
scoped_properties = scope ? properties.filter_by_scope(scope) : properties
|
18
|
+
|
19
|
+
scoped_properties.each_with_object(coerced) do |property, mutable|
|
20
|
+
setter_method = "#{property.name}="
|
21
|
+
value = property.value_from(object, scope)
|
22
|
+
mutable.send(setter_method, value)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/kartograph/version.rb
CHANGED
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Kartograph::Artist do
|
4
|
+
let(:map) { Kartograph::Map.new }
|
5
|
+
let(:properties) { map.properties }
|
6
|
+
|
7
|
+
describe '#initialize' do
|
8
|
+
it 'initializes with an object and a map' do
|
9
|
+
object = double('object', name: 'hello')
|
10
|
+
properties << Kartograph::Property.new(:name)
|
11
|
+
|
12
|
+
artist = Kartograph::Artist.new(object, map)
|
13
|
+
|
14
|
+
expect(artist.object).to be(object)
|
15
|
+
expect(artist.map).to be(map)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '#draw' do
|
20
|
+
it 'returns a hash of mapped properties' do
|
21
|
+
object = double('object', hello: 'world')
|
22
|
+
properties << Kartograph::Property.new(:hello)
|
23
|
+
|
24
|
+
artist = Kartograph::Artist.new(object, map)
|
25
|
+
masterpiece = artist.draw
|
26
|
+
|
27
|
+
expect(masterpiece).to include(hello: 'world')
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'raises for a property that the object does not have' do
|
31
|
+
object = double('object')
|
32
|
+
properties << Kartograph::Property.new(:bunk)
|
33
|
+
artist = Kartograph::Artist.new(object, map)
|
34
|
+
|
35
|
+
expect { artist.draw }.to raise_error(ArgumentError).with_message("#{object} does not respond to bunk, so we can't map it")
|
36
|
+
end
|
37
|
+
|
38
|
+
context 'for filtered drawing' do
|
39
|
+
it 'only returns the scoped properties' do
|
40
|
+
object = double('object', hello: 'world', foo: 'bar')
|
41
|
+
properties << Kartograph::Property.new(:hello, scopes: [:create, :read])
|
42
|
+
properties << Kartograph::Property.new(:foo, scopes: [:create])
|
43
|
+
|
44
|
+
artist = Kartograph::Artist.new(object, map)
|
45
|
+
masterpiece = artist.draw(:read)
|
46
|
+
|
47
|
+
expect(masterpiece).to eq(hello: 'world')
|
48
|
+
end
|
49
|
+
|
50
|
+
context 'on nested properties' do
|
51
|
+
it 'only returns the nested properties within the same scope' do
|
52
|
+
child = double('child', hello: 'world', foo: 'bunk')
|
53
|
+
object = double('object', child: child)
|
54
|
+
|
55
|
+
root_property = Kartograph::Property.new(:child, scopes: [:create, :read]) do
|
56
|
+
property :hello, scopes: [:create]
|
57
|
+
property :foo, scopes: [:read]
|
58
|
+
end
|
59
|
+
|
60
|
+
properties << root_property
|
61
|
+
|
62
|
+
artist = Kartograph::Artist.new(object, map)
|
63
|
+
masterpiece = artist.draw(:read)
|
64
|
+
|
65
|
+
expect(masterpiece).to eq(child: { foo: child.foo })
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Kartograph::DSL do
|
4
|
+
describe 'Inclusion' do
|
5
|
+
it 'gives you a class method for .kartograph' do
|
6
|
+
klass = Class.new
|
7
|
+
expect { klass.send(:include, described_class) }.to change { klass.respond_to?(:kartograph) }.to(true)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe '.kartograph' do
|
12
|
+
subject(:mapping) { Class.new { include Kartograph::DSL } }
|
13
|
+
|
14
|
+
it 'yields a Kartograph::Map instance' do
|
15
|
+
expect {|b| mapping.kartograph(&b) }.to yield_with_args(instance_of(Kartograph::Map))
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '.representation_for' do
|
20
|
+
include_context 'DSL Objects'
|
21
|
+
|
22
|
+
it 'returns the JSON representation for an object' do
|
23
|
+
json = mapped.representation_for(:create, object)
|
24
|
+
expect(json).to eq(
|
25
|
+
{ name: object.name }.to_json
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'with a root key for the scope' do
|
30
|
+
it 'returns the json with the root key' do
|
31
|
+
mapped.kartograph do
|
32
|
+
root_key singular: 'user', scopes: [:create]
|
33
|
+
end
|
34
|
+
json = mapped.representation_for(:create, object)
|
35
|
+
|
36
|
+
expect(json).to eq(
|
37
|
+
{
|
38
|
+
user: {
|
39
|
+
name: object.name
|
40
|
+
}
|
41
|
+
}.to_json
|
42
|
+
)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe '.extract_single' do
|
48
|
+
include_context 'DSL Objects'
|
49
|
+
let(:json) do
|
50
|
+
{ id: 1337, name: 'Paul the octopus' }
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'returns a populated object from a JSON representation' do
|
54
|
+
extracted = mapped.extract_single(json.to_json, :read)
|
55
|
+
|
56
|
+
expect(extracted.id).to eq(1337)
|
57
|
+
expect(extracted.name).to eq('Paul the octopus')
|
58
|
+
end
|
59
|
+
|
60
|
+
context 'with a root key in the JSON' do
|
61
|
+
let(:json) { { test: super() } }
|
62
|
+
|
63
|
+
before do
|
64
|
+
mapped.kartograph do
|
65
|
+
root_key singular: 'test', scopes: [:read]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'traverses into the key and pulls the object from there' do
|
70
|
+
extracted = mapped.extract_single(json.to_json, :read)
|
71
|
+
|
72
|
+
expect(extracted.id).to eq(1337)
|
73
|
+
expect(extracted.name).to eq('Paul the octopus')
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe '.extract_collection' do
|
79
|
+
include_context 'DSL Objects'
|
80
|
+
let(:json) do
|
81
|
+
[
|
82
|
+
{ id: 1337, name: 'Paul the octopus' },
|
83
|
+
{ id: 1338, name: 'Hank the octopus' }
|
84
|
+
]
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'returns a collection of objects from the json' do
|
88
|
+
extracted = mapped.extract_collection(json.to_json, :read)
|
89
|
+
|
90
|
+
expect(extracted.size).to be(2)
|
91
|
+
expect(extracted).to all(be_kind_of(DummyUser))
|
92
|
+
|
93
|
+
expect(extracted[0].id).to eq(json[0][:id])
|
94
|
+
expect(extracted[0].name).to eq(json[0][:name])
|
95
|
+
|
96
|
+
expect(extracted[1].id).to eq(json[1][:id])
|
97
|
+
expect(extracted[1].name).to eq(json[1][:name])
|
98
|
+
end
|
99
|
+
|
100
|
+
context 'for a nested key' do
|
101
|
+
let(:json) { { users: super() } }
|
102
|
+
|
103
|
+
before do
|
104
|
+
mapped.kartograph do
|
105
|
+
root_key plural: 'users', scopes: [:read]
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'returns a collection of objects from the json' do
|
110
|
+
extracted = mapped.extract_collection(json.to_json, :read)
|
111
|
+
|
112
|
+
expect(extracted.size).to be(2)
|
113
|
+
expect(extracted).to all(be_kind_of(DummyUser))
|
114
|
+
|
115
|
+
scoped = json[:users]
|
116
|
+
|
117
|
+
expect(extracted[0].id).to eq(scoped[0][:id])
|
118
|
+
expect(extracted[0].name).to eq(scoped[0][:name])
|
119
|
+
|
120
|
+
expect(extracted[1].id).to eq(scoped[1][:id])
|
121
|
+
expect(extracted[1].name).to eq(scoped[1][:name])
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Kartograph::Map do
|
4
|
+
subject(:map) { Kartograph::Map.new }
|
5
|
+
|
6
|
+
describe '#property' do
|
7
|
+
it 'adds a property to the map' do
|
8
|
+
map.property :attribute_name, scopes: [:read]
|
9
|
+
expect(map.properties.size).to be(1)
|
10
|
+
expect(map.properties.first).to be_kind_of(Kartograph::Property)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe '#properties' do
|
15
|
+
it 'returns a PropertyCollection object' do
|
16
|
+
properties = map.properties
|
17
|
+
expect(properties).to be_kind_of(Kartograph::PropertyCollection)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '#mapping' do
|
22
|
+
it 'sets the class we\'re mapping' do
|
23
|
+
map.mapping Class
|
24
|
+
expect(map.mapping).to be(Class)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '#root_key' do
|
29
|
+
it 'sets the root keys' do
|
30
|
+
map.root_key singlular: 'test', scopes: [:read]
|
31
|
+
map.root_key singlular: 'test', scopes: [:create]
|
32
|
+
|
33
|
+
expect(map.root_keys.size).to be(2)
|
34
|
+
expect(map.root_keys).to all(be_kind_of(Kartograph::RootKey))
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe '#root_key_for' do
|
39
|
+
it 'returns the first root key for the scope and type' do
|
40
|
+
map.root_key singular: 'test', scopes: [:read]
|
41
|
+
key = map.root_key_for(:read, :singular)
|
42
|
+
|
43
|
+
expect(key).to eq('test')
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Kartograph::PropertyCollection do
|
4
|
+
describe '#filter_by_scope' do
|
5
|
+
it 'only returns properties with a certain scope attached' do
|
6
|
+
collection = Kartograph::PropertyCollection.new
|
7
|
+
collection << Kartograph::Property.new(:hello, scopes: [:read, :create])
|
8
|
+
collection << Kartograph::Property.new(:id, scopes: [:read])
|
9
|
+
|
10
|
+
filtered = collection.filter_by_scope(:create)
|
11
|
+
expect(filtered.size).to be(1)
|
12
|
+
expect(filtered.first.name).to eq(:hello)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,155 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Kartograph::Property do
|
4
|
+
describe '#initialize' do
|
5
|
+
it 'initializes with an attribute name and options' do
|
6
|
+
name = :hey
|
7
|
+
options = { lol: 'whut' }
|
8
|
+
|
9
|
+
property = Kartograph::Property.new(name, options)
|
10
|
+
expect(property.name).to eq(name)
|
11
|
+
expect(property.options).to eq(options)
|
12
|
+
end
|
13
|
+
|
14
|
+
context 'with a block' do
|
15
|
+
it 'yields a map instance for the property' do
|
16
|
+
expect {|b| Kartograph::Property.new(:hello, &b) }.to yield_with_args(instance_of(Kartograph::Map))
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '#scopes' do
|
22
|
+
it 'returns the scopes that the property is for' do
|
23
|
+
property = Kartograph::Property.new(:name, scopes: [:read, :create])
|
24
|
+
expect(property.scopes).to include(:read, :create)
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'returns an empty array when no scopes are provided' do
|
28
|
+
property = Kartograph::Property.new(:name)
|
29
|
+
expect(property.scopes).to eq( [] )
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe '#plural?' do
|
34
|
+
it 'returns true when set to plural' do
|
35
|
+
property = Kartograph::Property.new(:name, scopes: [:read], plural: true)
|
36
|
+
expect(property).to be_plural
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'returns false when not set' do
|
40
|
+
property = Kartograph::Property.new(:name, scopes: [:read])
|
41
|
+
expect(property).to_not be_plural
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe '#value_for' do
|
46
|
+
it 'returns the value when passed an object' do
|
47
|
+
property = Kartograph::Property.new(:sammy)
|
48
|
+
object = double('object', sammy: 'cephalopod')
|
49
|
+
|
50
|
+
expect(property.value_for(object)).to eq('cephalopod')
|
51
|
+
end
|
52
|
+
|
53
|
+
context 'for a nested property set' do
|
54
|
+
it 'returns nested properties' do
|
55
|
+
top_level = Kartograph::Property.new(:sammy) do
|
56
|
+
property :cephalopod
|
57
|
+
end
|
58
|
+
|
59
|
+
child = double('child', cephalopod: 'I will ink you')
|
60
|
+
root = double('root', sammy: child)
|
61
|
+
|
62
|
+
expect(top_level.value_for(root)).to eq(cephalopod: child.cephalopod)
|
63
|
+
end
|
64
|
+
|
65
|
+
context 'when it is plural' do
|
66
|
+
it 'returns a pluralized representation' do
|
67
|
+
top_level = Kartograph::Property.new(:sammy, plural: true) do
|
68
|
+
property :cephalopod
|
69
|
+
end
|
70
|
+
|
71
|
+
child1 = double('child', cephalopod: 'I will ink you')
|
72
|
+
child2 = double('child', cephalopod: 'I wont because im cool')
|
73
|
+
|
74
|
+
root = double('root', sammy: [child1, child2])
|
75
|
+
|
76
|
+
expect(top_level.value_for(root)).to eq([
|
77
|
+
{ cephalopod: child1.cephalopod },
|
78
|
+
{ cephalopod: child2.cephalopod }
|
79
|
+
])
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe '#value_from' do
|
86
|
+
let(:hash) { { hello: 'world' } }
|
87
|
+
|
88
|
+
it 'retrieves the value from a hash for the property' do
|
89
|
+
property = Kartograph::Property.new(:hello)
|
90
|
+
expect(property.value_from(hash)).to eq('world')
|
91
|
+
end
|
92
|
+
|
93
|
+
context 'string and symbol agnostic' do
|
94
|
+
let(:hash) { { 'hello' => 'world' } }
|
95
|
+
|
96
|
+
it 'retrieves the value from a hash for the property' do
|
97
|
+
property = Kartograph::Property.new(:hello)
|
98
|
+
expect(property.value_from(hash)).to eq('world')
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
context 'for a nested property set' do
|
103
|
+
it 'returns an object with the properties set on it' do
|
104
|
+
dummy_class = Struct.new(:id, :name)
|
105
|
+
|
106
|
+
nested_property = Kartograph::Property.new(:hello) do
|
107
|
+
mapping dummy_class
|
108
|
+
property :id
|
109
|
+
property :name
|
110
|
+
end
|
111
|
+
|
112
|
+
hash = { hello: {
|
113
|
+
'id' => 555,
|
114
|
+
'name' => 'Buckstar'
|
115
|
+
}}
|
116
|
+
|
117
|
+
value = nested_property.value_from(hash)
|
118
|
+
expect(value).to be_kind_of(dummy_class)
|
119
|
+
expect(value.id).to eq(hash[:hello]['id'])
|
120
|
+
expect(value.name).to eq(hash[:hello]['name'])
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'returns a collection of objects when set to plural' do
|
124
|
+
dummy_class = Struct.new(:id, :name)
|
125
|
+
|
126
|
+
nested_property = Kartograph::Property.new(:hello, plural: true) do
|
127
|
+
mapping dummy_class
|
128
|
+
|
129
|
+
property :id
|
130
|
+
property :name
|
131
|
+
end
|
132
|
+
|
133
|
+
hash = {
|
134
|
+
hello: [{
|
135
|
+
'id' => 555,
|
136
|
+
'name' => 'Buckstar'
|
137
|
+
}, {
|
138
|
+
'id' => 556,
|
139
|
+
'name' => 'Starbuck'
|
140
|
+
}]
|
141
|
+
}
|
142
|
+
|
143
|
+
value = nested_property.value_from(hash)
|
144
|
+
expect(value).to be_kind_of(Array)
|
145
|
+
expect(value.size).to be(2)
|
146
|
+
|
147
|
+
expect(value[0].id).to eq(hash[:hello][0]['id'])
|
148
|
+
expect(value[0].name).to eq(hash[:hello][0]['name'])
|
149
|
+
|
150
|
+
expect(value[1].id).to eq(hash[:hello][1]['id'])
|
151
|
+
expect(value[1].name).to eq(hash[:hello][1]['name'])
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Kartograph::RootKey do
|
4
|
+
describe '#initialize' do
|
5
|
+
it 'initializes with options' do
|
6
|
+
options = { singular: 'hello', plural: 'hellos', scopes: [:read] }
|
7
|
+
|
8
|
+
instance = Kartograph::RootKey.new(options)
|
9
|
+
expect(instance.options).to eq(options)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe '#scopes' do
|
14
|
+
it 'reads the scopes' do
|
15
|
+
instance = Kartograph::RootKey.new(scopes: [:read])
|
16
|
+
expect(instance.scopes).to eq([:read])
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '#singular' do
|
21
|
+
it 'reads the singular key' do
|
22
|
+
instance = Kartograph::RootKey.new(singular: 'user')
|
23
|
+
expect(instance.singular).to eq('user')
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '#plural' do
|
28
|
+
it 'reads the plural key' do
|
29
|
+
instance = Kartograph::RootKey.new(plural: 'user')
|
30
|
+
expect(instance.plural).to eq('user')
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Kartograph::Sculptor do
|
4
|
+
describe '#initialize' do
|
5
|
+
it 'initializes with a hash and map' do
|
6
|
+
hash = {}
|
7
|
+
map = double('im a map')
|
8
|
+
|
9
|
+
sculptor = Kartograph::Sculptor.new(hash, map)
|
10
|
+
|
11
|
+
expect(sculptor.object).to be(hash)
|
12
|
+
expect(sculptor.map).to be(map)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#sculpt' do
|
17
|
+
let(:map) { Kartograph::Map.new }
|
18
|
+
let(:object) { { 'id' => 343, 'name' => 'Guilty Spark' } }
|
19
|
+
|
20
|
+
context 'without a scope' do
|
21
|
+
before do
|
22
|
+
map.mapping DummyUser
|
23
|
+
map.property :id, scopes: [:read]
|
24
|
+
map.property :name, scopes: [:read, :create]
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'returns a coerced user' do
|
28
|
+
sculptor = Kartograph::Sculptor.new(object, map)
|
29
|
+
sculpted = sculptor.sculpt
|
30
|
+
|
31
|
+
expect(sculpted).to be_kind_of(DummyUser)
|
32
|
+
expect(sculpted.id).to eq(object['id'])
|
33
|
+
expect(sculpted.name).to eq(object['name'])
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'with a scope' do
|
38
|
+
before do
|
39
|
+
map.mapping DummyUser
|
40
|
+
map.property :id, scopes: [:read]
|
41
|
+
map.property :name, scopes: [:create]
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'returns a coerced user' do
|
45
|
+
sculptor = Kartograph::Sculptor.new(object, map)
|
46
|
+
sculpted = sculptor.sculpt(:read)
|
47
|
+
|
48
|
+
expect(sculpted).to be_kind_of(DummyUser)
|
49
|
+
expect(sculpted.id).to eq(object['id'])
|
50
|
+
expect(sculpted.name).to be_nil
|
51
|
+
end
|
52
|
+
|
53
|
+
context 'for nested properties' do
|
54
|
+
let(:object) { super().merge('comment' => { 'id' => 123, 'text' => 'hello' }) }
|
55
|
+
|
56
|
+
before do
|
57
|
+
map.property :comment, scopes: [:read] do
|
58
|
+
mapping DummyComment
|
59
|
+
|
60
|
+
property :id, scopes: [:read]
|
61
|
+
property :text, scopes: [:create]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'returns the nested objects with scoped properties set' do
|
66
|
+
sculptor = Kartograph::Sculptor.new(object, map)
|
67
|
+
sculpted = sculptor.sculpt(:read)
|
68
|
+
|
69
|
+
expect(sculpted.comment).to be_kind_of(DummyComment)
|
70
|
+
expect(sculpted.comment.id).to eq(123)
|
71
|
+
expect(sculpted.comment.text).to be_nil
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'kartograph'
|
2
|
+
|
3
|
+
Dir['./spec/support/**/*.rb'].each {|f| load f }
|
4
|
+
|
5
|
+
RSpec.configure do |config|
|
6
|
+
# The settings below are suggested to provide a good initial experience
|
7
|
+
# with RSpec, but feel free to customize to your heart's content.
|
8
|
+
=begin
|
9
|
+
# These two settings work together to allow you to limit a spec run
|
10
|
+
# to individual examples or groups you care about by tagging them with
|
11
|
+
# `:focus` metadata. When nothing is tagged with `:focus`, all examples
|
12
|
+
# get run.
|
13
|
+
config.filter_run :focus
|
14
|
+
config.run_all_when_everything_filtered = true
|
15
|
+
|
16
|
+
# Many RSpec users commonly either run the entire suite or an individual
|
17
|
+
# file, and it's useful to allow more verbose output when running an
|
18
|
+
# individual spec file.
|
19
|
+
if config.files_to_run.one?
|
20
|
+
# Use the documentation formatter for detailed output,
|
21
|
+
# unless a formatter has already been configured
|
22
|
+
# (e.g. via a command-line flag).
|
23
|
+
config.default_formatter = 'doc'
|
24
|
+
end
|
25
|
+
|
26
|
+
# Print the 10 slowest examples and example groups at the
|
27
|
+
# end of the spec run, to help surface which specs are running
|
28
|
+
# particularly slow.
|
29
|
+
config.profile_examples = 10
|
30
|
+
|
31
|
+
# Run specs in random order to surface order dependencies. If you find an
|
32
|
+
# order dependency and want to debug it, you can fix the order by providing
|
33
|
+
# the seed, which is printed after each run.
|
34
|
+
# --seed 1234
|
35
|
+
config.order = :random
|
36
|
+
|
37
|
+
# Seed global randomization in this process using the `--seed` CLI option.
|
38
|
+
# Setting this allows you to use `--seed` to deterministically reproduce
|
39
|
+
# test failures related to randomization by passing the same `--seed` value
|
40
|
+
# as the one that triggered the failure.
|
41
|
+
Kernel.srand config.seed
|
42
|
+
|
43
|
+
# rspec-expectations config goes here. You can use an alternate
|
44
|
+
# assertion/expectation library such as wrong or the stdlib/minitest
|
45
|
+
# assertions if you prefer.
|
46
|
+
config.expect_with :rspec do |expectations|
|
47
|
+
# Enable only the newer, non-monkey-patching expect syntax.
|
48
|
+
# For more details, see:
|
49
|
+
# - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
|
50
|
+
expectations.syntax = :expect
|
51
|
+
end
|
52
|
+
|
53
|
+
# rspec-mocks config goes here. You can use an alternate test double
|
54
|
+
# library (such as bogus or mocha) by changing the `mock_with` option here.
|
55
|
+
config.mock_with :rspec do |mocks|
|
56
|
+
# Enable only the newer, non-monkey-patching expect syntax.
|
57
|
+
# For more details, see:
|
58
|
+
# - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
|
59
|
+
mocks.syntax = :expect
|
60
|
+
|
61
|
+
# Prevents you from mocking or stubbing a method that does not exist on
|
62
|
+
# a real object. This is generally recommended.
|
63
|
+
mocks.verify_partial_doubles = true
|
64
|
+
end
|
65
|
+
=end
|
66
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
shared_context "DSL Objects" do
|
2
|
+
let(:object) { double('object', id: 1066, name: 'Bruce (the dude from Finding Nemo)') }
|
3
|
+
let(:mapped) do
|
4
|
+
Class.new do
|
5
|
+
include Kartograph::DSL
|
6
|
+
|
7
|
+
kartograph do
|
8
|
+
mapping DummyUser
|
9
|
+
|
10
|
+
property :id, scopes: [:read]
|
11
|
+
property :name, scopes: [:read, :create]
|
12
|
+
|
13
|
+
property :comments, plural: true do
|
14
|
+
mapping DummyComment
|
15
|
+
|
16
|
+
property :id, scopes: [:read]
|
17
|
+
property :text, scopes: [:read, :create]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kartograph
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Ross
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-07-
|
11
|
+
date: 2014-07-31 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: 3.0.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: pry
|
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
|
description: Short Description
|
56
70
|
email:
|
57
71
|
- rross@digitalocean.com
|
@@ -60,13 +74,34 @@ extensions: []
|
|
60
74
|
extra_rdoc_files: []
|
61
75
|
files:
|
62
76
|
- .gitignore
|
77
|
+
- .rspec
|
63
78
|
- Gemfile
|
64
79
|
- LICENSE.txt
|
65
80
|
- README.md
|
66
81
|
- Rakefile
|
82
|
+
- examples/domains.rb
|
83
|
+
- examples/representation_for.rb
|
67
84
|
- kartograph.gemspec
|
68
85
|
- lib/kartograph.rb
|
86
|
+
- lib/kartograph/artist.rb
|
87
|
+
- lib/kartograph/dsl.rb
|
88
|
+
- lib/kartograph/map.rb
|
89
|
+
- lib/kartograph/property.rb
|
90
|
+
- lib/kartograph/property_collection.rb
|
91
|
+
- lib/kartograph/root_key.rb
|
92
|
+
- lib/kartograph/sculptor.rb
|
69
93
|
- lib/kartograph/version.rb
|
94
|
+
- spec/lib/kartograph/artist_spec.rb
|
95
|
+
- spec/lib/kartograph/dsl_spec.rb
|
96
|
+
- spec/lib/kartograph/map_spec.rb
|
97
|
+
- spec/lib/kartograph/property_collection_spec.rb
|
98
|
+
- spec/lib/kartograph/property_spec.rb
|
99
|
+
- spec/lib/kartograph/root_key_spec.rb
|
100
|
+
- spec/lib/kartograph/sculptor_spec.rb
|
101
|
+
- spec/spec_helper.rb
|
102
|
+
- spec/support/dsl_contexts.rb
|
103
|
+
- spec/support/dummy_comment.rb
|
104
|
+
- spec/support/dummy_user.rb
|
70
105
|
homepage: ''
|
71
106
|
licenses:
|
72
107
|
- MIT
|
@@ -91,4 +126,15 @@ rubygems_version: 2.2.2
|
|
91
126
|
signing_key:
|
92
127
|
specification_version: 4
|
93
128
|
summary: Short Summary
|
94
|
-
test_files:
|
129
|
+
test_files:
|
130
|
+
- spec/lib/kartograph/artist_spec.rb
|
131
|
+
- spec/lib/kartograph/dsl_spec.rb
|
132
|
+
- spec/lib/kartograph/map_spec.rb
|
133
|
+
- spec/lib/kartograph/property_collection_spec.rb
|
134
|
+
- spec/lib/kartograph/property_spec.rb
|
135
|
+
- spec/lib/kartograph/root_key_spec.rb
|
136
|
+
- spec/lib/kartograph/sculptor_spec.rb
|
137
|
+
- spec/spec_helper.rb
|
138
|
+
- spec/support/dsl_contexts.rb
|
139
|
+
- spec/support/dummy_comment.rb
|
140
|
+
- spec/support/dummy_user.rb
|