universals 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README +69 -0
- data/lib/universals.rb +8 -0
- data/lib/universals/exceptions.rb +40 -0
- data/lib/universals/universe.rb +161 -0
- data/lib/universals/version.rb +11 -0
- data/license.txt +21 -0
- metadata +52 -0
data/README
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
Universals
|
2
|
+
==========
|
3
|
+
Everyone knows that global variables are bad. Sometimes though there are values
|
4
|
+
that need to visible to all aspects of your code. Universals is a library that
|
5
|
+
provides a class that allows for the aggregation of application or library wide
|
6
|
+
data values. The Universe class becomes a centralized location for the storage
|
7
|
+
or retrieval of such values. This avoids the need to use global variables or to
|
8
|
+
pass such values down through the stack to the places they are actually used
|
9
|
+
while avoiding the namespace pollution of global variables.
|
10
|
+
|
11
|
+
Installing The Library
|
12
|
+
----------------------
|
13
|
+
The Universals library is provided as a Ruby gem and thus can be installed with
|
14
|
+
a line such as...
|
15
|
+
|
16
|
+
$> gem install universals
|
17
|
+
|
18
|
+
Using The Library
|
19
|
+
-----------------
|
20
|
+
The library contains a single class called Universe. This class acts very much
|
21
|
+
like a Hash (with a few restrictions). Universe has mixed in Singleton and so
|
22
|
+
there should only ever be one instance of the class that can be accessed using
|
23
|
+
code such as the following...
|
24
|
+
|
25
|
+
Universals::Universe.instance
|
26
|
+
|
27
|
+
The value returned from this call can be treated like a Hash in that values can
|
28
|
+
be set under keys using the array assignment operator like so...
|
29
|
+
|
30
|
+
Universals::Universe.instance["my_value"] = "Blah-de-blah"
|
31
|
+
|
32
|
+
Note that key names must be a String object. In addition, as the class makes the
|
33
|
+
stored values available as properties on the Universe instance (see later for
|
34
|
+
more details), the key must conform to many of the rules for method names. It
|
35
|
+
can only start with a letter (upper or lower case) or an underscore and the
|
36
|
+
remainder of the name must be made up of letters, underscores or numbers. The
|
37
|
+
key may end with either '!' or '?'. Names ending with '=' or containing '[' or
|
38
|
+
']' are explicitly excluded as they would be confusing. If you try to set a
|
39
|
+
value on a Universe instance that does not conform to these rules then you will
|
40
|
+
receive an exception for your trouble.
|
41
|
+
|
42
|
+
As was mention previously, the Universe class makes values stored within it
|
43
|
+
available as properties on the object itself. So, the value set in the previous
|
44
|
+
example could be accessed using code such as...
|
45
|
+
|
46
|
+
Universals::Universe.instance.my_value
|
47
|
+
|
48
|
+
Likewise, values can be set as if they were properties on the object. So, the
|
49
|
+
following are valid assignments...
|
50
|
+
|
51
|
+
Universals::Universe.instance.my_value = 12345
|
52
|
+
Universals::Universe.instance.other_value = "Something else."
|
53
|
+
|
54
|
+
As the Universe class is a singleton, any values assigned into it can be
|
55
|
+
retrieved anywhere. Note, that no effort has been made to make the Universe
|
56
|
+
class thread safe. It is recommended that you assign all of the values needed
|
57
|
+
to the object in a single place and thereafter treat the object as read only
|
58
|
+
if you want to use it in multi-threaded code.
|
59
|
+
|
60
|
+
Deferred Evaluation
|
61
|
+
-------------------
|
62
|
+
If you want to defer the evaluation of a value you can, instead of directly
|
63
|
+
storing the value under it's key, store a Proc that evaluates to the correct
|
64
|
+
value under the key. The next time this key is explicitly fetched from the
|
65
|
+
Universe instance the Proc will be called (with no parameters) and the value
|
66
|
+
it returns will be set under the key instead. Using this technique you can
|
67
|
+
defer the creation of items until they are actually used, allowing them to be
|
68
|
+
created at more appropriate points in the code and avoiding the possible
|
69
|
+
overhead associated with the values creation.
|
data/lib/universals.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# Copyright (c), 2012 Peter Wood
|
4
|
+
# See the license.txt for details of the licensing of the code in this file.
|
5
|
+
|
6
|
+
require 'stringio'
|
7
|
+
|
8
|
+
module Universals
|
9
|
+
# This class provides the exception class used by the Universals library.
|
10
|
+
class UniversalsError < StandardError
|
11
|
+
# Attribute accessor/mutator declarations.
|
12
|
+
attr_accessor :verbose
|
13
|
+
|
14
|
+
# Constructor for the UniversalsError class.
|
15
|
+
#
|
16
|
+
# ==== Parameters
|
17
|
+
# message:: The message to be associated with the error.
|
18
|
+
# cause:: Any underlying exception to be associated with the error.
|
19
|
+
# Defaults to nil.
|
20
|
+
def initialize(message, cause=nil, verbose=true)
|
21
|
+
super(message)
|
22
|
+
@cause = cause
|
23
|
+
@verbose = verbose
|
24
|
+
end
|
25
|
+
|
26
|
+
# This method fetches a stringified interpretation of an exception.
|
27
|
+
def to_s()
|
28
|
+
text = StringIO.new
|
29
|
+
text << super
|
30
|
+
if @verbose
|
31
|
+
text << "\n" + self.backtrace.join("\n")
|
32
|
+
if !@cause.nil?
|
33
|
+
text << "\n\nCause: #{@cause}"
|
34
|
+
text << "\n" + @cause.backtrace.join("\n")
|
35
|
+
end
|
36
|
+
end
|
37
|
+
text.string
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# Copyright (c), 2012 Peter Wood
|
4
|
+
# See the license.txt for details of the licensing of the code in this file.
|
5
|
+
|
6
|
+
require 'singleton'
|
7
|
+
|
8
|
+
module Universals
|
9
|
+
# The Universals class includes the Ruby Singleton mixin and provides a
|
10
|
+
# single source location to store and retrieve data that is used across
|
11
|
+
# the entirety of an application or libraries code base. This avoids having
|
12
|
+
# to use global variables or to have to pass data elements around. The class
|
13
|
+
# behaves very much like a Hash, mapping keys to values. The class also
|
14
|
+
# makes the values stored within it available as properties, so all values
|
15
|
+
# stored within a Universe must be stored under String keys with names that
|
16
|
+
# conform to standard Ruby method naming criteria.
|
17
|
+
class Universe
|
18
|
+
# Mixin the singleton stuff.
|
19
|
+
include Singleton
|
20
|
+
|
21
|
+
# Mixin the enumerable stuff.
|
22
|
+
include Enumerable
|
23
|
+
|
24
|
+
# Constructor for the Universe class.
|
25
|
+
def initialize
|
26
|
+
@values = {}
|
27
|
+
end
|
28
|
+
|
29
|
+
# This method is used to check whether a property is stored under a given
|
30
|
+
# key within a Universe object.
|
31
|
+
#
|
32
|
+
# ==== Parameters
|
33
|
+
# key:: The key to perform the check for.
|
34
|
+
def has_property?(key)
|
35
|
+
@values.include?(key)
|
36
|
+
end
|
37
|
+
|
38
|
+
# This method fetches the value associated with a given key.
|
39
|
+
#
|
40
|
+
# ==== Parameters
|
41
|
+
# key:: The key for the value to be retrieved.
|
42
|
+
# default:: The default to return if the key does not exist within the
|
43
|
+
# Universe object. Defaults to nil.
|
44
|
+
def get_property(key, default=nil)
|
45
|
+
value = @values.fetch(key, default)
|
46
|
+
if value.respond_to?(:call)
|
47
|
+
value = value.call
|
48
|
+
@values[key] = value
|
49
|
+
end
|
50
|
+
value
|
51
|
+
end
|
52
|
+
|
53
|
+
# This method assigns a value under a specified key within a Universe
|
54
|
+
# object. Existing entries will be overwritten by this method.
|
55
|
+
def set_property(key, value)
|
56
|
+
validate_name(key)
|
57
|
+
@values[key] = value
|
58
|
+
end
|
59
|
+
|
60
|
+
# This method allows multiple values to be set on the Universe object
|
61
|
+
# in a single request.
|
62
|
+
#
|
63
|
+
# ==== Parameters
|
64
|
+
# values:: A Hash of the values to be set on the object. Keys should be
|
65
|
+
# adhere to the standard name requirements for the Universe
|
66
|
+
# object or an exception will be raised. Values specified in
|
67
|
+
# this way will override values already in the object.
|
68
|
+
def set(values={})
|
69
|
+
values.each {|name, value| set_property(name, value)}
|
70
|
+
end
|
71
|
+
|
72
|
+
# Implementation of the each() method for the Universe class.
|
73
|
+
def each(&block)
|
74
|
+
@values.each(&block)
|
75
|
+
end
|
76
|
+
|
77
|
+
# This method fetches a count of the number of items held within an
|
78
|
+
# instance of the Universe class.
|
79
|
+
def size
|
80
|
+
@values.size
|
81
|
+
end
|
82
|
+
|
83
|
+
# This method fetches the key associated with a value stored in the
|
84
|
+
# Universe instance.
|
85
|
+
#
|
86
|
+
# ==== Parameters
|
87
|
+
# value:: The value to retrieve the key for.
|
88
|
+
def key(value)
|
89
|
+
@values.key(value)
|
90
|
+
end
|
91
|
+
|
92
|
+
# This method fetches a collection of the keys stored within a Universe
|
93
|
+
# instance.
|
94
|
+
def keys
|
95
|
+
@values.keys
|
96
|
+
end
|
97
|
+
|
98
|
+
# This method fetches a collection of the values stored within a Universe
|
99
|
+
# instance.
|
100
|
+
def values
|
101
|
+
@values.values
|
102
|
+
end
|
103
|
+
|
104
|
+
# This method deletes all entries from a Universe instance.
|
105
|
+
def clear
|
106
|
+
@values.clear
|
107
|
+
end
|
108
|
+
|
109
|
+
# This method overrides the default respond_to?() method to provide
|
110
|
+
# customer property handling for Universe objects.
|
111
|
+
#
|
112
|
+
# ==== Parameters
|
113
|
+
# name:: The name of the method to perform the check for.
|
114
|
+
def respond_to?(name)
|
115
|
+
@hash.include?(name) || super(name)
|
116
|
+
end
|
117
|
+
|
118
|
+
# This method is invoked whenever a method invocation takes place
|
119
|
+
# against a Universe object but an existing explicit method cannot be
|
120
|
+
# found to meet the call.
|
121
|
+
#
|
122
|
+
# ==== Parameters
|
123
|
+
# name:: The name of the method invoked.
|
124
|
+
# arguments:: A collection of the arguments passed to the method call.
|
125
|
+
# block:: Any block associated with the method call.
|
126
|
+
def method_missing(name, *arguments, &block)
|
127
|
+
if @values.include?(name.to_s)
|
128
|
+
get_property(name.to_s)
|
129
|
+
elsif name.to_s[-1, 1] == "="
|
130
|
+
property = name.to_s
|
131
|
+
property = property[0, property.length - 1]
|
132
|
+
set_property(property, arguments[0])
|
133
|
+
self
|
134
|
+
else
|
135
|
+
super
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
# Class aliases.
|
140
|
+
alias :[] :get_property
|
141
|
+
alias :[]= :set_property
|
142
|
+
alias :include? :has_property?
|
143
|
+
|
144
|
+
private
|
145
|
+
|
146
|
+
# This method is used internally by the class to determine whether a key
|
147
|
+
# is valid for use with a Universe object.
|
148
|
+
def validate_name(name)
|
149
|
+
if !name.kind_of?(String)
|
150
|
+
raise UniversalsError.new("The key '#{name}' cannot be used in a "\
|
151
|
+
"Universe object as it is not a String.")
|
152
|
+
end
|
153
|
+
|
154
|
+
if /^[a-zA-Z_]+[a-zA-Z_0-9]*[\?!]?$/.match(name).nil?
|
155
|
+
raise UniversalsError.new("The key '#{name}' cannot be used in a "\
|
156
|
+
"Universe object as it is not a valid "\
|
157
|
+
"method name.")
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# ! /usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# Copyright (c) 2012, Peter Wood
|
4
|
+
# See the license.txt for details of the licensing of the code in this file.
|
5
|
+
|
6
|
+
module Universals
|
7
|
+
MAJOR_VERSION = 0
|
8
|
+
MINOR_VERSION = 0
|
9
|
+
BUILD_VERSION = 2
|
10
|
+
VERSION = "#{MAJOR_VERSION}.#{MINOR_VERSION}.#{BUILD_VERSION}"
|
11
|
+
end
|
data/license.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2011 Peter Wood
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
metadata
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: universals
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Black North
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-01-21 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: Universals provides a simple singleton class that can be used to store
|
15
|
+
and retrieve data that is used throughout and application and thereby avoid global
|
16
|
+
variables.
|
17
|
+
email: ruby@blacknorth.com
|
18
|
+
executables: []
|
19
|
+
extensions: []
|
20
|
+
extra_rdoc_files: []
|
21
|
+
files:
|
22
|
+
- lib/universals.rb
|
23
|
+
- lib/universals/version.rb
|
24
|
+
- lib/universals/universe.rb
|
25
|
+
- lib/universals/exceptions.rb
|
26
|
+
- license.txt
|
27
|
+
- README
|
28
|
+
homepage: https://github.com/free-beer/Universals
|
29
|
+
licenses: []
|
30
|
+
post_install_message:
|
31
|
+
rdoc_options: []
|
32
|
+
require_paths:
|
33
|
+
- lib
|
34
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
35
|
+
none: false
|
36
|
+
requirements:
|
37
|
+
- - ! '>='
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
40
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
requirements: []
|
47
|
+
rubyforge_project:
|
48
|
+
rubygems_version: 1.8.24
|
49
|
+
signing_key:
|
50
|
+
specification_version: 3
|
51
|
+
summary: A library to provide a source for application wide data.
|
52
|
+
test_files: []
|