jei 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,12 +1,14 @@
1
1
  module Jei
2
- # @see http://jsonapi.org/format/#document-top-level
3
- class CollectionDataNode < DataNode
4
- # @param [Hash<Symbol, Object>] context
5
- def visit(context)
6
- context[:data] = children.map do |child|
7
- data = {}
8
- child.visit(data)
9
- data
2
+ module Nodes
3
+ # @see http://jsonapi.org/format/#document-top-level
4
+ class CollectionDataNode < DataNode
5
+ # @param [Hash<Symbol, Object>] context
6
+ def visit(context)
7
+ context[:data] = children.map do |child|
8
+ data = {}
9
+ child.visit(data)
10
+ data
11
+ end
10
12
  end
11
13
  end
12
14
  end
@@ -1,16 +1,18 @@
1
1
  module Jei
2
- # @see http://jsonapi.org/format/#document-top-level
3
- class DataNode < Node
4
- # @param [Hash<Symbol, Object>] context
5
- def visit(context)
6
- context[:data] =
7
- if children.empty?
8
- nil
9
- else
10
- data = {}
11
- children.each { |child| child.visit(data) }
12
- data
13
- end
2
+ module Nodes
3
+ # @see http://jsonapi.org/format/#document-top-level
4
+ class DataNode < Node
5
+ # @param [Hash<Symbol, Object>] context
6
+ def visit(context)
7
+ context[:data] =
8
+ if children.empty?
9
+ nil
10
+ else
11
+ data = {}
12
+ children.each { |child| child.visit(data) }
13
+ data
14
+ end
15
+ end
14
16
  end
15
17
  end
16
18
  end
@@ -1,9 +1,11 @@
1
1
  module Jei
2
- # @see http://jsonapi.org/format/1.0/#document-top-level
3
- class DocumentNode < Node
4
- # @param [Hash<Symbol, Object>] context
5
- def visit(context)
6
- children.each { |child| child.visit(context) }
2
+ module Nodes
3
+ # @see http://jsonapi.org/format/1.0/#document-top-level
4
+ class DocumentNode < Node
5
+ # @param [Hash<Symbol, Object>] context
6
+ def visit(context)
7
+ children.each { |child| child.visit(context) }
8
+ end
7
9
  end
8
10
  end
9
11
  end
@@ -1,11 +1,13 @@
1
1
  module Jei
2
- # @see http://jsonapi.org/format/1.0/#document-compound-documents
3
- class IncludedNode < Node
4
- def visit(context)
5
- context[:included] = children.map do |child|
6
- data = {}
7
- child.visit(data)
8
- data
2
+ module Nodes
3
+ # @see http://jsonapi.org/format/1.0/#document-compound-documents
4
+ class IncludedNode < Node
5
+ def visit(context)
6
+ context[:included] = children.map do |child|
7
+ data = {}
8
+ child.visit(data)
9
+ data
10
+ end
9
11
  end
10
12
  end
11
13
  end
@@ -1,9 +1,11 @@
1
1
  module Jei
2
- # @see http://jsonapi.org/format/1.0/#document-jsonapi-object
3
- class JSONAPINode < Node
4
- # @param [Hash<Symbol, Object>] context
5
- def visit(context)
6
- context[:jsonapi] = { version: Document::VERSION }
2
+ module Nodes
3
+ # @see http://jsonapi.org/format/1.0/#document-jsonapi-object
4
+ class JSONAPINode < Node
5
+ # @param [Hash<Symbol, Object>] context
6
+ def visit(context)
7
+ context[:jsonapi] = { version: Document::VERSION }
8
+ end
7
9
  end
8
10
  end
9
11
  end
@@ -1,20 +1,22 @@
1
1
  module Jei
2
- # @see http://jsonapi.org/format/1.0/#document-links
3
- class LinkNode < Node
4
- # @param [Link] link
5
- def initialize(link)
6
- super()
7
- @link = link
8
- end
2
+ module Nodes
3
+ # @see http://jsonapi.org/format/1.0/#document-links
4
+ class LinkNode < Node
5
+ # @param [Link] link
6
+ def initialize(link)
7
+ super()
8
+ @link = link
9
+ end
9
10
 
10
- # @param [Hash<Symbol, Object>] context
11
- def visit(context)
12
- context[@link.name] =
13
- if @link.meta.any?
14
- { href: @link.href, meta: @link.meta }
15
- else
16
- @link.href
17
- end
11
+ # @param [Hash<Symbol, Object>] context
12
+ def visit(context)
13
+ context[@link.name] =
14
+ if @link.meta.any?
15
+ { href: @link.href, meta: @link.meta }
16
+ else
17
+ @link.href
18
+ end
19
+ end
18
20
  end
19
21
  end
20
22
  end
@@ -1,11 +1,13 @@
1
1
  module Jei
2
- # @see http://jsonapi.org/format/1.0/#document-links
3
- class LinksNode < Node
4
- # @param [Hash<Symbol, Object>] context
5
- def visit(context)
6
- data = {}
7
- children.each { |child| child.visit(data) }
8
- context[:links] = data
2
+ module Nodes
3
+ # @see http://jsonapi.org/format/1.0/#document-links
4
+ class LinksNode < Node
5
+ # @param [Hash<Symbol, Object>] context
6
+ def visit(context)
7
+ data = {}
8
+ children.each { |child| child.visit(data) }
9
+ context[:links] = data
10
+ end
9
11
  end
10
12
  end
11
13
  end
@@ -1,15 +1,17 @@
1
1
  module Jei
2
- # @see http://jsonapi.org/format/1.0/#document-meta
3
- class MetaNode < Node
4
- # param [Hash<Symbol, Object>] meta
5
- def initialize(meta)
6
- super()
7
- @meta = meta
8
- end
2
+ module Nodes
3
+ # @see http://jsonapi.org/format/1.0/#document-meta
4
+ class MetaNode < Node
5
+ # param [Hash<Symbol, Object>] meta
6
+ def initialize(meta)
7
+ super()
8
+ @meta = meta
9
+ end
9
10
 
10
- # @param [Hash<Symbol, Object>] context
11
- def visit(context)
12
- context[:meta] = @meta
11
+ # @param [Hash<Symbol, Object>] context
12
+ def visit(context)
13
+ context[:meta] = @meta
14
+ end
13
15
  end
14
16
  end
15
17
  end
@@ -1,17 +1,19 @@
1
1
  module Jei
2
- # @abstract
3
- class Node
4
- # @return [Array<Node>]
5
- attr_reader :children
2
+ module Nodes
3
+ # @abstract
4
+ class Node
5
+ # @return [Array<Node>]
6
+ attr_reader :children
6
7
 
7
- def initialize
8
- @children = []
9
- end
8
+ def initialize
9
+ @children = []
10
+ end
10
11
 
11
- # @abstract
12
- # @param [Hash<Symbol, Object>] _context
13
- def visit(_context)
14
- raise NotImplementedError
12
+ # @abstract
13
+ # @param [Hash<Symbol, Object>] _context
14
+ def visit(_context)
15
+ raise NotImplementedError
16
+ end
15
17
  end
16
18
  end
17
19
  end
@@ -1,17 +1,19 @@
1
1
  module Jei
2
- # @see http://jsonapi.org/format/1.0/#document-resource-object-relationships
3
- class RelationshipNode < Node
4
- # @param [Relationship] relationship
5
- def initialize(relationship)
6
- super()
7
- @relationship = relationship
8
- end
2
+ module Nodes
3
+ # @see http://jsonapi.org/format/1.0/#document-resource-object-relationships
4
+ class RelationshipNode < Node
5
+ # @param [Relationship] relationship
6
+ def initialize(relationship)
7
+ super()
8
+ @relationship = relationship
9
+ end
9
10
 
10
- # @param [Hash<Symbol, Object>] context
11
- def visit(context)
12
- data = {}
13
- children.each { |child| child.visit(data) }
14
- context[@relationship.name] = data
11
+ # @param [Hash<Symbol, Object>] context
12
+ def visit(context)
13
+ data = {}
14
+ children.each { |child| child.visit(data) }
15
+ context[@relationship.name] = data
16
+ end
15
17
  end
16
18
  end
17
19
  end
@@ -1,11 +1,13 @@
1
1
  module Jei
2
- # @see http://jsonapi.org/format/1.0/#document-resource-object-relationships
3
- class RelationshipsNode < Node
4
- # @param [Hash<Symbol, Object>] context
5
- def visit(context)
6
- relationships = {}
7
- children.each { |child| child.visit(relationships) }
8
- context[:relationships] = relationships
2
+ module Nodes
3
+ # @see http://jsonapi.org/format/1.0/#document-resource-object-relationships
4
+ class RelationshipsNode < Node
5
+ # @param [Hash<Symbol, Object>] context
6
+ def visit(context)
7
+ relationships = {}
8
+ children.each { |child| child.visit(relationships) }
9
+ context[:relationships] = relationships
10
+ end
9
11
  end
10
12
  end
11
13
  end
@@ -1,16 +1,18 @@
1
1
  module Jei
2
- # http://jsonapi.org/format/1.0/#document-resource-identifier-objects
3
- class ResourceIdentifierNode < Node
4
- # param [Serializer] serializer
5
- def initialize(serializer)
6
- super()
7
- @serializer = serializer
8
- end
2
+ module Nodes
3
+ # http://jsonapi.org/format/1.0/#document-resource-identifier-objects
4
+ class ResourceIdentifierNode < Node
5
+ # param [Serializer] serializer
6
+ def initialize(serializer)
7
+ super()
8
+ @serializer = serializer
9
+ end
9
10
 
10
- # @param [Hash<Symbol, Object>] context
11
- def visit(context)
12
- context[:id] = @serializer.id
13
- context[:type] = @serializer.type
11
+ # @param [Hash<Symbol, Object>] context
12
+ def visit(context)
13
+ context[:id] = @serializer.id
14
+ context[:type] = @serializer.type
15
+ end
14
16
  end
15
17
  end
16
18
  end
@@ -1,9 +1,11 @@
1
1
  module Jei
2
- # http://jsonapi.org/format/1.0/#document-resource-objects
3
- class ResourceNode < Node
4
- # @param [Hash<Symbol, Object>] context
5
- def visit(context)
6
- children.each { |child| child.visit(context) }
2
+ module Nodes
3
+ # http://jsonapi.org/format/1.0/#document-resource-objects
4
+ class ResourceNode < Node
5
+ # @param [Hash<Symbol, Object>] context
6
+ def visit(context)
7
+ children.each { |child| child.visit(context) }
8
+ end
7
9
  end
8
10
  end
9
11
  end
data/lib/jei/path.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  module Jei
2
2
  class Path
3
+ class NameError < Jei::Error; end
4
+
3
5
  PATH_SEPARATOR = ','
4
6
  NAME_SEPARATOR = '.'
5
7
 
@@ -15,11 +17,11 @@ module Jei
15
17
  end
16
18
 
17
19
  # @param [Array<Path>] paths
18
- # @param [Object] resource
19
- # @param [Set<Object>] resources
20
- def self.find(paths, resource, resources)
20
+ # @param [Serializer] serializer
21
+ # @param [Set<Serializer>] serializers
22
+ def self.find(paths, serializer, serializers)
21
23
  paths.each do |path|
22
- path.walk(resource, resources)
24
+ path.walk(serializer, serializers)
23
25
  end
24
26
  end
25
27
 
@@ -28,22 +30,21 @@ module Jei
28
30
  @names = names
29
31
  end
30
32
 
31
- # @param [Object] root
32
- # @param [Set<Object>] set
33
+ # @param [Serializer] serializer
34
+ # @param [Set<Serializer>] serializers
33
35
  # @param [Integer] level
34
- def walk(root, set = Set.new, level = 0)
36
+ def walk(serializer, serializers, level = 0)
35
37
  return if level >= @names.length
36
38
 
37
- serializer = Serializer.factory(root)
38
-
39
39
  name = @names[level]
40
40
  relationship = serializer.relationships[name]
41
+ raise NameError, "invalid relationship name `#{name}'" unless relationship
41
42
  resources = [*relationship.evaluate(serializer)]
42
43
 
43
- set.merge(resources)
44
-
45
44
  resources.each do |resource|
46
- walk(resource, set, level + 1)
45
+ serializer = Serializer.factory(resource, relationship.options[:serializer])
46
+ serializers << serializer
47
+ walk(serializer, serializers, level + 1)
47
48
  end
48
49
  end
49
50
  end
@@ -1,23 +1,26 @@
1
1
  module Jei
2
- class Relationship < Attribute
2
+ class Relationship < Field
3
3
  # @return [Hash<Symbol, Object>] options
4
4
  attr_reader :options
5
5
 
6
6
  # @param [Symbol] name
7
7
  # @param [Proc, Symbol] value
8
8
  # @param [Hash<Symbol, Object>] options
9
- # @option options [Boolean] :no_data Whether to exclude data from the
10
- # relationship.
11
- # @option options [Proc] :links A Proc that evaulates to a list of links.
9
+ # @option options [Boolean] :data (true) whether to include data in the
10
+ # relationship
11
+ # @option options [Proc] :links a `Proc` that evaulates to a list of links
12
+ # @option options [Class] :serializer override the default serializer used
13
+ # for related resources
12
14
  def initialize(name, value = name, options = {})
13
15
  super(name, value)
16
+ options[:data] = options.fetch(:data, true)
14
17
  @options = options
15
18
  end
16
19
 
17
20
  # @param [Serializer] serializer
18
21
  # @return [Array<Link>]
19
22
  def links(serializer)
20
- serializer.instance_eval(&options[:links])
23
+ serializer.instance_exec(&options[:links])
21
24
  end
22
25
  end
23
26
 
@@ -4,8 +4,8 @@ module Jei
4
4
  attr_reader :resource
5
5
 
6
6
  # @return [Hash<Symbol, Attribute>]
7
- def self.serialization_map
8
- @serialization_map ||= Hash.new { |h, k| h[k] = {} }
7
+ def self.fields
8
+ @fields ||= Hash.new { |h, k| h[k] = {} }
9
9
  end
10
10
 
11
11
  # @overload attributes(name, ...)
@@ -18,27 +18,46 @@ module Jei
18
18
  # @param [Symbol] name
19
19
  def self.attribute(name, &blk)
20
20
  value = block_given? ? blk : name
21
- serialization_map[:attributes][name] = Attribute.new(name, value)
21
+ fields[:attributes][name] = Attribute.new(name, value)
22
22
  end
23
23
 
24
24
  # @param [Symbol] name
25
+ # @param [Hash<Symbol, Object>] options
26
+ # @see Relationship#initialize
25
27
  def self.belongs_to(name, options = {}, &blk)
26
28
  value = block_given? ? blk : name
27
- serialization_map[:relationships][name] =
29
+ fields[:relationships][name] =
28
30
  BelongsToRelationship.new(name, value, options)
29
31
  end
30
32
 
31
33
  # @param [Symbol] name
34
+ # @param [Hash<Symbol, Object>] options
35
+ # @see Relationship#initialize
32
36
  def self.has_many(name, options = {}, &blk)
33
37
  value = block_given? ? blk : name
34
- serialization_map[:relationships][name] =
38
+ fields[:relationships][name] =
35
39
  HasManyRelationship.new(name, value, options)
36
40
  end
37
41
 
42
+ # Instantiates a new serializer based on the type of the given resource.
43
+ #
44
+ # This assumes serializer classes are defined in the global namespace. If
45
+ # not, a serializer class can be passed to override the lookup.
46
+ #
47
+ # @example
48
+ # artist = Artist.new
49
+ #
50
+ # serializer = Serializer.factory(artist)
51
+ # # => #<ArtistSerializer>
52
+ #
53
+ # serializer = Serializer.factory(artist, SimpleArtistSerializer)
54
+ # # => #<SimpleArtistSerializer>
55
+ #
56
+ # @param [Object] resource
57
+ # @param [Class] klass the class used instead of serializer lookup
38
58
  # @return [Serializer]
39
- def self.factory(resource)
40
- name = resource.class.name
41
- klass = const_get("#{name}Serializer")
59
+ def self.factory(resource, klass = nil)
60
+ klass ||= const_get("#{resource.class.name}Serializer")
42
61
  klass.new(resource)
43
62
  end
44
63
 
@@ -57,19 +76,58 @@ module Jei
57
76
  "#{resource.class.name.downcase}s"
58
77
  end
59
78
 
79
+ # @param [Array<Symbol>] fieldset
60
80
  # @return [Hash<Symbol, Attribute>]
61
- def attributes
62
- self.class.serialization_map[:attributes]
81
+ def attributes(fieldset = nil)
82
+ fields(:attributes, fieldset)
63
83
  end
64
84
 
85
+ # @param [Array<Symbol>] fieldset
65
86
  # @return [Hash<Symbol, Relationship>]
66
- def relationships
67
- self.class.serialization_map[:relationships]
87
+ def relationships(fieldset = nil)
88
+ fields(:relationships, fieldset)
68
89
  end
69
90
 
70
91
  # @return [Array<Link>, nil]
71
92
  def links
72
93
  nil
73
94
  end
95
+
96
+ # @return [Array<String>]
97
+ def key
98
+ [type, id]
99
+ end
100
+
101
+ # @return [Boolean]
102
+ def ==(other)
103
+ key == other.key
104
+ end
105
+ alias_method :eql?, :==
106
+
107
+ # @return [Fixnum]
108
+ def hash
109
+ key.hash
110
+ end
111
+
112
+ private
113
+
114
+ # @param [Symbol] type
115
+ # @param [Array<Symbol>] fieldset
116
+ # @return [Hash<Symbol, Field>]
117
+ def fields(type, fieldset = nil)
118
+ fields = self.class.fields[type]
119
+
120
+ if fieldset
121
+ slice = {}
122
+
123
+ fieldset.each do |name|
124
+ slice[name] = fields[name] if fields.has_key?(name)
125
+ end
126
+
127
+ slice
128
+ else
129
+ fields
130
+ end
131
+ end
74
132
  end
75
133
  end