Class: Dao::Conducer

Inherits:
Object
  • Object
show all
Extended by:
ActiveModel::Translation
Includes:
ActiveModel::Conversion, ActiveModel::Naming, Validations
Defined in:
lib/dao/conducer.rb,
lib/dao/conducer/autocrud.rb,
lib/dao/conducer/attributes.rb,
lib/dao/conducer/collection.rb,
lib/dao/conducer/active_model.rb,
lib/dao/conducer/view_support.rb,
lib/dao/conducer/callback_support.rb,
lib/dao/conducer/controller_support.rb

Defined Under Namespace

Modules: AutoCRUD Classes: Attributes, Collection

Constant Summary collapse

ViewSupport =
proc do
  include Tagz.globally

  class << Conducer
    include Tagz.globally

    def install_routes!
      url_helpers = Rails.application.try(:routes).try(:url_helpers)
      include(url_helpers) if url_helpers
      include(ActionView::Helpers) if defined?(ActionView::Helpers)
      extend(url_helpers) if url_helpers
      extend(ActionView::Helpers) if defined?(ActionView::Helpers)
    end
  end
end
CallbackSupport =
proc do
  include Wrap

  class << self
    def method_missing(method, *args, &block)
      case method.to_s
        when %r/\A(before|after)_(.*)\Z/
          lifecycle, method = $1, $2
          send(lifecycle, method, *args, &block)
        else
          super
      end
    end
  end
end
ControllerSupport =
proc do
##
#
  def controller
    unless defined?(@controller)
      set_controller(Conducer.controller)
    end
    @controller
  end

  def controller=(controller)
    set_controller(controller)
  end

  def set_controller(controller)
    @controller = controller
  ensure
    if defined?(default_url_options)
      [:protocol, :host, :port].each{|attr| default_url_options[attr] = @controller.request.send(attr)}
    end
    @action = Action.new((@controller.send(:action_name) || :index).to_s, self)
  end

  def request
    @controller.send(:request) if @controller
  end

  def request_method
    method = request.try(:method)
    method ? method.to_s.to_sym.upcase : nil
  end

##
#
  class Action < ::String
    fattr :conducer

    def initialize(action, conducer = nil)
      super(action.to_s.downcase.strip)
      @conducer = conducer
    end

    def action
      to_s
    end

    def ==(other)
      super(other.to_s)
    end

    Synonyms = {
      'new'    => 'create',
      'create' => 'new',

      'edit'   => 'update',
      'update' => 'edit'
    }

    def call(method, *args, &block)
      return unless conducer

      action_method = "#{ method }_for_#{ action }"

      return Dao.call(conducer, action_method, *args, &block) if conducer.respond_to?(action_method)

      if((synonym = Synonyms[action]))
        action_method = "#{ method }_for_#{ synonym }"
        return Dao.call(conducer, action_method, *args, &block) if conducer.respond_to?(action_method)
      end

      nil
    end
  end

  def action
    unless defined?(@action)
      set_action(:new)
    end
    @action
  end

  def set_action(action)
    unless action.is_a?(Action)
      action = Action.new(action)
    end
    action.conducer = self
    @action = action
  end

  def action=(action)
    set_action(action)
  end

##
#
  controller_delegates = %w(
    render
    render_to_string
  )

  controller_delegates.each do |method|
    module_eval <<-__, __FILE__, __LINE__
      def #{ method }(*args, &block)
        controller.#{ method }(*args, &block)
      end
    __
  end
end

Constants included from Validations

Validations::ClassMethods, Validations::InstanceMethods

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Validations

add, included

Constructor Details

#initialize(*args, &block) ⇒ Conducer

Returns a new instance of Conducer.



170
171
172
173
# File 'lib/dao/conducer.rb', line 170

def initialize(*args, &block)
  @initialize_overridden = false
  update_models(models) unless models.empty?
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method, *args, &block) ⇒ Object



348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
# File 'lib/dao/conducer.rb', line 348

def method_missing(method, *args, &block)
  re = /^([^=!?]+)([=!?])?$/imox

  matched, key, suffix = re.match(method.to_s).to_a
  
  case suffix
    when '='
      set(key, args.first)
    when '!'
      set(key, args.size > 0 ? args.first : true)
    when '?'
      has?(key)
    else
      case key
        when /^current_(.*)/
          Current.send($1)
        else
          has?(key) ? get(key) : super
      end
  end
end

Class Method Details

.autocrud!Object Also known as: crud!



4
5
6
# File 'lib/dao/conducer/autocrud.rb', line 4

def autocrud!
  include(Conducer::AutoCRUD)
end

.build_collection_class!Object



6
7
8
9
10
11
# File 'lib/dao/conducer/collection.rb', line 6

def build_collection_class!
  conducer_class = self
  collection_class = const_set(:Collection, Class.new(Collection){})
  collection_class.conducer_class = conducer_class
  conducer_class.collection_class = collection_class
end

.call(*args, &block) ⇒ Object



123
124
125
# File 'lib/dao/conducer.rb', line 123

def Conducer.call(*args, &block)
  self.for(*args, &block)
end

.collectionObject



17
18
19
# File 'lib/dao/conducer/collection.rb', line 17

def collection
  collection_class
end

.collection_for(models, *args, &block) ⇒ Object



13
14
15
# File 'lib/dao/conducer/collection.rb', line 13

def collection_for(models, *args, &block)
  collection_class.load(models, *args, &block)
end

.collection_nameObject Also known as: table_name



50
51
52
# File 'lib/dao/conducer/active_model.rb', line 50

def collection_name
  @collection_name ||= model_name.plural.to_s
end

.collection_name=(collection_name) ⇒ Object Also known as: set_collection_name, table_name=, set_table_name



55
56
57
# File 'lib/dao/conducer/active_model.rb', line 55

def collection_name=(collection_name)
  @collection_name = collection_name.to_s
end

.conduces(*args) ⇒ Object



73
74
75
76
77
78
79
# File 'lib/dao/conducer.rb', line 73

def conduces(*args)
  unless args.blank?
    @conduces = args.shift
    raise(ArgumentError, @conduces.inspect) unless @conduces.respond_to?(:model_name)
  end
  @conduces ||= nil
end

.conduces=(model) ⇒ Object



81
82
83
# File 'lib/dao/conducer.rb', line 81

def conduces=(model)
  conduces(model)
end

.conduces?(model) ⇒ Boolean

Returns:

  • (Boolean)


68
69
70
71
# File 'lib/dao/conducer.rb', line 68

def conduces?(model)
  model_class = model.is_a?(Class) ? model : model.class
  model_class.model_name == (self.conduces || self).model_name
end

.controllerObject



43
44
45
# File 'lib/dao/conducer.rb', line 43

def controller
  Dao.current_controller || Dao.mock_controller
end

.controller=(controller) ⇒ Object



47
48
49
# File 'lib/dao/conducer.rb', line 47

def controller=(controller)
  Dao.current_controller = controller
end

.currentObject



51
52
53
# File 'lib/dao/conducer.rb', line 51

def current
  @current ||= (defined?(::Current) ? ::Current : Map.new)
end

.default_model_nameObject



38
39
40
41
42
43
44
45
46
47
48
# File 'lib/dao/conducer/active_model.rb', line 38

def default_model_name
  return model_name_for('Conducer') if self == Dao::Conducer

  suffixes = /(Conducer|Resource|Importer|Presenter|Conductor|Cell)\Z/o

  name = self.name.split('::').last.to_s
  name.sub!(suffixes, '') unless name.sub(suffixes, '').blank?
  name.sub!(/(:|_)+$/, '')

  model_name_for(name)
end

.for(*args, &block) ⇒ Object



111
112
113
114
115
116
117
118
119
120
121
# File 'lib/dao/conducer.rb', line 111

def Conducer.for(*args, &block)
  action =
    case args.first
      when Action, Symbol, String
        args.shift.to_s
      else
        controller.send(:action_name).to_s
    end

  conducer = new(Action.new(action), *args, &block)
end

.inherited(other) ⇒ Object



22
23
24
25
26
27
28
# File 'lib/dao/conducer.rb', line 22

def inherited(other)
  super
ensure
  other.build_collection_class!
  subclasses.push(other)
  subclasses.uniq!
end

.model_name(*args) ⇒ Object Also known as: resource_name



23
24
25
26
# File 'lib/dao/conducer/active_model.rb', line 23

def model_name(*args)
  return send('model_name=', args.first.to_s) unless args.empty?
  @model_name ||= default_model_name
end

.model_name=(model_name) ⇒ Object Also known as: resource_name=



29
30
31
# File 'lib/dao/conducer/active_model.rb', line 29

def model_name=(model_name)
  @model_name = model_name_for(model_name)
end

.model_name_for(model_name) ⇒ Object



34
35
36
# File 'lib/dao/conducer/active_model.rb', line 34

def model_name_for(model_name)
  ActiveModel::Name.new(Map[:name, model_name])
end

.mount(*args, &block) ⇒ Object



273
274
275
# File 'lib/dao/conducer.rb', line 273

def self.mount(*args, &block)
  mounted.push([args, block])
end

.mountedObject



277
278
279
# File 'lib/dao/conducer.rb', line 277

def self.mounted
  @mounted ||= []
end

.name(*args) ⇒ Object



34
35
36
37
# File 'lib/dao/conducer.rb', line 34

def name(*args)
  return send('name=', args.first) unless args.empty?
  @name ||= super
end

.name=(name) ⇒ Object



39
40
41
# File 'lib/dao/conducer.rb', line 39

def name=(name)
  @name = name.to_s
end

.new(*args, &block) ⇒ Object

ctors



101
102
103
104
105
106
107
108
109
# File 'lib/dao/conducer.rb', line 101

def Conducer.new(*args, &block)
  allocate.tap do |conducer|
    args = Dao.call(conducer, :process_arguments, *args)

    Dao.call(conducer, :before_initialize, *args, &block)
    Dao.call(conducer, :initialize, *args, &block)
    Dao.call(conducer, :after_initialize, *args, &block)
  end
end

.raise!(*args, &block) ⇒ Object



55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/dao/conducer.rb', line 55

def raise!(*args, &block)
  kind = (args.first.is_a?(Symbol) ? args.shift : 'error').to_s.sub(/_error$/, '')

  case kind
    when /validation/ 
      raise Validations::Error.new(*args, &block)
    when /error/ 
      raise Error.new(*args, &block)
    else
      raise Error.new(*args, &block)
  end
end

.subclassesObject



30
31
32
# File 'lib/dao/conducer.rb', line 30

def subclasses
  defined?(@@subclasses) ? @@subclasses : (@@subclasses = [])
end

Instance Method Details

#[](key) ⇒ Object



340
341
342
# File 'lib/dao/conducer.rb', line 340

def [](key)
  get(key)
end

#[]=(key, val) ⇒ Object



344
345
346
# File 'lib/dao/conducer.rb', line 344

def []=(key, val)
  set(key, val)
end

#after_initialize(*args, &block) ⇒ Object



175
176
177
178
179
180
# File 'lib/dao/conducer.rb', line 175

def after_initialize(*args, &block)
  unless @initialize_overridden
    initialize_for_action(*args, &block)
    update_attributes(params) unless params.empty?
  end
end

#as_json(*args, &block) ⇒ Object



509
510
511
# File 'lib/dao/conducer.rb', line 509

def as_json(*args, &block)
  @attributes
end

#attributes=(attributes) ⇒ Object



321
322
323
324
# File 'lib/dao/conducer.rb', line 321

def attributes=(attributes)
  @attributes.clear
  update_attributes(attributes)
end

#before_initialize(*args, &block) ⇒ Object



148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/dao/conducer.rb', line 148

def before_initialize(*args, &block)
  models, args = args.partition{|arg| arg.respond_to?(:persisted?) }
  params, args = args.partition{|arg| arg.is_a?(Hash)}

  @params = Map.new
  @attributes = Attributes.for(self)
  @messages = Messages.for(self)

  @form = Form.for(self)
  @form.name = self.class.model_name.singular.sub(/_+$/, '')

  @errors = validator.errors

  set_models(models)

  set_mounts(self.class.mounted)

  update_params(*params) unless params.empty?

  @initialize_overridden = true
end

#conducerObject



513
514
515
# File 'lib/dao/conducer.rb', line 513

def conducer
  self
end

#conduces(*args) ⇒ Object Also known as: set_model



229
230
231
232
233
234
235
236
237
238
# File 'lib/dao/conducer.rb', line 229

def conduces(*args)
  if args.empty?
    @model
  else
    @model = args.flatten.compact.first
    @models.delete(@model)
    @models.unshift(@model)
    @model
  end
end

#conduces=(model) ⇒ Object



241
242
243
# File 'lib/dao/conducer.rb', line 241

def conduces=(model)
  conduces(model)
end

#conduces?(model) ⇒ Boolean

Returns:

  • (Boolean)


245
246
247
248
249
250
251
# File 'lib/dao/conducer.rb', line 245

def conduces?(model)
  if @model
    @model == model
  else
    self.class.conduces?(model)
  end
end

#default_saveObject



427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
# File 'lib/dao/conducer.rb', line 427

def default_save
  return false unless valid?

  if @model
    attributes = self.attributes.dup

    @models.each do |model|
      next if model == @model
      key = model_key_for(model)
      attributes.delete(key)
    end

    mounted.each do |mnt|
      attributes.set(mnt._key, mnt._value)
    end

    @model.update_attributes(attributes)

    if @model.save
      mounted.each{|mnt| mnt._clear}
      return true
    else
      errors.relay(@model.errors)
      return false
    end
  else
    raise NotImplementedError
  end
end

#destroyObject



462
463
464
465
466
467
468
# File 'lib/dao/conducer.rb', line 462

def destroy
  if @model and @model.destroy
    return true
  else
    raise NotImplementedError
  end
end

#destroy!Object



470
471
472
473
# File 'lib/dao/conducer.rb', line 470

def destroy!
  raise!(:deletion_error) unless !!destroy
  true
end

#destroyedObject



89
90
91
# File 'lib/dao/conducer/active_model.rb', line 89

def destroyed
  !!(defined?(@destroyed) ? @destroyed : @model ? @model.destroyed : id.blank?)
end

#destroyed!Object



98
99
100
# File 'lib/dao/conducer/active_model.rb', line 98

def destroyed!
  self.destroyed = true
end

#destroyed=(value) ⇒ Object



95
96
97
# File 'lib/dao/conducer/active_model.rb', line 95

def destroyed=(value)
  @destroyed = !!value
end

#destroyed?Boolean

Returns:

  • (Boolean)


92
93
94
# File 'lib/dao/conducer/active_model.rb', line 92

def destroyed?
  destroyed
end

#errorsObject



481
482
483
# File 'lib/dao/conducer.rb', line 481

def errors
  validator.errors
end

#formObject



489
490
491
# File 'lib/dao/conducer.rb', line 489

def form
  @form
end

#form_builderObject



493
494
495
# File 'lib/dao/conducer.rb', line 493

def form_builder
  Form::Builder
end

#get(*key) ⇒ Object



335
336
337
338
# File 'lib/dao/conducer.rb', line 335

def get(*key)
  key = key_for(key)
  @attributes.get(key)
end

#h(*args) ⇒ Object



501
502
503
# File 'lib/dao/conducer.rb', line 501

def h(*args)
  CGI.escapeHTML(args.join(' '))
end

#has?(*key) ⇒ Boolean

Returns:

  • (Boolean)


330
331
332
333
# File 'lib/dao/conducer.rb', line 330

def has?(*key)
  key = key_for(key)
  @attributes.has?(key)
end

#helperObject



497
498
499
# File 'lib/dao/conducer.rb', line 497

def helper
  @helper ||= ::Helper.new
end

#id(*args) ⇒ Object

id support



379
380
381
382
383
384
385
386
387
# File 'lib/dao/conducer.rb', line 379

def id(*args)
  if args.blank?
    @attributes[:_id] || @attributes[:id]
  else
    id = args.flatten.compact.shift
    key = [:_id, :id].detect{|k| @attributes.has_key?(k)} || :id
    @attributes[key] = id_for(id)
  end
end

#id!(id) ⇒ Object



397
398
399
# File 'lib/dao/conducer.rb', line 397

def id!(id)
  self.id(id)
end

#id=(id) ⇒ Object



393
394
395
# File 'lib/dao/conducer.rb', line 393

def id=(id)
  self.id(id)
end

#id?Boolean

Returns:

  • (Boolean)


389
390
391
# File 'lib/dao/conducer.rb', line 389

def id?
  self.id
end

#id_for(object) ⇒ Object



401
402
403
# File 'lib/dao/conducer.rb', line 401

def id_for(object)
  model?(object) ? object.id : object
end

#initialize_for_action(*args, &block) ⇒ Object



182
183
184
# File 'lib/dao/conducer.rb', line 182

def initialize_for_action(*args, &block)
  @action.call(:initialize, *args, &block)
end

#inspectObject



517
518
519
# File 'lib/dao/conducer.rb', line 517

def inspect
  "#{ self.class.name }(#{ @attributes.inspect.strip })"
end

#key_for(key) ⇒ Object

misc



477
478
479
# File 'lib/dao/conducer.rb', line 477

def key_for(key)
  Dao.key_for(key)
end

#model?(object) ⇒ Boolean

Returns:

  • (Boolean)


405
406
407
# File 'lib/dao/conducer.rb', line 405

def model?(object)
  object.respond_to?(:persisted?)
end

#model_key_for(model) ⇒ Object



219
220
221
222
223
224
225
226
# File 'lib/dao/conducer.rb', line 219

def model_key_for(model)
  case model
    when String
      model
    else
      model.class.name
  end.demodulize.underscore
end

#model_nameObject



485
486
487
# File 'lib/dao/conducer.rb', line 485

def model_name
  self.class.model_name
end

#mount(object, *args, &block) ⇒ Object



260
261
262
263
264
265
266
267
# File 'lib/dao/conducer.rb', line 260

def mount(object, *args, &block)
  mounted = object.mount(self, *args, &block)
ensure
  if mounted
    Dao.ensure_interface!(mounted, :_set, :_key, :_value, :_clear)
    self.mounted.push(mounted)
  end
end

#mountedObject



269
270
271
# File 'lib/dao/conducer.rb', line 269

def mounted
  @mounted ||= []
end

#new_recordObject



76
77
78
# File 'lib/dao/conducer/active_model.rb', line 76

def new_record
  !!(defined?(@new_record) ? @new_record : @model ? @model.new_record? : id.blank?)
end

#new_record!Object



85
86
87
# File 'lib/dao/conducer/active_model.rb', line 85

def new_record!
  self.new_record = true
end

#new_record=(value) ⇒ Object



82
83
84
# File 'lib/dao/conducer/active_model.rb', line 82

def new_record=(value)
  @new_record = !!value
end

#new_record?Boolean

Returns:

  • (Boolean)


79
80
81
# File 'lib/dao/conducer/active_model.rb', line 79

def new_record?
  new_record
end

#persistedObject



63
64
65
# File 'lib/dao/conducer/active_model.rb', line 63

def persisted
  !!(defined?(@persisted) ? @persisted : @model ? @model.persisted? : !id.blank?)
end

#persisted!Object



72
73
74
# File 'lib/dao/conducer/active_model.rb', line 72

def persisted!
  self.persisted = true
end

#persisted=(value) ⇒ Object



69
70
71
# File 'lib/dao/conducer/active_model.rb', line 69

def persisted=(value)
  @persisted = !!value
end

#persisted?Boolean

Returns:

  • (Boolean)


66
67
68
# File 'lib/dao/conducer/active_model.rb', line 66

def persisted?
  persisted
end

#process_arguments(*args) ⇒ Object



135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/dao/conducer.rb', line 135

def process_arguments(*args)
  controllers, args = args.partition{|arg| arg.is_a?(ActionController::Base)}
  actions, args = args.partition{|arg| arg.is_a?(Action)}

  controller = controllers.shift || Dao.current_controller || Dao.mock_controller
  action = actions.shift

  set_controller(controller) if controller
  set_action(action) if action

  args.map{|arg| arg.class == Hash ? Map.for(arg) : arg}
end

#raise!(*args, &block) ⇒ Object



505
506
507
# File 'lib/dao/conducer.rb', line 505

def raise!(*args, &block)
  self.class.raise!(*args, &block)
end

#read_attribute_for_validation(key) ⇒ Object



102
103
104
# File 'lib/dao/conducer/active_model.rb', line 102

def read_attribute_for_validation(key)
  get(key)
end

#saveObject

persistence



423
424
425
# File 'lib/dao/conducer.rb', line 423

def save
  default_save
end

#save!Object



457
458
459
460
# File 'lib/dao/conducer.rb', line 457

def save!
  raise!(:validation_error, errors) unless !!save
  true
end

#set(*args, &block) ⇒ Object



326
327
328
# File 'lib/dao/conducer.rb', line 326

def set(*args, &block)
  update_attributes(*args, &block)
end

#set_models(*models) ⇒ Object



187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
# File 'lib/dao/conducer.rb', line 187

def set_models(*models)
  @models =
    models.flatten.compact

  candidates =
    @models.select{|model| conduces?(model)}

  @model =
    case
      when candidates.size == 1
        candidates.first
      else
        @models.first
    end

  @models.each do |model|
    key = model_key_for(model)
    ivar = "@#{ key }"
    instance_variable_set(ivar, model) unless instance_variable_defined?(ivar)
  end
end

#set_mounts(list) ⇒ Object



254
255
256
257
258
# File 'lib/dao/conducer.rb', line 254

def set_mounts(list)
  list.each do |args, block|
    mount(*args, &block)
  end
end

#to_sObject



521
522
523
# File 'lib/dao/conducer.rb', line 521

def to_s
  inspect
end

#update_attributes(*args, &block) ⇒ Object



282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
# File 'lib/dao/conducer.rb', line 282

def update_attributes(*args, &block)
  attributes =
    case
      when args.size == 1 && args.first.is_a?(Hash)
        args.first
      else
        if args.size >= 2
          val = args.pop
          key = args.flatten.compact
          {key => val}
        else
          {}
        end
    end

  @attributes.add(attributes)

  update_mounted_attributes!

  @attributes
end

#update_attributes!(*args, &block) ⇒ Object



315
316
317
318
319
# File 'lib/dao/conducer.rb', line 315

def update_attributes!(*args, &block)
  update_attributes(*args, &block)
ensure
  save!
end

#update_models(*models) ⇒ Object



209
210
211
212
213
214
215
216
217
# File 'lib/dao/conducer.rb', line 209

def update_models(*models)
  models.flatten.compact.each do |model|
    if conduces?(model)
      update_attributes(model.attributes)
    else
      update_attributes(model_key_for(model), model.attributes)
    end
  end
end

#update_mounted_attributes!Object



304
305
306
307
308
309
310
311
312
313
# File 'lib/dao/conducer.rb', line 304

def update_mounted_attributes!
  deepest_mounts_first = mounted.sort_by{|mnt| mnt._key.size}.reverse

  deepest_mounts_first.each do |mount|
    value = @attributes.get(mount._key)
    next if(value.nil? or value.object_id == mount.object_id)
    mount._set(value) if mount.respond_to?(:_set)
    @attributes.set(mount._key => mount)
  end
end

#update_params(*hashes) ⇒ Object



371
372
373
374
375
# File 'lib/dao/conducer.rb', line 371

def update_params(*hashes)
  hashes.flatten.compact.each do |hash|
    @params.add(hash)
  end
end