Class: Pod4::NebulousInterface
- Defined in:
- lib/pod4/nebulous_interface.rb
Overview
An interface to talk to a Nebulous Target.
Each interface can only speak with one target, designated with #set_target. The developer must also set a unique ID key using #set_id_fld.
The primary challenge here is to map the CRUDL methods (which interfaces contract to implement) to nebulous verbs. The programmer uses #set_verb for this purpose: the first parameter indicates the CRUDL method, the next is the verb name, and the rest are hash keys.
In the case of the #create and #update methods, the list of keys controls which parts of the incoming hash end up in the verb parameters, and in what order. For #update, the list must include the ID key that you gave to #set_id_fld.
Parameters for the #list method similarly constrain how its selection parameter is translated to a nebulous verb parameter string.
Parameters for #read and #delete can be whatever you like, but since the only value passed to read is the ID, the only symbol there should be the same as the one in #set_id_fld.
class CustomerInterface < SwingShift::NebulousInterface
set_target 'accord'
set_id_fld :id
set_verb :read, 'customerread', :id, '100'
set_verb :list, 'customerlist', :name
set_verb :create, 'customerupdate', 'create', :name, :price
set_verb :update, 'customerupdate', 'update', :name, :id, :price
def update(id, name, price)
super( id, name: name, price: price)
end
end
In this example both the create and update methods point to the same nebulous verb. Note that only keys which are symbols are translated to the corresponding values in the record or selection hash; anything else is passed literally in the Nebulous parameter string.
When you subclass NebulousInterface, you may want to override some or all of the CRUDL methods so that your callers can pass specific parameters rather than a hash; the above example demonstrates this.
We assume that the response to the #create message returns the ID as the parameter part of the success verb. If that’s not true, then you will have to override #create and sort this out yourself.
Calls to create, update and delete avoid (for obvious reasons) Nebulous’ Redis cache if present. Read and list use it, but can take an extra options hash to control it, which, of course, Pod4::Model does not know about. If you want to enable a non-cached read in your model, it will need a method something like this:
def read_no_cache
r = interface.read(@model_id, caching: false)
if r.empty?
add_alert(:error, "Record ID '#@model_id' not found on the data source")
else
map_to_model(r)
run_validation(:read)
@model_status = :okay if @model_status == :empty
end
self
rescue Pod4::WeakError
add_alert(:error, $!)
self
end
NB: Connections: Nebulous does not use the Connection class. The user must configure NebulousStomp themselves, once, when their application starts; but they don’t need to do this before requiring the models. And there is no need for a connection pool.
Defined Under Namespace
Classes: Verb
Constant Summary
Constants inherited from Interface
Instance Attribute Summary collapse
-
#id_ai ⇒ Object
readonly
Returns the value of attribute id_ai.
-
#id_fld ⇒ Object
readonly
Returns the value of attribute id_fld.
-
#response ⇒ Object
readonly
The NebulousStomp Message object holding the response from the last message sent, or, nil.
-
#response_status ⇒ Object
readonly
The status of the response from the last message: * nil - we didn’t send a request yet * :off - Nebulous is turned off, so nothing happened * :timeout we sent the message but timed out waiting for a response * :verberror - we got an error verb in response * :verbsuccess - we got a success verb in response * :response - we got some response that doesn’t follow The Protocol.
Class Method Summary collapse
- .id_ai ⇒ Object
- .id_fld ⇒ Object
-
.set_id_fld(idFld, opts = {}) ⇒ Object
Set the name of the ID parameter (needs to be in the CRUD verbs param list).
-
.set_target(target) ⇒ Object
Set the name of the Nebulous target in the interface definition.
-
.set_verb(action, verb, *paramKeys) ⇒ Object
Set a verb.
- .target ⇒ Object
-
.validate_params ⇒ Object
Make sure all of the above is consistent.
- .verbs ⇒ Object
Instance Method Summary collapse
-
#clearing_cache ⇒ Object
Bonus method: chain this method before a CRUDL method to clear the cache for that parameter string:.
-
#create(record) ⇒ Object
Pass a parameter string or an array as the record.
-
#delete(id) ⇒ Object
Given an ID, delete the record.
-
#initialize(requestObj = nil) ⇒ NebulousInterface
constructor
In normal operation, takes no parameters.
-
#list(selection = nil, opts = {}) ⇒ Object
Pass a parameter string or array (which will be taken as the literal Nebulous parameter) or a Hash or Octothorpe (which will be interpreted as per your list of keys set in add_verb :list).
-
#read(id, opts = {}) ⇒ Object
Given the id, return an Octothorpe of the record.
-
#send_message(verb, paramStr, with_cache = true) ⇒ Object
Bonus method: send an arbitrary Nebulous message to the target and return the response object.
-
#update(id, record) ⇒ Object
Given an id an a record (Octothorpe or Hash), update the record.
Methods inherited from Interface
#_connection, #close_connection, #new_connection
Methods included from Metaxing
#define_class_method, #metaclass
Constructor Details
#initialize(requestObj = nil) ⇒ NebulousInterface
In normal operation, takes no parameters.
For testing purposes you may pass something here. Whatever it is you pass, it must respond to a ‘send` method, take the same parameters as NebulousStomp::Request.new (that is, a target and a message) and return something that behaves like a NebulousStomp::Request. This method will be called instead of creating a NebulousStomp::Request directly.
183 184 185 186 187 188 189 190 191 |
# File 'lib/pod4/nebulous_interface.rb', line 183 def initialize(requestObj=nil) @request_object = requestObj # might as well be a reference @response = nil @response_status = nil @id_fld = self.class.id_fld @id_ai = self.class.id_ai self.class.validate_params end |
Instance Attribute Details
#id_ai ⇒ Object (readonly)
Returns the value of attribute id_ai.
81 82 83 |
# File 'lib/pod4/nebulous_interface.rb', line 81 def id_ai @id_ai end |
#id_fld ⇒ Object (readonly)
Returns the value of attribute id_fld.
81 82 83 |
# File 'lib/pod4/nebulous_interface.rb', line 81 def id_fld @id_fld end |
#response ⇒ Object (readonly)
The NebulousStomp Message object holding the response from the last message sent, or, nil.
84 85 86 |
# File 'lib/pod4/nebulous_interface.rb', line 84 def response @response end |
#response_status ⇒ Object (readonly)
The status of the response from the last message:
-
nil - we didn’t send a request yet
-
:off - Nebulous is turned off, so nothing happened
-
:timeout we sent the message but timed out waiting for a response
-
:verberror - we got an error verb in response
-
:verbsuccess - we got a success verb in response
-
:response - we got some response that doesn’t follow The Protocol
NB: if we got an exception sending the message, we raised it on the caller, so there is no status for that.
96 97 98 |
# File 'lib/pod4/nebulous_interface.rb', line 96 def response_status @response_status end |
Class Method Details
.id_ai ⇒ Object
149 150 151 |
# File 'lib/pod4/nebulous_interface.rb', line 149 def id_ai raise Pod4Error, "You need to use set_id_fld" end |
.id_fld ⇒ Object
145 146 147 |
# File 'lib/pod4/nebulous_interface.rb', line 145 def id_fld raise Pod4Error, "You need to use set_id_fld" end |
.set_id_fld(idFld, opts = {}) ⇒ Object
Set the name of the ID parameter (needs to be in the CRUD verbs param list)
139 140 141 142 143 |
# File 'lib/pod4/nebulous_interface.rb', line 139 def set_id_fld(idFld, opts={}) ai = opts.fetch(:autoincrement) { true } define_class_method(:id_fld) {idFld} define_class_method(:id_ai) {!!ai} end |
.set_target(target) ⇒ Object
Set the name of the Nebulous target in the interface definition
a reference to the interface object.
129 130 131 |
# File 'lib/pod4/nebulous_interface.rb', line 129 def set_target(target) define_class_method(:target) {target.to_s} end |
.set_verb(action, verb, *paramKeys) ⇒ Object
Set a verb.
-
action - must be one of CRUDL
-
verb - the name of the verb
-
parameters - array of symbols to order the hash passed to create, etc
113 114 115 116 117 118 119 120 |
# File 'lib/pod4/nebulous_interface.rb', line 113 def set_verb(action, verb, *paramKeys) raise ArgumentError, "Bad action" unless Interface::ACTIONS.include? action v = verbs.dup v[action] = Verb.new( verb, paramKeys.flatten ) define_class_method(:verbs) {v} end |
.target ⇒ Object
133 134 135 |
# File 'lib/pod4/nebulous_interface.rb', line 133 def target raise Pod4Error, "You need to use set_target on your interface" end |
.validate_params ⇒ Object
Make sure all of the above is consistent
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 |
# File 'lib/pod4/nebulous_interface.rb', line 156 def validate_params raise Pod4Error, "You need to use set_verb" if verbs == {} %i|create read update delete|.each do |action| raise Pod4Error, "set_verb #{action} is missing a parameter list" \ if verbs[action] && !verbs[action].params == [] end %i|read update delete|.each do |action| raise Pod4Error, "#{action} verb doesn't have an #{id_fld} key" \ if verbs[action] && !verbs[action].params.include?(id_fld) end end |
.verbs ⇒ Object
122 |
# File 'lib/pod4/nebulous_interface.rb', line 122 def verbs; {}; end |
Instance Method Details
#clearing_cache ⇒ Object
Bonus method: chain this method before a CRUDL method to clear the cache for that parameter string:
@interface.clearing_cache.read(14)
Note that there is no guarantee that the request that clears the cache is actually the one you chain after (if multiple model instances are running against the same interface instance) but for the instance that calls ‘clearing_cache`, this is not important.
299 300 301 302 |
# File 'lib/pod4/nebulous_interface.rb', line 299 def clearing_cache @clear_cache = true self end |
#create(record) ⇒ Object
Pass a parameter string or an array as the record. returns the ID. We assume that the response to the create message returns the ID as the parameter part of the success verb. If that’s not true, then you will have to override #create and sort this out yourself.
226 227 228 229 230 231 232 233 234 235 236 237 |
# File 'lib/pod4/nebulous_interface.rb', line 226 def create(record) raise ArgumentError, 'create takes a Hash or an Octothorpe' unless hashy?(record) raise ArgumentError, "ID field missing from record" \ if !@id_ai && record[@id_fld].nil? && record[@id_fld.to_s].nil? ( verb_for(:create), param_string(:create, record), false ) @response.params rescue => e handle_error(e) end |
#delete(id) ⇒ Object
Given an ID, delete the record. Return self.
The actual parameters passed to nebulous depend on how you #set_verb
279 280 281 282 283 284 285 286 287 |
# File 'lib/pod4/nebulous_interface.rb', line 279 def delete(id) raise ArgumentError, 'You must pass an ID to delete' unless id ( verb_for(:delete), param_string(:delete, nil, id), false ) self end |
#list(selection = nil, opts = {}) ⇒ Object
Pass a parameter string or array (which will be taken as the literal Nebulous parameter) or a Hash or Octothorpe (which will be interpreted as per your list of keys set in add_verb :list).
Returns an array of Octothorpes, or an empty array if the responder could not make any records out of our message.
Note that the ‘opts` hash is not part of the protocol supported by Pod4::Model. If you want to make use of it, you will have to write your own method for that. Supported keys:
-
caching: true if you want to use redis caching (defaults to true)
206 207 208 209 210 211 212 213 214 215 216 217 218 219 |
# File 'lib/pod4/nebulous_interface.rb', line 206 def list(selection=nil, opts={}) sel = case selection when Array, Hash, Octothorpe then param_string(:list, selection) else selection end caching = opts[:caching].nil? ? true : !!opts[:caching] ( verb_for(:list), sel, caching ) @response.body.is_a?(Array) ? @response.body.map{|e| Octothorpe.new e} : [] rescue => e handle_error(e) end |
#read(id, opts = {}) ⇒ Object
Given the id, return an Octothorpe of the record.
The actual parameters passed to nebulous depend on how you #set_verb
Note that the ‘opts` hash is not part of the protocol supported by Pod4::Model. If you want to make use of it, you will have to write your own method for that. Supported keys:
-
caching: true if you want to use redis caching (defaults to true)
249 250 251 252 253 254 255 256 257 258 |
# File 'lib/pod4/nebulous_interface.rb', line 249 def read(id, opts={}) raise ArgumentError, 'You must pass an ID to read' unless id caching = opts[:caching].nil? ? true : !!opts[:caching] ( verb_for(:read), param_string(:read, nil, id), caching ) Octothorpe.new( @response.body.is_a?(Hash) ? @response.body : {} ) end |
#send_message(verb, paramStr, with_cache = true) ⇒ Object
Bonus method: send an arbitrary Nebulous message to the target and return the response object.
We don’t trap errors here - see #handle_error - but we raise extra ones if we think things look fishy.
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 |
# File 'lib/pod4/nebulous_interface.rb', line 311 def (verb, paramStr, with_cache=true) unless NebulousStomp.on? @response_status = :off raise Pod4::DatabaseError, "Nebulous is turned off!" end Pod4.logger.debug(__FILE__) do "Sending v:#{verb} p:#{paramStr} c?: #{with_cache}" end @response = (verb, paramStr, with_cache) raise Pod4::DatabaseError, "Null response" if @response.nil? @response_status = case @response.verb when 'error' then :verberror when 'success' then :verbsuccess else :response end raise Pod4::WeakError, "Nebulous returned an error verb: #{@response.description}" \ if @response_status == :verberror self rescue => err handle_error(err) end |
#update(id, record) ⇒ Object
Given an id an a record (Octothorpe or Hash), update the record. Returns self.
263 264 265 266 267 268 269 270 271 272 |
# File 'lib/pod4/nebulous_interface.rb', line 263 def update(id, record) raise ArgumentError, 'You must pass an ID to update' unless id raise ArgumentError, 'update record takes a Hash or an Octothorpe' unless hashy?(record) ( verb_for(:update), param_string(:update, record, id), false ) self end |