Class: Ripple::Association

Inherits:
Object show all
Includes:
Translation
Defined in:
lib/ripple/associations.rb

Overview

The “reflection” for an association - metadata about how it is configured.

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Translation

#i18n_scope

Constructor Details

#initialize(type, name, options = {}) ⇒ Association

association options :using, :class_name, :class, :extend, options that may be added :validate



201
202
203
# File 'lib/ripple/associations.rb', line 201

def initialize(type, name, options={})
  @type, @name, @options = type, name, options.to_options
end

Instance Attribute Details

#nameObject (readonly)

Returns the value of attribute name.



196
197
198
# File 'lib/ripple/associations.rb', line 196

def name
  @name
end

#optionsObject (readonly)

Returns the value of attribute options.



196
197
198
# File 'lib/ripple/associations.rb', line 196

def options
  @options
end

#typeObject (readonly)

Returns the value of attribute type.



196
197
198
# File 'lib/ripple/associations.rb', line 196

def type
  @type
end

Instance Method Details

#bucket_nameObject



291
292
293
# File 'lib/ripple/associations.rb', line 291

def bucket_name
  polymorphic? ? '_' : klass.bucket_name
end

#class_nameObject

Returns String The class name of the associated object(s).

Returns:

  • String The class name of the associated object(s)



216
217
218
219
220
221
222
223
224
225
226
227
# File 'lib/ripple/associations.rb', line 216

def class_name
  @class_name ||= case
                  when @options[:class_name]
                    @options[:class_name]
                  when @options[:class]
                    @options[:class].to_s
                  when many?
                    @name.to_s.classify
                  else
                    @name.to_s.camelize
                  end
end

#define_callbacks_on(klass) ⇒ Object



350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
# File 'lib/ripple/associations.rb', line 350

def define_callbacks_on(klass)
  _association = self

  klass.before_save do
    if _association.linked? && !@_in_save_loaded_documents_callback
      @_in_save_loaded_documents_callback = true

      begin
        send(_association.name).loaded_documents.each do |document|
          document.save if document.new? || document.changed?
        end
      ensure
        remove_instance_variable(:@_in_save_loaded_documents_callback)
      end
    end
  end
end

#embedded?true, false

Returns Is the associated class an EmbeddedDocument.

Returns:

  • (true, false)

    Is the associated class an EmbeddedDocument



245
246
247
# File 'lib/ripple/associations.rb', line 245

def embedded?
  klass.embeddable?
end

#ivarString

Returns the instance variable in the owner where the association will be stored.

Returns:

  • (String)

    the instance variable in the owner where the association will be stored



266
267
268
# File 'lib/ripple/associations.rb', line 266

def ivar
  "@_#{name}"
end

#klassClass

Returns The class of the associated object(s).

Returns:

  • (Class)

    The class of the associated object(s)



230
231
232
# File 'lib/ripple/associations.rb', line 230

def klass
  @klass ||= discover_class
end

Returns a filter proc to be used with Enumerable#select for collecting links that belong to this association (only when #linked? is true).

Returns:

  • (Proc)

    a filter proc to be used with Enumerable#select for collecting links that belong to this association (only when #linked? is true)



282
283
284
# File 'lib/ripple/associations.rb', line 282

def link_filter
  linked? ? lambda {|link| link.tag == link_tag } : lambda {|_| false }
end

Returns when #linked? is true, a specification for which links to follow to retrieve the associated documents.

Returns:

  • (Riak::WalkSpec)

    when #linked? is true, a specification for which links to follow to retrieve the associated documents



296
297
298
299
300
301
302
303
304
# File 'lib/ripple/associations.rb', line 296

def link_spec
  # TODO: support transitive linked associations
  if linked?
    tag = name.to_s
    Riak::WalkSpec.new(:tag => tag, :bucket => bucket_name)
  else
    nil
  end
end

Returns when #linked? is true, the tag for outgoing links.

Returns:

  • (String, nil)

    when #linked? is true, the tag for outgoing links



287
288
289
# File 'lib/ripple/associations.rb', line 287

def link_tag
  linked? ? Array(link_spec).first.tag : nil
end

#linked?true, false

Returns Does the association use links.

Returns:

  • (true, false)

    Does the association use links



256
257
258
# File 'lib/ripple/associations.rb', line 256

def linked?
  using == :linked
end

#many?true, false

Returns Is the cardinality of the association > 1.

Returns:

  • (true, false)

    Is the cardinality of the association > 1



235
236
237
# File 'lib/ripple/associations.rb', line 235

def many?
  @type == :many
end

#one?true, false

Returns Is the cardinality of the association == 1.

Returns:

  • (true, false)

    Is the cardinality of the association == 1



240
241
242
# File 'lib/ripple/associations.rb', line 240

def one?
  @type == :one
end

#polymorphic?true, false

TODO: Polymorphic not supported

Returns:

  • (true, false)

    Does the association support more than one associated class



251
252
253
# File 'lib/ripple/associations.rb', line 251

def polymorphic?
  false
end

#proxy_classClass

Returns the association proxy class.

Returns:

  • (Class)

    the association proxy class



271
272
273
# File 'lib/ripple/associations.rb', line 271

def proxy_class
  @proxy_class ||= proxy_class_name.constantize
end

#proxy_class_nameString

Returns the class name of the association proxy.

Returns:

  • (String)

    the class name of the association proxy



276
277
278
279
# File 'lib/ripple/associations.rb', line 276

def proxy_class_name
  klass_name = (many? ? 'Many' : 'One') + using.to_s.camelize + ('Polymorphic' if polymorphic?).to_s + 'Proxy'
  "Ripple::Associations::#{klass_name}"
end

#setup_on(model) ⇒ Object



338
339
340
341
342
343
344
345
346
347
348
# File 'lib/ripple/associations.rb', line 338

def setup_on(model)
  @model = model
  define_callbacks_on(model)
  if uses_search?
    klass.before_save do |o|
      unless o.class.bucket.is_indexed?
        o.class.bucket.enable_index!
      end
    end
  end
end

#stored_key?true, false

Returns Does the association use stored_key.

Returns:

  • (true, false)

    Does the association use stored_key



261
262
263
# File 'lib/ripple/associations.rb', line 261

def stored_key?
  using == :stored_key
end

#type_matches?(value) ⇒ Boolean

Returns:



323
324
325
326
327
328
329
330
331
332
# File 'lib/ripple/associations.rb', line 323

def type_matches?(value)
  case
  when polymorphic?
    one? || Array === value
  when many?
    Array === value && value.all? {|d| (embedded? && Hash === d) || klass === d }
  when one?
    value.nil? || (embedded? && Hash === value) || value.kind_of?(klass)
  end
end

#uses_search?Boolean

Returns:



334
335
336
# File 'lib/ripple/associations.rb', line 334

def uses_search?
  (options[:using] == :reference)
end

#usingSymbol

Returns which method is used for representing the association - currently only supports :embedded and :linked.

Returns:

  • (Symbol)

    which method is used for representing the association - currently only supports :embedded and :linked



307
308
309
# File 'lib/ripple/associations.rb', line 307

def using
  @using ||= options[:using] || (embedded? ? :embedded : :linked)
end

#validate!(owner) ⇒ Object



205
206
207
208
209
210
211
212
213
# File 'lib/ripple/associations.rb', line 205

def validate!(owner)
  # TODO: Refactor this into an association subclass. See also GH #284
  if @options[:using] == :stored_key
    single_name = ActiveSupport::Inflector.singularize(@name.to_s)
    prop_name = "#{single_name}_key"
    prop_name << "s" if many?
    raise ArgumentError, t('stored_key_requires_property', :name => prop_name) unless owner.properties.include?(prop_name)
  end
end

#verify_type!(value, owner) ⇒ Object

Raises:

  • (ArgumentError)

    if the value does not match the class of the association



312
313
314
315
316
317
318
319
320
# File 'lib/ripple/associations.rb', line 312

def verify_type!(value, owner)
  unless type_matches?(value)
    raise ArgumentError.new(t('invalid_association_value',
                              :name => name,
                              :owner => owner.inspect,
                              :klass => polymorphic? ? "<polymorphic>" : klass.name,
                              :value => value.inspect))
  end
end