Skip to content

Commit

Permalink
Revises docs (namely for Yard)
Browse files Browse the repository at this point in the history
  • Loading branch information
brgix committed Aug 16, 2023
1 parent a46b432 commit d6e3188
Show file tree
Hide file tree
Showing 5 changed files with 173 additions and 241 deletions.
84 changes: 1 addition & 83 deletions .github/workflows/pull_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:
- dev

jobs:
test_300x:
test_oslg:
runs-on: ubuntu-22.04
steps:
- name: Check out repository
Expand All @@ -15,88 +15,6 @@ jobs:
run: |
echo $(pwd)
echo $(ls)
docker pull nrel/openstudio:3.0.0
docker run --name test --rm -d -t -v $(pwd):/work -w /work nrel/openstudio:3.0.0
docker exec -t test pwd
docker exec -t test ls
docker exec -t test bundle update
docker exec -t test bundle exec rake
docker kill test
test_321x:
runs-on: ubuntu-22.04
steps:
- name: Check out repository
uses: actions/checkout@v2
- name: Run Tests
run: |
echo $(pwd)
echo $(ls)
docker pull nrel/openstudio:3.2.1
docker run --name test --rm -d -t -v $(pwd):/work -w /work nrel/openstudio:3.2.1
docker exec -t test pwd
docker exec -t test ls
docker exec -t test bundle update
docker exec -t test bundle exec rake
docker kill test
test_330x:
runs-on: ubuntu-22.04
steps:
- name: Check out repository
uses: actions/checkout@v2
- name: Run Tests
run: |
echo $(pwd)
echo $(ls)
docker pull nrel/openstudio:3.3.0
docker run --name test --rm -d -t -v $(pwd):/work -w /work nrel/openstudio:3.3.0
docker exec -t test pwd
docker exec -t test ls
docker exec -t test bundle update
docker exec -t test bundle exec rake
docker kill test
test_340x:
runs-on: ubuntu-22.04
steps:
- name: Check out repository
uses: actions/checkout@v2
- name: Run Tests
run: |
echo $(pwd)
echo $(ls)
docker pull nrel/openstudio:3.4.0
docker run --name test --rm -d -t -v $(pwd):/work -w /work nrel/openstudio:3.4.0
docker exec -t test pwd
docker exec -t test ls
docker exec -t test bundle update
docker exec -t test bundle exec rake
docker kill test
test_351x:
runs-on: ubuntu-22.04
steps:
- name: Check out repository
uses: actions/checkout@v2
- name: Run Tests
run: |
echo $(pwd)
echo $(ls)
docker pull nrel/openstudio:3.5.1
docker run --name test --rm -d -t -v $(pwd):/work -w /work nrel/openstudio:3.5.1
docker exec -t test pwd
docker exec -t test ls
docker exec -t test bundle update
docker exec -t test bundle exec rake
docker kill test
test_361x:
runs-on: ubuntu-22.04
steps:
- name: Check out repository
uses: actions/checkout@v2
- name: Run Tests
run: |
echo $(pwd)
echo $(ls)
docker pull nrel/openstudio:3.6.1
docker run --name test --rm -d -t -v $(pwd):/work -w /work nrel/openstudio:3.6.1
docker exec -t test pwd
docker exec -t test ls
docker exec -t test bundle update
Expand Down
68 changes: 33 additions & 35 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
# oslg

A logger module for _picky_ [OpenStudio](https://openstudio.net) [Measure](https://nrel.github.io/OpenStudio-user-documentation/reference/measure_writing_guide/) developers who wish to select what gets logged to which target (e.g. OpenStudio _runner_ vs custom JSON file). Add:
A logger module, initially for _picky_ [OpenStudio](https://openstudio.net) [Measure](https://nrel.github.io/OpenStudio-user-documentation/reference/measure_writing_guide/) developers who wish to select what gets logged to which target (e.g. OpenStudio _runner_ vs custom JSON file). __oslg__ has no OpenStudio dependency, however; it can be integrated within any other environment. Just add:

```
gem "oslg", git: "https://github.com/rd2/oslg", branch: "main"
```

... in a v2.1 [bundled](https://bundler.io) _Measure_ development environment "Gemfile" (or instead as a _gemspec_ dependency), and then run:
... in a v2.1 [bundled](https://bundler.io) development environment "Gemfile" (or instead as a _gemspec_ dependency), and then run:

```
bundle install (or 'bundle update')
```

### OpenStudio & EnergyPlus

In most cases, critical (and many non-critical) OpenStudio anomalies will be caught by EnergyPlus at the start of a simulation. Standalone applications (e.g. _Apply Measures Now_) or [SDK](https://openstudio-sdk-documentation.s3.amazonaws.com/index.html)-based iterative solutions can't rely on EnergyPlus to catch such errors - and somehow warn users of potentially invalid results. This Ruby module provides developers a means to log warnings, as well as non-fatal & fatal errors, that may eventually put OpenStudio's (or EnergyPlus') internal processes at risk. Developers are free to decide how to harness __oslg__ as they see fit, e.g. output logged WARNING messages to the OpenStudio _runner_, while writing out DEBUG messages to a bug report file.
In most cases, critical (and many non-critical) OpenStudio anomalies will be caught by EnergyPlus at the start of a simulation. Standalone applications (e.g. _Apply Measures Now_) or [SDK](https://openstudio-sdk-documentation.s3.amazonaws.com/index.html) based iterative solutions can't rely on EnergyPlus to catch such errors - and somehow warn users of potentially invalid results. This Ruby module provides developers a means to log warnings, as well as non-fatal & fatal errors, that may eventually put OpenStudio's (or EnergyPlus') internal processes at risk. Developers are free to decide how to harness __oslg__ as they see fit, e.g. output logged WARNING messages to the OpenStudio _runner_, while writing out DEBUG messages to a bug report file.

### Recommended use

Expand All @@ -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 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 when the latter's log level is more severe than its predecessors (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 2 _global_ 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 when the latter's log level is more severe than its predecessors (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 @@ -54,7 +54,7 @@ It's sometimes not a bad idea to rely on a _clean_ slate (e.g. within RSpecs). T
M.clean!
```

EnergyPlus will run with e.g. out-of-range material or fluid properties, while logging ERROR messages in the process. It remains up to users to decide what to do with simulation results. We recommend something similar with __oslg__. For instance, we suggest logging as __FATAL__ any error that should halt Measure processes and prevent OpenStudio from launching an EnergyPlus simulation. This could be missing or poorly-defined OpenStudio files.
EnergyPlus will run with e.g. out-of-range material or fluid properties, while logging ERROR messages in the process. It remains up to users to decide what to do with simulation results. We recommend something similar with __oslg__. For instance, we suggest logging as __FATAL__ any error that should halt Measure processes and prevent OpenStudio from launching an EnergyPlus simulation. This could be missing or poorly formatted files.

```
M.log(M::FATAL, "Missing input JSON file")
Expand All @@ -78,13 +78,13 @@ There's also the possibility of logging __INFO__-rmative messages for users, e.g
M.log(M::INFO, "Envelope compliant to prescriptive code requirements")
```

Finally, a number of sanity checks are likely warranted to ensure Ruby doesn't crash (e.g., invalid access to uninitialized variables), especially for lower-level functions. We suggest implementing safe fallbacks when this occurs, but __DEBUG__ errors could nonetheless be triggered to signal a bug.
Finally, a number of sanity checks are likely warranted to ensure Ruby doesn't crash (e.g., invalid access to uninitialized variables), especially for lower-level functions. We suggest implementing safe fallbacks when this occurs, but __DEBUG__ errors could nonetheless be logged to signal buggy code.

```
M.log(M::DEBUG, "Hash? expecting Array (method)")
```

All log entries are stored in a single Ruby _Array_, with each individual log entry as a Ruby _Hash_ with 2x _keys_ ```:level``` and ```:message```, e.g.:
All log entries are stored in a single Ruby _Array_, with each individual log entry as a Ruby _Hash_ with 2 _keys_ ```:level``` and ```:message```, e.g.:

```
M.logs.each do |log|
Expand All @@ -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 an object (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__), such as:

```
unless var.is_a?(Array)
Expand All @@ -109,19 +109,19 @@ The following are __oslg__ one-liner methods that _log & return_ in one go. Thes

---

__invalid__: for logging e.g. uninitialized or nilled objects:
__invalid__: for logging e.g. nilled or inapplicable objects:

```
return M.invalid("area", "sum", 0, M::ERROR, false) unless area
return M.invalid("area", "sum", 0, M::FATAL, false) if area > 1000000
```

This logs an ERROR message informing users that an invalid object, 'area', was caught while running method 'sum', and then exits by returning _false_. The logged message would be:
This logs a FATAL error message informing users that an invalid object, 'area', was caught while running method 'sum', and then exits by returning _false_. The logged message would be:

```
"Invalid 'area' (sum)"
```

The 3rd argument (e.g. _0_) is ignored unless `> 0` - a useful option when asserting method arguments:
The 3rd parameter (e.g. _0_) is ignored unless `> 0` - a useful option when asserting method arguments:

```
def sum(areas, units)
Expand All @@ -131,93 +131,91 @@ def sum(areas, units)
end
```

... would generate the following if both `areas` and `units` arguments were for instance _nilled_:
... would generate the following if both `areas` and `units` arguments were, for instance, _nilled_:
```
"Invalid 'areas' arg #1 (sum)"
"Invalid 'units' arg #2 (sum)"
```

The first 2x __invalid__ method arguments (faulty object ID, calling method ID) are required. The remaining 3x arguments are optional; in such cases, __invalid__ `level` defaults to DEBUG, and __invalid__ returns _nil_).
The first 2 __invalid__ method parameters (faulty object ID, calling method ID) are required. The remaining 3 parameters are optional; in such cases, __invalid__ `level` defaults to DEBUG, and __invalid__ returns _nil_.

---

__mismatch__: for logging incompatible instances vs classes:

```
return M.mismatch("areas", areas, Array, "sum") unless areas.is_a?(Array)
return M.mismatch("area", area, Float, "sum") unless area.is_a?(Numeric)
```

If 'areas' were for example a _String_, __mismatch__ would generate the following DEBUG log message (before returning _nil_):
If 'area' were for example a _String_, __mismatch__ would generate the following DEBUG log message (before returning _nil_):

```
"'areas' String? expecting Array (sum)"
"'area' String? expecting Float (sum)"
```

These 4x __mismatch__ arguments are required (an object ID, a valid Ruby object, the mismatched Ruby class, and the calling method ID). As a safeguard, __oslg__ will NOT log a _mismatch_ if the object is an actual instance of the class. As with __invalid__, there are 2x optional _terminal_ arguments, e.g. `M::ERROR, false)`.
These 4 __mismatch__ parameters are required (an object ID, a valid Ruby object, the mismatched Ruby class, and the calling method ID). As a safeguard, __oslg__ will NOT log a _mismatch_ if the object is an actual instance of the class. As with __invalid__, there are 2 optional _terminal_ parameters (e.g. `M::FATAL, false)`.

---

__hashkey__: for logging missing _Hash_ keys:

```
return M.hashkey("faces", faces, :area, "sum") unless faces.key?(:area)
return M.hashkey("floor area", floor, :area, "sum") unless floor.key?(:area)
```

If the _Hash_ `faces` does not hold `:area` as one of its keys, then __hashkey__ would generate the following DEBUG log message (before returning _nil_):
If the _Hash_ `floor` does not hold `:area` as one of its keys, then __hashkey__ would generate the following DEBUG log message (before returning _nil_):

```
"Missing 'area' key in 'faces' Hash (sum)"
"Missing 'area' key in 'floor' Hash (sum)"
```

Similar to __mismatch__, the method __hashkey__ requires 4x arguments (a _Hash_ ID, a valid Ruby _Hash_, the missing _key_, and the calling method ID). There are also 2x optional _terminal_ arguments, e.g. `M::ERROR, false)`.
Similar to __mismatch__, the method __hashkey__ requires 4 parameters (a _Hash_ ID, a valid Ruby _Hash_, the missing _key_, and the calling method ID). There are also 2 optional _terminal_ parameters (e.g. `M::FATAL, false)`.

---

__empty__: for logging empty _Enumerable_ (e.g. _Array_, _Hash_) instances or uninitialized boost optionals (e.g. uninitialized _ThermalZone_ object of an _OpenStudio Space_):
__empty__: for logging empty _Enumerable_ (e.g. _Array_, _Hash_) instances or uninitialized _Boost_ optionals (e.g. uninitialized _ThermalZone_ object of an _OpenStudio Space_):

```
return M.empty("faces", "sum", M::ERROR, false) if faces.empty?
return M.empty("zone", "conditioned?") if space.thermalZone.empty?
```

An empty `faces` _Hash_ would generate the following ERROR log message (before returning _false_):
An empty (i.e. uninitialized) `thermalZone` would generate the following DEBUG log message (before returning _nil_):

```
"Empty 'faces' (sum)"
"Empty 'zone' (conditioned?)"
```

Again, the first 2x arguments are required; the last 2x are optional.
Again, the first 2 parameters are required; the last 2 are optional.

---

__zero__: for logging zero'ed (or nearly-zero'ed) values:

```
M.zero("area", "sum", M::FATAL, false) if area.zero?
M.zero("area", "sum", M::FATAL, false) if area.abs < TOL
M.zero("floor area", "sum", M::FATAL, false) if floor[:area].abs < TOL
```
... generating the following FATAL log message (before returning _false_):

```
"Zero 'area' (sum)"
"Zero 'area' (sum)"
"Zero 'floor area' (sum)"
```

And again, the first 2x arguments are required; the last 2x are optional.
And again, the first 2 parameters are required; the last 2 are optional.

---

__negative__: for logging negative (< 0) values:

```
M.negative("area", "sum", M::FATAL, false) if area < 0
M.negative("floor area", "sum", M::FATAL, false) if floor[:area] < 0
```
... generating this FATAL log message (before returning _false_):

```
"Negative 'area' (sum)"
"Negative 'floor area' (sum)"
```

You guessed it: the first 2x arguments are required; the last 2x as optionals.
You guessed it: the first 2 parameters are required; the last 2 as optionals.

---

Expand Down
Loading

0 comments on commit d6e3188

Please sign in to comment.