Class: Attributor::Collection

Inherits:
Array
  • Object
show all
Includes:
Container, Dumpable
Defined in:
lib/attributor/types/collection.rb

Direct Known Subclasses

CSV

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Container

included

Class Method Details

.check_option!(name, definition) ⇒ Object



146
147
148
149
150
151
152
153
154
155
156
# File 'lib/attributor/types/collection.rb', line 146

def self.check_option!(name, definition)
  # TODO: support more options like :max_size
  case name
  when :reference
  when :member_options
  else
    return :unknown
  end

  :ok
end

.construct(constructor_block, options) ⇒ Object



129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/attributor/types/collection.rb', line 129

def self.construct(constructor_block, options)
  member_options =  (options[:member_options]  || {} ).clone
  if options.has_key?(:reference) && !member_options.has_key?(:reference)
    member_options[:reference] = options[:reference]
  end

  # create the member_attribute, passing in our member_type and whatever constructor_block is.
  # that in turn will call construct on the type if applicable.
  @member_attribute = Attributor::Attribute.new self.member_type, member_options, &constructor_block

  # overwrite our type with whatever type comes out of the attribute
  @member_type = @member_attribute.type

  return self
end

.constructable?Boolean

Returns:



124
125
126
# File 'lib/attributor/types/collection.rb', line 124

def self.constructable?
  true
end

.decode_string(value, context) ⇒ Object



105
106
107
# File 'lib/attributor/types/collection.rb', line 105

def self.decode_string(value,context)
  decode_json(value,context)
end

.describe(shallow = false, example: nil) ⇒ Object



115
116
117
118
119
120
121
# File 'lib/attributor/types/collection.rb', line 115

def self.describe(shallow=false, example: nil)
  hash = super(shallow)
  hash[:options] = {} unless hash[:options]
  member_example = example.first if example
  hash[:member_attribute] = self.member_attribute.describe(true, example: member_example )
  hash
end

.dump(values, **opts) ⇒ Object



110
111
112
113
# File 'lib/attributor/types/collection.rb', line 110

def self.dump(values, **opts)
  return nil if values.nil?
  values.collect { |value| member_attribute.dump(value,opts) }
end

.example(context = nil, options: {}) ⇒ Object

generates an example Collection

Returns:

  • An Array of native type objects conforming to the specified member_type



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/attributor/types/collection.rb', line 65

def self.example(context=nil, options: {})
  result = []
  size = options[:size] || (rand(3) + 1)
  size = [*size].sample if size.is_a?(Range)

  context ||= ["Collection-#{result.object_id}"]
  context = Array(context)

  # avoid infinite recursion in example generation
  example_depth = context.size
  size = 0 if example_depth > Hash::MAX_EXAMPLE_DEPTH

  size.times do |i|
    subcontext = context + ["at(#{i})"]
    result << self.member_attribute.example(subcontext)
  end

  self.new(result)
end

.familyObject



46
47
48
# File 'lib/attributor/types/collection.rb', line 46

def self.family
  'array'
end

.inherited(klass) ⇒ Object



27
28
29
30
31
# File 'lib/attributor/types/collection.rb', line 27

def self.inherited(klass)
  klass.instance_eval do
    @options = {}
  end
end

.load(value, context = Attributor::DEFAULT_ROOT_CONTEXT, **options) ⇒ Object

The incoming value should be array-like here, so the only decoding that we need to do is from the members (if there’s an :member_type defined option).



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/attributor/types/collection.rb', line 88

def self.load(value,context=Attributor::DEFAULT_ROOT_CONTEXT, **options)
  if value.nil?
    return nil
  elsif value.is_a?(Enumerable)
    loaded_value = value
  elsif value.is_a?(::String)
    loaded_value = decode_string(value,context)
  elsif value.respond_to?(:to_a)
    loaded_value = value.to_a
  else
    raise Attributor::IncompatibleTypeError, context: context, value_type: value.class, type: self
  end

  self.new(loaded_value.collect { |member| self.member_attribute.load(member,context) })
end

.member_attributeObject



54
55
56
57
58
59
60
# File 'lib/attributor/types/collection.rb', line 54

def self.member_attribute
  @member_attribute ||= begin
    self.construct(nil,{})

    @member_attribute
  end
end

.member_typeObject



50
51
52
# File 'lib/attributor/types/collection.rb', line 50

def self.member_type
  @member_type ||= Attributor::Object
end

.native_typeObject



38
39
40
# File 'lib/attributor/types/collection.rb', line 38

def self.native_type
  self
end

.of(type) ⇒ Object

Returns anonymous class with specified type of collection members.

Examples:

Collection.of(Integer)

Parameters:

  • type (Attributor::Type)

    optional, defines the type of all collection members

Returns:

  • anonymous class with specified type of collection members



15
16
17
18
19
20
21
22
23
# File 'lib/attributor/types/collection.rb', line 15

def self.of(type)
  resolved_type = Attributor.resolve_type(type)
  unless resolved_type.ancestors.include?(Attributor::Type)
    raise Attributor::AttributorException.new("Collections can only have members that are Attributor::Types")
  end
  ::Class.new(self) do
    @member_type = resolved_type
  end
end

.optionsObject



33
34
35
# File 'lib/attributor/types/collection.rb', line 33

def self.options
  @options
end

.valid_type?(type) ⇒ Boolean

Returns:



42
43
44
# File 'lib/attributor/types/collection.rb', line 42

def self.valid_type?(type)
  type.kind_of?(self) || type.kind_of?(::Enumerable)
end

.validate(object, context = Attributor::DEFAULT_ROOT_CONTEXT, attribute = nil) ⇒ Object

Parameters:

  • object (Collection)

    Collection instance to validate.



159
160
161
162
163
164
165
166
167
# File 'lib/attributor/types/collection.rb', line 159

def self.validate(object, context=Attributor::DEFAULT_ROOT_CONTEXT, attribute=nil)
  context = [context] if context.is_a? ::String

  unless object.kind_of?(self)
    raise ArgumentError, "#{self.name} can not validate object of type #{object.class.name} for #{Attributor.humanize_context(context)}."
  end

  object.validate(context)
end

.validate_options(value, context, attribute) ⇒ Object



169
170
171
172
# File 'lib/attributor/types/collection.rb', line 169

def self.validate_options( value, context, attribute )
  errors = []
  errors
end

Instance Method Details

#dump(**opts) ⇒ Object



183
184
185
# File 'lib/attributor/types/collection.rb', line 183

def dump(**opts)
  self.collect { |value| self.class.member_attribute.dump(value,opts) }
end

#validate(context = Attributor::DEFAULT_ROOT_CONTEXT) ⇒ Object



175
176
177
178
179
180
# File 'lib/attributor/types/collection.rb', line 175

def validate(context=Attributor::DEFAULT_ROOT_CONTEXT)
  self.each_with_index.collect do |value, i|
    subcontext = context + ["at(#{i})"]
    self.class.member_attribute.validate(value, subcontext)
  end.flatten.compact
end