Module: DataMapper::Is::Remixable::RemixerClassMethods

Includes:
Assertions
Defined in:
lib/dm-is-remixable/is/remixable.rb

Overview

  • RemixerClassMethods

Description

Methods available to all DataMapper::Resources

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.included(base) ⇒ Object



73
# File 'lib/dm-is-remixable/is/remixable.rb', line 73

def self.included(base);end

Instance Method Details

#enhance(remixable, remixable_model = nil, &block) ⇒ Object

  • enhance

Description

Enhance a remix; allows nesting remixables, adding columns & functions to a remixed resource

Parameters

remixable <Symbol> Name of remixable to enhance (plural or singular name of is :remixable module)
model_class <Class, symbol, String> Name of the remixable generated Model Class.
block     <Proc>    Enhancements to perform

Examples

 When you have one remixable:

 class Video
   include DataMapper::Resource
   remix Comment

   enhance :comments do
     remix n, :votes        #This would result in something like YouTubes Voting comments up/down

     property :updated_at, DateTime

     def backwards; self.test.reverse; end;
   end

When you remixe the same remixable modules twice:

 class Article
   include DataMapper::Resource
   remix n, :taggings, :for => User, :model => "UserArticleTagging"
   remix n, :taggings, :for => Bot,  :model => "BotArticleTagging"

   enhance :taggings, "UserArticleTagging" do
     property :updated_at, DateTime
     belongs_to :user
     belongs_to :tag
   end

   enhance :taggings, "BotArticleTagging" do
     belongs_to :bot
     belongs_to :tag
   end


260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
# File 'lib/dm-is-remixable/is/remixable.rb', line 260

def enhance(remixable,remixable_model=nil, &block)
  inflector = DataMapper::Inflector

  # always use innermost singular underscored constant name
  remixable_name = inflector.singularize(remixable.to_s)
  remixable_name = inflector.underscore(remixable_name).to_sym
  class_name = if remixable_model.nil?
    @remixables[remixable_name].keys.first
  else
    name = inflector.demodulize(remixable_model.to_s)
    inflector.underscore(name).to_sym
  end

  model = @remixables[remixable_name][class_name][:model] unless @remixables[remixable_name][class_name].nil?

  unless model.nil?
    model.class_eval &block
  else
    raise Exception, "#{remixable} must be remixed with :model option as #{remixable_model} before it can be enhanced"
  end
end

#is_remixable?Boolean

Returns:

  • (Boolean)


75
76
77
# File 'lib/dm-is-remixable/is/remixable.rb', line 75

def is_remixable?
  @is_remixable ||= false
end

#remix(cardinality, remixable, options = {}) ⇒ Object

  • remix

Description

Remixes a Remixable Module

Parameters

cardinality <~Fixnum> 1, n, x ...
remixable   <Symbol> plural of remixable; i.e. Comment => :comments
options     <Hash>   options hash
                    :model      <String> Remixed Model name (Also creates a storage_name as tableize(:model))
                                This is the class that will be created from the Remixable Module
                                The storage_name can be changed via 'enhance' in the class that is remixing
                                Default: self.name.downcase + "_" + remixable.suffix.pluralize
                    :as         <String> Alters the name that the remixable items will be available through, this WILL NOT
                                create the standard accessor
                                Default: tableize(:class_name)
                    :for|:on    <String> Class name to join to through Remixable
                                This will create a M:M relationship THROUGH the remixable, rather than
                                a 1:M with the remixable
                    :via        <String> changes the name of the second id in a unary relationship
                                see example below; only used when remixing a module between the same class twice
                                ie: self.class.to_s == options[:for||:on]
                    :unique     <Boolean> Only works with :for|:on; creates a unique composite key
                                over the two table id's

Examples

Given: User (Class), Addressable (Module)

One-To-Many; Class-To-Remixable

remix n, :addressables, :model => "UserAddress", :as => "addresses"

Tables: users, user_addresses
Classes: User, UserAddress
  User.user_addresses << UserAddress.new => Raise No Method Exception since it was alias with :as
  User.addresses << UserAddress.new
--------------------------------------------
--------------------------------------------

Given: User (Class), Video (Class), Commentable (Module)

Many-To-Many; Class-To-Class through RemixableIntermediate (Video allows Commentable for User)

Video.remix n, :commentables
  :for        => 'User'    #:for & :on have same effect, just a choice of wording...
--------------------------------------------
--------------------------------------------

Given: User (Class), User (Class), Commentable (Module)

Many-To-Many Unary relationship between User & User through comments
User.remix n, :commentables, :as => "comments", :for => 'User', :via => "commentor"
=> This would create user_id and commentor_id as the


139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
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
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
# File 'lib/dm-is-remixable/is/remixable.rb', line 139

def remix(cardinality, remixable, options={})
  assert_kind_of 'remixable', Symbol, String, Module

  #A map for remixable names to Remixed Models
  @remixables = {} if @remixables.nil?

  # Allow nested modules to be remixable to better support using dm-is-remixable in gems
  # Example (from my upcoming dm-is-rateable gem)
  # remix n, "DataMapper::Is::Rateable::Rating", :as => :ratings
  remixable_module = case remixable
    when Symbol then DataMapper::Ext::Object.full_const_get(Object, DataMapper::Inflector.classify(remixable))
    when String then DataMapper::Ext::Object.full_const_get(Object, remixable)
    when Module then remixable
  end

  unless remixable_module.is_remixable?
    raise Exception, "#{remixable_module} is not remixable"
  end

  if options[:class_name]
    warn '+options[:class_name]+ is deprecated, use :model instead'
    options[:model] = options.delete(:class_name)
  end

  inflector = DataMapper::Inflector
  model = inflector.underscore(self.name)
  model = inflector.camelize(model + '_' + remixable_module.suffix)

  #Merge defaults/options
  options = {
    :as      => nil,
    :model   => model,
    :for     => nil,
    :on      => nil,
    :unique  => false,
    :via     => nil,
    :connect => false
  }.update(options)

  #Make sure the class hasn't been remixed already
  unless DataMapper::Ext::Object.full_const_defined?(inflector.classify(options[:model]))

    #Storage name of our remixed model
    options[:table_name] = DataMapper::Inflector.tableize(inflector.demodulize(options[:model]))

    #Other model to mix with in case of M:M through Remixable
    options[:other_model] = options[:for] || options[:on]

    DataMapper.logger.info "Generating Remixed Model: #{options[:model]}"
    model = generate_remixed_model(remixable_module, options)

    # map the remixable to the remixed model
    # since this will be used from 'enhance api' i think it makes perfect sense to
    # always refer to a remixable by its demodulized underscored constant name
    remixable_key = inflector.demodulize(remixable_module.name)
    remixable_key = inflector.underscore(remixable_key).to_sym
    populate_remixables_mapping(model, options.merge(:remixable_key => remixable_key))

    # attach RemixerClassMethods and RemixerInstanceMethods to remixer if defined by remixee
    if DataMapper::Ext::Object.full_const_defined? "#{remixable_module}::RemixerClassMethods"
      extend DataMapper::Ext::Object.full_const_get("#{remixable_module}::RemixerClassMethods")
    end

    if DataMapper::Ext::Object.full_const_defined? "#{remixable_module}::RemixerInstanceMethods"
      include DataMapper::Ext::Object.full_const_get("#{remixable_module}::RemixerInstanceMethods")
    end

    #Create relationships between Remixer and remixed class
    if options[:other_model]
      # M:M Class-To-Class w/ Remixable Module as intermediate table
      # has n and belongs_to (or One-To-Many)
      remix_many_to_many cardinality, model, options
    else
      # 1:M Class-To-Remixable
      # has n and belongs_to (or One-To-Many)
      remix_one_to_many cardinality, model, options
    end
  else
    DataMapper.logger.warn "#{__FILE__}:#{__LINE__} warning: already remixed constant #{options[:model]}"
  end
end

#remixablesObject

  • remixables

Description

Returns a hash of the remixables used by this class

Returns

<Hash> Remixable Class Name => Remixed Class Name


84
85
86
# File 'lib/dm-is-remixable/is/remixable.rb', line 84

def remixables
  @remixables
end