class WAB::Controller

A Controller class or a duck-typed alternative should be created and registered with a Shell for any type that implements behavior other than the default REST API processing.

When a request arrives at the Shell, the expected Controller method is identifed and a check is made to verify if the Controller responds to the method. If it does not, then the handle method is called. Since respond_to includes only public methods, only those methods made public in a Controller subclass are considered. The candidates for making public are create, read, update, and delete.

A description of the available methods is included as private methods.

Attributes

shell[RW]

Public Class Methods

new(shell) click to toggle source

Create a instance.

# File lib/wab/controller.rb, line 19
def initialize(shell)
  @shell = shell
end

Public Instance Methods

handle(_data) click to toggle source

Handler for paths that do not match the REST pattern or for unregistered types.

Processing result are passed back to the view which forward the result on to the requester. The result, if not nil, should be a Data instance.

data

data to be processed

# File lib/wab/controller.rb, line 30
def handle(_data)
  nil
end

Private Instance Methods

and_where(kind, query) click to toggle source

Form an AND expression for a TQL where clause.

# File lib/wab/controller.rb, line 222
def and_where(kind, query)
  where = ['AND']
  where << form_where_eq(@shell.type_key, kind)
  query.each_pair { |k,v| where << form_where_eq(k, v) }
  where
end
changed(data) click to toggle source

Called by the model when data changes if supported by the model storage component.

data

the data that has changed

# File lib/wab/controller.rb, line 158
def changed(data) # :doc:
  # TBD filter accoding to subscriptions
  @shell.changed(data)
end
create(path, query, data) click to toggle source

Create a new data object. If a query is provided it is treated as a check against an existing object with the same key/value pairs.

On error an Exception should be raised.

path

array of tokens in the path.

query

query parameters from a URL.

data

the data to use as a new object.

# File lib/wab/controller.rb, line 47
def create(path, query, data) # :doc:
  tql = { }
  kind = path[path_pos]
  if WAB::Utils.populated_hash?(query)
    tql[:where] = and_where(kind, query)
  end
  tql[:insert] = data.native
  shell_query(tql, kind, 'create')
end
delete(path, query) click to toggle source

Delete the identified object.

On success the deleted object identifier is returned. If the object is not found then nil is returned. On error an Exception should be raised.

If no id is present in the path then the return should be a Hash where the keys are the matching object identifiers and the value are the object data. An empty Hash or nil indicates there were no matches.

path

identifier of the object to be deleted

query

query parameters from a URL as a Hash with keys matching paths into the target objects and value equal to the target attribute values. A path can be an array of keys used to walk a path to the target or a . delimited set of keys.

# File lib/wab/controller.rb, line 129
def delete(path, query) # :doc:
  tql = { }
  kind = path[path_pos]
  tql[:where] = if path_pos + 2 == path.length # has an object reference in the path
                  path[path_pos + 1].to_i
                elsif WAB::Utils.populated_hash?(query)
                  and_where(kind, query)
                else
                  form_where_eq(@shell.type_key, kind)
                end
  tql[:delete] = nil
  shell_query(tql, kind, 'delete')
end
detect_string(value) click to toggle source

Detects strings that are representation of something else such as an integer, UUID, Time, or URI. Used to convert URL query parameters to TQL types. That also means string are quoted for TQL with a single leading single quote character unless one is already present. No trailing single quote is added.

# File lib/wab/controller.rb, line 234
def detect_string(value)
  # if the string matches a detectable type then don't quote it
  # ok as is
  return value if !value.empty? && value.start_with?("'")

  if /^-?\d+$/ === value
    value.to_i
  elsif /^-?\d*\.?\d+([eE][-+]?\d+)?$/ === value
    value.to_f
  elsif WAB::Utils.uuid_format?(value)
    WAB::UUID.new(value)
  elsif WAB::Utils.wab_time_format?(value)
    begin
      DateTime.parse(value).to_time
    rescue
      "'" + value
    end
  elsif value.downcase.start_with?('http://')
    begin
      URI(value)
    rescue
      "'" + value
    end
  else
    "'" + value
  end
end
form_where_eq(key, value) click to toggle source

Form a EQ expression for a TQL where clause. Used as a helper to the primary API calls.

key

key in the expression

value

value portion converted to the correct format if necessary

# File lib/wab/controller.rb, line 199
def form_where_eq(key, value)
  value_class = value.class
  x = ['EQ', key.to_s]
  x << if Time == value_class
         value.utc.iso8601(9)
       elsif value.nil? ||
           TrueClass == value_class ||
           FalseClass == value_class ||
           Integer == value_class ||
           Float == value_class
         value
       elsif String == value_class
         # if the string matches a detectable type then don't quote it
         detect_string(value)
       elsif WAB::Utils.pre_24_fixnum?(value)
         value
       else
         value.to_s
       end
  x
end
list_match(kind, query) click to toggle source

A private method to gather a list of objects that match the query parameters.

# File lib/wab/controller.rb, line 182
def list_match(kind, query)
  tql = {}
  # If there is a query set up a where clause.
  tql[:where] = if WAB::Utils.populated_hash?(query)
                  and_where(kind, query)
                else
                  form_where_eq(@shell.type_key, kind)
                end
  tql[:select] = { id: '$ref', data: '$' }
  shell_query(tql, kind, 'read')
end
list_select(kind, fields) click to toggle source

A private method to gather sets of Hashes that include the fields specified in the fields Hash.

# File lib/wab/controller.rb, line 169
def list_select(kind, fields)
  tql = {}
  select = { ref: '$ref' }
  if WAB::Utils.populated_hash?(fields)
    fields.each_pair { |k,v| select[k] = v }
  end
  tql[:where] = form_where_eq(@shell.type_key, kind)
  tql[:select] = select
  shell_query(tql, kind, 'read')
end
path_pos() click to toggle source
# File lib/wab/controller.rb, line 163
def path_pos
  @path_pos ||= @shell.path_pos
end
read(path, query) click to toggle source

Return the objects according to the path and query arguments. The following patterns supported:

  • MyType/12345

    looks for MyType with reference ID of 12345

  • MyType?name=fred&age=63

    looks for all MyTypes with a name of 'fred' and an age of 63.

  • MyType/list?Name=name&Age=age

    returns only the name and age attributes and places them in a Hash with the keys :Name and :Age along with the record reference as :ref.

path

array of tokens in the path.

query

query parameters from a URL as a Hash with key and value pairs. Note that duplicate keys will result in only the last option being present,

# File lib/wab/controller.rb, line 72
def read(path, query) # :doc:
  kind = path[path_pos]
  # Check for the type and object reference pattern as well as the list
  # pattern.
  if path_pos + 2 == path.length
    ref = path[path_pos + 1]
    return list_select(kind, query) if 'list' == ref

    # Read a single object/record.
    ref = ref.to_i
    obj = @shell.get(ref)
    obj = obj.native if obj.is_a?(WAB::Data)
    results = []
    results << {id: ref, data: obj} unless obj.nil?
    @shell.data({ code: 0, results: results})
  else
    list_match(kind, query)
  end
end
shell_query(tql, kind, op) click to toggle source

Helper to send TQL requests to the shell.

# File lib/wab/controller.rb, line 263
def shell_query(tql, kind, op)
  result = @shell.query(tql)
  raise WAB::Error.new("nil result on #{kind} #{op}.") if result.nil?
  raise WAB::Error.new("error on #{kind} #{op}. #{result[:error]}") if 0 != result[:code]
  result
end
subscribe(filter) click to toggle source

Subscribe to changes in data pushed from the model that will be passed to the view with the push method if it passes the supplied filter.

The view changed method is called when changes in data cause the associated object to pass the provided filter.

filter

the filter to apply to the data. TBD the nature of the filter is pending.

# File lib/wab/controller.rb, line 150
def subscribe(filter)
  # TBD
end
update(path, query, data) click to toggle source

Replaces the object data for the identified object.

The return should be the identifiers for the object updated.

On error an Exception should be raised.

path

array of tokens in the path.

query

query parameters from a URL.

data

the data to use as a new object.

# File lib/wab/controller.rb, line 101
def update(path, query, data) # :doc:
  tql = { }
  kind = path[path_pos]
  if path_pos + 2 == path.length # has an object reference in the path
    tql[:where] = path[path_pos + 1].to_i
  elsif WAB::Utils.populated_hash?(query)
    tql[:where] = and_where(kind, query)
  else
    raise WAB::Error.new("update on all #{kind} not allowed.")
  end
  tql[:update] = data.native
  shell_query(tql, kind, 'update')
end