mellon-core 0.8.0.2 → 0.8.0.3
raw patch · 3 files changed
+242/−203 lines, 3 filesdep +faildep +mellon-coredep +semigroupsdep ~QuickCheckdep ~doctestPVP ok
version bump matches the API change (PVP)
Dependencies added: fail, mellon-core, semigroups
Dependency ranges changed: QuickCheck, doctest
API changes (from Hackage documentation)
Files
- changelog.md +12/−0
- mellon-core.cabal +226/−198
- test/Mellon/Controller/AsyncSpec.hs +4/−5
changelog.md view
@@ -1,3 +1,15 @@+## 0.8.0.3 (2018-01-11)++- Use hpack.++- Support for GHC 8.2.2.++- Bump doctest, QuickCheck upper bounds.++- A few test fixes.++- Maintainer-related changes (better Nix support, Makefile, etc.).+ ## 0.8.0.2 (2017-09-04) - No changes, version bump only.
mellon-core.cabal view
@@ -1,207 +1,235 @@-Name: mellon-core-Version: 0.8.0.2-Cabal-Version: >= 1.18-Build-Type: Simple-Author: Drew Hess <dhess-src@quixoftic.com>-Maintainer: Drew Hess <dhess-src@quixoftic.com>-Homepage: https://github.com/quixoftic/mellon/-Bug-Reports: https://github.com/quixoftic/mellon/issues/-Stability: experimental-License: BSD3-License-File: LICENSE-Copyright: Copyright (c) 2017, Quixoftic, LLC-Tested-With: GHC == 7.10.3, GHC == 8.0.1, GHC == 8.0.2-Category: System-Synopsis: Control physical access devices-Description:- /Speak, friend, and enter./- .- @mellon-core@ is a Haskell package for controlling physical access- devices designed for human factors, e.g., electric strikes. The- access control protocol is quite simple: a device is either locked,- or it is unlocked until a particular date and time (an- /expiration date/). Once the expiration date passes, the device is- automatically locked again. In the meantime, the device can be- locked immediately, overriding the unlocked state; or the unlock- period can be extended.- .- User programs incorporate @mellon-core@ functionality via a- /controller/, which is responsible for handling user lock and unlock- commands, and for scheduling and canceling unlock expirations.- .- User programs must also adapt their physical access devices to the- interface expected by the controller. For this purpose,- @mellon-core@ defines a /device/ type with 2 simple 'IO' actions for- locking and unlocking the device. (@mellon-core@ does not provide- any useful device implementations; see the companion @mellon-gpio@- package for a GPIO-driven implementation.)- .- Note that @mellon-core@ does not provide authentication mechanisms- or network services for interacting with controllers; that is the- domain of higher-level packages which use the base @mellon-core@- package (e.g., @mellon-web@).- .- /On the use of UTC dates for timers/- .- @mellon-core@ uses UTC dates for unlock expiration, rather than a- time delta or a monotonic clock. You might disagree with this- decision based on the common wisdom that it's a bad idea to use- \"wall clock time\" (of which UTC is one flavor) for timers. In- general, the common wisdom is correct. Wall clocks have lots of- problems: they may not be accurate, they may disagree from one- system to the next, they may \"jump around\" if the system is running- a time daemon such as NTP, and they occasionally do something- unexpected like adding a leap second.- .- If your timers must be high-precision (i.e., this timer must run for- exactly /n/ microseconds, for some definition of \"exactly\"), then- there's no argument: using a wall clock is a bad idea. However, as- @mellon-core@ is designed for use with physical access devices,- which themselves are typically designed for human factors, accuracy- to within a second or two is acceptable in most cases. (If you have- higher-precision needs, especially for extreme safety- or- security-related scenarios, you should probably be using a real-time- system anyway, not a Haskell program.)- .- Once the need for high precision is eliminated, and assuming that- the system(s) controlling your physical access devices use a- synchronized time source such as that provided by- <https://en.wikipedia.org/wiki/Network_Time_Protocol NTP>, the- advantages of using UTC over most of the alternatives become- apparent:- .- * Absolute time deltas without a common reference do not work well- in networked environments, where network problems may appreciably- delay the delivery of commands from client to server. If a user- wants to unlock a device for 7 seconds, does that mean 7 seconds- from the clock time @T@ when the user presses \"send,\" or does it- mean 7 seconds from opening to close, regardless of when the- server receives the command? Without a common reference, there is- no way for the user to communicate her intent.- .- * Monotonic clocks never go backwards, which is a nice invariant and- eliminates a problem that occurs in some NTP implementations.- However, monotonic clocks are a) non-portable, and not even- supported on all systems; b) usually system-dependent, which- renders them useless when attempting to communicate time across- two systems; c) sometimes even process-dependent, in which case- they're not even useful for communicating time between two- processes on the same system; and d) often idle while the system- is suspending or sleeping, in which case the clock does not move- forward while the system is suspended, rendering the clock useless- for absolute timers if there's any possibility that the system- will be suspended or otherwise go into a low-power mode.- .- Using the TAI coordinate system rather than UTC has the advantage of- guaranteeing that every (TAI) day is exactly 86400 (TAI) seconds,- unlike UTC and all of the time systems based on it, where very- rarely a day may have 86401 seconds, i.e., one standard day plus 1- leap second. If TAI were well-supported and generally available,- @mellon-core@ would probably use it, but circa 2016 it is not.- Anyway, at worst, a @mellon-core@ unlock command which spans a time- period in which a leap second is added will expire approximately 1- second too soon / too early, depending on whether the user accounted- for the leap second when she issued the command. As this error is- more or less within the expected accuracy of a @mellon-core@ system- under normal operation (due to the vagaries of thread scheduling,- and not even accounting for clock drift and other real-world- factors), it doesn't really seem worth the effort just to avoid the- minor inconvenience of leap seconds.- .- In short, synchronizing time (and timers) across multiple systems is- a very difficult problem, and one which the universally-supported- Network Time Protocol attempts to address, mostly successfully.- Given its intended application to controlling physical access for- human beings, most likely in a networked environment, @mellon-core@- makes the choice of relying on a working, accurate NTP (or other- wall-clock synchronization) deployment for coordinating and- synchronizing time across devices. If you cannot guarantee accurate- wall clock time in your system, @mellon-core@ will not work- properly, and you should look for an alternative solution.+-- This file has been generated from package.yaml by hpack version 0.21.2.+--+-- see: https://github.com/sol/hpack+--+-- hash: 03b9d6e60a651567d584825fe6863c4f050e9deb4b6986d9d668be39fb4bbdff -Extra-Doc-Files: README.md-Extra-Source-Files: changelog.md+name: mellon-core+version: 0.8.0.3+synopsis: Control physical access devices+description: /Speak, friend, and enter./+ .+ @mellon-core@ is a Haskell package for controlling physical access+ devices designed for human factors, e.g., electric strikes. The+ access control protocol is quite simple: a device is either locked,+ or it is unlocked until a particular date and time (an+ /expiration date/). Once the expiration date passes, the device is+ automatically locked again. In the meantime, the device can be+ locked immediately, overriding the unlocked state; or the unlock+ period can be extended.+ .+ User programs incorporate @mellon-core@ functionality via a+ /controller/, which is responsible for handling user lock and unlock+ commands, and for scheduling and canceling unlock expirations.+ .+ User programs must also adapt their physical access devices to the+ interface expected by the controller. For this purpose,+ @mellon-core@ defines a /device/ type with 2 simple 'IO' actions for+ locking and unlocking the device. (@mellon-core@ does not provide+ any useful device implementations; see the companion @mellon-gpio@+ package for a GPIO-driven implementation.)+ .+ Note that @mellon-core@ does not provide authentication mechanisms+ or network services for interacting with controllers; that is the+ domain of higher-level packages which use the base @mellon-core@+ package (e.g., @mellon-web@).+ .+ /On the use of UTC dates for timers/+ .+ @mellon-core@ uses UTC dates for unlock expiration, rather than a+ time delta or a monotonic clock. You might disagree with this+ decision based on the common wisdom that it's a bad idea to use+ \"wall clock time\" (of which UTC is one flavor) for timers. In+ general, the common wisdom is correct. Wall clocks have lots of+ problems: they may not be accurate, they may disagree from one+ system to the next, they may \"jump around\" if the system is running+ a time daemon such as NTP, and they occasionally do something+ unexpected like adding a leap second.+ .+ If your timers must be high-precision (i.e., this timer must run for+ exactly /n/ microseconds, for some definition of \"exactly\"), then+ there's no argument: using a wall clock is a bad idea. However, as+ @mellon-core@ is designed for use with physical access devices,+ which themselves are typically designed for human factors, accuracy+ to within a second or two is acceptable in most cases. (If you have+ higher-precision needs, especially for extreme safety- or+ security-related scenarios, you should probably be using a real-time+ system anyway, not a Haskell program.)+ .+ Once the need for high precision is eliminated, and assuming that+ the system(s) controlling your physical access devices use a+ synchronized time source such as that provided by+ <https://en.wikipedia.org/wiki/Network_Time_Protocol NTP>, the+ advantages of using UTC over most of the alternatives become+ apparent:+ .+ * Absolute time deltas without a common reference do not work well+ in networked environments, where network problems may appreciably+ delay the delivery of commands from client to server. If a user+ wants to unlock a device for 7 seconds, does that mean 7 seconds+ from the clock time @T@ when the user presses \"send,\" or does it+ mean 7 seconds from opening to close, regardless of when the+ server receives the command? Without a common reference, there is+ no way for the user to communicate her intent.+ .+ * Monotonic clocks never go backwards, which is a nice invariant and+ eliminates a problem that occurs in some NTP implementations.+ However, monotonic clocks are a) non-portable, and not even+ supported on all systems; b) usually system-dependent, which+ renders them useless when attempting to communicate time across+ two systems; c) sometimes even process-dependent, in which case+ they're not even useful for communicating time between two+ processes on the same system; and d) often idle while the system+ is suspending or sleeping, in which case the clock does not move+ forward while the system is suspended, rendering the clock useless+ for absolute timers if there's any possibility that the system+ will be suspended or otherwise go into a low-power mode.+ .+ Using the TAI coordinate system rather than UTC has the advantage of+ guaranteeing that every (TAI) day is exactly 86400 (TAI) seconds,+ unlike UTC and all of the time systems based on it, where very+ rarely a day may have 86401 seconds, i.e., one standard day plus 1+ leap second. If TAI were well-supported and generally available,+ @mellon-core@ would probably use it, but circa 2016 it is not.+ Anyway, at worst, a @mellon-core@ unlock command which spans a time+ period in which a leap second is added will expire approximately 1+ second too soon / too early, depending on whether the user accounted+ for the leap second when she issued the command. As this error is+ more or less within the expected accuracy of a @mellon-core@ system+ under normal operation (due to the vagaries of thread scheduling,+ and not even accounting for clock drift and other real-world+ factors), it doesn't really seem worth the effort just to avoid the+ minor inconvenience of leap seconds.+ .+ In short, synchronizing time (and timers) across multiple systems is+ a very difficult problem, and one which the universally-supported+ Network Time Protocol attempts to address, mostly successfully.+ Given its intended application to controlling physical access for+ human beings, most likely in a networked environment, @mellon-core@+ makes the choice of relying on a working, accurate NTP (or other+ wall-clock synchronization) deployment for coordinating and+ synchronizing time across devices. If you cannot guarantee accurate+ wall clock time in your system, @mellon-core@ will not work+ properly, and you should look for an alternative solution.+category: System+stability: experimental+homepage: https://github.com/quixoftic/mellon#readme+bug-reports: https://github.com/quixoftic/mellon/issues+author: Drew Hess <dhess-src@quixoftic.com>+maintainer: Drew Hess <dhess-src@quixoftic.com>+copyright: Copyright (c) 2017, Quixoftic, LLC+license: BSD3+license-file: LICENSE+tested-with: GHC==7.10.3 GHC==8.0.1 GHC==8.0.2 GHC==8.2.1 GHC==8.2.2+build-type: Simple+cabal-version: >= 1.10 --- Build doctests-Flag test-doctests- Default: True- Manual: True+extra-source-files:+ changelog.md+ README.md --- Build hlint test-Flag test-hlint- Default: True- Manual: True+source-repository head+ type: git+ location: https://github.com/quixoftic/mellon -Library- Default-Language: Haskell2010- HS-Source-Dirs: src- GHC-Options: -Wall -fwarn-incomplete-uni-patterns -fwarn-incomplete-record-updates- If impl(ghc > 8)- GHC-Options: -Wcompat -Wnoncanonical-monad-instances -Wnoncanonical-monadfail-instances -fno-warn-redundant-constraints- Exposed-Modules: Mellon.Controller- , Mellon.Controller.Async- , Mellon.Device- , Mellon.StateMachine- Other-Extensions: DeriveDataTypeable- , DeriveGeneric- , Safe- Build-Depends: base >= 4.8 && < 5- , async == 2.1.*- , mtl == 2.2.*- , time >= 1.5 && < 2- , transformers >= 0.4.2 && < 0.6+flag test-doctests+ description: Build doctests+ manual: True+ default: True -Test-Suite hlint- Type: exitcode-stdio-1.0- Default-Language: Haskell2010- Hs-Source-Dirs: test- Ghc-Options: -w -threaded -rtsopts -with-rtsopts=-N- Main-Is: hlint.hs- If !flag(test-hlint)- Buildable: False- Else- Build-Depends: base- , hlint >= 1.9 && < 2.1+flag test-hlint+ description: Build hlint test+ manual: True+ default: True -Test-Suite doctest- Type: exitcode-stdio-1.0- Default-Language: Haskell2010- Hs-Source-Dirs: test- Ghc-Options: -Wall -threaded- Main-Is: doctest.hs- If !flag(test-doctests)- Buildable: False- Else- Build-Depends: base- , QuickCheck >= 2.8 && < 2.10- , quickcheck-instances == 0.3.*- , doctest == 0.11.*+library+ hs-source-dirs:+ src+ other-extensions: DeriveDataTypeable DeriveGeneric Safe+ build-depends:+ async ==2.1.*+ , base >=4.8 && <5+ , mtl ==2.2.*+ , time >=1.5 && <2+ , transformers >=0.4.2 && <0.6+ if impl(ghc >= 8.0)+ ghc-options: -Wall -Wincomplete-uni-patterns -Wincomplete-record-updates+ else+ ghc-options: -Wall -fwarn-incomplete-uni-patterns -fwarn-incomplete-record-updates+ if impl(ghc >= 8.0)+ ghc-options: -Wcompat -Wnoncanonical-monad-instances -Wnoncanonical-monadfail-instances+ else+ build-depends:+ fail ==4.9.*+ , semigroups ==0.18.*+ exposed-modules:+ Mellon.Controller+ Mellon.Controller.Async+ Mellon.Device+ Mellon.StateMachine+ other-modules:+ Paths_mellon_core+ default-language: Haskell2010 -Test-Suite spec- Type: exitcode-stdio-1.0- Default-Language: Haskell2010- Hs-Source-Dirs: src- , test- Ghc-Options: -w -threaded -rtsopts -with-rtsopts=-N- Main-Is: Main.hs- Build-Depends: base- , async- , hspec >= 2.2 && < 2.5- , mtl- , time- , transformers- Other-Modules: Mellon.Controller- , Mellon.Controller.Async- , Mellon.Device- , Mellon.StateMachine- , Spec- , Mellon.Controller.AsyncSpec+test-suite doctest+ type: exitcode-stdio-1.0+ main-is: doctest.hs+ hs-source-dirs:+ test+ ghc-options: -threaded+ if impl(ghc >= 8.0)+ ghc-options: -Wall -Wincomplete-uni-patterns -Wincomplete-record-updates+ else+ ghc-options: -Wall -fwarn-incomplete-uni-patterns -fwarn-incomplete-record-updates+ if !(flag(test-doctests))+ buildable: False+ else+ build-depends:+ QuickCheck >=2.8 && <2.11+ , base+ , doctest >=0.11 && <0.14+ , quickcheck-instances ==0.3.*+ default-language: Haskell2010 -Source-Repository head- Type: git- Location: git://github.com/quixoftic/mellon.git+test-suite hlint+ type: exitcode-stdio-1.0+ main-is: hlint.hs+ hs-source-dirs:+ test+ ghc-options: -w -threaded -rtsopts -with-rtsopts=-N+ if impl(ghc >= 8.0)+ ghc-options: -Wall -Wincomplete-uni-patterns -Wincomplete-record-updates+ else+ ghc-options: -Wall -fwarn-incomplete-uni-patterns -fwarn-incomplete-record-updates+ if !(flag(test-hlint))+ buildable: False+ else+ build-depends:+ base+ , hlint >=1.9 && <2.1+ default-language: Haskell2010 -Source-Repository this- Type: git- Location: git://github.com/quixoftic/mellon.git- Tag: v0.8.0.2+test-suite spec+ type: exitcode-stdio-1.0+ main-is: Main.hs+ hs-source-dirs:+ test+ other-extensions: DeriveDataTypeable+ ghc-options: -w -threaded -rtsopts -with-rtsopts=-N+ build-depends:+ async+ , base+ , hspec >=2.2 && <2.5+ , mellon-core+ , mtl+ , time+ , transformers+ if impl(ghc >= 8.0)+ ghc-options: -Wall -Wincomplete-uni-patterns -Wincomplete-record-updates+ else+ ghc-options: -Wall -fwarn-incomplete-uni-patterns -fwarn-incomplete-record-updates+ other-modules:+ Mellon.Controller.AsyncSpec+ Spec+ Paths_mellon_core+ default-language: Haskell2010
test/Mellon/Controller/AsyncSpec.hs view
@@ -17,8 +17,7 @@ (Controller, State(..), controller, minUnlockTime, lockController, queryController, unlockController) import Mellon.Device- (Device(..), MockLock, MockLockEvent(..), events, mockLock,- mockLockDevice)+ (Device(..), MockLockEvent(..), events, mockLock, mockLockDevice) sleep :: (MonadIO m) => Int -> m () sleep = liftIO . threadDelay . (* 1000000)@@ -32,8 +31,8 @@ type TestController d a = RWST (Controller d) [MockLockEvent] () IO a testController :: Controller d -> IO [MockLockEvent]-testController cc =- do (_, expectedResults) <- execRWST theTest cc ()+testController ctrl =+ do (_, expectedResults) <- execRWST theTest ctrl () return expectedResults where theTest :: TestController d ()@@ -153,7 +152,7 @@ cc <- controller Nothing $ exceptionLockDevice el -- 1st lock op now <- getCurrentTime let expire = timePlusN now 3- unlockController expire cc -- 2nd & 3rd lock op (unlock, timed lock)+ void $ unlockController expire cc -- 2nd & 3rd lock op (unlock, timed lock) (sleep 5) `shouldThrow` isExceptionLockException -- async exception queryController cc `shouldReturn` StateUnlocked expire -- should have state prior to exception