Module: Kojac::ControllerOpMethods

Included in:
KojacController, KojacFrontController
Defined in:
lib/kojac/kojac_rails.rb

Class Method Summary collapse

Class Method Details

.add_opObject

def execute_op puts ‘execute_op’ end



555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
# File 'lib/kojac/kojac_rails.rb', line 555

def add_op
  ring = current_ring
  op = (self.respond_to?(:op) && self.op || params[:op])
  model = KojacUtils.model_class_for_key(op[:key].base_key)
  raise "ADD only supports associated collections at present eg order.items" unless op[:key].index('.')

  item = KojacUtils.model_for_key(op[:key].base_key)
  assoc = (assoc=op[:key].key_assoc) && assoc.to_sym
  id = op[:value]['id']

  ma = item.class.reflect_on_association(assoc)
  case ma.macro
    when :has_many
      assoc_class = ma.klass
      assoc_item = assoc_class.find(id)
      item.send(assoc) << assoc_item

      #ids_method = assoc.to_s.singularize+'_ids'
      #ids = item.send(ids_method.to_sym)
      #item.send((ids_method+'=').to_sym,ids + [id])
      result_key = assoc_item.kojac_key
      merge_model_into_results(assoc_item)
    else
      raise "ADD does not yet support #{ma.macro} associations"
  end
  {
    key: op[:key],
    verb: op[:verb],
    result_key: result_key,
    results: results
  }
end

.create_on_association(aItem, aAssoc, aValues, aRing) ⇒ Object



221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
# File 'lib/kojac/kojac_rails.rb', line 221

def create_on_association(aItem,aAssoc,aValues,aRing)
  raise "User does not have permission for create on #{aAssoc}" unless aItem.class.ring_can?(aRing,:create_on,aAssoc.to_sym)

  return nil unless ma = aItem.class.reflect_on_association(aAssoc.to_sym)
  a_model_class = ma.klass
  policy = Pundit.policy!(current_user,a_model_class)

  aValues = KojacUtils.upgrade_hashes_to_params(aValues || {})

  case ma.macro
    when :belongs_to
      return nil if !aValues.is_a?(Hash)
      fields = aValues.permit( *policy.permitted_fields(:write) )
      a_model_class.write_op_filter(current_user,fields,aValues) if a_model_class.respond_to? :write_op_filter
      return aItem.send("build_#{aAssoc}".to_sym,fields)
    when :has_many
      aValues = [aValues] if aValues.is_a?(Hash)
      return nil unless aValues.is_a? Array
      aValues.each do |v|
        fields = v.permit( *policy.permitted_fields(:write) )
        new_sub_item = nil
        case ma.macro
          when :has_many
            a_model_class.write_op_filter(current_user,fields,aValues) if a_model_class.respond_to? :write_op_filter
            new_sub_item = aItem.send(aAssoc.to_sym).create(fields)
          else
            raise "#{ma.macro} association unsupported in CREATE"
        end
        merge_model_into_results(new_sub_item)
      end
  end
  #
  #
  #
  #a_value = op[:value][a]        # get data for this association, assume {}
  #if a_value.is_a?(Hash)
  #  a_model_class = ma.klass
  #  fields = a_value.permit( *permitted_fields(:write,a_model_class) )
  #  item.send("build_#{a}".to_sym,fields)
  #  included_assocs << a.to_sym
  #elsif a_value.is_a?(Array)
  #  raise "association collections not yet implemented for create"
  #else
  #  next
  #end
end

.create_opObject

Unknown resource : CREATE: already there with given id => error READ: isn’t there, or we don’t have access => nil UPDATE: isn’t there, or we don’t have access => nil DESTROY: isn’t there, or we don’t have access => nil



274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
# File 'lib/kojac/kojac_rails.rb', line 274

def create_op
  ring = current_ring
  op = (self.respond_to?(:op) && self.op || params[:op])
  #op = params[:op] unless self.respond_to? :op
  options = op[:options] || {}
  resource,id,assoc = op['key'].split_kojac_key
  if model_class = KojacUtils.model_class_for_key(resource)
    if assoc  # create operation on an association eg. {verb: "CREATE", key: "order.items"}
      if model_class.ring_can?(ring,:create_on,assoc.to_sym)
        item = KojacUtils.model_for_key(key_join(resource,id))
        ma = model_class.reflect_on_association(assoc.to_sym)
        a_value = op[:value]        # get data for this association, assume {}
        raise "create multiple not yet implemented for associations" unless a_value.is_a?(Hash)

        a_model_class = ma.klass
        policy = Pundit.policy!(current_user,a_model_class)
        p_fields = policy.permitted_fields(:write)
        fields = a_value.permit( *p_fields )
        new_sub_item = nil
        case ma.macro
          when :has_many
            a_model_class.write_op_filter(current_user,fields,a_value) if a_model_class.respond_to? :write_op_filter
            new_sub_item = item.send(assoc.to_sym).create(fields)
          else
            raise "#{ma.macro} association unsupported in CREATE"
        end
        result_key = op[:result_key] || new_sub_item.kojac_key
        merge_model_into_results(new_sub_item)
      else
        error = {
          code: 403,
          status: "Forbidden",
          message: "User does not have permission for #{op[:verb]} operation on #{model_class.to_s}.#{assoc}"
        }
      end
    else    # create operation on a resource eg. {verb: "CREATE", key: "order_items"} but may have embedded association values
      if model_class.ring_can?(:create,ring)
        policy = Pundit.policy!(current_user,model_class)
        p_fields = policy.permitted_fields(:write)

        p_fields = op[:value].permit( *p_fields )
        model_class.write_op_filter(current_user,p_fields,op[:value]) if model_class.respond_to? :write_op_filter
        item = model_class.create!(p_fields)

        options_include = options['include'] || []
        included_assocs = []
        p_assocs = policy.permitted_associations(:write)
        if p_assocs
          p_assocs.each do |a|
            next unless (a_value = op[:value][a]) || options_include.include?(a.to_s)
            create_on_association(item,a,a_value,ring)
            included_assocs << a.to_sym
          end
        end
        item.save!
        result_key = op[:result_key] || item.kojac_key
        merge_model_into_results(item,result_key,:include => included_assocs)
      else
        error = {
          code: 403,
          status: "Forbidden",
          message: "User does not have permission for #{op[:verb]} operation on #{model_class.to_s}"
        }
      end
    end
  else
    error = {
      code: 501,
      status: "Not Implemented",
      message: "model class not found"
    }
  end
  response = {
    key: op[:key],
    verb: op[:verb],
  }
  if error
    response[:error] = error
  else
    response[:results] = results
    response[:result_key] = result_key
  end
  response
end

.current_ringObject



217
218
219
# File 'lib/kojac/kojac_rails.rb', line 217

def current_ring
  current_user.try(:ring).to_i
end

.destroy_opObject



527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
# File 'lib/kojac/kojac_rails.rb', line 527

def destroy_op
  ring = current_ring
  op = (self.respond_to?(:op) && self.op || params[:op])
  result_key = op[:result_key] || op[:key]
  # item = KojacUtils.model_for_key(op[:key])
  # item.destroy if item
  r,id,a = op[:key].split_kojac_key

  if id
    model = KojacUtils.model_class_for_key(op[:key].base_key)
    scope = Pundit.policy_scope(current_user, model) || model
    scope = after_scope(scope) if scope && respond_to?(:after_scope)
    item = scope.where(id: id).first
    item.destroy if item
  end
  results[result_key] = nil
  {
    key: op[:key],
    verb: op[:verb],
    result_key: result_key,
    results: results
  }
end

.execute_opObject



622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
# File 'lib/kojac/kojac_rails.rb', line 622

def execute_op
  op = (self.respond_to?(:op) && self.op || params[:op])
  resource,action = op[:key].split_kojac_key
  raise "action not given" unless action.is_a? String
  action = rails_controller? ? "execute_#{action}" : "execute__#{action}"
  raise "action #{action} not implemented on #{resource}" unless respond_to? action.to_sym
  result = rails_controller? ? send(action.to_sym,op) : send(action)
  error = rails_controller? ? op[:error] : (respond_to?(:error).to_nil && send(:error))
  if error
    {
      key: op[:key],
      verb: op[:verb],
      error: error
    }
  else
    result_key = op[:result_key] || op[:key]
    results = op[:results] || {}             # look at op[:results][result_key]. If empty, fill with returned value from action
    results[result_key] = KojacUtils.to_jsono(result,scope: current_user) unless results.has_key? result_key
    {
      key: op[:key],
      verb: op[:verb],
      result_key: result_key,
      results: results
    }
  end
end

.included(aClass) ⇒ Object



200
201
202
203
204
# File 'lib/kojac/kojac_rails.rb', line 200

def self.included(aClass)
  #aClass.send :extend, ClassMethods
  # aClass.send :include, ActiveSupport::Callbacks
  # aClass.send :define_callbacks, :update_op, :scope => [:kind, :name]
end

.kojac_setup(aCurrentUser, aOp) ⇒ Object



403
404
405
406
407
408
409
410
411
412
413
# File 'lib/kojac/kojac_rails.rb', line 403

def kojac_setup(aCurrentUser,aOp)
  self.current_user = aCurrentUser if self.respond_to? :current_user
  self.op = aOp if self.respond_to? :op
  self.verb = aOp['verb'] if self.respond_to? :verb
  self.key = aOp['key'] if self.respond_to? :key
  self.value = aOp['value'] if self.respond_to? :value
  self.params = aOp['params'] || {} if self.respond_to? :params
  self.options = aOp['options'] || {} if self.respond_to? :options
  self.error = aOp['error'] if self.respond_to? :error
  self
end

.read_opObject



415
416
417
418
419
420
421
422
423
424
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
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
# File 'lib/kojac/kojac_rails.rb', line 415

def read_op
  op = (self.respond_to?(:op) && self.op || params[:op])
  key = op[:key]
  result_key = nil
  error = nil
  resource,id = key.split '__'
  model = KojacUtils.model_class_for_key(key)
  #raise "model class not found" unless
  if scope = Pundit.policy_scope(current_user, model) || model
    if id   # item
      scope = scope.where(id: id)
      scope = after_scope(scope) if respond_to?(:after_scope)
      if item = scope.first
        #item.prepare(key,op) if item.respond_to? :prepare
        result_key = op[:result_key] || (item && item.kojac_key) || op[:key]
        merge_model_into_results(item,result_key,op[:options])
      else
        result_key = op[:result_key] || op[:key]
        results[result_key] = nil
      end
    else    # collection
      if rails_controller? # deprecated
        items = scope.respond_to?(:all) ? scope.all : scope.to_a
        result_key = op[:result_key] || op[:key]
        results[result_key] = []
        items = after_scope(items) if respond_to?(:after_scope)
      else
        scope = after_scope(scope) if respond_to?(:after_scope)
        items = scope.respond_to?(:all) ? scope.all : scope.to_a
        result_key = op[:result_key] || op[:key]
        results[result_key] = []
      end
      if op[:options] and op[:options][:atomise]==false
        items_json = []
        items_json = items.map {|i| KojacUtils.to_jsono(i,scope: current_user) }
        results[result_key] = items_json
      else
        items.each do |m|
          item_key = m.kojac_key
          results[result_key] << item_key.split_kojac_key[1]
          merge_model_into_results(m,item_key,op[:options])
        end
      end
    end
  else
    error = {
      code: 501,
      status: "Not Implemented",
      message: "model class not found"
    }
  end
  response = {
    key: op[:key],
    verb: op[:verb],
  }
  if error
    response[:error] = error
  else
    response[:results] = results
    response[:result_key] = result_key
  end
  response
end

.remove_opObject



588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
# File 'lib/kojac/kojac_rails.rb', line 588

def remove_op
  ring = current_ring
  op = (self.respond_to?(:op) && self.op || params[:op])
  model = KojacUtils.model_class_for_key(op[:key].base_key)
  raise "REMOVE only supports associated collections at present eg order.items" unless op[:key].key_assoc

  item = KojacUtils.model_for_key(op[:key].base_key)
  assoc = (assoc=op[:key].key_assoc) && assoc.to_sym
  id = op[:value]['id']

  ma = item.class.reflect_on_association(assoc)
  case ma.macro
    when :has_many
      assoc_class = ma.klass
      if assoc_item = item.send(assoc).find(id)
        item.send(assoc).delete(assoc_item)
        result_key = assoc_item.kojac_key
        if (assoc_item.destroyed?)
          results[result_key] = nil
        else
          merge_model_into_results(assoc_item,result_key)
        end
      end
    else
      raise "REMOVE does not yet support #{ma.macro} associations"
  end
  {
    key: op[:key],
    verb: op[:verb],
    result_key: result_key,
    results: results
  }
end

.resultsObject



213
214
215
# File 'lib/kojac/kojac_rails.rb', line 213

def results
  @results ||= {}
end

.update_opObject



479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
# File 'lib/kojac/kojac_rails.rb', line 479

def update_op
  result = nil
  ring = current_ring
  op = (self.respond_to?(:op) && self.op || params[:op])
  result_key = nil
  model = KojacUtils.model_class_for_key(op[:key].base_key)
  scope = Pundit.policy_scope(current_user, model) || model
  scope = after_scope(scope) if scope && respond_to?(:after_scope)
  if scope and item = scope.load_by_key(op[:key],op)
    #run_callbacks :update_op do
      policy = Pundit.policy!(current_user,item)
      item.update_permitted_attributes!(op[:value], policy)

      associations = policy.permitted_associations(:write)
      associations.each do |k|
        next unless assoc = model.reflect_on_association(k)
        next unless op[:value][k]
        case assoc.macro
          when :belongs_to
            if leaf = (item.send(k) || item.send("build_#{k}".to_sym))
              policy = Pundit.policy!(current_user,leaf)
              leaf.update_permitted_attributes!(op[:value][k], policy)
            end
        end
      end

      result_key = op[:result_key] || (item && item.kojac_key) || op[:key]
      merge_model_into_results(item,result_key,op[:options])

      associations.each do |a|
        next unless assoc_item = item.send(a)
        next unless key = assoc_item.respond_to?(:kojac_key) && assoc_item.kojac_key
        #results[key] = assoc_item
        merge_model_into_results(assoc_item,key)
      end
    #end
  else
    result_key = op[:result_key] || op[:key]
    results[result_key] = nil
  end
  {
    key: op[:key],
    verb: op[:verb],
    result_key: result_key,
    results: results
  }
end