Module: PlainRecord::Associations

Included in:
Model
Defined in:
lib/plain_record/associations.rb

Overview

Extention for model to store or have link to another model. There is two type of association.

Virtual property

In virtual method this definer create only link to another model. When you try to use this virtual property, model will find association object by rules in map.

Rules in map is only Hash with model properties in key and association properties in value. For example, if model contain name property and association must have post_name with same value, map will be { :name => :post_name }.

If you didn’t set map definer will try to find it automatically: it will find in model and association class all property pairs, what have name like propertymodel_property. For example, if model Post have property name and Comment have post_name, you may not set map – definer will find it automatically.

class Review
  include PlainRecord::Resource

  entry_in 'reviews/*.md'

  virtual  :author, one(Author)
  property :author_login
  text     :review
end

class Author
  include PlainRecord::Resource

  list_in 'authors.yml'

  virtual  :reviews, many(Review)
  property :login
  property :name
end

Real property

If you will use this definer in property method, association object data will store in you model file. For example model:

class Movie
  include PlainRecord::Resource

  property :title
  property :genre
  property :release_year
end

class Tag
  include PlainRecord::Resource
  property :name
end

class Review
  include PlainRecord::Resource

  entry_in 'reviews/*.md'

  property :author
  property :movie, one(Movie)
  property :tags, many(Tag)
  text     :review
end

will be store as:

author: John Smith 
movie:
  title: Watchmen
  genre: action
  release_year: 2009
tags:
- name: Great movies
- name: Comics
---
Movie is great!

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Attribute Details

#association_cacheObject

Hash with cached values for virtual associations.



105
106
107
# File 'lib/plain_record/associations.rb', line 105

def association_cache
  @association_cache
end

#association_mapsObject

Hash with map for virtual associations.



102
103
104
# File 'lib/plain_record/associations.rb', line 102

def association_maps
  @association_maps
end

Class Method Details

Define that virtual property name in klass contain links to model witch are finded by map.



246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
# File 'lib/plain_record/associations.rb', line 246

def define_link_many(klass, model, name, map)
  klass.association_cache ||= { }
  klass.association_maps  ||= { }
  klass.association_maps[name] = map

  klass.class_eval <<-EOS, __FILE__, __LINE__
    def #{name}
      unless self.class.association_cache[:#{name}]
        search = Hash[
          self.class.association_maps[:#{name}].map do |from, to|
            [from, send(to)]
          end]
        self.class.association_cache[:#{name}] = AssociationProxy.new(
            #{model}.all(search), self, :#{name})
      end
      self.class.association_cache[:#{name}]
    end
    def #{name}=(values)
      self.class.association_cache[:#{name}] = AssociationProxy.link(
          values, self, :#{name})
    end
  EOS
end

Define that virtual property name in klass contain link to model witch is finded by map.



219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'lib/plain_record/associations.rb', line 219

def define_link_one(klass, model, name, map)
  klass.association_cache ||= { }
  klass.association_maps  ||= { }
  klass.association_maps[name] = map

  klass.class_eval <<-EOS, __FILE__, __LINE__
    def #{name}
      unless self.class.association_cache[:#{name}]
        search = Hash[
          self.class.association_maps[:#{name}].map do |from, to|
            [to, send(from)]
          end]
        self.class.association_cache[:#{name}] = #{model}.first(search)
      end
      self.class.association_cache[:#{name}]
    end
    def #{name}=(value)
      self.class.association_maps[:#{name}].each do |from, to|
        value.send(to.to_s + '=', send(from))
      end
      self.class.association_cache[:#{name}] = value
    end
  EOS
end

.define_real_many(klass, property, model) ⇒ Object

Define, that property in klass contain in file array of data from model objects.



169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/plain_record/associations.rb', line 169

def define_real_many(klass, property, model)
  name = property.to_s
  klass.after :load do |result, entry|
    if entry.data[name].is_a? Enumerable
      entry.data[name].map! { |i| model.new(entry.file, i) }
    else
      entry.data[name] = []
    end
    result
  end
  klass.before :save do |entry|
    if entry.data[name].empty?
      entry.data.delete(name)
    else
      entry.data[name].map! do |obj|
        model.call_before_callbacks(:save, [obj])
        obj.data
      end
    end
  end
  klass.after :save do |result, entry|
    entry.data[name].map! { |i| model.new(entry.file, i) }
    entry.data[name].each do |i|
      model.call_after_callbacks(:save, nil, [i])
    end
    result
  end
end

.define_real_one(klass, property, model) ⇒ Object

Define, that property in klass contain in file data from model.



150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# File 'lib/plain_record/associations.rb', line 150

def define_real_one(klass, property, model)
  name = property.to_s
  klass.after :load do |result, entry|
    entry.data[name] = model.new(entry.file, entry.data[name])
    result
  end
  klass.before :save do |entry|
    model.call_before_callbacks(:save, [entry.data[name]])
    entry.data[name] = entry.data[name]
  end
  klass.after :save do |result, entry|
    entry.data[name] = model.new(entry.file, entry.data[name])
    model.call_after_callbacks(:save, nil, [entry.data[name]])
    result
  end
end

.map(from, to, prefix) ⇒ Object

Find properties pairs in from and to models, witch is like prefix_fromto.

For example, if Comment contain post_name property and Post contain name:

Associations.map(Comment, Post, :post) #=> { :post_name => :name }


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

def map(from, to, prefix)
  from_fields = (from.properties + from.virtuals).map { |i| i.to_s }
  mapped = { }
  (to.properties + to.virtuals).each do |to_field|
    from_field = prefix + to_field.to_s
    if from_fields.include? from_field
      mapped[from_field.to_sym] = to_field
    end
  end
  mapped
end