hookr 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +4 -0
- data/Manifest.txt +22 -0
- data/README.txt +491 -0
- data/Rakefile +28 -0
- data/bin/hookr +8 -0
- data/lib/hookr.rb +653 -0
- data/spec/hookr_spec.rb +1041 -0
- data/spec/spec_helper.rb +16 -0
- data/tasks/ann.rake +80 -0
- data/tasks/bones.rake +20 -0
- data/tasks/gem.rake +192 -0
- data/tasks/git.rake +40 -0
- data/tasks/manifest.rake +48 -0
- data/tasks/notes.rake +27 -0
- data/tasks/post_load.rake +39 -0
- data/tasks/rdoc.rake +50 -0
- data/tasks/rubyforge.rake +55 -0
- data/tasks/setup.rb +279 -0
- data/tasks/spec.rake +54 -0
- data/tasks/svn.rake +47 -0
- data/tasks/test.rake +40 -0
- data/test/test_hookr.rb +0 -0
- metadata +77 -0
data/History.txt
ADDED
data/Manifest.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
History.txt
|
2
|
+
Manifest.txt
|
3
|
+
README.txt
|
4
|
+
Rakefile
|
5
|
+
bin/hookr
|
6
|
+
lib/hookr.rb
|
7
|
+
spec/hookr_spec.rb
|
8
|
+
spec/spec_helper.rb
|
9
|
+
tasks/ann.rake
|
10
|
+
tasks/bones.rake
|
11
|
+
tasks/gem.rake
|
12
|
+
tasks/git.rake
|
13
|
+
tasks/manifest.rake
|
14
|
+
tasks/notes.rake
|
15
|
+
tasks/post_load.rake
|
16
|
+
tasks/rdoc.rake
|
17
|
+
tasks/rubyforge.rake
|
18
|
+
tasks/setup.rb
|
19
|
+
tasks/spec.rake
|
20
|
+
tasks/svn.rake
|
21
|
+
tasks/test.rake
|
22
|
+
test/test_hookr.rb
|
data/README.txt
ADDED
@@ -0,0 +1,491 @@
|
|
1
|
+
= HookR
|
2
|
+
by Avdi Grimm
|
3
|
+
http://hookr.rubyforge.org
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
HookR is a publish/subscribe callback hook facility for Ruby.
|
8
|
+
|
9
|
+
=== What is it?
|
10
|
+
|
11
|
+
HookR can be understood in a few different ways.
|
12
|
+
|
13
|
+
* If you are familiar with Events and Event Listeners in
|
14
|
+
Java[http://java.sun.com/docs/books/tutorial/javabeans/events/index.html] or
|
15
|
+
C#[http://msdn.microsoft.com/en-us/library/aa645739(VS.71).aspx];
|
16
|
+
Hooks[http://www.gnu.org/software/emacs/manual/html_node/elisp/Hooks.html#Hooks]
|
17
|
+
in Emacs-lisp; or signals-and-slots as implemented in the
|
18
|
+
Qt[http://doc.trolltech.com/4.4/signalsandslots.html],
|
19
|
+
Boost.Signals[http://www.boost.org/doc/libs/1_37_0/doc/html/signals.html], or
|
20
|
+
libsigc++[http://libsigc.sourceforge.net/] frameworks - HookR provides a
|
21
|
+
very similar facility.
|
22
|
+
* If youve ever used the Observer standard library, but wished you could
|
23
|
+
have more than one type of notification per observable object, HookR is the
|
24
|
+
library for you.
|
25
|
+
* HookR is an easy way to add
|
26
|
+
Rails-style[http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html]
|
27
|
+
before- and after-filters to your own classes.
|
28
|
+
* HookR is an
|
29
|
+
Inversion-of-Control[http://martinfowler.com/bliki/InversionOfControl.html]
|
30
|
+
framework in that it makes it easy to write event-driven code.
|
31
|
+
* HookR is a way to support a limited, structured form of Aspect Oriented
|
32
|
+
Programming (AOP[http://en.wikipedia.org/wiki/Aspect-oriented_programming])
|
33
|
+
where the advisable events are explicitly defined.
|
34
|
+
|
35
|
+
=== What HookR is not:
|
36
|
+
|
37
|
+
* HookR is not (yet) an asynchronous event notification system. No provision is
|
38
|
+
made for multi-threaded operation or event queueing.
|
39
|
+
* HookR will show you a good time, but it will not make you breakfast in the
|
40
|
+
morning.
|
41
|
+
|
42
|
+
== FEATURES:
|
43
|
+
|
44
|
+
* Fully spec'd
|
45
|
+
* Provides class-level and instance-level callbacks
|
46
|
+
* Inheritance-safe
|
47
|
+
* Supports both iterative and recursive callback models
|
48
|
+
* "Wildcard" callbacks can observe all events
|
49
|
+
* Three types of callback supported - internal (instance-eval'd), external, and
|
50
|
+
method callbacks.
|
51
|
+
|
52
|
+
== SYNOPSIS:
|
53
|
+
|
54
|
+
require 'rubygems'
|
55
|
+
require 'hookr'
|
56
|
+
|
57
|
+
class ZeroWing
|
58
|
+
include HookR::Hooks
|
59
|
+
define_hook :we_get_signal, :message
|
60
|
+
|
61
|
+
def start_game
|
62
|
+
execute_hook(:we_get_signal, "How are you gentlemen?")
|
63
|
+
end
|
64
|
+
|
65
|
+
def bomb(event, message)
|
66
|
+
puts "somebody set us up the bomb!"
|
67
|
+
end
|
68
|
+
|
69
|
+
we_get_signal do |event, message|
|
70
|
+
puts "Main screen turn on!"
|
71
|
+
puts "Cats: #{message}"
|
72
|
+
end
|
73
|
+
|
74
|
+
we_get_signal :bomb
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
zw = ZeroWing.new
|
79
|
+
zw.we_get_signal do
|
80
|
+
puts "Take off every zig!"
|
81
|
+
end
|
82
|
+
|
83
|
+
zw.start_game
|
84
|
+
# >> Main screen turn on!
|
85
|
+
# >> Cats: How are you gentlemen?
|
86
|
+
# >> somebody set us up the bomb!
|
87
|
+
# >> Take off every zig!
|
88
|
+
|
89
|
+
== DETAILS
|
90
|
+
|
91
|
+
Pour yourself a drink, loosen your tie, and let's get cozy with HookR.
|
92
|
+
|
93
|
+
=== Hooks
|
94
|
+
|
95
|
+
Hooks are at the center of HookR's functionality. A hook is a named attachment
|
96
|
+
point for arbitrary callbacks. It is the "publish" portion of
|
97
|
+
publish/subscribe. From the event-handling perspective, hooks define interesting
|
98
|
+
events in an object's lifetime. For example, an XML parser might define hooks
|
99
|
+
named <code>:tag_start</code> and <code>:tag_end</code> hooks. A network
|
100
|
+
protocol class might define <code>:connected</code> and
|
101
|
+
<code>:message_received</code> hooks. A database-backed model might define
|
102
|
+
<code>:before_save</code> and <code>:after_save</code> hooks.
|
103
|
+
|
104
|
+
Hooks are defined at the class level, using the <code>define_hook</code> method:
|
105
|
+
|
106
|
+
class ZeroWing
|
107
|
+
define_hook :we_get_signal
|
108
|
+
end
|
109
|
+
|
110
|
+
==== Hook Parameters
|
111
|
+
|
112
|
+
Sometimes we want to pass some data along with our events. Hooks can define
|
113
|
+
named parameters by passing extra symbol arguments to <code>define_hook</code>:
|
114
|
+
|
115
|
+
class ZeroWing
|
116
|
+
include HookR::Hooks
|
117
|
+
define_hook :we_get_signal, :message
|
118
|
+
end
|
119
|
+
|
120
|
+
==== Listing Hooks
|
121
|
+
|
122
|
+
You can access the full set of hooks defined for a particular class by calling
|
123
|
+
the #hooks class method:
|
124
|
+
|
125
|
+
ZeroWing.hooks # => #<Hookr::HookSet: ... >
|
126
|
+
|
127
|
+
If you are playing along at home you may notice a <code>:__wildcards__</code> hook in this
|
128
|
+
list. We'll talk about that in the Advanced section.
|
129
|
+
|
130
|
+
=== Callbacks
|
131
|
+
|
132
|
+
Hooks aren't much use without callbacks. Callbacks represent a piece of code to
|
133
|
+
be run when a hook is executed. They are the "subscribe" part of the
|
134
|
+
publish/subscribe duo.
|
135
|
+
|
136
|
+
HookR defines three types of callback:
|
137
|
+
|
138
|
+
==== Internal Callbacks
|
139
|
+
|
140
|
+
An internal callback represents a block of code which will be run in the context
|
141
|
+
of the source object (the object executing the hook). That is, it will be run
|
142
|
+
using #instance_eval. In general this type of callback should only be defined
|
143
|
+
internally to the class having the hook, since the called code will have full
|
144
|
+
access to private members and instance variables of the source object.
|
145
|
+
|
146
|
+
One drawback of internal callbacks is that due to limitations of
|
147
|
+
<code>#instance_eval</code>, they cannot receive arguments.
|
148
|
+
|
149
|
+
==== External Callbacks
|
150
|
+
|
151
|
+
An external callback is a block of code which will be executed in the context in
|
152
|
+
which it was defined. That is, a Proc which will be called with the Event
|
153
|
+
object (see below) and any parameters defined by the hook.
|
154
|
+
|
155
|
+
==== Method Callbacks
|
156
|
+
|
157
|
+
A method callback is a callback which when executed will call an instance method
|
158
|
+
on the source object (the object executing the hook). Like internal callbacks,
|
159
|
+
these should usually only be added internally by the source class, since private
|
160
|
+
methods may be called. The method will receive as arguments the Event (see
|
161
|
+
below), and an argument for each parameter defined on the hook.
|
162
|
+
|
163
|
+
==== Named Callbacks
|
164
|
+
|
165
|
+
A callback may be *named* or *anonymous*. Naming callbacks makes it easier to
|
166
|
+
reference them after adding them, for instance if you want to remove a
|
167
|
+
callback. Naming calbacks also ensures that only one callback with the given name
|
168
|
+
will be added to a hook.
|
169
|
+
|
170
|
+
==== Adding Callbacks
|
171
|
+
|
172
|
+
There are several ways to add callbacks to hooks.
|
173
|
+
|
174
|
+
===== Adding callbacks In the class definition
|
175
|
+
|
176
|
+
The first way to define a callback is to do it in the class definition:
|
177
|
+
|
178
|
+
class ZeroWing
|
179
|
+
include HookR::Hooks
|
180
|
+
define_hook :we_get_signal, :message
|
181
|
+
|
182
|
+
we_get_signal do
|
183
|
+
main_screen.turn_on!
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
HookR creates class-level macros for each defined hook. The above example
|
188
|
+
demonstrates an anonymous *internal* callback being defined on the hook
|
189
|
+
<code>:we_get_signal</code>. Why internal? HookR uses a set of rules to
|
190
|
+
determine what kind of callback to generate. If the block passed to the
|
191
|
+
callback macro has no arguments, it will generate an internal callback. If,
|
192
|
+
however, the block defines arguments:
|
193
|
+
|
194
|
+
class ZeroWing
|
195
|
+
include HookR::Hooks
|
196
|
+
define_hook :we_get_signal, :message
|
197
|
+
|
198
|
+
we_get_signal do |event, message|
|
199
|
+
puts message.what_you_say?
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
An *external* callback will be defined. Why external? As discussed earlier,
|
204
|
+
it is impossible for <code>instance_eval</code>-ed code to receive arguments. So in order
|
205
|
+
to supply the defined parameters an external callback must be defined.
|
206
|
+
|
207
|
+
If no block is passed, but a method name is supplied, a *method* callback will
|
208
|
+
be generated:
|
209
|
+
|
210
|
+
class ZeroWing
|
211
|
+
include HookR::Hooks
|
212
|
+
|
213
|
+
def take_off_every_zig(event, message)
|
214
|
+
# ...
|
215
|
+
end
|
216
|
+
|
217
|
+
define_hook :we_get_signal, :message
|
218
|
+
|
219
|
+
we_get_signal :take_off_every_zig
|
220
|
+
end
|
221
|
+
|
222
|
+
For any of the variations demonstrated above, an explicit symbolic callback
|
223
|
+
handle may be supplied. This handle can then be used to access or remove the
|
224
|
+
callback.
|
225
|
+
|
226
|
+
class ZeroWing
|
227
|
+
include HookR::Hooks
|
228
|
+
|
229
|
+
define_hook :we_get_signal, :message
|
230
|
+
|
231
|
+
we_get_signal :zig do
|
232
|
+
take_off_every_zig
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
ZeroWing.remove_callback(:zig)
|
237
|
+
|
238
|
+
===== In instance methods
|
239
|
+
|
240
|
+
In instance methods of the class defining the hook, it is possible to explicitly
|
241
|
+
add the different types of callback using the methods <code>add_external_callback</code>,
|
242
|
+
<code>add_internal_callback</code>, and #add_method_callback. See the method documentation
|
243
|
+
for details.
|
244
|
+
|
245
|
+
The methods all return a callback *handle*, which can be used to access or remove
|
246
|
+
the callback. This will be the same as the <code>handle</code> argument, if one is supplied.
|
247
|
+
|
248
|
+
===== In client code
|
249
|
+
|
250
|
+
In code that uses a hook-enabled object, callbacks can be easily added using a
|
251
|
+
method with the same name as the hook:
|
252
|
+
|
253
|
+
zw = ZeroWing.new
|
254
|
+
zw.we_get_signal do |event, message|
|
255
|
+
puts "it's you!"
|
256
|
+
end
|
257
|
+
|
258
|
+
Only *external* callbacks may be added using this method. This is consistent
|
259
|
+
with public/private class protection.
|
260
|
+
|
261
|
+
Like the <code>add_*_callback</code> methods described above, this method may be passed an
|
262
|
+
explicit symbolic handle. Whether an explicit handle is supplied or not, it
|
263
|
+
will always return a handle which can be used to access or remove the added
|
264
|
+
callback.
|
265
|
+
|
266
|
+
==== Removing Callbacks
|
267
|
+
|
268
|
+
<code>remove_callback</code> methods are available at both the class and instance levels.
|
269
|
+
They can be used to remove class- or instance-level callbacks, respectively.
|
270
|
+
Both forms take either a callback index or a handle.
|
271
|
+
|
272
|
+
=== Listeners
|
273
|
+
|
274
|
+
Listeners embody an alternative model of publish/subscribe event handling. A
|
275
|
+
Listener is an object which "listens" to another object. Instead of attaching
|
276
|
+
callbacks to individual hooks, you attach a listener to an entire object.
|
277
|
+
Anytime a hook is executed on the object being listened to, a method with a name
|
278
|
+
corresponding to the hook is called on the listener. These handler methods
|
279
|
+
should take arguments corresponding to the parameters defined on the hook.
|
280
|
+
|
281
|
+
This model is similar to the SAX XML event model, and to the Java
|
282
|
+
Event/EventListener model.
|
283
|
+
|
284
|
+
For more convenient listener definition, HookR can generate a base class for you
|
285
|
+
to base your listeners on. The base class will provide default do-nothing
|
286
|
+
methods for each hook, so you only have to redefine the methods you care about.
|
287
|
+
|
288
|
+
class ZeroWing
|
289
|
+
include HookR::Hooks
|
290
|
+
|
291
|
+
define_hook :we_get_signal, :message
|
292
|
+
|
293
|
+
define_hook :set_us_up_the_bomb
|
294
|
+
end
|
295
|
+
|
296
|
+
class MyListener < ZeroWing::Listener
|
297
|
+
def we_get_signal(message)
|
298
|
+
# ...
|
299
|
+
end
|
300
|
+
|
301
|
+
# :set_us_up_the_bomb events are silently ignored
|
302
|
+
end
|
303
|
+
|
304
|
+
zw = ZeroWing.new
|
305
|
+
l = MyListener.new
|
306
|
+
|
307
|
+
zw.add_listener(l)
|
308
|
+
|
309
|
+
=== Events
|
310
|
+
|
311
|
+
Events represent the execution of a hook. They encapsulate information about
|
312
|
+
the hook, the object executing the hook, and any parameters passed when the hook
|
313
|
+
was executed (see Execution, below). Events are normally passed as the first
|
314
|
+
argument to external callbacks and method callbacks.
|
315
|
+
|
316
|
+
Events have a few important attributes:
|
317
|
+
|
318
|
+
==== Source
|
319
|
+
|
320
|
+
The event *source* is the object which initiated the hook execution. Ordinarily
|
321
|
+
this is an instance of the class which defines the hook.
|
322
|
+
|
323
|
+
==== Name
|
324
|
+
|
325
|
+
Name is the hook name of the hook being executed. For instance, given the
|
326
|
+
following hook definition:
|
327
|
+
|
328
|
+
class ZeroWing
|
329
|
+
include HookR::Hooks
|
330
|
+
|
331
|
+
define_hook :we_get_signal, :message
|
332
|
+
end
|
333
|
+
|
334
|
+
the <code>name</code> would be <code>:we_get_signal</code>.
|
335
|
+
|
336
|
+
==== Arguments
|
337
|
+
|
338
|
+
Event <code>arguments</code> are the extra arguments passed to #execute_hook, corresponding
|
339
|
+
to the hook parameters (if any).
|
340
|
+
|
341
|
+
=== Execution
|
342
|
+
|
343
|
+
An instance of the hook-bearing class can initiate hook execution by calling
|
344
|
+
#execute_hook. It takes as arguments the hook name and an argument for every
|
345
|
+
parameter defined by the hook. Example:
|
346
|
+
|
347
|
+
class ZeroWing
|
348
|
+
include HookR::Hooks
|
349
|
+
|
350
|
+
define_hook :we_get_signal, :message
|
351
|
+
|
352
|
+
def game_start
|
353
|
+
execute_hook(:we_get_signal, "You have no chance to survive")
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
357
|
+
There are two models of callback execution. Each is described below.
|
358
|
+
|
359
|
+
==== Iterative
|
360
|
+
|
361
|
+
In the simple case, callback execution follows the iterative model. Each
|
362
|
+
callback is executed in turn (in order of addition). Callback return calues are
|
363
|
+
ignored.
|
364
|
+
|
365
|
+
==== Recursive
|
366
|
+
|
367
|
+
When #execute_hook is called with a block argument, recursive execution is
|
368
|
+
triggered. E.g.:
|
369
|
+
|
370
|
+
class ZeroWing
|
371
|
+
include HookR::Hooks
|
372
|
+
|
373
|
+
define_hook :we_get_signal, :message
|
374
|
+
|
375
|
+
def game_start
|
376
|
+
execute_hook(:we_get_signal, "You have no chance to survive") do |event, message|
|
377
|
+
puts message
|
378
|
+
end
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
In this model, the most recently defined hook will be called first. As usual,
|
383
|
+
it will be passed an event as its first argument. In order to continue
|
384
|
+
execution to the next callback, the first callback must call <code>event.next</code>. This
|
385
|
+
will cause the next-most-recently-defined callback to be executed, which will
|
386
|
+
again be passed an event with a #next method, and so on. Finally, when the last
|
387
|
+
callback is executed and calls <code>event.next</code>, the block passed to #execute_hook
|
388
|
+
will be called.
|
389
|
+
|
390
|
+
In this way, it is possible to "wrap" an event with callbacks or, in the
|
391
|
+
language of AOP, "around advice". At any point in the chain, a callback can opt
|
392
|
+
to pass new arguments to Event#next, which will then override the original
|
393
|
+
arguments for any callbacks further down the chain. This enables callbacks to
|
394
|
+
act as "filters" on the callback arguments.
|
395
|
+
|
396
|
+
WARNING: This area is still under active development, and the API may change.
|
397
|
+
Some ideas under consideration include automatically executing the next callback
|
398
|
+
even if Event#next is not explicitly called; and an Event#cancel method which
|
399
|
+
will prevent further callbacks from running.
|
400
|
+
|
401
|
+
=== Advanced
|
402
|
+
|
403
|
+
In which we take a look under HookR's clothes.
|
404
|
+
|
405
|
+
==== Adding multiple callbacks with the same name
|
406
|
+
|
407
|
+
When adding callbacks with an explicit handle, only one callback for that handle
|
408
|
+
can be added to a given hook. Subsequent attempts to add a callback with the
|
409
|
+
same name will silently fail. This makes adding named callbacks an idempotent
|
410
|
+
operation.
|
411
|
+
|
412
|
+
==== Hook Chaining
|
413
|
+
|
414
|
+
Every hook has a parent, to which it delegates execution when it is finished
|
415
|
+
executing its own callbacks. This is how class inheritance is handles, and how
|
416
|
+
it is possible for callbacks to be added at both the class and instanve levels.
|
417
|
+
Under normal circumstances, however, this is an implementation detail which Just
|
418
|
+
Works, and you can safely ignore it.
|
419
|
+
|
420
|
+
==== Wildcard Callbacks
|
421
|
+
|
422
|
+
It is possible to define a "wildcard" callback which will be called when *any*
|
423
|
+
hook is executed, using the #add_wildcard_callback class and instance methods.
|
424
|
+
|
425
|
+
==== Callback Arity
|
426
|
+
|
427
|
+
External and method callbacks must take at least as many arguments as
|
428
|
+
there are parameters on the hook. For instance, given the following hook
|
429
|
+
definition:
|
430
|
+
|
431
|
+
class ZeroWing
|
432
|
+
include HookR::Hooks
|
433
|
+
|
434
|
+
define_hook :take_off_every, :what, :why
|
435
|
+
end
|
436
|
+
|
437
|
+
Callbacks for <code>:take_off_every</code> must accept at least two arguments. If they accept
|
438
|
+
exactly two arguments, they will be passed the two arguments only. If they
|
439
|
+
accept three arguments, they will be passed the event object followed by the two
|
440
|
+
arguments. More than three arguments would be an error.
|
441
|
+
|
442
|
+
==== Custom Hook Classes
|
443
|
+
|
444
|
+
In some special cases it may be desirable to customize the specific class of
|
445
|
+
Hook generated by #define_hook. When this is the case you may define a custom
|
446
|
+
<code>make_hook</code> class method. This method will be passed a hook name, a parent
|
447
|
+
hook, and a list of parameters, and should return an instance of a subclass of
|
448
|
+
HookR::Hook or something that behaves very similarly.
|
449
|
+
|
450
|
+
== REQUIREMENTS:
|
451
|
+
|
452
|
+
* FailFast (http://fail-fast.rubyforge.org)
|
453
|
+
|
454
|
+
== INSTALL:
|
455
|
+
|
456
|
+
* sudo gem install hookr
|
457
|
+
|
458
|
+
== KNOWN BUGS
|
459
|
+
|
460
|
+
* It is currently not possible to define a method callback before the method has
|
461
|
+
been defined. This is either a bug or a feature, depending on your point of
|
462
|
+
view.
|
463
|
+
|
464
|
+
== SUPPORT/CONTRIBUTING
|
465
|
+
|
466
|
+
Questions, comments, suggestions, bug reports: Email Avdi Grimm at mailto:avdi@avdi.org
|
467
|
+
|
468
|
+
== LICENSE:
|
469
|
+
|
470
|
+
(The MIT License)
|
471
|
+
|
472
|
+
Copyright (c) 2008
|
473
|
+
|
474
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
475
|
+
a copy of this software and associated documentation files (the
|
476
|
+
'Software'), to deal in the Software without restriction, including
|
477
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
478
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
479
|
+
permit persons to whom the Software is furnished to do so, subject to
|
480
|
+
the following conditions:
|
481
|
+
|
482
|
+
The above copyright notice and this permission notice shall be
|
483
|
+
included in all copies or substantial portions of the Software.
|
484
|
+
|
485
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
486
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
487
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
488
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
489
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
490
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
491
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|