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

##
#
  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.



168
169
170
171
# File 'lib/dao/conducer.rb', line 168

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



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

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



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

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



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

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



53
54
55
# File 'lib/dao/conducer/active_model.rb', line 53

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



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

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.to_s
  name.sub!(suffixes, '') unless name.sub(suffixes, '').blank?
  name.sub!(/(:|_)+$/, '')

  model_name_for(name)
end

.for(*args, &block) ⇒ Object



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

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



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



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

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

.model_name_for(model_name) ⇒ Object



32
33
34
# File 'lib/dao/conducer/active_model.rb', line 32

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

.mount(*args, &block) ⇒ Object



271
272
273
# File 'lib/dao/conducer.rb', line 271

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

.mountedObject



275
276
277
# File 'lib/dao/conducer.rb', line 275

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



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

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



338
339
340
# File 'lib/dao/conducer.rb', line 338

def [](key)
  get(key)
end

#[]=(key, val) ⇒ Object



342
343
344
# File 'lib/dao/conducer.rb', line 342

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

#after_initialize(*args, &block) ⇒ Object



173
174
175
176
177
178
# File 'lib/dao/conducer.rb', line 173

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



503
504
505
# File 'lib/dao/conducer.rb', line 503

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

#attributes=(attributes) ⇒ Object



319
320
321
322
# File 'lib/dao/conducer.rb', line 319

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

#before_initialize(*args, &block) ⇒ Object



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

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)

  @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



507
508
509
# File 'lib/dao/conducer.rb', line 507

def conducer
  self
end

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



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

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



239
240
241
# File 'lib/dao/conducer.rb', line 239

def conduces=(model)
  conduces(model)
end

#conduces?(model) ⇒ Boolean

Returns:

  • (Boolean)


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

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

#default_saveObject



425
426
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
# File 'lib/dao/conducer.rb', line 425

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



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

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

#destroy!Object



468
469
470
471
# File 'lib/dao/conducer.rb', line 468

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

#destroyedObject



87
88
89
# File 'lib/dao/conducer/active_model.rb', line 87

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

#destroyed!Object



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

def destroyed!
  self.destroyed = true
end

#destroyed=(value) ⇒ Object



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

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

#destroyed?Boolean

Returns:

  • (Boolean)


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

def destroyed?
  destroyed
end

#errorsObject



479
480
481
# File 'lib/dao/conducer.rb', line 479

def errors
  validator.errors
end

#formObject



487
488
489
# File 'lib/dao/conducer.rb', line 487

def form
  @form
end

#form_builderObject



491
492
493
# File 'lib/dao/conducer.rb', line 491

def form_builder
  Form::Builder
end

#get(*key) ⇒ Object



333
334
335
336
# File 'lib/dao/conducer.rb', line 333

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

#has?(*key) ⇒ Boolean

Returns:

  • (Boolean)


328
329
330
331
# File 'lib/dao/conducer.rb', line 328

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

#helperObject



495
496
497
# File 'lib/dao/conducer.rb', line 495

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

#id(*args) ⇒ Object

id support



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

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



395
396
397
# File 'lib/dao/conducer.rb', line 395

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

#id=(id) ⇒ Object



391
392
393
# File 'lib/dao/conducer.rb', line 391

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

#id?Boolean

Returns:

  • (Boolean)


387
388
389
# File 'lib/dao/conducer.rb', line 387

def id?
  self.id
end

#id_for(object) ⇒ Object



399
400
401
# File 'lib/dao/conducer.rb', line 399

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

#initialize_for_action(*args, &block) ⇒ Object



180
181
182
# File 'lib/dao/conducer.rb', line 180

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

#inspectObject



511
512
513
# File 'lib/dao/conducer.rb', line 511

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

#key_for(key) ⇒ Object

misc



475
476
477
# File 'lib/dao/conducer.rb', line 475

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

#model?(object) ⇒ Boolean

Returns:

  • (Boolean)


403
404
405
# File 'lib/dao/conducer.rb', line 403

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

#model_key_for(model) ⇒ Object



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

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

#model_nameObject



483
484
485
# File 'lib/dao/conducer.rb', line 483

def model_name
  self.class.model_name
end

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



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

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



267
268
269
# File 'lib/dao/conducer.rb', line 267

def mounted
  @mounted ||= []
end

#new_recordObject



74
75
76
# File 'lib/dao/conducer/active_model.rb', line 74

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

#new_record!Object



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

def new_record!
  self.new_record = true
end

#new_record=(value) ⇒ Object



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

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

#new_record?Boolean

Returns:

  • (Boolean)


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

def new_record?
  new_record
end

#persistedObject



61
62
63
# File 'lib/dao/conducer/active_model.rb', line 61

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

#persisted!Object



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

def persisted!
  self.persisted = true
end

#persisted=(value) ⇒ Object



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

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

#persisted?Boolean

Returns:

  • (Boolean)


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

def persisted?
  persisted
end

#process_arguments(*args) ⇒ Object



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

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



499
500
501
# File 'lib/dao/conducer.rb', line 499

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

#read_attribute_for_validation(key) ⇒ Object



100
101
102
# File 'lib/dao/conducer/active_model.rb', line 100

def read_attribute_for_validation(key)
  get(key)
end

#saveObject

persistence



421
422
423
# File 'lib/dao/conducer.rb', line 421

def save
  default_save
end

#save!Object



455
456
457
458
# File 'lib/dao/conducer.rb', line 455

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

#set(*args, &block) ⇒ Object



324
325
326
# File 'lib/dao/conducer.rb', line 324

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

#set_models(*models) ⇒ Object



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

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



252
253
254
255
256
# File 'lib/dao/conducer.rb', line 252

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

#to_sObject



515
516
517
# File 'lib/dao/conducer.rb', line 515

def to_s
  inspect
end

#update_attributes(*args, &block) ⇒ Object



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

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



313
314
315
316
317
# File 'lib/dao/conducer.rb', line 313

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

#update_models(*models) ⇒ Object



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

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



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

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



369
370
371
372
373
# File 'lib/dao/conducer.rb', line 369

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