Skip to content

Commit

Permalink
Merge pull request #7 from rd2/dev
Browse files Browse the repository at this point in the history
Trims down one-liners
  • Loading branch information
brgix authored Aug 1, 2022
2 parents 802b600 + d57f52f commit 3f36fb7
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 77 deletions.
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ FATAL

DEBUG messages aren't benign at all, but are certainly less informative for the typical Measure user.

Initially, __oslg__ sets 2x internal variable states: `level` (INFO) and `status` (< DEBUG). The variable `level` is a user-set threshold below which less severe logs (e.g. DEBUG) are ignored. For instance, if `level` were _reset_ to DEBUG (e.g. `M.reset(M::DEBUG)`), then all DEBUG messages would also be logged. The variable `status` is reset with each new log entry if the latter's log level is more severe than its predecessor (e.g. `status == M::FATAL` if there is a single log entry registered as FATAL). To check the curent __oslg__ `status` (true or false):
Initially, __oslg__ sets 2x internal attributes: `level` (INFO) and `status` (< DEBUG). The `level` attribute is a user-set threshold below which less severe logs (e.g. DEBUG) are ignored. For instance, if `level` were _reset_ to DEBUG (e.g. `M.reset(M::DEBUG)`), then all DEBUG messages would also be logged. The `status` attribute is reset with each new log entry if the latter's log level is more severe than its predecessor (e.g. `status == M::FATAL` if there is a single log entry registered as FATAL). To check the curent __oslg__ `status` (true or false):

```
M.debug?
Expand All @@ -48,7 +48,7 @@ M.error?
M.fatal?
```

It's sometimes not a bad idea to rely on a _clean_ slate (e.g. within RSpecs). The following flushes out all previous logs and resets `level` (INFO) and `status` (< DEBUG) - use with caution in production code!
It's sometimes not a bad idea to rely on a _clean_ slate (e.g. within RSpecs). The following purges all previous logs and resets `level` (INFO) and `status` (< DEBUG) - use with caution in production code!

```
M.clean!
Expand All @@ -60,7 +60,7 @@ EnergyPlus will run, with e.g. out-of-range material or fluid properties, while
M.log(M::FATAL, "Missing input JSON file")
```

Consider logging non-fatal __ERROR__ messages when encountering invalid OpenStudio file entries, i.e. well-defined, yet invalid vis-à-vis EnergyPlus limitations. The invalid object could be simply ignored, while the measure pursues its (otherwise valid) calculations ... with OpenStudio ultimately launching an EnergyPlus simulation. If a simulation indeed ran (ultimately a go/no-go decision made by the EnergyPlus simulation engine), it would be up to users to decide if simulation results were valid or useful, given the context - maybe based on __oslg__ logged messages. In short, non-fatal ERROR logs should ideally point to bad input users can fix.
Consider logging non-fatal __ERROR__ messages when encountering invalid OpenStudio file entries, i.e. well-defined, yet invalid vis-à-vis EnergyPlus limitations. The invalid object could be simply ignored, while the measure pursues its (otherwise valid) calculations ... with OpenStudio ultimately launching an EnergyPlus simulation. If a simulation indeed ran (ultimately a go/no-go decision made by the EnergyPlus simulation engine), it would be up to users to decide if simulation results were valid or useful, given the context - maybe based on __oslg__ logged messages. In short, non-fatal ERROR logs should ideally point to bad input (that users can fix).

```
M.log(M::ERROR, "Measure won't process MASSLESS materials")
Expand Down Expand Up @@ -96,7 +96,7 @@ These logs can be first _mapped_ to other structures (then edited), depending on

### Preset log templates

Typically, developers would first catch bad input, log an error message and possibly exit by returning a variable (e.g. __false__, __nil__), e.g.:
Typically, developers would first catch bad input, log an error message and possibly exit by returning an object (e.g. __false__, __nil__), e.g.:

```
unless var.is_a?(Array)
Expand All @@ -105,7 +105,7 @@ unless var.is_a?(Array)
end
```

The following are __oslg__ one-liner methods that _log & return_ in one go. These are for some of the most common checks OpenStudio SDK Ruby developers are likely to need. The methods require _valid_ arguments for __oslg__ to actually log. Although often expecting strings as arguments, the methods will try to convert other types to strings (e.g. classes, numbers, even entire arrays) if possible.
The following are __oslg__ one-liner methods that _log & return_ in one go. These are for some of the most common checks OpenStudio SDK Ruby developers are likely to need. The methods require _valid_ arguments for __oslg__ to actually log. Although often expecting either strings or integers as arguments, the methods will try to convert other types to strings (e.g. classes, numbers, even entire arrays) or integers if possible.

---

Expand All @@ -125,8 +125,8 @@ The 3rd argument (e.g. _0_) is ignored unless `> 0` - a useful option when asser

```
def sum(areas, units)
return M.invalid("areas", "sum", 1) unless areas
return M.invalid("units", "sum", 2) unless units
return M.invalid("areas", "sum", 1) unless areas.respond_to?(:to_f)
return M.invalid("units", "sum", 2) unless units.respond_to?(:to_s)
...
end
```
Expand Down
147 changes: 82 additions & 65 deletions lib/oslg/oslog.rb
Original file line number Diff line number Diff line change
Expand Up @@ -171,28 +171,32 @@ def log(level = DEBUG, message = "")
#
# @param id [String] invalid object identifier
# @param mth [String] calling method identifier
# @param ord [String] calling method argument order number of obj (optional)
# @param ord [Integer] calling method argument order number of obj (optional)
# @param lvl [Integer] DEBUG, INFO, WARN, ERROR or FATAL (optional)
# @param res [Object] what to return (optional)
#
# @return [Object] return object if specified by user
# @return [Nil] nil if return object missing or invalid
# @return [Nil] nil if return object undefined
def invalid(id = "", mth = "", ord = 0, lvl = DEBUG, res = nil)
return nil unless defined?(res)
return res unless defined?(id ) && id
return res unless defined?(mth) && mth
return res unless defined?(ord) && ord
return res unless defined?(lvl) && lvl
return res unless id.respond_to?(:to_s)
return res unless mth.respond_to?(:to_s)
return res unless ord.respond_to?(:to_i)
return res unless lvl.respond_to?(:to_i)

id = id.to_s.strip
mth = mth.to_s.strip
mth = mth[0...60] + " ..." if mth.length > 60
return res if mth.empty?
id = id.to_s.strip
ord = ord.to_i
lvl = lvl.to_i

id = id[0...60] + " ..." if id.length > 60
return res if id.empty?

mth = mth[0...60] + " ..." if mth.length > 60
return res if mth.empty?

msg = "Invalid '#{id}' "
msg += "arg ##{ord} " if ord.is_a?(Integer) && ord > 0
msg += "arg ##{ord} " if ord > 0
msg += "(#{mth})"
lvl = lvl.to_i unless lvl.is_a?(Integer)
log(lvl, msg) if lvl >= DEBUG && lvl <= FATAL
res
end
Expand All @@ -208,24 +212,25 @@ def invalid(id = "", mth = "", ord = 0, lvl = DEBUG, res = nil)
# @param res [Object] what to return (optional)
#
# @return [Object] return object if specified by user
# @return [Nil] nil if return object missing or invalid
# @return [Nil] nil if return object undefined
def mismatch(id = "", obj = nil, cl = nil, mth = "", lvl = DEBUG, res = nil)
return nil unless defined?(res)
return res unless defined?(id ) && id
return res unless defined?(obj) && obj
return res unless defined?(cl ) && cl
return res unless defined?(mth) && mth
return res unless defined?(lvl) && lvl
return res unless id.respond_to?(:to_s)
return res unless cl.is_a?(Class)
return res if obj.is_a?(cl)
return res unless mth.respond_to?(:to_s)
return res unless lvl.respond_to?(:to_i)

mth = mth.to_s.strip
mth = mth[0...60] + " ..." if mth.length > 60
return res if mth.empty?
id = id.to_s.strip
id = id.to_s.strip
lvl = lvl.to_i

id = id[0...60] + " ..." if id.length > 60
return res if id.empty?
return res unless cl.is_a?(Class)
return res if obj.is_a?(cl)

mth = mth[0...60] + " ..." if mth.length > 60
return res if mth.empty?

msg = "'#{id}' #{obj.class}? expecting #{cl} (#{mth})"
lvl = lvl.to_i unless lvl.is_a?(Integer)
log(lvl, msg) if lvl >= DEBUG && lvl <= FATAL
res
end
Expand All @@ -241,24 +246,25 @@ def mismatch(id = "", obj = nil, cl = nil, mth = "", lvl = DEBUG, res = nil)
# @param res [Object] what to return (optional)
#
# @return [Object] return object if specified by user
# @return [Nil] nil if return object missing or invalid
# @return [Nil] nil if return object undefined
def hashkey(id = "", hsh = {}, key = "", mth = "", lvl = DEBUG, res = nil)
return nil unless defined?(res)
return res unless defined?(id ) && id
return res unless defined?(hsh) && hsh
return res unless defined?(key) && key
return res unless defined?(mth) && mth
return res unless defined?(lvl) && lvl
return res unless id.respond_to?(:to_s)
return res unless hsh.is_a?(Hash)
return res if hsh.key?(key)
return res unless mth.respond_to?(:to_s)
return res unless lvl.respond_to?(:to_i)

id = id.to_s.strip
mth = mth.to_s.strip
mth = mth[0...60] + " ..." if mth.length > 60
return res if mth.empty?
id = id.to_s.strip
lvl = lvl.to_i

id = id[0...60] + " ..." if id.length > 60
return res if id.empty?
return mismatch(id, hsh, Hash, mth, lvl, res) unless hsh.is_a?(Hash)
return res if hsh.key?(key)

mth = mth[0...60] + " ..." if mth.length > 60
return res if mth.empty?

msg = "Missing '#{key}' key in '#{id}' Hash (#{mth})"
lvl = lvl.to_i unless lvl.is_a?(Integer)
log(lvl, msg) if lvl >= DEBUG && lvl <= FATAL
res
end
Expand All @@ -272,20 +278,23 @@ def hashkey(id = "", hsh = {}, key = "", mth = "", lvl = DEBUG, res = nil)
# @param res [Object] what to return (optional)
#
# @return [Object] return object if specified by user
# @return [Nil] nil if return object missing or invalid
# @return [Nil] nil if return object undefined
def empty(id = "", mth = "", lvl = DEBUG, res = nil)
return nil unless defined?(res)
return res unless defined?(id ) && id
return res unless defined?(mth) && mth
return res unless defined?(lvl) && lvl
return res unless id.respond_to?(:to_s)
return res unless mth.respond_to?(:to_s)
return res unless lvl.respond_to?(:to_i)

id = id.to_s.strip
mth = mth.to_s.strip
mth = mth[0...60] + " ..." if mth.length > 60
return res if mth.empty?
id = id.to_s.strip
lvl = lvl.to_i

id = id[0...60] + " ..." if id.length > 60
return res if id.empty?

mth = mth[0...60] + " ..." if mth.length > 60
return res if mth.empty?

msg = "Empty '#{id}' (#{mth})"
lvl = lvl.to_i unless lvl.is_a?(Integer)
log(lvl, msg) if lvl >= DEBUG && lvl <= FATAL
res
end
Expand All @@ -299,20 +308,24 @@ def empty(id = "", mth = "", lvl = DEBUG, res = nil)
# @param res [Object] what to return (optional)
#
# @return [Object] return object if specified by user
# @return [Nil] nil if return object missing or invalid
# @return [Nil] nil if return object undefined
def zero(id = "", mth = "", lvl = DEBUG, res = nil)
return nil unless defined?(res)
return res unless defined?(id ) && id
return res unless defined?(mth) && mth
return res unless defined?(lvl) && lvl
return res unless id.respond_to?(:to_s)
return res unless mth.respond_to?(:to_s)
return res unless lvl.respond_to?(:to_i)

id = id.to_s.strip
mth = mth.to_s.strip
mth = mth[0...60] + " ..." if mth.length > 60
return res if mth.empty?
id = id.to_s.strip
ord = ord.to_i
lvl = lvl.to_i

id = id[0...60] + " ..." if id.length > 60
return res if id.empty?

mth = mth[0...60] + " ..." if mth.length > 60
return res if mth.empty?

msg = "Zero '#{id}' (#{mth})"
lvl = lvl.to_i unless lvl.is_a?(Integer)
log(lvl, msg) if lvl >= DEBUG && lvl <= FATAL
res
end
Expand All @@ -326,20 +339,24 @@ def zero(id = "", mth = "", lvl = DEBUG, res = nil)
# @param res [Object] what to return (optional)
#
# @return [Object] return object if specified by user
# @return [Nil] nil if return object missing or invalid
# @return [Nil] nil if return object undefined
def negative(id = "", mth = "", lvl = DEBUG, res = nil)
return nil unless defined?(res)
return res unless defined?(id ) && id
return res unless defined?(mth) && mth
return res unless defined?(lvl) && lvl
return res unless id.respond_to?(:to_s)
return res unless mth.respond_to?(:to_s)
return res unless lvl.respond_to?(:to_i)

id = id.to_s.strip
mth = mth.to_s.strip
mth = mth[0...60] + " ..." if mth.length > 60
return res if mth.empty?
id = id.to_s.strip
ord = ord.to_i
lvl = lvl.to_i

id = id[0...60] + " ..." if id.length > 60
return res if id.empty?

mth = mth[0...60] + " ..." if mth.length > 60
return res if mth.empty?

msg = "Negative '#{id}' (#{mth})"
lvl = lvl.to_i unless lvl.is_a?(Integer)
log(lvl, msg) if lvl >= DEBUG && lvl <= FATAL
res
end
Expand Down
2 changes: 1 addition & 1 deletion lib/oslg/version.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,5 @@
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

module OSlg
VERSION = "0.2.4".freeze
VERSION = "0.2.5".freeze
end
11 changes: 7 additions & 4 deletions spec/oslg_tests_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@
expect(cls2.logs.first.key?(:message))
expect(cls2.logs.first[:message]).to eq("Invalid 'x' arg #2 (String)")

expect(cls2.clean!).to eq(cls2::DEBUG)
expect(cls2.invalid("x", String, 2, nil).nil?).to be(true)
expect(cls2.logs.empty?).to be(true)

expect(cls2.clean!).to eq(cls2::DEBUG)
expect(cls2.mismatch("x", "y", Hash, "foo").nil?).to be(true)
expect(cls2.logs.size).to eq(1)
Expand All @@ -78,7 +82,8 @@
expect(cls2.mismatch("x", "String", String, "foo").nil?).to be(true)
expect(cls2.logs.empty?).to be(true)

expect(cls2.mismatch("x", "String", Array, (1..60).to_a).nil?).to be(true)
array = (1..60).to_a
expect(cls2.mismatch("x", "String", Array, array).nil?).to be(true)
expect(cls2.logs.size).to eq(1)
expect(cls2.logs.first.key?(:message))
str = "'x' String? expecting Array ([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,"
Expand All @@ -97,9 +102,7 @@

expect(cls2.clean!).to eq(cls2::DEBUG)
expect(cls2.hashkey("x", [0, 1], :foo, "bar")).to be(nil)
expect(cls2.logs.size).to eq(1)
expect(cls2.logs.first.key?(:message))
expect(cls2.logs.first[:message]).to eq("'x' Array? expecting Hash (bar)")
expect(cls2.logs.empty?).to be(true)

expect(cls2.clean!).to eq(cls2::DEBUG)
expect(cls2.empty("x", "foo")).to be(nil)
Expand Down

0 comments on commit 3f36fb7

Please sign in to comment.