mirror of
https://github.com/flipstone/orville.git
synced 2024-10-26 04:34:17 +03:00
Adds prototype docsite with existing tutorials.
This commit is contained in:
parent
5f3fca5d1c
commit
e9ad7d1086
7
.gitignore
vendored
7
.gitignore
vendored
@ -27,3 +27,10 @@ instantclient*
|
||||
# We use a ci compose file that keeps some extra bits locally to cache
|
||||
# They are ignored below to avoid them being included in various operations, locally and in ci
|
||||
.stack-root
|
||||
|
||||
# Ignore Hakyll build directories
|
||||
orville-docsite/site-builder/_cache
|
||||
orville-docsite/site-builder/_site
|
||||
|
||||
# Ignore directories created by samples
|
||||
orville-docsite/samples/getting-started/orville-getting-started
|
||||
|
4
orville-docsite/Dockerfile
Normal file
4
orville-docsite/Dockerfile
Normal file
@ -0,0 +1,4 @@
|
||||
FROM ghcr.io/flipstone/haskell-tools:debian-stable-ghc-9.4.7-2023-10-31-3286ef4
|
||||
|
||||
ADD samples/install-packages.sh /install-packages.sh
|
||||
RUN sh /install-packages.sh
|
20
orville-docsite/compose.yml
Normal file
20
orville-docsite/compose.yml
Normal file
@ -0,0 +1,20 @@
|
||||
version: "3"
|
||||
services:
|
||||
dev:
|
||||
build: .
|
||||
environment:
|
||||
STACK_ROOT: /stack-root
|
||||
volumes:
|
||||
- .:/orville-docsite
|
||||
- stack-root:/stack-root
|
||||
- ./stack-config.yaml:/stack-root/config.yaml
|
||||
working_dir: /orville-docsite
|
||||
# A TTY is required for the test-loop script to use
|
||||
# stack test. stdin_open would be sufficient, but
|
||||
# allocating a tty provides colorful test output :)
|
||||
tty: true
|
||||
ports:
|
||||
- 8000:8000
|
||||
|
||||
volumes:
|
||||
stack-root:
|
26
orville-docsite/samples/getting-started/Main.hs
Normal file
26
orville-docsite/samples/getting-started/Main.hs
Normal file
@ -0,0 +1,26 @@
|
||||
module Main
|
||||
( main
|
||||
) where
|
||||
|
||||
import qualified Orville.PostgreSQL as O
|
||||
import qualified Orville.PostgreSQL.Execution as Execution
|
||||
import qualified Orville.PostgreSQL.Raw.Connection as Connection
|
||||
import qualified Orville.PostgreSQL.Raw.RawSql as RawSql
|
||||
import qualified Orville.PostgreSQL.Raw.SqlValue as SqlValue
|
||||
|
||||
main :: IO ()
|
||||
main = do
|
||||
pool <-
|
||||
O.createConnectionPool
|
||||
O.ConnectionOptions
|
||||
{ O.connectionString = "host=localhost user=postgres password=postgres"
|
||||
, O.connectionNoticeReporting = O.DisableNoticeReporting
|
||||
, O.connectionPoolStripes = O.OneStripePerCapability
|
||||
, O.connectionPoolLingerTime = 10
|
||||
, O.connectionPoolMaxConnections = O.MaxConnectionsPerStripe 1
|
||||
}
|
||||
|
||||
Connection.withPoolConnection pool $ \connection -> do
|
||||
result <- RawSql.execute connection (RawSql.fromString "SELECT 1+1")
|
||||
[[(_, sqlValue)]] <- Execution.readRows result
|
||||
print (SqlValue.toInt sqlValue)
|
@ -0,0 +1,5 @@
|
||||
42c42,43
|
||||
< # extra-deps: []
|
||||
---
|
||||
> extra-deps:
|
||||
> - orville-postgresql-1.0.0.0
|
@ -0,0 +1,4 @@
|
||||
22c22
|
||||
< build-depends: base >= 4.7 && < 5
|
||||
---
|
||||
> build-depends: base >= 4.7 && < 5, orville-postgresql ^>= 1.0.0.0
|
@ -0,0 +1 @@
|
||||
Right 2
|
31
orville-docsite/samples/getting-started/run.sh
Normal file
31
orville-docsite/samples/getting-started/run.sh
Normal file
@ -0,0 +1,31 @@
|
||||
# SNIPPET: hidden
|
||||
set -e
|
||||
rm -rf orville-getting-started
|
||||
service postgresql start
|
||||
# SNIPPET: initProject
|
||||
mkdir orville-getting-started
|
||||
cd orville-getting-started
|
||||
stack new orville-getting-started --bare simple --resolver lts-21.19
|
||||
# SNIPPET: hidden
|
||||
echo "system-ghc: true" >> stack.yaml
|
||||
echo "install-ghc: false" >> stack.yaml
|
||||
# SNIPPET: hidden
|
||||
patch stack.yaml ../add-orville-extra-dep-to-stack-yaml.patch
|
||||
# SNIPPET: hidden
|
||||
patch orville-getting-started.cabal ../add-orville-to-cabal-file.patch
|
||||
# SNIPPET: hidden
|
||||
cp ../Main.hs src/Main.hs
|
||||
# SNIPPET: buildAndExecute
|
||||
stack build
|
||||
stack exec orville-getting-started
|
||||
# SNIPPET: hidden
|
||||
expected=$(cat ../expected-output.txt)
|
||||
actual=$(stack exec orville-getting-started)
|
||||
|
||||
if [ "$expected" = "$actual" ]; then
|
||||
echo "Output matches expected"
|
||||
else
|
||||
echo "Expected output to be: $expected"
|
||||
echo "But it was actually : $actual"
|
||||
exit 1
|
||||
fi;
|
1
orville-docsite/samples/hero/expected-output.txt
Normal file
1
orville-docsite/samples/hero/expected-output.txt
Normal file
@ -0,0 +1 @@
|
||||
Spot found!
|
32
orville-docsite/samples/hero/hero.cabal
Normal file
32
orville-docsite/samples/hero/hero.cabal
Normal file
@ -0,0 +1,32 @@
|
||||
cabal-version: 2.2
|
||||
|
||||
name: hero
|
||||
version: 0.1.0.0
|
||||
-- synopsis:
|
||||
-- description:
|
||||
homepage: https://github.com/flipstone/hero#readme
|
||||
license: BSD-3-Clause
|
||||
author: Flipstone Technology Partners, Inc
|
||||
maintainer: maintainers@flipstone.com
|
||||
copyright:
|
||||
category: sample
|
||||
build-type: Simple
|
||||
|
||||
executable hero
|
||||
hs-source-dirs: src
|
||||
main-is: Main.hs
|
||||
default-language: Haskell2010
|
||||
|
||||
build-depends: base >= 4.7 && < 5,
|
||||
orville-postgresql,
|
||||
text
|
||||
|
||||
ghc-options: -Wall
|
||||
-Wcompat
|
||||
-Widentities
|
||||
-Wincomplete-record-updates
|
||||
-Wincomplete-uni-patterns
|
||||
-Wmissing-export-lists
|
||||
-Wmissing-home-modules
|
||||
-Wpartial-fields
|
||||
-Wredundant-constraints
|
13
orville-docsite/samples/hero/run.sh
Normal file
13
orville-docsite/samples/hero/run.sh
Normal file
@ -0,0 +1,13 @@
|
||||
set -e
|
||||
service postgresql start
|
||||
stack build
|
||||
expected=$(cat expected-output.txt)
|
||||
actual=$(stack exec hero)
|
||||
|
||||
if [ "$expected" = "$actual" ]; then
|
||||
echo "Output matches expected"
|
||||
else
|
||||
echo "Expected output to be: $expected"
|
||||
echo "But it was actually : $actual"
|
||||
exit 1
|
||||
fi;
|
106
orville-docsite/samples/hero/src/Main.hs
Normal file
106
orville-docsite/samples/hero/src/Main.hs
Normal file
@ -0,0 +1,106 @@
|
||||
module Main (main) where
|
||||
|
||||
import Data.Int (Int32)
|
||||
import qualified Data.Text as T
|
||||
import qualified Orville.PostgreSQL as O
|
||||
import qualified Orville.PostgreSQL.AutoMigration as AutoMigration
|
||||
|
||||
{- |
|
||||
Pet is a plain old Haskell record that will be marshalled to and from the
|
||||
@pet@ table.
|
||||
-}
|
||||
data Pet =
|
||||
Pet
|
||||
{ petId :: PetId
|
||||
, petName :: T.Text
|
||||
}
|
||||
|
||||
{- |
|
||||
It's good practice to create newtype specific to each entity to hold its
|
||||
primary key value
|
||||
-}
|
||||
newtype PetId = PetId Int32
|
||||
|
||||
{- |
|
||||
A marshaller must be defined to convert Pet to and from SQL.
|
||||
-}
|
||||
petMarshaller :: O.SqlMarshaller Pet Pet
|
||||
petMarshaller =
|
||||
Pet
|
||||
<$> O.marshallField petId petIdField
|
||||
<*> O.marshallField petName nameField
|
||||
|
||||
{- |
|
||||
Defines the @id@ field for the marshaller to marshall the 'petId' record
|
||||
field to and from.
|
||||
-}
|
||||
petIdField :: O.FieldDefinition O.NotNull PetId
|
||||
petIdField =
|
||||
O.coerceField (O.integerField "id")
|
||||
|
||||
{- |
|
||||
Defines the @name@ field for the marshaller to marshall the 'petName' record
|
||||
field to and from.
|
||||
-}
|
||||
nameField :: O.FieldDefinition O.NotNull T.Text
|
||||
nameField =
|
||||
O.unboundedTextField "name"
|
||||
|
||||
{- |
|
||||
Marshaller above is associated with the @pet@ table. The marshallers fields
|
||||
will define the column of the table.
|
||||
-}
|
||||
petTable :: O.TableDefinition (O.HasKey PetId) Pet Pet
|
||||
petTable =
|
||||
O.mkTableDefinition
|
||||
"pet"
|
||||
(O.primaryKey petIdField)
|
||||
petMarshaller
|
||||
|
||||
{- |
|
||||
A simple demo that connects to a database, inserts 2 pets and then finds the
|
||||
pet named "Spot"
|
||||
-}
|
||||
main :: IO ()
|
||||
main = do
|
||||
pool <-
|
||||
O.createConnectionPool
|
||||
O.ConnectionOptions
|
||||
{ O.connectionString = "host=localhost user=postgres password=postgres"
|
||||
, O.connectionNoticeReporting = O.DisableNoticeReporting
|
||||
, O.connectionPoolStripes = O.OneStripePerCapability
|
||||
, O.connectionPoolMaxConnections = O.MaxConnectionsPerStripe 1
|
||||
, O.connectionPoolLingerTime = 10
|
||||
}
|
||||
|
||||
mbSpot <- O.runOrville pool insertAndFindSpot
|
||||
|
||||
case mbSpot of
|
||||
Nothing -> putStrLn "No Spot Found!"
|
||||
Just _spot -> putStrLn "Spot found!"
|
||||
|
||||
{- |
|
||||
The Orville monad provides a starter pack for running Orville operations
|
||||
against a connection pool.
|
||||
-}
|
||||
insertAndFindSpot :: O.Orville (Maybe Pet)
|
||||
insertAndFindSpot = do
|
||||
AutoMigration.autoMigrateSchema
|
||||
AutoMigration.defaultOptions
|
||||
[AutoMigration.SchemaTable petTable]
|
||||
|
||||
O.insertEntity petTable $
|
||||
Pet
|
||||
{ petId = PetId 1
|
||||
, petName = T.pack "FuFu"
|
||||
}
|
||||
|
||||
O.insertEntity petTable $
|
||||
Pet
|
||||
{ petId = PetId 2
|
||||
, petName = T.pack "Spot"
|
||||
}
|
||||
|
||||
O.findFirstEntityBy
|
||||
petTable
|
||||
(O.where_ (O.fieldEquals nameField (T.pack "Spot")))
|
69
orville-docsite/samples/hero/stack.yaml
Normal file
69
orville-docsite/samples/hero/stack.yaml
Normal file
@ -0,0 +1,69 @@
|
||||
# This file was automatically generated by 'stack init'
|
||||
#
|
||||
# Some commonly used options have been documented as comments in this file.
|
||||
# For advanced use and comprehensive documentation of the format, please see:
|
||||
# https://docs.haskellstack.org/en/stable/yaml_configuration/
|
||||
|
||||
# Resolver to choose a 'specific' stackage snapshot or a compiler version.
|
||||
# A snapshot resolver dictates the compiler version and the set of packages
|
||||
# to be used for project dependencies. For example:
|
||||
#
|
||||
# resolver: lts-21.13
|
||||
# resolver: nightly-2023-09-24
|
||||
# resolver: ghc-9.6.2
|
||||
#
|
||||
# The location of a snapshot can be provided as a file or url. Stack assumes
|
||||
# a snapshot provided as a file might change, whereas a url resource does not.
|
||||
#
|
||||
# resolver: ./custom-snapshot.yaml
|
||||
# resolver: https://example.com/snapshots/2023-01-01.yaml
|
||||
resolver: lts-21.19
|
||||
system-ghc: true
|
||||
install-ghc: false
|
||||
|
||||
# User packages to be built.
|
||||
# Various formats can be used as shown in the example below.
|
||||
#
|
||||
# packages:
|
||||
# - some-directory
|
||||
# - https://example.com/foo/bar/baz-0.0.2.tar.gz
|
||||
# subdirs:
|
||||
# - auto-update
|
||||
# - wai
|
||||
packages:
|
||||
- .
|
||||
# Dependency packages to be pulled from upstream that are not in the resolver.
|
||||
# These entries can reference officially published versions as well as
|
||||
# forks / in-progress versions pinned to a git hash. For example:
|
||||
#
|
||||
# extra-deps:
|
||||
# - acme-missiles-0.3
|
||||
# - git: https://github.com/commercialhaskell/stack.git
|
||||
# commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a
|
||||
#
|
||||
extra-deps:
|
||||
- orville-postgresql-1.0.0.0
|
||||
|
||||
# Override default flag values for local packages and extra-deps
|
||||
# flags: {}
|
||||
|
||||
# Extra package databases containing global packages
|
||||
# extra-package-dbs: []
|
||||
|
||||
# Control whether we use the GHC we find on the path
|
||||
# system-ghc: true
|
||||
#
|
||||
# Require a specific version of Stack, using version ranges
|
||||
# require-stack-version: -any # Default
|
||||
# require-stack-version: ">=2.13"
|
||||
#
|
||||
# Override the architecture used by Stack, especially useful on Windows
|
||||
# arch: i386
|
||||
# arch: x86_64
|
||||
#
|
||||
# Extra directories used by Stack for building
|
||||
# extra-include-dirs: [/path/to/dir]
|
||||
# extra-lib-dirs: [/path/to/dir]
|
||||
#
|
||||
# Allow a newer minor version of GHC than the snapshot specifies
|
||||
# compiler-check: newer-minor
|
19
orville-docsite/samples/hero/stack.yaml.lock
Normal file
19
orville-docsite/samples/hero/stack.yaml.lock
Normal file
@ -0,0 +1,19 @@
|
||||
# This file was autogenerated by Stack.
|
||||
# You should not edit this file by hand.
|
||||
# For more information, please see the documentation at:
|
||||
# https://docs.haskellstack.org/en/stable/lock_files
|
||||
|
||||
packages:
|
||||
- completed:
|
||||
hackage: orville-postgresql-1.0.0.0@sha256:35e9b9f8bc0bc1ee1847bcb5340fa39bed320f1573099ec16ca394726a50593a,9018
|
||||
pantry-tree:
|
||||
sha256: b8d324f2ad94f12ac419996cc2947ee0c69c5178b2caf13dc92135118602bbd8
|
||||
size: 12020
|
||||
original:
|
||||
hackage: orville-postgresql-1.0.0.0
|
||||
snapshots:
|
||||
- completed:
|
||||
sha256: fb482b8e2d5d061cdda4ba1da2957c012740c893a5ee1c1b99001adae7b1fbe7
|
||||
size: 640046
|
||||
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/21/19.yaml
|
||||
original: lts-21.19
|
13
orville-docsite/samples/install-packages.sh
Normal file
13
orville-docsite/samples/install-packages.sh
Normal file
@ -0,0 +1,13 @@
|
||||
# SNIPPET: hidden
|
||||
set -e
|
||||
# SNIPPET: installLibPqClient
|
||||
apt update
|
||||
apt install -y libpq-dev
|
||||
# SNIPPET: hidden
|
||||
apt install -y postgresql
|
||||
sed \
|
||||
-i \
|
||||
"s/#listen_addresses = 'localhost'/listen_addresses = 'localhost' /" \
|
||||
/etc/postgresql/15/main/postgresql.conf
|
||||
service postgresql start
|
||||
echo "ALTER USER postgres PASSWORD 'postgres'" | su postgres -c psql
|
2
orville-docsite/samples/using-json/expected-output.txt
Normal file
2
orville-docsite/samples/using-json/expected-output.txt
Normal file
@ -0,0 +1,2 @@
|
||||
Just (Foo {fooId = 0, fooTags = Array [Number 1.0,Number 2.0,Number 3.0]})
|
||||
[(0,Number 1.0),(0,Number 2.0),(0,Number 3.0)]
|
17
orville-docsite/samples/using-json/run.sh
Normal file
17
orville-docsite/samples/using-json/run.sh
Normal file
@ -0,0 +1,17 @@
|
||||
# SNIPPET: hidden
|
||||
set -e
|
||||
service postgresql start
|
||||
# SNIPPET: buildAndExecute
|
||||
stack build
|
||||
stack exec using-json
|
||||
# SNIPPET: hidden
|
||||
expected=$(cat expected-output.txt)
|
||||
actual=$(stack exec using-json)
|
||||
|
||||
if [ "$expected" = "$actual" ]; then
|
||||
echo "Output matches expected"
|
||||
else
|
||||
echo "Expected output to be: $expected"
|
||||
echo "But it was actually : $actual"
|
||||
exit 1
|
||||
fi;
|
94
orville-docsite/samples/using-json/src/Main.hs
Normal file
94
orville-docsite/samples/using-json/src/Main.hs
Normal file
@ -0,0 +1,94 @@
|
||||
-- SNIPPET: moduleHeader
|
||||
module Main
|
||||
( main
|
||||
) where
|
||||
|
||||
import qualified Orville.PostgreSQL as O
|
||||
import qualified Orville.PostgreSQL.AutoMigration as AutoMigration
|
||||
import qualified Orville.PostgreSQL.Marshall as Marshall
|
||||
import qualified Orville.PostgreSQL.Raw.RawSql as RawSql
|
||||
|
||||
import Control.Monad.IO.Class (MonadIO(liftIO))
|
||||
import Data.Aeson (Value, eitherDecodeStrict')
|
||||
import Data.Aeson.Text (encodeToLazyText)
|
||||
import qualified Data.Aeson as Aeson
|
||||
import qualified Data.Int as Int
|
||||
import qualified Data.Text as T
|
||||
import qualified Data.Text.Encoding as Enc
|
||||
import qualified Data.Text.Lazy as LazyText
|
||||
import qualified Data.Vector as Vector
|
||||
-- SNIPPET: dataTypes
|
||||
data Foo = Foo
|
||||
{ fooId :: Int.Int32
|
||||
, fooTags :: Value
|
||||
}
|
||||
deriving Show
|
||||
|
||||
fooIdField :: O.FieldDefinition O.NotNull Int.Int32
|
||||
fooIdField =
|
||||
O.integerField "id"
|
||||
|
||||
fooTagsField :: O.FieldDefinition O.NotNull Value
|
||||
fooTagsField =
|
||||
aesonValueField "tags"
|
||||
-- SNIPPET: aesonValueField
|
||||
aesonValueField :: String -> O.FieldDefinition O.NotNull Value
|
||||
aesonValueField name =
|
||||
O.convertField
|
||||
(O.tryConvertSqlType encodeJSON decodeJSON)
|
||||
(O.jsonbField name)
|
||||
|
||||
decodeJSON :: T.Text -> Either String Value
|
||||
decodeJSON =
|
||||
eitherDecodeStrict' . Enc.encodeUtf8
|
||||
|
||||
encodeJSON :: Value -> T.Text
|
||||
encodeJSON =
|
||||
LazyText.toStrict . encodeToLazyText
|
||||
-- SNIPPET: tableDefinition
|
||||
fooMarshaller :: O.SqlMarshaller Foo Foo
|
||||
fooMarshaller =
|
||||
Foo
|
||||
<$> O.marshallField fooId fooIdField
|
||||
<*> O.marshallField fooTags fooTagsField
|
||||
|
||||
table :: O.TableDefinition (O.HasKey Int.Int32) Foo Foo
|
||||
table =
|
||||
O.mkTableDefinition "json_demo" (O.primaryKey fooIdField) fooMarshaller
|
||||
-- SNIPPET: mainFunction
|
||||
main :: IO ()
|
||||
main = do
|
||||
pool <-
|
||||
O.createConnectionPool
|
||||
O.ConnectionOptions
|
||||
{ O.connectionString = "host=localhost user=postgres password=postgres"
|
||||
, O.connectionNoticeReporting = O.DisableNoticeReporting
|
||||
, O.connectionPoolStripes = O.OneStripePerCapability
|
||||
, O.connectionPoolLingerTime = 10
|
||||
, O.connectionPoolMaxConnections = O.MaxConnectionsPerStripe 1
|
||||
}
|
||||
|
||||
O.runOrville pool $ do
|
||||
AutoMigration.autoMigrateSchema AutoMigration.defaultOptions [ AutoMigration.SchemaTable table ]
|
||||
_ <- O.deleteEntity table 0
|
||||
-- SNIPPET: insertEntity
|
||||
_ <- O.insertEntity table Foo { fooId = 0
|
||||
, fooTags = Aeson.Array $ Vector.fromList
|
||||
[ Aeson.Number 1
|
||||
, Aeson.Number 2
|
||||
, Aeson.Number 3
|
||||
]
|
||||
}
|
||||
liftIO . print =<< O.findEntity table 0
|
||||
-- SNIPPET: selectJSONArray
|
||||
let
|
||||
marshaller :: O.SqlMarshaller w (Int.Int32, Value)
|
||||
marshaller =
|
||||
(,) <$> O.marshallReadOnlyField fooIdField
|
||||
<*> O.marshallReadOnlyField (aesonValueField "tag")
|
||||
readEntities <-
|
||||
O.executeAndDecode
|
||||
O.SelectQuery
|
||||
(RawSql.fromString "SELECT id, jsonb_array_elements(tags) AS tag FROM json_demo")
|
||||
(Marshall.annotateSqlMarshallerEmptyAnnotation marshaller)
|
||||
liftIO $ print readEntities
|
69
orville-docsite/samples/using-json/stack.yaml
Normal file
69
orville-docsite/samples/using-json/stack.yaml
Normal file
@ -0,0 +1,69 @@
|
||||
# This file was automatically generated by 'stack init'
|
||||
#
|
||||
# Some commonly used options have been documented as comments in this file.
|
||||
# For advanced use and comprehensive documentation of the format, please see:
|
||||
# https://docs.haskellstack.org/en/stable/yaml_configuration/
|
||||
|
||||
# Resolver to choose a 'specific' stackage snapshot or a compiler version.
|
||||
# A snapshot resolver dictates the compiler version and the set of packages
|
||||
# to be used for project dependencies. For example:
|
||||
#
|
||||
# resolver: lts-21.13
|
||||
# resolver: nightly-2023-09-24
|
||||
# resolver: ghc-9.6.2
|
||||
#
|
||||
# The location of a snapshot can be provided as a file or url. Stack assumes
|
||||
# a snapshot provided as a file might change, whereas a url resource does not.
|
||||
#
|
||||
# resolver: ./custom-snapshot.yaml
|
||||
# resolver: https://example.com/snapshots/2023-01-01.yaml
|
||||
resolver: lts-21.19
|
||||
system-ghc: true
|
||||
install-ghc: false
|
||||
|
||||
# User packages to be built.
|
||||
# Various formats can be used as shown in the example below.
|
||||
#
|
||||
# packages:
|
||||
# - some-directory
|
||||
# - https://example.com/foo/bar/baz-0.0.2.tar.gz
|
||||
# subdirs:
|
||||
# - auto-update
|
||||
# - wai
|
||||
packages:
|
||||
- .
|
||||
# Dependency packages to be pulled from upstream that are not in the resolver.
|
||||
# These entries can reference officially published versions as well as
|
||||
# forks / in-progress versions pinned to a git hash. For example:
|
||||
#
|
||||
# extra-deps:
|
||||
# - acme-missiles-0.3
|
||||
# - git: https://github.com/commercialhaskell/stack.git
|
||||
# commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a
|
||||
#
|
||||
extra-deps:
|
||||
- orville-postgresql-1.0.0.0
|
||||
|
||||
# Override default flag values for local packages and extra-deps
|
||||
# flags: {}
|
||||
|
||||
# Extra package databases containing global packages
|
||||
# extra-package-dbs: []
|
||||
|
||||
# Control whether we use the GHC we find on the path
|
||||
# system-ghc: true
|
||||
#
|
||||
# Require a specific version of Stack, using version ranges
|
||||
# require-stack-version: -any # Default
|
||||
# require-stack-version: ">=2.13"
|
||||
#
|
||||
# Override the architecture used by Stack, especially useful on Windows
|
||||
# arch: i386
|
||||
# arch: x86_64
|
||||
#
|
||||
# Extra directories used by Stack for building
|
||||
# extra-include-dirs: [/path/to/dir]
|
||||
# extra-lib-dirs: [/path/to/dir]
|
||||
#
|
||||
# Allow a newer minor version of GHC than the snapshot specifies
|
||||
# compiler-check: newer-minor
|
19
orville-docsite/samples/using-json/stack.yaml.lock
Normal file
19
orville-docsite/samples/using-json/stack.yaml.lock
Normal file
@ -0,0 +1,19 @@
|
||||
# This file was autogenerated by Stack.
|
||||
# You should not edit this file by hand.
|
||||
# For more information, please see the documentation at:
|
||||
# https://docs.haskellstack.org/en/stable/lock_files
|
||||
|
||||
packages:
|
||||
- completed:
|
||||
hackage: orville-postgresql-1.0.0.0@sha256:35e9b9f8bc0bc1ee1847bcb5340fa39bed320f1573099ec16ca394726a50593a,9018
|
||||
pantry-tree:
|
||||
sha256: b8d324f2ad94f12ac419996cc2947ee0c69c5178b2caf13dc92135118602bbd8
|
||||
size: 12020
|
||||
original:
|
||||
hackage: orville-postgresql-1.0.0.0
|
||||
snapshots:
|
||||
- completed:
|
||||
sha256: fb482b8e2d5d061cdda4ba1da2957c012740c893a5ee1c1b99001adae7b1fbe7
|
||||
size: 640046
|
||||
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/21/19.yaml
|
||||
original: lts-21.19
|
34
orville-docsite/samples/using-json/using-json.cabal
Normal file
34
orville-docsite/samples/using-json/using-json.cabal
Normal file
@ -0,0 +1,34 @@
|
||||
cabal-version: 2.2
|
||||
|
||||
name: using-json
|
||||
version: 0.1.0.0
|
||||
-- synopsis:
|
||||
-- description:
|
||||
homepage: https://github.com/flipstone/using-json#readme
|
||||
license: BSD-3-Clause
|
||||
author: Flipstone Technology Partners, Inc
|
||||
maintainer: maintainers@flipstone.com
|
||||
copyright:
|
||||
category: sample
|
||||
build-type: Simple
|
||||
|
||||
executable using-json
|
||||
hs-source-dirs: src
|
||||
main-is: Main.hs
|
||||
default-language: Haskell2010
|
||||
|
||||
build-depends: base >= 4.7 && < 5,
|
||||
orville-postgresql,
|
||||
aeson,
|
||||
vector,
|
||||
text
|
||||
|
||||
ghc-options: -Wall
|
||||
-Wcompat
|
||||
-Widentities
|
||||
-Wincomplete-record-updates
|
||||
-Wincomplete-uni-patterns
|
||||
-Wmissing-export-lists
|
||||
-Wmissing-home-modules
|
||||
-Wpartial-fields
|
||||
-Wredundant-constraints
|
@ -0,0 +1,2 @@
|
||||
Just (Foo3 {foo3Id = 0, foo3Age = 91})
|
||||
Just (Foo1 {foo1Id = 0})
|
17
orville-docsite/samples/using-migrations/run.sh
Normal file
17
orville-docsite/samples/using-migrations/run.sh
Normal file
@ -0,0 +1,17 @@
|
||||
# SNIPPET: hidden
|
||||
set -e
|
||||
service postgresql start
|
||||
# SNIPPET: buildAndExecute
|
||||
stack build
|
||||
stack exec using-migrations
|
||||
# SNIPPET: hidden
|
||||
expected=$(cat expected-output.txt)
|
||||
actual=$(stack exec using-migrations)
|
||||
|
||||
if [ "$expected" = "$actual" ]; then
|
||||
echo "Output matches expected"
|
||||
else
|
||||
echo "Expected output to be: $expected"
|
||||
echo "But it was actually : $actual"
|
||||
exit 1
|
||||
fi;
|
89
orville-docsite/samples/using-migrations/src/Main.hs
Normal file
89
orville-docsite/samples/using-migrations/src/Main.hs
Normal file
@ -0,0 +1,89 @@
|
||||
-- SNIPPET: moduleHeaderAndTypes
|
||||
module Main
|
||||
( main
|
||||
) where
|
||||
|
||||
import qualified Orville.PostgreSQL as O
|
||||
import qualified Orville.PostgreSQL.AutoMigration as AutoMigration
|
||||
import qualified Orville.PostgreSQL.Raw.RawSql as RawSql
|
||||
|
||||
import Control.Monad.IO.Class (MonadIO(liftIO))
|
||||
import qualified Data.Int as Int
|
||||
|
||||
data Foo1 = Foo1
|
||||
{ foo1Id :: Int.Int32
|
||||
}
|
||||
deriving Show
|
||||
|
||||
data Foo2 = Foo2
|
||||
{ foo2Id :: Int.Int32
|
||||
, foo2Age :: Maybe Int.Int32
|
||||
}
|
||||
deriving Show
|
||||
|
||||
data Foo3 = Foo3
|
||||
{ foo3Id :: Int.Int32
|
||||
, foo3Age :: Int.Int32
|
||||
}
|
||||
deriving Show
|
||||
|
||||
fooIdField :: O.FieldDefinition O.NotNull Int.Int32
|
||||
fooIdField =
|
||||
O.integerField "id"
|
||||
|
||||
fooAgeField :: O.FieldDefinition O.NotNull Int.Int32
|
||||
fooAgeField =
|
||||
O.integerField "age"
|
||||
|
||||
foo1Marshaller :: O.SqlMarshaller Foo1 Foo1
|
||||
foo1Marshaller =
|
||||
Foo1
|
||||
<$> O.marshallField foo1Id fooIdField
|
||||
|
||||
foo2Marshaller :: O.SqlMarshaller Foo2 Foo2
|
||||
foo2Marshaller =
|
||||
Foo2
|
||||
<$> O.marshallField foo2Id fooIdField
|
||||
<*> O.marshallField foo2Age (O.nullableField fooAgeField)
|
||||
|
||||
foo3Marshaller :: O.SqlMarshaller Foo3 Foo3
|
||||
foo3Marshaller =
|
||||
Foo3
|
||||
<$> O.marshallField foo3Id fooIdField
|
||||
<*> O.marshallField foo3Age fooAgeField
|
||||
-- SNIPPET: tableDefinitions
|
||||
table1 :: O.TableDefinition (O.HasKey Int.Int32) Foo1 Foo1
|
||||
table1 =
|
||||
O.mkTableDefinition "migration_demo1" (O.primaryKey fooIdField) foo1Marshaller
|
||||
|
||||
table2 :: O.TableDefinition (O.HasKey Int.Int32) Foo2 Foo2
|
||||
table2 =
|
||||
O.mkTableDefinition "migration_demo1" (O.primaryKey fooIdField) foo2Marshaller
|
||||
|
||||
table3 :: O.TableDefinition (O.HasKey Int.Int32) Foo3 Foo3
|
||||
table3 =
|
||||
O.mkTableDefinition "migration_demo1" (O.primaryKey fooIdField) foo3Marshaller
|
||||
-- SNIPPET: mainFunction
|
||||
main :: IO ()
|
||||
main = do
|
||||
pool <-
|
||||
O.createConnectionPool
|
||||
O.ConnectionOptions
|
||||
{ O.connectionString = "host=localhost user=postgres password=postgres"
|
||||
, O.connectionNoticeReporting = O.DisableNoticeReporting
|
||||
, O.connectionPoolStripes = O.OneStripePerCapability
|
||||
, O.connectionPoolLingerTime = 10
|
||||
, O.connectionPoolMaxConnections = O.MaxConnectionsPerStripe 1
|
||||
}
|
||||
|
||||
O.runOrville pool $ do
|
||||
O.executeVoid O.DDLQuery (RawSql.fromString "DROP TABLE IF EXISTS migration_demo1")
|
||||
AutoMigration.autoMigrateSchema AutoMigration.defaultOptions [ AutoMigration.SchemaTable table1 ]
|
||||
_ <- O.insertEntity table1 Foo1 { foo1Id = 0 }
|
||||
AutoMigration.autoMigrateSchema AutoMigration.defaultOptions [ AutoMigration.SchemaTable table2 ]
|
||||
_ <- O.updateEntity table2 0 Foo2 { foo2Id = 0, foo2Age = Just 91 }
|
||||
AutoMigration.autoMigrateSchema AutoMigration.defaultOptions [ AutoMigration.SchemaTable table3 ]
|
||||
liftIO . print =<< O.findEntity table3 0
|
||||
-- SNIPPET: droppingColumns
|
||||
AutoMigration.autoMigrateSchema AutoMigration.defaultOptions [ AutoMigration.SchemaTable $ O.dropColumns ["age"] table1 ]
|
||||
liftIO . print =<< O.findEntity table1 0
|
69
orville-docsite/samples/using-migrations/stack.yaml
Normal file
69
orville-docsite/samples/using-migrations/stack.yaml
Normal file
@ -0,0 +1,69 @@
|
||||
# This file was automatically generated by 'stack init'
|
||||
#
|
||||
# Some commonly used options have been documented as comments in this file.
|
||||
# For advanced use and comprehensive documentation of the format, please see:
|
||||
# https://docs.haskellstack.org/en/stable/yaml_configuration/
|
||||
|
||||
# Resolver to choose a 'specific' stackage snapshot or a compiler version.
|
||||
# A snapshot resolver dictates the compiler version and the set of packages
|
||||
# to be used for project dependencies. For example:
|
||||
#
|
||||
# resolver: lts-21.13
|
||||
# resolver: nightly-2023-09-24
|
||||
# resolver: ghc-9.6.2
|
||||
#
|
||||
# The location of a snapshot can be provided as a file or url. Stack assumes
|
||||
# a snapshot provided as a file might change, whereas a url resource does not.
|
||||
#
|
||||
# resolver: ./custom-snapshot.yaml
|
||||
# resolver: https://example.com/snapshots/2023-01-01.yaml
|
||||
resolver: lts-21.19
|
||||
system-ghc: true
|
||||
install-ghc: false
|
||||
|
||||
# User packages to be built.
|
||||
# Various formats can be used as shown in the example below.
|
||||
#
|
||||
# packages:
|
||||
# - some-directory
|
||||
# - https://example.com/foo/bar/baz-0.0.2.tar.gz
|
||||
# subdirs:
|
||||
# - auto-update
|
||||
# - wai
|
||||
packages:
|
||||
- .
|
||||
# Dependency packages to be pulled from upstream that are not in the resolver.
|
||||
# These entries can reference officially published versions as well as
|
||||
# forks / in-progress versions pinned to a git hash. For example:
|
||||
#
|
||||
# extra-deps:
|
||||
# - acme-missiles-0.3
|
||||
# - git: https://github.com/commercialhaskell/stack.git
|
||||
# commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a
|
||||
#
|
||||
extra-deps:
|
||||
- orville-postgresql-1.0.0.0
|
||||
|
||||
# Override default flag values for local packages and extra-deps
|
||||
# flags: {}
|
||||
|
||||
# Extra package databases containing global packages
|
||||
# extra-package-dbs: []
|
||||
|
||||
# Control whether we use the GHC we find on the path
|
||||
# system-ghc: true
|
||||
#
|
||||
# Require a specific version of Stack, using version ranges
|
||||
# require-stack-version: -any # Default
|
||||
# require-stack-version: ">=2.13"
|
||||
#
|
||||
# Override the architecture used by Stack, especially useful on Windows
|
||||
# arch: i386
|
||||
# arch: x86_64
|
||||
#
|
||||
# Extra directories used by Stack for building
|
||||
# extra-include-dirs: [/path/to/dir]
|
||||
# extra-lib-dirs: [/path/to/dir]
|
||||
#
|
||||
# Allow a newer minor version of GHC than the snapshot specifies
|
||||
# compiler-check: newer-minor
|
19
orville-docsite/samples/using-migrations/stack.yaml.lock
Normal file
19
orville-docsite/samples/using-migrations/stack.yaml.lock
Normal file
@ -0,0 +1,19 @@
|
||||
# This file was autogenerated by Stack.
|
||||
# You should not edit this file by hand.
|
||||
# For more information, please see the documentation at:
|
||||
# https://docs.haskellstack.org/en/stable/lock_files
|
||||
|
||||
packages:
|
||||
- completed:
|
||||
hackage: orville-postgresql-1.0.0.0@sha256:35e9b9f8bc0bc1ee1847bcb5340fa39bed320f1573099ec16ca394726a50593a,9018
|
||||
pantry-tree:
|
||||
sha256: b8d324f2ad94f12ac419996cc2947ee0c69c5178b2caf13dc92135118602bbd8
|
||||
size: 12020
|
||||
original:
|
||||
hackage: orville-postgresql-1.0.0.0
|
||||
snapshots:
|
||||
- completed:
|
||||
sha256: fb482b8e2d5d061cdda4ba1da2957c012740c893a5ee1c1b99001adae7b1fbe7
|
||||
size: 640046
|
||||
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/21/19.yaml
|
||||
original: lts-21.19
|
@ -0,0 +1,32 @@
|
||||
cabal-version: 2.2
|
||||
|
||||
name: using-migrations
|
||||
version: 0.1.0.0
|
||||
-- synopsis:
|
||||
-- description:
|
||||
homepage: https://github.com/flipstone/using-migrations#readme
|
||||
license: BSD-3-Clause
|
||||
author: Flipstone Technology Partners, Inc
|
||||
maintainer: maintainers@flipstone.com
|
||||
copyright:
|
||||
category: sample
|
||||
build-type: Simple
|
||||
|
||||
executable using-migrations
|
||||
hs-source-dirs: src
|
||||
main-is: Main.hs
|
||||
default-language: Haskell2010
|
||||
|
||||
build-depends: base >= 4.7 && < 5,
|
||||
orville-postgresql,
|
||||
text
|
||||
|
||||
ghc-options: -Wall
|
||||
-Wcompat
|
||||
-Widentities
|
||||
-Wincomplete-record-updates
|
||||
-Wincomplete-uni-patterns
|
||||
-Wmissing-export-lists
|
||||
-Wmissing-home-modules
|
||||
-Wpartial-fields
|
||||
-Wredundant-constraints
|
7
orville-docsite/samples/using-plans/expected-output.txt
Normal file
7
orville-docsite/samples/using-plans/expected-output.txt
Normal file
@ -0,0 +1,7 @@
|
||||
Just (Student {studentId = 0, studentName = "Name", studentAge = 91})
|
||||
Just (Student {studentId = 1, studentName = "Other Name", studentAge = 42})
|
||||
[Just (Student {studentId = 1, studentName = "Other Name", studentAge = 42}),Just (Student {studentId = 0, studentName = "Name", studentAge = 91})]
|
||||
Class {classId = 1, classSubject = "Cooking"}
|
||||
["SELECT \"student_id\",\"id\",\"class_id\",\"student_id\" FROM \"plan_demo_student_class\" WHERE (\"student_id\") = ($1)","SELECT \"id\",\"id\",\"subject\" FROM \"plan_demo_class\" WHERE (\"id\") IN ($1, $2)"]
|
||||
["SELECT \"student_id\",\"id\",\"class_id\",\"student_id\" FROM \"plan_demo_student_class\" WHERE (\"student_id\") IN ($1, $2)","SELECT \"id\",\"id\",\"subject\" FROM \"plan_demo_class\" WHERE (\"id\") IN ($1, $2)"]
|
||||
[[Class {classId = 0, classSubject = "Painting"},Class {classId = 2, classSubject = "Swimming"}]]
|
17
orville-docsite/samples/using-plans/run.sh
Normal file
17
orville-docsite/samples/using-plans/run.sh
Normal file
@ -0,0 +1,17 @@
|
||||
# SNIPPET: hidden
|
||||
set -e
|
||||
service postgresql start
|
||||
# SNIPPET: buildAndExecute
|
||||
stack build
|
||||
stack exec using-plans
|
||||
# SNIPPET: hidden
|
||||
expected=$(cat expected-output.txt)
|
||||
actual=$(stack exec using-plans)
|
||||
|
||||
if [ "$expected" = "$actual" ]; then
|
||||
echo "Output matches expected"
|
||||
else
|
||||
echo "Expected output to be: $expected"
|
||||
echo "But it was actually : $actual"
|
||||
exit 1
|
||||
fi;
|
178
orville-docsite/samples/using-plans/src/Main.hs
Normal file
178
orville-docsite/samples/using-plans/src/Main.hs
Normal file
@ -0,0 +1,178 @@
|
||||
-- SNIPPET: moduleHeader
|
||||
module Main
|
||||
( main
|
||||
) where
|
||||
|
||||
import qualified Orville.PostgreSQL as O
|
||||
import qualified Orville.PostgreSQL.AutoMigration as AutoMigration
|
||||
import qualified Orville.PostgreSQL.Plan as Plan
|
||||
|
||||
import Data.List (sort)
|
||||
import Data.List.NonEmpty (NonEmpty((:|)))
|
||||
import qualified Data.Int as Int
|
||||
import qualified Data.Text as T
|
||||
|
||||
-------------
|
||||
-- Student --
|
||||
-------------
|
||||
|
||||
type StudentId = Int.Int32
|
||||
type StudentName = T.Text
|
||||
type StudentAge = Int.Int32
|
||||
|
||||
data Student = Student
|
||||
{ studentId :: StudentId
|
||||
, studentName :: StudentName
|
||||
, studentAge :: StudentAge
|
||||
}
|
||||
deriving Show
|
||||
|
||||
studentIdField :: O.FieldDefinition O.NotNull StudentId
|
||||
studentIdField =
|
||||
O.integerField "id"
|
||||
|
||||
studentNameField :: O.FieldDefinition O.NotNull StudentName
|
||||
studentNameField =
|
||||
O.unboundedTextField "name"
|
||||
|
||||
studentAgeField :: O.FieldDefinition O.NotNull StudentAge
|
||||
studentAgeField =
|
||||
O.integerField "age"
|
||||
|
||||
studentMarshaller :: O.SqlMarshaller Student Student
|
||||
studentMarshaller =
|
||||
Student
|
||||
<$> O.marshallField studentId studentIdField
|
||||
<*> O.marshallField studentName studentNameField
|
||||
<*> O.marshallField studentAge studentAgeField
|
||||
|
||||
studentTable :: O.TableDefinition (O.HasKey StudentId) Student Student
|
||||
studentTable =
|
||||
O.mkTableDefinition "plan_demo_student" (O.primaryKey studentIdField) studentMarshaller
|
||||
|
||||
----------------------------
|
||||
-- Student to Class Table --
|
||||
----------------------------
|
||||
|
||||
studentClassIdField :: O.FieldDefinition O.NotNull Int.Int32
|
||||
studentClassIdField =
|
||||
O.integerField "id"
|
||||
|
||||
studentClassClassIdField :: O.FieldDefinition O.NotNull Int.Int32
|
||||
studentClassClassIdField =
|
||||
O.integerField "class_id"
|
||||
|
||||
studentClassStudentIdField :: O.FieldDefinition O.NotNull Int.Int32
|
||||
studentClassStudentIdField =
|
||||
O.integerField "student_id"
|
||||
|
||||
data StudentClass = StudentClass
|
||||
{ studentClassId :: Int.Int32
|
||||
, studentClassClassId :: Int.Int32
|
||||
, studentClassStudentId :: Int.Int32
|
||||
}
|
||||
|
||||
studentClassMarshaller :: O.SqlMarshaller StudentClass StudentClass
|
||||
studentClassMarshaller =
|
||||
StudentClass
|
||||
<$> O.marshallField studentClassId studentClassIdField
|
||||
<*> O.marshallField studentClassClassId studentClassClassIdField
|
||||
<*> O.marshallField studentClassStudentId studentClassStudentIdField
|
||||
|
||||
studentClassTable :: O.TableDefinition (O.HasKey Int.Int32) StudentClass StudentClass
|
||||
studentClassTable =
|
||||
O.addTableConstraints
|
||||
[ O.foreignKeyConstraint (O.tableIdentifier classTable) $
|
||||
O.foreignReference (O.fieldName studentClassClassIdField) (O.fieldName classIdField) :| []
|
||||
, O.foreignKeyConstraint (O.tableIdentifier studentTable) $
|
||||
O.foreignReference (O.fieldName studentClassStudentIdField) (O.fieldName studentIdField) :| []
|
||||
]
|
||||
$ O.mkTableDefinition "plan_demo_student_class" (O.primaryKey studentClassIdField) studentClassMarshaller
|
||||
|
||||
-----------
|
||||
-- Class --
|
||||
-----------
|
||||
|
||||
classIdField :: O.FieldDefinition O.NotNull Int.Int32
|
||||
classIdField =
|
||||
O.integerField "id"
|
||||
|
||||
classSubjectField :: O.FieldDefinition O.NotNull T.Text
|
||||
classSubjectField =
|
||||
O.unboundedTextField "subject"
|
||||
|
||||
data Class = Class
|
||||
{ classId :: Int.Int32
|
||||
, classSubject :: T.Text
|
||||
}
|
||||
deriving (Show, Eq, Ord)
|
||||
|
||||
classMarshaller :: O.SqlMarshaller Class Class
|
||||
classMarshaller =
|
||||
Class
|
||||
<$> O.marshallField classId classIdField
|
||||
<*> O.marshallField classSubject classSubjectField
|
||||
|
||||
classTable :: O.TableDefinition (O.HasKey Int.Int32) Class Class
|
||||
classTable =
|
||||
O.mkTableDefinition "plan_demo_class" (O.primaryKey classIdField) classMarshaller
|
||||
-- SNIPPET: mainFunction
|
||||
main :: IO ()
|
||||
main = do
|
||||
pool <-
|
||||
O.createConnectionPool
|
||||
O.ConnectionOptions
|
||||
{ O.connectionString = "host=localhost user=postgres password=postgres"
|
||||
, O.connectionNoticeReporting = O.DisableNoticeReporting
|
||||
, O.connectionPoolStripes = O.OneStripePerCapability
|
||||
, O.connectionPoolLingerTime = 10
|
||||
, O.connectionPoolMaxConnections = O.MaxConnectionsPerStripe 1
|
||||
}
|
||||
|
||||
O.runOrville pool $ do
|
||||
AutoMigration.autoMigrateSchema AutoMigration.defaultOptions [AutoMigration.SchemaTable studentTable, AutoMigration.SchemaTable classTable, AutoMigration.SchemaTable studentClassTable]
|
||||
_ <- O.deleteEntity studentClassTable 0
|
||||
_ <- O.deleteEntity studentClassTable 1
|
||||
_ <- O.deleteEntity studentClassTable 2
|
||||
_ <- O.deleteEntity classTable 0
|
||||
_ <- O.deleteEntity classTable 1
|
||||
_ <- O.deleteEntity classTable 2
|
||||
_ <- O.deleteEntity studentTable 0
|
||||
_ <- O.deleteEntity studentTable 1
|
||||
_ <- O.insertEntity studentTable Student { studentId = 0, studentName = T.pack "Name", studentAge = 91 }
|
||||
_ <- O.insertEntity studentTable Student { studentId = 1, studentName = T.pack "Other Name", studentAge = 42 }
|
||||
_ <- O.insertEntity classTable Class { classId = 0, classSubject = T.pack "Painting" }
|
||||
_ <- O.insertEntity classTable Class { classId = 1, classSubject = T.pack "Cooking" }
|
||||
_ <- O.insertEntity classTable Class { classId = 2, classSubject = T.pack "Swimming" }
|
||||
_ <- O.insertEntity studentClassTable $ StudentClass {studentClassId=0, studentClassClassId=0, studentClassStudentId=0}
|
||||
_ <- O.insertEntity studentClassTable $ StudentClass {studentClassId=1, studentClassClassId=2, studentClassStudentId=0}
|
||||
_ <- O.insertEntity studentClassTable $ StudentClass {studentClassId=2, studentClassClassId=1, studentClassStudentId=1}
|
||||
pure ()
|
||||
-- SNIPPET: findByStudentId
|
||||
print =<< O.runOrville pool (Plan.execute (Plan.findMaybeOne studentTable studentIdField) 0)
|
||||
-- SNIPPET: findByStudentName
|
||||
print =<< O.runOrville pool (Plan.execute (Plan.findMaybeOne studentTable studentNameField) (T.pack "Other Name"))
|
||||
-- SNIPPET: findByStudentNameList
|
||||
print =<< O.runOrville pool (Plan.execute (Plan.planList (Plan.findMaybeOne studentTable studentNameField)) [T.pack "Other Name", T.pack "Name"])
|
||||
-- SNIPPET: findStudentAndClass
|
||||
(print =<<) . O.runOrville pool $ Plan.execute
|
||||
( Plan.findOne studentTable studentNameField
|
||||
`Plan.chain` Plan.focusParam studentId (Plan.findOne studentClassTable studentClassStudentIdField)
|
||||
`Plan.chain` Plan.focusParam studentClassClassId (Plan.findOne classTable classIdField)
|
||||
)
|
||||
(T.pack "Other Name")
|
||||
-- SNIPPET: studentsToClassesPlan
|
||||
let
|
||||
studentToClassesPlan :: Plan.Plan scope Student [Class]
|
||||
studentToClassesPlan =
|
||||
Plan.focusParam studentId (Plan.findAll studentClassTable studentClassStudentIdField)
|
||||
`Plan.chain` Plan.planList (Plan.focusParam studentClassClassId $ Plan.findOne classTable classIdField)
|
||||
-- SNIPPET: explainStudentsToClassesPlan
|
||||
print $ Plan.explain studentToClassesPlan
|
||||
print $ Plan.explain (Plan.planList studentToClassesPlan)
|
||||
-- SNIPPET: executeStudentsToClassesPlan
|
||||
(print =<<) . fmap (fmap sort) . O.runOrville pool $ Plan.execute
|
||||
( Plan.findAll studentTable studentNameField
|
||||
`Plan.chain` Plan.planList studentToClassesPlan
|
||||
)
|
||||
(T.pack "Name")
|
69
orville-docsite/samples/using-plans/stack.yaml
Normal file
69
orville-docsite/samples/using-plans/stack.yaml
Normal file
@ -0,0 +1,69 @@
|
||||
# This file was automatically generated by 'stack init'
|
||||
#
|
||||
# Some commonly used options have been documented as comments in this file.
|
||||
# For advanced use and comprehensive documentation of the format, please see:
|
||||
# https://docs.haskellstack.org/en/stable/yaml_configuration/
|
||||
|
||||
# Resolver to choose a 'specific' stackage snapshot or a compiler version.
|
||||
# A snapshot resolver dictates the compiler version and the set of packages
|
||||
# to be used for project dependencies. For example:
|
||||
#
|
||||
# resolver: lts-21.13
|
||||
# resolver: nightly-2023-09-24
|
||||
# resolver: ghc-9.6.2
|
||||
#
|
||||
# The location of a snapshot can be provided as a file or url. Stack assumes
|
||||
# a snapshot provided as a file might change, whereas a url resource does not.
|
||||
#
|
||||
# resolver: ./custom-snapshot.yaml
|
||||
# resolver: https://example.com/snapshots/2023-01-01.yaml
|
||||
resolver: lts-21.19
|
||||
system-ghc: true
|
||||
install-ghc: false
|
||||
|
||||
# User packages to be built.
|
||||
# Various formats can be used as shown in the example below.
|
||||
#
|
||||
# packages:
|
||||
# - some-directory
|
||||
# - https://example.com/foo/bar/baz-0.0.2.tar.gz
|
||||
# subdirs:
|
||||
# - auto-update
|
||||
# - wai
|
||||
packages:
|
||||
- .
|
||||
# Dependency packages to be pulled from upstream that are not in the resolver.
|
||||
# These entries can reference officially published versions as well as
|
||||
# forks / in-progress versions pinned to a git hash. For example:
|
||||
#
|
||||
# extra-deps:
|
||||
# - acme-missiles-0.3
|
||||
# - git: https://github.com/commercialhaskell/stack.git
|
||||
# commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a
|
||||
#
|
||||
extra-deps:
|
||||
- orville-postgresql-1.0.0.0
|
||||
|
||||
# Override default flag values for local packages and extra-deps
|
||||
# flags: {}
|
||||
|
||||
# Extra package databases containing global packages
|
||||
# extra-package-dbs: []
|
||||
|
||||
# Control whether we use the GHC we find on the path
|
||||
# system-ghc: true
|
||||
#
|
||||
# Require a specific version of Stack, using version ranges
|
||||
# require-stack-version: -any # Default
|
||||
# require-stack-version: ">=2.13"
|
||||
#
|
||||
# Override the architecture used by Stack, especially useful on Windows
|
||||
# arch: i386
|
||||
# arch: x86_64
|
||||
#
|
||||
# Extra directories used by Stack for building
|
||||
# extra-include-dirs: [/path/to/dir]
|
||||
# extra-lib-dirs: [/path/to/dir]
|
||||
#
|
||||
# Allow a newer minor version of GHC than the snapshot specifies
|
||||
# compiler-check: newer-minor
|
19
orville-docsite/samples/using-plans/stack.yaml.lock
Normal file
19
orville-docsite/samples/using-plans/stack.yaml.lock
Normal file
@ -0,0 +1,19 @@
|
||||
# This file was autogenerated by Stack.
|
||||
# You should not edit this file by hand.
|
||||
# For more information, please see the documentation at:
|
||||
# https://docs.haskellstack.org/en/stable/lock_files
|
||||
|
||||
packages:
|
||||
- completed:
|
||||
hackage: orville-postgresql-1.0.0.0@sha256:35e9b9f8bc0bc1ee1847bcb5340fa39bed320f1573099ec16ca394726a50593a,9018
|
||||
pantry-tree:
|
||||
sha256: b8d324f2ad94f12ac419996cc2947ee0c69c5178b2caf13dc92135118602bbd8
|
||||
size: 12020
|
||||
original:
|
||||
hackage: orville-postgresql-1.0.0.0
|
||||
snapshots:
|
||||
- completed:
|
||||
sha256: fb482b8e2d5d061cdda4ba1da2957c012740c893a5ee1c1b99001adae7b1fbe7
|
||||
size: 640046
|
||||
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/21/19.yaml
|
||||
original: lts-21.19
|
32
orville-docsite/samples/using-plans/using-plans.cabal
Normal file
32
orville-docsite/samples/using-plans/using-plans.cabal
Normal file
@ -0,0 +1,32 @@
|
||||
cabal-version: 2.2
|
||||
|
||||
name: using-plans
|
||||
version: 0.1.0.0
|
||||
-- synopsis:
|
||||
-- description:
|
||||
homepage: https://github.com/flipstone/using-plans#readme
|
||||
license: BSD-3-Clause
|
||||
author: Flipstone Technology Partners, Inc
|
||||
maintainer: maintainers@flipstone.com
|
||||
copyright:
|
||||
category: sample
|
||||
build-type: Simple
|
||||
|
||||
executable using-plans
|
||||
hs-source-dirs: src
|
||||
main-is: Main.hs
|
||||
default-language: Haskell2010
|
||||
|
||||
build-depends: base >= 4.7 && < 5,
|
||||
orville-postgresql,
|
||||
text
|
||||
|
||||
ghc-options: -Wall
|
||||
-Wcompat
|
||||
-Widentities
|
||||
-Wincomplete-record-updates
|
||||
-Wincomplete-uni-patterns
|
||||
-Wmissing-export-lists
|
||||
-Wmissing-home-modules
|
||||
-Wpartial-fields
|
||||
-Wredundant-constraints
|
@ -0,0 +1 @@
|
||||
Just (Foo {fooId = 0, fooName = "Name", fooAge = 91})
|
17
orville-docsite/samples/using-sql-marshaller/run.sh
Normal file
17
orville-docsite/samples/using-sql-marshaller/run.sh
Normal file
@ -0,0 +1,17 @@
|
||||
# SNIPPET: hidden
|
||||
set -e
|
||||
service postgresql start
|
||||
# SNIPPET: buildAndExecute
|
||||
stack build
|
||||
stack exec using-sql-marshaller
|
||||
# SNIPPET: hidden
|
||||
expected=$(cat expected-output.txt)
|
||||
actual=$(stack exec using-sql-marshaller)
|
||||
|
||||
if [ "$expected" = "$actual" ]; then
|
||||
echo "Output matches expected"
|
||||
else
|
||||
echo "Expected output to be: $expected"
|
||||
echo "But it was actually : $actual"
|
||||
exit 1
|
||||
fi;
|
63
orville-docsite/samples/using-sql-marshaller/src/Main.hs
Normal file
63
orville-docsite/samples/using-sql-marshaller/src/Main.hs
Normal file
@ -0,0 +1,63 @@
|
||||
-- SNIPPET: moduleHeader
|
||||
module Main
|
||||
( main
|
||||
) where
|
||||
|
||||
import qualified Orville.PostgreSQL as O
|
||||
import qualified Orville.PostgreSQL.AutoMigration as AutoMigration
|
||||
|
||||
import qualified Data.Int as Int
|
||||
import qualified Data.Text as T
|
||||
-- SNIPPET: dataTypes
|
||||
type FooId = Int.Int32
|
||||
type FooName = T.Text
|
||||
type FooAge = Int.Int32
|
||||
|
||||
data Foo = Foo
|
||||
{ fooId :: FooId
|
||||
, fooName :: FooName
|
||||
, fooAge :: FooAge
|
||||
}
|
||||
deriving (Eq, Show)
|
||||
-- SNIPPET: fieldDefinitions
|
||||
fooIdField :: O.FieldDefinition O.NotNull FooId
|
||||
fooIdField =
|
||||
O.integerField "id"
|
||||
|
||||
fooNameField :: O.FieldDefinition O.NotNull FooName
|
||||
fooNameField =
|
||||
O.unboundedTextField "name"
|
||||
|
||||
fooAgeField :: O.FieldDefinition O.NotNull FooAge
|
||||
fooAgeField =
|
||||
O.integerField "age"
|
||||
-- SNIPPET: sqlMarshaller
|
||||
fooMarshaller :: O.SqlMarshaller Foo Foo
|
||||
fooMarshaller =
|
||||
Foo
|
||||
<$> O.marshallField fooId fooIdField
|
||||
<*> O.marshallField fooName fooNameField
|
||||
<*> O.marshallField fooAge fooAgeField
|
||||
-- SNIPPET: tableDefinition
|
||||
table :: O.TableDefinition (O.HasKey FooId) Foo Foo
|
||||
table =
|
||||
O.mkTableDefinition "foo" (O.primaryKey fooIdField) fooMarshaller
|
||||
-- SNIPPET: mainFunction
|
||||
main :: IO ()
|
||||
main = do
|
||||
pool <-
|
||||
O.createConnectionPool
|
||||
O.ConnectionOptions
|
||||
{ O.connectionString = "host=localhost user=postgres password=postgres"
|
||||
, O.connectionNoticeReporting = O.DisableNoticeReporting
|
||||
, O.connectionPoolStripes = O.OneStripePerCapability
|
||||
, O.connectionPoolLingerTime = 10
|
||||
, O.connectionPoolMaxConnections = O.MaxConnectionsPerStripe 1
|
||||
}
|
||||
|
||||
mbFoo <- O.runOrville pool $ do
|
||||
AutoMigration.autoMigrateSchema AutoMigration.defaultOptions [AutoMigration.SchemaTable table]
|
||||
_ <- O.deleteEntity table 0
|
||||
_ <- O.insertEntity table Foo { fooId = 0, fooName = T.pack "Name", fooAge = 91 }
|
||||
O.findEntity table 0
|
||||
print mbFoo
|
69
orville-docsite/samples/using-sql-marshaller/stack.yaml
Normal file
69
orville-docsite/samples/using-sql-marshaller/stack.yaml
Normal file
@ -0,0 +1,69 @@
|
||||
# This file was automatically generated by 'stack init'
|
||||
#
|
||||
# Some commonly used options have been documented as comments in this file.
|
||||
# For advanced use and comprehensive documentation of the format, please see:
|
||||
# https://docs.haskellstack.org/en/stable/yaml_configuration/
|
||||
|
||||
# Resolver to choose a 'specific' stackage snapshot or a compiler version.
|
||||
# A snapshot resolver dictates the compiler version and the set of packages
|
||||
# to be used for project dependencies. For example:
|
||||
#
|
||||
# resolver: lts-21.13
|
||||
# resolver: nightly-2023-09-24
|
||||
# resolver: ghc-9.6.2
|
||||
#
|
||||
# The location of a snapshot can be provided as a file or url. Stack assumes
|
||||
# a snapshot provided as a file might change, whereas a url resource does not.
|
||||
#
|
||||
# resolver: ./custom-snapshot.yaml
|
||||
# resolver: https://example.com/snapshots/2023-01-01.yaml
|
||||
resolver: lts-21.19
|
||||
system-ghc: true
|
||||
install-ghc: false
|
||||
|
||||
# User packages to be built.
|
||||
# Various formats can be used as shown in the example below.
|
||||
#
|
||||
# packages:
|
||||
# - some-directory
|
||||
# - https://example.com/foo/bar/baz-0.0.2.tar.gz
|
||||
# subdirs:
|
||||
# - auto-update
|
||||
# - wai
|
||||
packages:
|
||||
- .
|
||||
# Dependency packages to be pulled from upstream that are not in the resolver.
|
||||
# These entries can reference officially published versions as well as
|
||||
# forks / in-progress versions pinned to a git hash. For example:
|
||||
#
|
||||
# extra-deps:
|
||||
# - acme-missiles-0.3
|
||||
# - git: https://github.com/commercialhaskell/stack.git
|
||||
# commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a
|
||||
#
|
||||
extra-deps:
|
||||
- orville-postgresql-1.0.0.0
|
||||
|
||||
# Override default flag values for local packages and extra-deps
|
||||
# flags: {}
|
||||
|
||||
# Extra package databases containing global packages
|
||||
# extra-package-dbs: []
|
||||
|
||||
# Control whether we use the GHC we find on the path
|
||||
# system-ghc: true
|
||||
#
|
||||
# Require a specific version of Stack, using version ranges
|
||||
# require-stack-version: -any # Default
|
||||
# require-stack-version: ">=2.13"
|
||||
#
|
||||
# Override the architecture used by Stack, especially useful on Windows
|
||||
# arch: i386
|
||||
# arch: x86_64
|
||||
#
|
||||
# Extra directories used by Stack for building
|
||||
# extra-include-dirs: [/path/to/dir]
|
||||
# extra-lib-dirs: [/path/to/dir]
|
||||
#
|
||||
# Allow a newer minor version of GHC than the snapshot specifies
|
||||
# compiler-check: newer-minor
|
19
orville-docsite/samples/using-sql-marshaller/stack.yaml.lock
Normal file
19
orville-docsite/samples/using-sql-marshaller/stack.yaml.lock
Normal file
@ -0,0 +1,19 @@
|
||||
# This file was autogenerated by Stack.
|
||||
# You should not edit this file by hand.
|
||||
# For more information, please see the documentation at:
|
||||
# https://docs.haskellstack.org/en/stable/lock_files
|
||||
|
||||
packages:
|
||||
- completed:
|
||||
hackage: orville-postgresql-1.0.0.0@sha256:35e9b9f8bc0bc1ee1847bcb5340fa39bed320f1573099ec16ca394726a50593a,9018
|
||||
pantry-tree:
|
||||
sha256: b8d324f2ad94f12ac419996cc2947ee0c69c5178b2caf13dc92135118602bbd8
|
||||
size: 12020
|
||||
original:
|
||||
hackage: orville-postgresql-1.0.0.0
|
||||
snapshots:
|
||||
- completed:
|
||||
sha256: fb482b8e2d5d061cdda4ba1da2957c012740c893a5ee1c1b99001adae7b1fbe7
|
||||
size: 640046
|
||||
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/21/19.yaml
|
||||
original: lts-21.19
|
@ -0,0 +1,32 @@
|
||||
cabal-version: 2.2
|
||||
|
||||
name: using-sql-marshaller
|
||||
version: 0.1.0.0
|
||||
-- synopsis:
|
||||
-- description:
|
||||
homepage: https://github.com/flipstone/using-sql-marshaller#readme
|
||||
license: BSD-3-Clause
|
||||
author: Flipstone Technology Partners, Inc
|
||||
maintainer: maintainers@flipstone.com
|
||||
copyright:
|
||||
category: sample
|
||||
build-type: Simple
|
||||
|
||||
executable using-sql-marshaller
|
||||
hs-source-dirs: src
|
||||
main-is: Main.hs
|
||||
default-language: Haskell2010
|
||||
|
||||
build-depends: base >= 4.7 && < 5,
|
||||
orville-postgresql,
|
||||
text
|
||||
|
||||
ghc-options: -Wall
|
||||
-Wcompat
|
||||
-Widentities
|
||||
-Wincomplete-record-updates
|
||||
-Wincomplete-uni-patterns
|
||||
-Wmissing-export-lists
|
||||
-Wmissing-home-modules
|
||||
-Wpartial-fields
|
||||
-Wredundant-constraints
|
5
orville-docsite/scripts/build.sh
Executable file
5
orville-docsite/scripts/build.sh
Executable file
@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
|
||||
./scripts/stack.sh install
|
||||
./scripts/test-all-samples.sh
|
||||
./scripts/site.sh rebuild
|
5
orville-docsite/scripts/shell.sh
Executable file
5
orville-docsite/scripts/shell.sh
Executable file
@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
docker compose run --rm dev bash
|
10
orville-docsite/scripts/site.sh
Executable file
10
orville-docsite/scripts/site.sh
Executable file
@ -0,0 +1,10 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
docker compose run \
|
||||
--rm \
|
||||
--service-ports \
|
||||
--workdir /orville-docsite/site-builder \
|
||||
dev \
|
||||
stack exec site -- "$@"
|
5
orville-docsite/scripts/stack.sh
Executable file
5
orville-docsite/scripts/stack.sh
Executable file
@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
docker compose run --rm dev stack "$@"
|
11
orville-docsite/scripts/test-all-samples.sh
Executable file
11
orville-docsite/scripts/test-all-samples.sh
Executable file
@ -0,0 +1,11 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
for sample_dir in samples/*; do
|
||||
# If anything in the samples directory is not directory then don't try
|
||||
# to run it as a sample
|
||||
if [ -d "$sample_dir" ]; then
|
||||
./scripts/test-sample.sh $sample_dir
|
||||
fi
|
||||
done
|
20
orville-docsite/scripts/test-sample.sh
Executable file
20
orville-docsite/scripts/test-sample.sh
Executable file
@ -0,0 +1,20 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
sample_dir=$1
|
||||
|
||||
if [ "$1" = "" ]; then
|
||||
echo "path to sample directory must be specified"
|
||||
exit 1
|
||||
fi;
|
||||
|
||||
echo "===================================================="
|
||||
echo "Running $sample_dir"
|
||||
echo "============================i======================="
|
||||
|
||||
docker compose run \
|
||||
--rm \
|
||||
--workdir /orville-docsite/$sample_dir \
|
||||
dev \
|
||||
sh ./run.sh
|
7
orville-docsite/site-builder/contact.md
Normal file
7
orville-docsite/site-builder/contact.md
Normal file
@ -0,0 +1,7 @@
|
||||
---
|
||||
title: Contact
|
||||
---
|
||||
|
||||
Orville is written and maintained by [Flipstone Technology
|
||||
Partners](http://flipstone.com). You can reach the package maintainers at <a
|
||||
href="mailto:maintainers@flipstone.com">maintainers@flipstone.com</a>.
|
87
orville-docsite/site-builder/css/default.css
Normal file
87
orville-docsite/site-builder/css/default.css
Normal file
@ -0,0 +1,87 @@
|
||||
html {
|
||||
font-size: 62.5%;
|
||||
background-color: #0d0d13;
|
||||
}
|
||||
|
||||
body {
|
||||
font-size: 1.6rem;
|
||||
color: #c7c2c2;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
header {
|
||||
border-bottom: 0.2rem solid #232629;
|
||||
}
|
||||
|
||||
.leftbar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 200px;
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
nav {
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
main {
|
||||
margin-left: 260px;
|
||||
}
|
||||
|
||||
nav a {
|
||||
display: block;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: #8383ff;
|
||||
}
|
||||
|
||||
a:visited {
|
||||
color: #6455f7;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #e768d4;
|
||||
}
|
||||
|
||||
footer {
|
||||
margin-top: 3rem;
|
||||
padding: 1.2rem 0;
|
||||
border-top: 0.2rem solid #232629;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.4rem;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
article .header {
|
||||
font-size: 1.4rem;
|
||||
font-style: italic;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.logo a {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
div.sourceCode {
|
||||
padding: 5px 10px;
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
.codeblock-label {
|
||||
background: #1f1f80;
|
||||
display: inline-block;
|
||||
padding: 3px 10px;
|
||||
}
|
||||
|
BIN
orville-docsite/site-builder/images/haskell-logo.png
Normal file
BIN
orville-docsite/site-builder/images/haskell-logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.5 KiB |
392
orville-docsite/site-builder/images/orville-waving-pennant.svg
Normal file
392
orville-docsite/site-builder/images/orville-waving-pennant.svg
Normal file
@ -0,0 +1,392 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 27.8.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.2" baseProfile="tiny" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
x="0px" y="0px" viewBox="0 0 201 200" overflow="visible" xml:space="preserve">
|
||||
<g id="Layer_1_00000106848644063259509600000005299531540001616283_">
|
||||
<path fill="#F4CB68" d="M72.8,46c0.9,0.7,1.7,1.1,2.5,1.2c-0.6,0.6-1.2,1.1-1.8,1.1c-0.6-0.3-1.1-0.6-1.7-1
|
||||
C72.2,47,72.6,46.5,72.8,46z"/>
|
||||
<path fill="#EEBC4A" d="M72.9,46c-0.2,0.5,1.3,1.3,1,2c0,0.1-0.1,0.2-0.1,0.2c0.1,0.1,1.2-0.4,1.3-1.1c0.1-0.8-1-1.5-1.8-1.3
|
||||
C73.2,45.7,73,45.8,72.9,46z"/>
|
||||
<path fill="#E1B759" d="M73.5,47.2C73,46.3,73,45.1,73,44.4c-0.7,0.4-1.7,1.2-1.8,1.9c0.2,0.6,0.5,1.2,0.8,1.8
|
||||
C72.4,47.5,72.9,47.3,73.5,47.2z"/>
|
||||
<path fill="#E1B759" d="M106.7,5.4c0.5-0.5,0.9-1,1.4-1.5c-0.1,1.3,0.7,3.1,1.8,4.8c-0.4,0.4-0.7,0.8-1.1,1.1
|
||||
C107.9,8.6,107,7,106.7,5.4z"/>
|
||||
<g>
|
||||
<path fill="#CB4417" d="M97.5,28C97.2,28.2,74.3,45.5,74,45.7c0,0,0,0,0.1,0.1L84.3,54c2.6,1.8,5.1,3.8,7.5,5.9
|
||||
c1.6,1.4,3.1,2.8,4.6,4.3c1.9,1.9,3.7,3.9,5.4,5.9c2.5,3,4.8,6.1,6.8,9.4c2,3.2,3.9,6.4,5.9,9.6c1.9,3.1,4.2,5.9,6.8,8.2
|
||||
c3.2,2.8,6.7,5,10.6,6.6c4,1.7,8.2,2.9,12.4,3.9c2.7,0.6,5.4,1.2,8,1.8c7.1,1.8,13.8,4.6,20.1,8.4c3.1,1.9,6.1,4,8.9,6.3
|
||||
c2.3,1.9,4.5,3.9,6.7,6c2.1,2,4.1,4,6.2,6c1.3,1.2,2.6,2.4,3.9,3.5c0.1,0.1,0.3,0.2,0.4,0.2c0.1-0.1,0.1-0.1,0.2-0.2
|
||||
c-0.3-2.5-0.9-5-1.7-7.4c-1.7-5.6-4-11-6.7-16.1c-1.6-3-3.5-5.9-5.5-8.6c-1.9-2.5-3.9-5-6-7.3c-1.9-2.1-3.9-4-5.9-5.9
|
||||
c-1.7-1.5-3.4-2.9-5.1-4.4c-1.3-1.1-2.5-2.2-3.8-3.4c-1-1-2-2-3-3c-1.6-1.7-3.2-3.5-4.7-5.4c-3.6-4.6-6.5-9.6-9.1-14.8
|
||||
c-1.8-3.6-3.5-7.3-4.9-11.2c-0.6-1.6-1.1-3.3-1.5-5c-1.3-5.1-3-9.9-5.2-14.6c-1.6-3.3-3.4-6.4-5.5-9.3c-1.6-2.3-3.4-4.4-5.2-6.5
|
||||
c-0.1-0.1-0.1-0.2-0.1-0.2c-0.9-0.9-1.7-1.8-2.6-2.6c-0.2-0.2-0.5-0.4-0.7-0.6c-2.9-2.7-6-5.2-9.3-7.3c-0.3-0.2-0.5-0.3-0.8-0.5
|
||||
c0,0.2-0.1,0.4-0.1,0.6L97.5,28z"/>
|
||||
</g>
|
||||
<path fill="#F4CB68" d="M199.5,139.8c-0.3-2.4-0.9-4.9-1.7-7.6c-1.8-5.8-4.1-11.2-6.8-16.3c-1.6-2.9-3.4-5.7-5.6-8.7
|
||||
c-1.9-2.6-3.9-5-6-7.4c-1.9-2.1-3.9-4.1-6-6c-1-0.9-2-1.8-3-2.6c-0.7-0.6-1.4-1.2-2.1-1.8c-1.3-1.2-2.5-2.3-3.7-3.4
|
||||
c-1-0.9-2-1.9-3-3c-1.5-1.6-3-3.3-4.6-5.4c-3.2-4-6.1-8.7-9.1-14.7c-2.1-4.1-3.6-7.7-4.9-11.1c-0.6-1.6-1.1-3.2-1.5-4.9
|
||||
c-1.3-5.3-3.1-10.2-5.3-14.7c-1.6-3.3-3.5-6.5-5.6-9.4c-1.7-2.4-3.5-4.6-5.3-6.5l0,0c0,0,0-0.1-0.1-0.1l-0.1-0.1l-2.6-2.7l-0.1-0.1
|
||||
c-0.1-0.1-0.2-0.1-0.2-0.2c-0.1-0.1-0.3-0.2-0.4-0.3c-2.9-2.8-6.1-5.3-9.4-7.4L112.1,5c-0.5-0.3-0.9-0.6-1.4-0.9
|
||||
c-0.1,0-0.1-0.1-0.2-0.1c-0.7-0.4-1.4-0.5-2.3-0.4c-0.5,0.6-1,1.2-1.5,1.8c0.8-0.2,1.8,0.1,2.9,0.5c-1.3,2.3-2.6,4.7-3.9,7
|
||||
c-1.6,2.8-3.3,5.4-5.3,7.8c-1.7,2.1-3.5,4-5.4,5.8l-15.3,14L74,44.7c-0.4-0.2-0.7-0.4-1-0.5c-0.8,0.3-1.5,0.8-1.7,1.7
|
||||
c1,0.1,2.4,0.7,3.5,1.4l9,7.2l0,0c2.6,1.8,5.1,3.8,7.4,5.9c1.5,1.3,3,2.7,4.5,4.3c1.8,1.8,3.6,3.8,5.4,5.9c2.6,3,4.8,6.2,6.8,9.3
|
||||
c1.3,2,2.5,4.1,3.7,6.1c0.7,1.2,1.5,2.4,2.2,3.6c2,3.2,4.3,6,7,8.4c3.1,2.8,6.6,5,10.8,6.7c3.7,1.5,7.6,2.8,12.5,3.9
|
||||
c1.1,0.3,2.3,0.5,3.4,0.8c1.5,0.3,3.1,0.7,4.6,1.1c6.9,1.7,13.6,4.5,19.8,8.3c3,1.8,6,3.9,8.8,6.2c2.2,1.8,4.4,3.7,6.7,5.9
|
||||
c0.7,0.7,1.4,1.3,2,2c1.4,1.3,2.8,2.7,4.2,4c1,0.9,2,1.8,3,2.7c0.3,0.3,0.6,0.6,1,0.9c0.2,0.1,0.3,0.2,0.4,0.3h0.1l0.5,0.3l0.9-0.9
|
||||
L199.5,139.8z M197.7,138.4c-1-0.9-2-1.8-2.9-2.7c-1.4-1.3-2.8-2.7-4.2-4c-0.7-0.7-1.4-1.3-2-2c-2.3-2.2-4.5-4.2-6.8-6
|
||||
c-2.9-2.3-5.9-4.5-9-6.3c-6.4-3.9-13.2-6.7-20.3-8.5c-1.6-0.4-3.1-0.7-4.7-1.1c-1.1-0.2-2.2-0.5-3.3-0.8c-4.8-1.1-8.7-2.4-12.3-3.8
|
||||
c-4-1.7-7.4-3.8-10.4-6.4c-2.6-2.3-4.8-5-6.7-8.1c-0.7-1.2-1.5-2.4-2.2-3.6c-1.2-2-2.5-4.1-3.7-6.1c-2-3.2-4.3-6.4-6.9-9.5
|
||||
c-1.8-2.1-3.6-4.1-5.5-6c-1.6-1.6-3.1-3-4.6-4.3c-2.4-2.1-4.9-4.1-7.5-6l-6.9-5.5l2.5-2.3l17.4-15.9c2.2-2,4.1-4.1,5.9-6.3
|
||||
c2.1-2.6,4-5.4,5.6-8.4c0.8-1.4,1.6-2.9,2.4-4.3l1.7-2.9c2.6,1.8,5.2,3.9,7.6,6.1c0.2,0.2,0.3,0.3,0.5,0.4c0.1,0,0.1,0.1,0.2,0.1
|
||||
L124,17c0,0.1,0.1,0.2,0.2,0.3c1.8,1.9,3.5,4.1,5.2,6.4c2,2.9,3.9,5.9,5.5,9.2c2.1,4.4,3.9,9.3,5.2,14.4c0.4,1.8,1,3.5,1.6,5.1
|
||||
c1.3,3.5,2.9,7.1,4.9,11.2c3,6.1,6,10.9,9.2,15c1.7,2.1,3.2,3.9,4.7,5.5c1.1,1.1,2.1,2.1,3.1,3.1c1.2,1.1,2.4,2.2,3.8,3.4
|
||||
c0.7,0.6,1.4,1.2,2.1,1.8c1,0.8,2,1.7,2.9,2.6c2.1,1.9,4,3.8,5.9,5.9c2.1,2.3,4.1,4.7,5.9,7.2c2.1,2.9,3.9,5.7,5.4,8.5
|
||||
c2.6,4.9,4.9,10.3,6.7,16C196.9,134.7,197.4,136.6,197.7,138.4L197.7,138.4z"/>
|
||||
<g>
|
||||
<path fill="#F4CB68" d="M96.6,42c5.3-4.5,10.3-9.4,15-14.6c1.9-2.3,7.2-7.2,9.9-3.3c2.5,3.7,0.3,9.3-2.4,12.3
|
||||
c-4.4,4.8-9,9.3-13.9,13.4c-3.2,2.8-8.3,4.2-12,1.7C89.2,48.8,94.4,43.7,96.6,42z M102.2,47.4c5.3-4.4,10.2-9.2,14.8-14.3
|
||||
c1.8-2.1,1.9-3.6,1.2-4.5c-0.8-1-1.9-0.7-3.7,1.4c-4.8,5.3-9.8,10.2-15.2,14.8c-2.2,1.7-2.5,2.8-1.5,3.6
|
||||
C98.6,49.1,100.1,49.1,102.2,47.4z M101.6,55.7c8.8-6.5,16.4-13.9,23.3-22.6c0.7-0.9,1.3-1.1,1.7-0.4c0.9,1.6,1.4,2.4,2.2,4
|
||||
c1.9,3.8,0.4,8.7-2.3,11.6c-0.5,0.6-0.8,0.9-1.3,1.6c-1.5,1.5-3.4,3.6-5.8,2.6c0,0,0,0-0.1,0.1c0.4,2.8-1.8,5.3-3.7,6.8
|
||||
c-1.4,1.2-2.9,2.4-4.4,3.5c-0.7,0.5-1.2,0.9-1.7,1.3c-0.2,0.2-0.3,0.3-0.4,0.4c-0.6,0.4-1.2,0.3-1.6-0.2s-0.6-0.7-1-1.2
|
||||
c-0.5-0.5-0.4-1.2,0.3-1.8c0.4-0.3,1-0.8,2.1-1.6c1.7-1.2,3.3-2.4,4.8-3.8c1.5-1.1,2.9-2.9,1.7-4.7c-0.3-0.4-0.5-0.6-0.8-1.1
|
||||
c-3.3,2.9-6.7,5.6-10.3,8.2c-0.9,0.6-1.8,0.6-2.3,0.1c-0.4-0.4-0.6-0.6-1-1C100.4,57,100.6,56.3,101.6,55.7z M118.9,47.9
|
||||
c0.8,1.2,1.9,1.3,3.7-0.7c0.7-0.8,1.1-1.2,1.8-2c1.6-1.9,2-3.3,1.3-4.5c-0.3-0.6-0.5-0.9-0.9-1.5c-2.1,2.6-4.4,5.2-6.7,7.6
|
||||
C118.5,47.2,118.6,47.5,118.9,47.9z M112.3,68.2c7.2-6.8,13.2-14.5,18.6-22.9c0.2-0.3,0.2-0.3,0.2-0.4c0.5-0.8,1-0.8,1.3-0.1
|
||||
c0.2,0.5,0.3,0.8,0.5,1.4c0.3,0.7,0.1,1.6-0.5,2.6c-4.3,6.7-8.9,13-14.4,18.7c0,0,0,0,0,0.1c5.8-4,11-8.6,15.9-13.9
|
||||
c0.6-0.7,1-0.7,1.2,0.1c0.1,0.4,0.2,0.6,0.3,1.1c0.2,0.7,0.1,1.6-0.4,2.3c-0.1,0.1-0.1,0.1-0.2,0.3c-5.9,6.2-12.2,11.3-19.6,15.4
|
||||
c-0.8,0.4-1.4,0.2-1.8-0.4c-0.6-0.9-0.9-1.4-1.6-2.3C111.5,69.4,111.6,68.8,112.3,68.2z M118.3,77.1c6.7-5.1,12.3-11.1,17.4-18
|
||||
c0.5-0.8,1-0.8,1.3-0.1c0.2,0.5,0.3,0.8,0.5,1.4c0.3,0.7,0.2,1.6-0.3,2.4c-4.9,6.7-10.2,12.6-16.7,17.6c-0.7,0.5-1.3,0.3-1.7-0.3
|
||||
c-0.3-0.5-0.5-0.7-0.8-1.2C117.5,78.3,117.6,77.6,118.3,77.1z M122,82.7c6.3-4.9,11.6-10.6,16.3-17.2c0.5-0.7,1-0.8,1.3,0
|
||||
c0.2,0.5,0.4,0.8,0.6,1.3c0.3,0.7,0.3,1.6-0.2,2.3c-4,5.8-8.4,10.8-13.7,15.2c1.3,1.6,2,2.3,3.5,3.7c0.6,0.5,0.5,1.1,0,1.5
|
||||
c-0.3,0.2-0.5,0.4-0.8,0.6c-0.6,0.4-1.3,0.4-1.9-0.1c-1.9-1.8-3.6-3.6-5.3-5.6C121.2,83.8,121.4,83.2,122,82.7z M130.3,91.3
|
||||
c5.4-4.3,9.8-9.1,13.6-14.9c0.4-0.6,0.8-0.6,1.2,0c0.3,0.5,0.4,0.7,0.7,1.2c0.4,0.7,0.5,1.4,0.1,2c-3.2,4.9-6.7,9-11.1,12.7
|
||||
c1.6,1.2,2.5,1.7,4.2,2.8c0.6,0.4,0.7,0.8,0.2,1.2c-0.3,0.2-0.4,0.3-0.7,0.5c-0.5,0.3-1.2,0.3-1.9,0c-2.2-1.3-4.4-2.6-6.5-4
|
||||
C129.7,92.3,129.7,91.7,130.3,91.3z M140.3,97.6c4.4-3.3,7.8-6.8,10.8-11.5c0.3-0.5,0.7-0.5,1.2,0.1c1.8,2,3.6,3.9,5.5,5.8
|
||||
c0.5,0.5,0.8,1.1,0.6,1.5c-0.1,0.2-0.2,0.3-0.3,0.5c-0.2,0.4-0.7,0.3-1.2-0.2c-1.6-1.4-2.4-2.1-3.9-3.6c-0.9,1.5-1.4,2.1-2.4,3.4
|
||||
c1.2,0.9,1.9,1.4,3.2,2.3c0.6,0.4,0.8,0.9,0.5,1.2c-0.2,0.2-0.2,0.3-0.4,0.4c-0.3,0.3-0.9,0.2-1.5-0.2c-1.3-0.8-2-1.3-3.3-2.1
|
||||
c-1.4,1.4-2.2,2-3.9,3.2c1.9,1,2.8,1.4,4.7,2.3c0.7,0.3,0.8,0.7,0.4,0.9c-0.2,0.1-0.3,0.2-0.6,0.3c-0.4,0.2-1.1,0.2-1.8-0.1
|
||||
c-2.4-1-4.8-2.1-7.2-3.2C139.9,98.4,139.8,98,140.3,97.6z M96.6,42c5.3-4.5,10.3-9.4,15-14.6c1.9-2.3,7.2-7.2,9.9-3.3
|
||||
c2.5,3.7,0.3,9.3-2.4,12.3c-4.4,4.8-9,9.3-13.9,13.4c-3.2,2.8-8.3,4.2-12,1.7C89.2,48.8,94.4,43.7,96.6,42z M102.2,47.4
|
||||
c5.3-4.4,10.2-9.2,14.8-14.3c1.8-2.1,1.9-3.6,1.2-4.5c-0.8-1-1.9-0.7-3.7,1.4c-4.8,5.3-9.8,10.2-15.2,14.8
|
||||
c-2.2,1.7-2.5,2.8-1.5,3.6C98.6,49.1,100.1,49.1,102.2,47.4z M101.6,55.7c8.8-6.5,16.4-13.9,23.3-22.6c0.7-0.9,1.3-1.1,1.7-0.4
|
||||
c0.9,1.6,1.4,2.4,2.2,4c1.9,3.8,0.4,8.7-2.3,11.6c-0.5,0.6-0.8,0.9-1.3,1.6c-1.5,1.5-3.4,3.6-5.8,2.6c0,0,0,0-0.1,0.1
|
||||
c0.4,2.8-1.8,5.3-3.7,6.8c-1.4,1.2-2.9,2.4-4.4,3.5c-0.7,0.5-1.2,0.9-1.7,1.3c-0.2,0.2-0.3,0.3-0.4,0.4c-0.6,0.4-1.2,0.3-1.6-0.2
|
||||
s-0.6-0.7-1-1.2c-0.5-0.5-0.4-1.2,0.3-1.8c0.4-0.3,1-0.8,2.1-1.6c1.7-1.2,3.3-2.4,4.8-3.8c1.5-1.1,2.9-2.9,1.7-4.7
|
||||
c-0.3-0.4-0.5-0.6-0.8-1.1c-3.3,2.9-6.7,5.6-10.3,8.2c-0.9,0.6-1.8,0.6-2.3,0.1c-0.4-0.4-0.6-0.6-1-1
|
||||
C100.4,57,100.6,56.3,101.6,55.7z M118.9,47.9c0.8,1.2,1.9,1.3,3.7-0.7c0.7-0.8,1.1-1.2,1.8-2c1.6-1.9,2-3.3,1.3-4.5
|
||||
c-0.3-0.6-0.5-0.9-0.9-1.5c-2.1,2.6-4.4,5.2-6.7,7.6C118.5,47.2,118.6,47.5,118.9,47.9z M112.3,68.2c7.2-6.8,13.2-14.5,18.6-22.9
|
||||
c0.2-0.3,0.2-0.3,0.2-0.4c0.5-0.8,1-0.8,1.3-0.1c0.2,0.5,0.3,0.8,0.5,1.4c0.3,0.7,0.1,1.6-0.5,2.6c-4.3,6.7-8.9,13-14.4,18.7
|
||||
c0,0,0,0,0,0.1c5.8-4,11-8.6,15.9-13.9c0.6-0.7,1-0.7,1.2,0.1c0.1,0.4,0.2,0.6,0.3,1.1c0.2,0.7,0.1,1.6-0.4,2.3
|
||||
c-0.1,0.1-0.1,0.1-0.2,0.3c-5.9,6.2-12.2,11.3-19.6,15.4c-0.8,0.4-1.4,0.2-1.8-0.4c-0.6-0.9-0.9-1.4-1.6-2.3
|
||||
C111.5,69.4,111.6,68.8,112.3,68.2z M118.3,77.1c6.7-5.1,12.3-11.1,17.4-18c0.5-0.8,1-0.8,1.3-0.1c0.2,0.5,0.3,0.8,0.5,1.4
|
||||
c0.3,0.7,0.2,1.6-0.3,2.4c-4.9,6.7-10.2,12.6-16.7,17.6c-0.7,0.5-1.3,0.3-1.7-0.3c-0.3-0.5-0.5-0.7-0.8-1.2
|
||||
C117.5,78.3,117.6,77.6,118.3,77.1z M122,82.7c6.3-4.9,11.6-10.6,16.3-17.2c0.5-0.7,1-0.8,1.3,0c0.2,0.5,0.4,0.8,0.6,1.3
|
||||
c0.3,0.7,0.3,1.6-0.2,2.3c-4,5.8-8.4,10.8-13.7,15.2c1.3,1.6,2,2.3,3.5,3.7c0.6,0.5,0.5,1.1,0,1.5c-0.3,0.2-0.5,0.4-0.8,0.6
|
||||
c-0.6,0.4-1.3,0.4-1.9-0.1c-1.9-1.8-3.6-3.6-5.3-5.6C121.2,83.8,121.4,83.2,122,82.7z M130.3,91.3c5.4-4.3,9.8-9.1,13.6-14.9
|
||||
c0.4-0.6,0.8-0.6,1.2,0c0.3,0.5,0.4,0.7,0.7,1.2c0.4,0.7,0.5,1.4,0.1,2c-3.2,4.9-6.7,9-11.1,12.7c1.6,1.2,2.5,1.7,4.2,2.8
|
||||
c0.6,0.4,0.7,0.8,0.2,1.2c-0.3,0.2-0.4,0.3-0.7,0.5c-0.5,0.3-1.2,0.3-1.9,0c-2.2-1.3-4.4-2.6-6.5-4
|
||||
C129.7,92.3,129.7,91.7,130.3,91.3z M140.3,97.6c4.4-3.3,7.8-6.8,10.8-11.5c0.3-0.5,0.7-0.5,1.2,0.1c1.8,2,3.6,3.9,5.5,5.8
|
||||
c0.5,0.5,0.8,1.1,0.6,1.5c-0.1,0.2-0.2,0.3-0.3,0.5c-0.2,0.4-0.7,0.3-1.2-0.2c-1.6-1.4-2.4-2.1-3.9-3.6c-0.9,1.5-1.4,2.1-2.4,3.4
|
||||
c1.2,0.9,1.9,1.4,3.2,2.3c0.6,0.4,0.8,0.9,0.5,1.2c-0.2,0.2-0.2,0.3-0.4,0.4c-0.3,0.3-0.9,0.2-1.5-0.2c-1.3-0.8-2-1.3-3.3-2.1
|
||||
c-1.4,1.4-2.2,2-3.9,3.2c1.9,1,2.8,1.4,4.7,2.3c0.7,0.3,0.8,0.7,0.4,0.9c-0.2,0.1-0.3,0.2-0.6,0.3c-0.4,0.2-1.1,0.2-1.8-0.1
|
||||
c-2.4-1-4.8-2.1-7.2-3.2C139.9,98.4,139.8,98,140.3,97.6z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path fill="#885C00" d="M39.8,97c-4-27,27-39.5,39-39.5c20.5,0,39.8,17.4,43.1,32.9c2.5,11.7-2.5,20.8-2.5,25.7"/>
|
||||
</g>
|
||||
<g>
|
||||
<path fill="#885C00" d="M43,129.4V99.8c-7.1,19.1-15.8,20.5-23.2,22.7c-5.2,1.5-9.9,0.8-11.1,3.6c-1.9,4.6,7.2,15.9,17,16.1
|
||||
c7.2,0.1,12.4-5.7,17.5-11.8C43,130.1,43,129.7,43,129.4z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path fill-rule="evenodd" fill="#FAE5B8" d="M98.1,104.5l-4,82.8c3.3-0.2,6.6-0.4,9.7-0.7l5.8-82.1
|
||||
C109.6,104.5,98.1,104.5,98.1,104.5z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path fill-rule="evenodd" fill="#CB4417" d="M40.4,104.5l8.3,81.1c3,0.4,6.3,0.8,9.7,1.1l-6.6-82.2L40.4,104.5L40.4,104.5z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path fill-rule="evenodd" fill="#FAE5B8" d="M52,104.5l5.8,82.1c3.1,0.3,6.3,0.5,9.7,0.7l-4-82.8H52z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path fill-rule="evenodd" fill="#CB4417" d="M63.8,104.5l3.3,82.8c3.1,0.2,6.3,0.3,9.6,0.3l-1.4-83.1
|
||||
C75.3,104.5,63.8,104.5,63.8,104.5z"/>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path fill-rule="evenodd" fill="#FAE5B8" d="M75,104.5l1.2,83.1c1.5,0,3,0,4.6,0c1.7,0,3.4,0,5,0l0.7-83.1
|
||||
C86.5,104.5,75,104.5,75,104.5z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<path fill-rule="evenodd" fill="#CB4417" d="M86.3,104.5l-1.4,83.1c3.3,0,6.5-0.2,9.6-0.3l3.3-82.8H86.3z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path fill-rule="evenodd" fill="#CB4417" d="M109.9,104.5l-6.6,82.2c3.4-0.3,6.7-0.7,9.7-1.1l8.3-81L109.9,104.5L109.9,104.5z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path fill-rule="evenodd" fill="#F4CB68" d="M84.8,100.7C84.8,100.7,84.8,100.6,84.8,100.7v-9.3H64.6v8.8l-3.2,2.4H41.3
|
||||
c-0.1,2.4,1.4,4.9,3.5,5.4c0.9,0.2,1.7,0.3,2.3,0.2c1.6,1.8,4.2,2.8,5.9,2.1c0.2,0.9,0.5,1.7,1.1,2.3c0.9,0.9,2.2,1.4,3.6,1.3
|
||||
c1.5-0.1,3-0.8,4-2c0.1-0.1,0.2-0.3,0.2-0.4c0.5,0.1,1.1,0.2,1.6,0.1c1.2-0.2,2.4-0.9,3.3-2c0.8-1.1,1.3-2.4,1.3-3.8
|
||||
c0.5,0.5,1.1,1,1.9,1.3c0.4,0.2,0.9,0.3,1.3,0.4v0.1c0.7,3.1,3.4,5.4,6.5,5.4c2.5,0.1,4.9-1.4,6.2-3.7c0.1-0.2,0.1-0.5,0-0.7
|
||||
c0.7,0,1.4-0.1,2-0.3c0.5-0.2,1.1-0.5,1.5-0.9c0.4,2,1.9,3.8,3.9,4.4c0.7,0.2,1.3,0.2,2,0.2c0.5,0.7,1.2,1.2,1.9,1.6
|
||||
c1.4,0.6,3.2,0.6,4.8-0.2c1.3-0.6,2.2-1.6,2.5-2.8c0.1-0.3,0.1-0.5,0.1-0.8c0.3-0.1,0.6-0.2,0.8-0.3v-6.1h-16L84.8,100.7z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path fill="#885C00" d="M102.2,94.9c0.4,18.7,4.3,34.1,9.4,40.2c5.3,6.5,10.8,12.8,20.1,13c12.6,0.3,26.6-10.8,25.1-15.6
|
||||
c-0.9-2.9-6.9-4.2-13.5-4.3c-21.5-0.3-24.3-14.3-20.6-28.5"/>
|
||||
</g>
|
||||
<path fill="#936037" d="M116.9,107.2c1.8,1.3,3.4,2.5,4.7,3.5l1.1-10c-2.9-2.9-7.3-6.6-13.2-9.9c-0.1-0.8-0.2-1.6-0.5-2.4
|
||||
c-1.6-5-5.8-7.4-7.4-8.2c-6.5-3.6-13.2-1.6-14.9-1.1c-2.1,0.7-5.3,1.8-7.3,4.7c-0.3,0-0.5-0.1-0.8-0.1c-2.5-0.3-5-0.3-7.4-0.1
|
||||
c-1.7,0.2-3.4,0.4-5,0.6c-0.2-0.3-0.4-0.5-0.6-0.8c-2.6-3-6.4-3.5-8.8-3.8c-1.8-0.2-8.7-0.8-14.4,4c-1.3,1.1-5,4.3-5.6,9.5
|
||||
c-0.5,4.6,1.7,8,2.6,9.3c0.2,0.3,0.5,0.7,0.8,1.2l0.4,4.3c0.5-0.6,1.1-1.2,1.8-1.9c1.2,1.1,2.8,2.1,4.8,2.7
|
||||
c4.8,1.4,8.8-0.5,10.4-1.2c0.7-0.3,8.1-3.9,9.9-12.1c0.1-0.3,0.1-0.6,0.2-1.1c2.9-0.4,6.1-0.6,9.6-0.5c0.3,0,0.5,0,0.8,0
|
||||
c0.4,1.3,0.8,2.3,1.1,2.9c3.4,7.7,11.4,9.7,12.1,9.9c1.8,0.4,6.1,1.4,10.5-0.9c2.3-1.2,3.8-2.9,4.8-4.5c0.6,0.3,1.3,0.6,1.9,1
|
||||
c-0.3,9.5,2.4,20,7,28.4c1,1.8,3.2,2.2,4.7,0.8c0.1-0.1,0.1-0.1,0.2-0.2l0,0c1-1.1,1.7-2.5,2-4c0.2-1.2,0.2-2.7-0.7-3.7
|
||||
C118,119.1,116.8,112.9,116.9,107.2z"/>
|
||||
<path fill="#683C11" d="M53,81.5c-2.5,0-6.3,0.6-9.5,3.4c-1,0.9-4.1,3.5-4.5,7.8c-0.4,3.8,1.5,6.7,2.1,7.7s2.6,4,6.5,5.2
|
||||
c1,0.3,2,0.4,3,0.4c2.6,0,4.6-0.9,5.6-1.4c0.4-0.2,2-1,3.6-2.5c2.2-2,3.7-4.5,4.3-7.3c0.1-0.7,0.5-2.2,0.4-4
|
||||
c-0.1-2.4-0.8-4.4-2.2-5.9c-2.3-2.6-5.4-3-7.3-3.2C54.3,81.6,53.6,81.5,53,81.5"/>
|
||||
<g>
|
||||
<path fill="#B8AC47" d="M61.6,85.3c2.7,3,1.7,7.3,1.4,8.6c-1.3,5.9-6.6,8.5-7.1,8.7c-1.2,0.5-4.1,1.9-7.5,0.9
|
||||
c-3.3-1-5-3.6-5.6-4.5s-2.2-3.4-1.8-6.7c0.4-3.8,3-6,4-6.8c4.1-3.5,9-3,10.3-2.9C57,82.8,59.7,83.1,61.6,85.3z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path fill="#D3C684" d="M50.7,82.7l-0.9,3.4l0,0l-4.3,15.7c0.2,0.2,0.5,0.4,0.8,0.6l0.6-2.1l0,0l4.8-17.7
|
||||
C51.3,82.6,51,82.6,50.7,82.7z"/>
|
||||
<path fill="#D3C684" d="M44.8,101.3l0.6-2.2l0,0l4.4-16.2c-1.6,0.4-3.3,1.2-4.8,2.5c-0.4,0.3-1,0.9-1.7,1.7l-0.9,3.3l0,0l-1.2,4.5
|
||||
c0.4,1.9,1.3,3.3,1.7,4C43.1,99.4,43.8,100.4,44.8,101.3z"/>
|
||||
</g>
|
||||
<circle fill="#F2C897" cx="118.8" cy="126.8" r="1.5"/>
|
||||
<circle fill="#F2C897" cx="116.4" cy="123.1" r="1.3"/>
|
||||
<circle fill="#F2C897" cx="114.5" cy="119.1" r="1"/>
|
||||
<g>
|
||||
|
||||
<ellipse transform="matrix(0.9998 -1.893504e-02 1.893504e-02 0.9998 -2.497 1.6749)" fill="#FCF2DD" cx="87.2" cy="132.7" rx="7.6" ry="16.3"/>
|
||||
</g>
|
||||
<g>
|
||||
|
||||
<ellipse transform="matrix(0.9998 -1.893504e-02 1.893504e-02 0.9998 -2.4933 1.2696)" fill="#FCF2DD" cx="65.8" cy="132.3" rx="7.6" ry="16.3"/>
|
||||
</g>
|
||||
<g>
|
||||
<path fill="#F4CB68" d="M110.8,154.6c-0.1-0.1-0.3-0.1-0.4,0c-0.7,0.2-1.6,0.5-2.7,0.8c-2.5,0.6-3.7,0.9-4.9,1c-3.2,0.2-5.8-1-8-2
|
||||
c-1.9-0.9-1.7-1.1-4.4-2.7c-3.9-2.2-6.2-3.5-9.2-3.5c-1.1,0-3.6,0-5.6,1.7c-0.2,0.2-0.4,0.3-0.5,0.5c-0.2-0.2-0.3-0.3-0.5-0.5
|
||||
c-2-1.7-4.4-1.7-5.6-1.7c-3.1,0-5.4,1.3-9.2,3.5c-2.7,1.5-2.4,1.7-4.4,2.7c-2.1,1-4.7,2.2-8,2c-1.3-0.1-2.5-0.4-4.9-1
|
||||
c-1.1-0.3-2-0.5-2.7-0.8c-0.1,0-0.3,0-0.4,0c-0.2,0.1-0.3,0.4-0.2,0.6c0.9,1.8,2.6,4.7,5.9,7c0.8,0.6,2.6,1.8,5.3,2.3
|
||||
c2.9,0.6,5.1,0.2,7.4-0.3c1.4-0.3,3.5-0.6,5.7-1.6l0,0c0.1,0.3-0.1,0.6-0.5,1.1c-0.7,0.9-1.3,1.1-1.3,1.4c0.1,0.4,0.5,0.6,0.6,0.6
|
||||
c1.4,0.7,6.5,0.6,10.6-3c0.6-0.5,1.4-1.3,2.1-2.3c0.8,1,1.5,1.8,2.1,2.3c4.1,3.7,9.2,3.7,10.6,3c0.2-0.1,0.6-0.3,0.6-0.6
|
||||
c0.1-0.4-0.5-0.5-1.3-1.4c-0.4-0.5-0.5-0.8-0.5-1.1l0,0c2.2,1,4.3,1.3,5.7,1.6c2.3,0.4,4.5,0.9,7.4,0.3c2.7-0.6,4.5-1.8,5.3-2.3
|
||||
c3.3-2.2,5-5.2,5.9-7C111.1,155,111,154.7,110.8,154.6z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path fill="#FAE5B8" d="M78.3,142.3c-1.2-1.6-3.1-2.4-5.2-2c-2.8,0.5-4.7,3.3-4.2,6.1s3.3,4.7,6.1,4.2c1.6-0.3,3-1.4,3.7-2.7"/>
|
||||
</g>
|
||||
<path fill="#683C11" d="M106.9,88.3c-1.3-4.2-4.8-6.1-6-6.8c-2.1-1.1-4.3-1.7-6.8-1.7c-2.2,0-4,0.5-5.1,0.8
|
||||
c-1.8,0.6-4.8,1.6-6.5,4.6c-1,1.8-1.4,3.9-1,6.3c0.3,1.8,0.9,3.2,1.2,3.8c1.1,2.6,3.1,4.7,5.7,6.3c1.9,1.1,3.6,1.6,4.1,1.7
|
||||
c0.7,0.2,2,0.5,3.5,0.5c1.8,0,3.5-0.4,5.1-1.2c3.6-1.9,4.9-5.2,5.3-6.3C106.8,95.2,108.1,92,106.9,88.3z"/>
|
||||
<path fill="#FCF2DD" d="M32.5,107.5C32.5,107.5,32.5,107.4,32.5,107.5c-0.2-0.2-0.2-0.3-0.3-0.4l-0.1-0.1c-0.1-0.1-0.1-0.2-0.2-0.2
|
||||
l-0.1-0.1c-0.1-0.1-0.2-0.2-0.3-0.3c0,0,0,0-0.1,0c-0.1-0.1-0.3-0.2-0.5-0.3c0,0-0.1,0-0.1-0.1c-0.1-0.1-0.3-0.1-0.4-0.2
|
||||
c-0.1,0-0.1,0-0.2-0.1s-0.3-0.1-0.4-0.2c-0.1,0-0.1,0-0.2-0.1c-0.2-0.1-0.3-0.1-0.5-0.1c0,0,0,0-0.1,0c0,0,0.1,0,0.1-0.1
|
||||
c0.2-0.1,0.3-0.3,0.5-0.4c0.1,0,0.1-0.1,0.2-0.2s0.3-0.2,0.4-0.4c0.1-0.1,0.1-0.1,0.2-0.2s0.3-0.3,0.4-0.4s0.1-0.1,0.2-0.2
|
||||
c0.2-0.2,0.4-0.4,0.5-0.7c2.3-3.2,3.6-8.7,0.7-11.8c-0.2-0.2-0.4-0.4-0.7-0.6c-0.1,0-0.1-0.1-0.2-0.1c-0.2-0.2-0.5-0.3-0.7-0.4
|
||||
c0,0-0.1,0-0.1-0.1c-0.2-0.1-0.4-0.2-0.7-0.3c0,0-0.1,0-0.1-0.1c-0.2-0.1-0.5-0.2-0.7-0.2c-0.1,0-0.1,0-0.2,0
|
||||
C28.6,89,28.3,89,28.1,89c0,0,0,0-0.1,0c-0.2,0-0.5-0.1-0.7-0.1h-0.1c-0.2,0-0.4,0-0.6,0l0,0c-0.2,0-0.4,0-0.5,0l0,0
|
||||
c0-0.5-0.1-1-0.1-1.4c0-0.1,0-0.3-0.1-0.4c0-0.3-0.1-0.6-0.2-1c0-0.1,0-0.2-0.1-0.4c-0.1-0.4-0.2-0.8-0.3-1.2l0,0
|
||||
c-0.1-0.4-0.2-0.7-0.3-1.1c0-0.1-0.1-0.2-0.1-0.3c-0.1-0.2-0.2-0.5-0.3-0.7c0-0.1-0.1-0.2-0.1-0.3c-0.1-0.3-0.2-0.6-0.3-0.8
|
||||
c-1.8-4.1-3.4-4.5-4-4.6c-0.2,0-0.5,0-0.7,0c-0.5,0.1-0.9,0.2-1.3,0.4c-0.2,0.1-0.4,0.2-0.6,0.3c-0.4,0.2-0.6,0.4-0.9,0.6
|
||||
c-0.2,0.2-0.3,0.3-0.4,0.3c-0.2,0.2-0.3,0.3-0.4,0.5c0,0.1-0.1,0.1-0.1,0.2c-0.1,0.1-0.1,0.2-0.2,0.4c0,0.1-0.1,0.1-0.1,0.2
|
||||
s-0.1,0.2-0.1,0.3c0,0.1,0,0.2-0.1,0.3c0,0.1-0.1,0.2-0.1,0.4c0,0.1,0,0.2,0,0.3c0,0.1,0,0.3,0,0.4s0,0.2,0,0.3c0,0.1,0,0.3,0,0.5
|
||||
c0,0.1,0,0.2,0,0.3c0,0.2,0.1,0.4,0.1,0.6c0,0.1,0,0.2,0,0.3l0,0c-0.3,0.1-0.5,0.1-0.8,0.3c0,0-1.4,0.6-2.1,2
|
||||
c-0.1,0.2-0.2,0.4-0.2,0.5v0.1c0,0.1-0.1,0.3-0.1,0.5c0,0,0,0,0,0.1c-0.1,0-0.1,0-0.2,0c-0.1,0-0.2,0-0.3,0s-0.3,0-0.4,0
|
||||
c-0.1,0-0.2,0-0.3,0c-0.2,0-0.3,0-0.5,0c-0.1,0-0.2,0-0.3,0c-0.2,0-0.5,0.1-0.7,0.2c-1.1,0.4-1.8,1.1-2.2,1.7
|
||||
C7,89,6.8,89.3,6.7,89.5C7,89.8,6.9,90,6.8,90.3c0,0.1,0,0.2-0.1,0.2c0,0.2-0.1,0.3-0.1,0.5c0,0.1,0,0.2,0,0.3c0,0.2,0,0.3-0.1,0.5
|
||||
c0,0.1,0,0.2,0,0.3c0,0.2,0,0.4,0,0.5s0,0.2,0,0.3c0,0.2,0,0.4,0.1,0.6c0,0.1,0,0.1,0,0.2c0.1,0.6,0.3,1.1,0.5,1.7
|
||||
c0,0.1,0,0.1,0.1,0.2c0.1,0.2,0.2,0.4,0.3,0.6v0.1c0,0.3-0.1,0.5-0.1,0.8c0,0.1,0,0.2,0,0.2c0,0.2,0,0.5,0,0.7c0,0.1,0,0.2,0,0.3
|
||||
c0,0.3,0,0.7,0,1c0,0.5,0.2,2.3,0.9,4.2c0.6,1.4,1.4,3,2.9,4c0.5,0.4,1,0.7,1.6,0.9c0.3,0.1,0.6,0.2,0.9,0.3c0.1,0,0.1,0,0.2,0
|
||||
c0.3,0.1,0.6,0.1,0.9,0.2l0,0l-0.1,0.1c-0.1,0.1-0.2,0.1-0.2,0.2c-0.2,0.2-0.3,0.3-0.5,0.5c-0.1,0.1-0.1,0.2-0.2,0.2
|
||||
c-0.2,0.2-0.3,0.4-0.4,0.5c0,0.1-0.1,0.1-0.1,0.2c-0.2,0.2-0.3,0.5-0.4,0.7c0,0.1,0,0.1-0.1,0.2c-0.1,0.2-0.1,0.4-0.2,0.5
|
||||
c0,0.1,0,0.2-0.1,0.2c0,0.2-0.1,0.3-0.1,0.5c0,0.1,0,0.1,0,0.2c0,0.2,0,0.5,0.1,0.7c0.8,2.8,6,3.8,11.6,2.2s9.5-5.2,8.7-8
|
||||
C32.6,107.8,32.6,107.6,32.5,107.5z"/>
|
||||
<path fill="#FCF2DD" d="M153.9,163c-0.6-1.6-3.2-1.9-3.5-2c-3.7-0.4-4.9,2.9-8.4,2.6c-2.4-0.2-2.7-1.8-5.3-2.1
|
||||
c-0.8-0.1-1.5,0-2.2,0.1c0-0.2,0-0.4,0-0.6c0.1-0.7,0-1.3-0.3-1.8c-1.3-1.9-5.8-1.2-9.9,1.6c-3.5,2.4-5.7,5.6-5.4,7.7
|
||||
c-0.1,0.7,0,1.3,0.4,1.8c0.6,0.9,1.8,1.2,3.3,1.1c0.1,1.5,0.4,2.7,0.5,3.1c0.3,1.1,0.7,2.3,1.1,3.2c0.8,2.1,1.3,4.3,1.2,6.5
|
||||
l-0.2,3.5c-0.1,0.2-0.7,2.5-0.6,4.6c-0.1,1.7,0.2,3.5,1.6,4.3c1.2,0.7,3.2,0.5,4.4-0.3c0.7-0.5,1.1-1.2,1.3-2.2
|
||||
c0.2,0.3,1.5,2.2,3.2,2.1c0.3,0,1.8,0,2.8-2c0.3-0.3,0.6-0.7,0.9-1.2c0.1,0,0.2,0.1,0.3,0.1l-0.2,0.9c1.4,0.7,3.4,1.5,5.1,0.6
|
||||
c0.2-0.1,0.5-0.3,0.8-0.6c0,0,0.1,0,0.2-0.1c0.6-0.4,2.3-1.8,1.6-9.9c-0.1-0.6-0.1-1.3-0.2-2c-0.2-3.1-0.6-6.9-1.7-11.2
|
||||
c1.5,0.1,3.7-0.1,5.8-1.4c1.8-1.1,4.1-3.6,3.8-5.5C154.1,163.8,154.1,163.4,153.9,163z M132,192.6l0.4,0.5l-0.6,1.1
|
||||
C131.9,193.8,132,193.2,132,192.6z M144,171.9c-0.1-0.3-0.2-0.6-0.3-0.9c0.2,0,0.3,0,0.5,0.1L144,171.9z"/>
|
||||
<g>
|
||||
<path fill="#B8AC47" d="M83.2,85.6c-2,3.5-0.2,7.5,0.3,8.7c2.5,5.5,8.2,7,8.7,7.1c1.3,0.3,4.4,1,7.5-0.6s4.2-4.5,4.6-5.5
|
||||
s1.5-3.7,0.5-6.9c-1.1-3.6-4.2-5.3-5.3-5.9c-4.7-2.6-9.4-1.2-10.6-0.8C87.2,82.2,84.7,83.1,83.2,85.6z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path fill="#D3C684" d="M93.2,80.9c-2,0.1-3.6,0.5-4.3,0.8c-0.9,0.3-2,0.7-3.1,1.4l-3.4,5.7c-0.2,2.5,0.8,4.8,1.2,5.6
|
||||
c0.2,0.5,0.4,0.9,0.7,1.3L93.2,80.9z"/>
|
||||
<path fill="#D3C684" d="M84.7,96.3c0.2,0.3,0.4,0.5,0.6,0.8L95.1,81c-0.3,0-0.7-0.1-1-0.1L84.7,96.3z"/>
|
||||
</g>
|
||||
<path fill="#F2C897" d="M156.3,135.4c-2.6,5.3-14.1,13-24.6,12.8c-1.6,0-3.1-0.3-4.5-0.6v0.1c0,1.6,1.2,2.9,2.8,2.9
|
||||
c1,0,2-0.5,2.5-1.4c0.4,1,1.4,1.8,2.6,1.8c1.2,0,2.2-0.7,2.7-1.7c0.5,0.5,1.2,0.8,1.9,0.8c1.1,0,2.1-0.6,2.6-1.6
|
||||
c0.5,0.5,1.2,0.8,1.9,0.8c1.5,0,2.7-1,2.9-2.5c0.4,0.2,0.7,0.3,1.2,0.3c1.6,0,2.9-1.2,2.9-2.8c0-0.1,0-0.1,0-0.2l0,0
|
||||
c1.6,0,2.9-1.2,2.9-2.8c0-0.4-0.1-0.7-0.2-1.1c0.3,0.1,0.7,0.2,1,0.2c1.6,0,2.9-1.2,2.9-2.8C157.7,136.8,157.1,135.9,156.3,135.4z"
|
||||
/>
|
||||
</g>
|
||||
<g id="Black">
|
||||
<g>
|
||||
<path fill="#27271F" d="M100,81.5c-2-1.1-4.2-1.7-6.5-1.7c-2.1,0-3.9,0.4-4.9,0.8c-1.7,0.6-4.6,1.6-6.2,4.4
|
||||
c-1,1.7-1.3,3.7-0.9,5.9c0.3,1.7,0.9,3.1,1.2,3.7c2.6,5.9,8.7,7.5,9.4,7.7c0.7,0.2,1.9,0.4,3.4,0.4c1.7,0,3.4-0.4,4.8-1.2
|
||||
c3.4-1.8,4.7-5,5.1-6.1s1.6-4.1,0.5-7.6C104.5,84,101.1,82.1,100,81.5z M104.3,95.2c-0.4,1-1.5,3.9-4.6,5.5
|
||||
c-1.5,0.8-3,1.1-4.4,1.1s-2.5-0.3-3.1-0.4c-0.5-0.1-6.2-1.6-8.7-7.1c-0.5-1.1-2.3-5.2-0.3-8.7c1.4-2.5,4-3.4,5.6-3.9
|
||||
c0.7-0.2,2.4-0.8,4.6-0.8c1.8,0,3.9,0.4,6,1.5c1.1,0.6,4.1,2.3,5.3,5.9C105.8,91.5,104.7,94.2,104.3,95.2z"/>
|
||||
<path fill="#27271F" d="M48.1,104.4c0.9,0.3,1.9,0.4,2.8,0.4c2.5,0,4.4-0.9,5.4-1.3c0.6-0.3,6.3-3.1,7.7-9.4
|
||||
c0.1-0.6,0.5-2.1,0.4-3.9c-0.1-2.2-0.8-4.1-2.1-5.6c-2.1-2.5-5.2-2.8-7-3.1c-0.6-0.1-1.3-0.1-1.9-0.1v1v-1c-2.5,0-6,0.6-9.2,3.2
|
||||
c-0.5,0.4-1.5,1.3-2.4,2.5c-1.1,1.5-1.8,3.2-2,5c-0.4,3.7,1.3,6.3,2,7.3C42.6,100.4,44.4,103.3,48.1,104.4z M41,92.2
|
||||
c0.4-3.8,3-6,4-6.8c2.9-2.5,6.4-3,8.5-3c0.8,0,1.4,0.1,1.8,0.1c1.8,0.2,4.4,0.6,6.3,2.7c2.7,3,1.7,7.3,1.4,8.6
|
||||
c-1.3,5.9-6.6,8.5-7.1,8.7c-0.9,0.4-2.7,1.2-4.9,1.2c-0.8,0-1.6-0.1-2.5-0.4c-3.3-1-5-3.6-5.6-4.5C42.2,98,40.6,95.5,41,92.2z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path fill="#27271F" d="M110.5,4.1c0.1,0,0.1,0,0.2,0.1c0.4,0.3,0.8,0.5,1.2,0.8c0.1-0.1,0.2-0.3,0.3-0.4c0.5-0.7,0.4-1.7-0.3-2.2
|
||||
c-0.7-0.5-1.7-0.4-2.2,0.3c-0.3,0.4-0.6,0.7-0.9,1.1C109.6,3.7,110,3.8,110.5,4.1z"/>
|
||||
<path fill="#27271F" d="M102.1,107.2C102.1,107.2,102.2,107.2,102.1,107.2c0.1,0,0.1,0.2,0.1,0.3
|
||||
C102.2,107.5,102.1,107.4,102.1,107.2z"/>
|
||||
<path fill="#27271F" d="M73.9,44.8l5.7-4.2l9.6-8.7c5.7-6.3,11.3-12.8,16.8-19.5c1.2-2.1,2.3-4.2,3.5-6.3c-0.7-0.3-1.4-0.5-2-0.5
|
||||
C99,16.2,90.2,26.4,81.2,36c-2.7,2.9-5.4,5.7-8.2,8.5C73.4,44.4,73.6,44.6,73.9,44.8z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<path fill="#2C2C22" d="M110.5,4.1c0.1,0,0.1,0,0.2,0.1c0.4,0.3,0.8,0.5,1.2,0.8c0.1-0.1,0.2-0.3,0.3-0.4c0.5-0.7,0.4-1.7-0.3-2.2
|
||||
c-0.7-0.5-1.7-0.4-2.2,0.3c-0.3,0.4-0.6,0.7-0.9,1.1C109.6,3.7,110,3.8,110.5,4.1z"/>
|
||||
<path fill="#2C2C22" d="M102.1,107.2c0,0,0.1,0,0.1-0.1c0,0.1,0,0.3,0,0.4C102.2,107.5,102.1,107.4,102.1,107.2z"/>
|
||||
<path fill="#2C2C22" d="M73.9,44.8l5.7-4.2l9.6-8.7c5.7-6.3,11.3-12.8,16.8-19.5c1.2-2.1,2.3-4.2,3.5-6.3c-0.7-0.3-1.4-0.5-2-0.5
|
||||
c-8.5,10.6-17.3,20.8-26.3,30.4c-2.7,2.9-5.4,5.7-8.2,8.5C73.4,44.4,73.6,44.6,73.9,44.8z"/>
|
||||
</g>
|
||||
<path fill="#2C2C22" d="M157.4,135.3c0.4-1,0.5-2,0.2-2.8c-0.5-1.6-2.2-2.9-5.1-3.7c-2.4-0.7-5.5-1.1-9.1-1.1
|
||||
c-8.5-0.1-14.6-2.4-18-6.9c-1.9-2.6-2.9-5.6-3.1-8.8l1.3-11.6l-0.1-0.1c0-0.1,0-0.1,0-0.2h-0.1c0.2-3,0.2-6.3-0.6-9.8
|
||||
c-0.8-3.9-2.7-8-5.4-12c-2.7-3.9-6.1-7.6-10-10.8c-8.7-7-18.8-10.8-28.5-10.8c-4.3,0-10.2,1.5-15.8,3.9c-1.6,0.7-3.3,1.6-5.2,2.6
|
||||
c5.4-4.9,10.7-10,16-15.3c0.1-0.5-0.6-1-0.9-1.4c-0.6-0.2-1.1-0.4-1.6-0.5c-5.9,5.9-11.8,11.5-17.7,16.8c-8.8,7.6-18,14.7-27.4,21.6
|
||||
c-0.3-1.1-0.7-2.2-1.1-3.2c-1.4-3.2-2.9-4.8-4.7-5c-2.3-0.2-4.3,1.7-4.5,1.9c-1.2,1.2-1.6,2.7-1.3,4.9c-0.1,0-0.1,0-0.2,0.1
|
||||
c-0.1,0-1.6,0.7-2.5,2.4c-0.1,0.2-0.2,0.5-0.3,0.7c-0.7-0.1-1.5,0-2.3,0.3c-0.9,0.3-2.2,1.1-3.1,2.8c-0.9,1.8-0.9,4.1-0.1,6.3
|
||||
c0.1,0.3,0.3,0.6,0.4,0.9c-0.1,0.6-0.1,1.1-0.1,1.7C5.3,99,4,99.8,2.7,100.6c-1.3,0.8-1.7,2.6-0.8,3.9c0.9,1.2,2.6,1.5,3.8,0.7
|
||||
c0.6-0.4,1.2-0.8,1.8-1.3c1,2.6,2.7,4.4,4.9,5.3c0.2,0.1,0.4,0.1,0.7,0.2c-1.3,1.5-1.9,3.1-1.5,4.5c0.5,1.7,2.1,2.8,4.6,3.3
|
||||
c0.8,0.1,1.6,0.2,2.5,0.2h0.8c0.4,1.2,0.8,2.3,1.2,3.4c-0.5,0.1-1,0.3-1.4,0.4c-1.7,0.5-3.4,0.8-5,1c-3.2,0.5-5.9,0.9-6.9,3.4
|
||||
c-0.8,2-0.2,4.8,1.8,7.9c1.7,2.6,4.1,5,6.8,6.7c3.1,2.1,6.4,3.2,9.6,3.2h0.3c4.2,0,7.7-1.9,10.9-4.5c0.5,0.3,1.1,0.6,1.6,0.8
|
||||
c1.8,0.8,3.3,1.2,4.7,1.2h0.1l1.4,14.1c-0.6-0.1-1.3-0.3-2.1-0.5c-0.9-0.2-1.8-0.5-2.7-0.7c-0.4-0.1-0.8-0.1-1.1,0.1
|
||||
c-0.5,0.3-0.7,1-0.5,1.6c1,2.1,2.9,5.1,6.2,7.3c0.2,0.2,0.6,0.4,1,0.6l2.3,22.8l0.6,0.1c2.7,0.4,5.6,0.7,8.5,1l0.7,0.1
|
||||
c0.1,0,0.1,0,0.2,0h0.4l0.9,0.1c2.3,0.2,4.7,0.4,7.1,0.5h0.7c0.1,0,0.1,0,0.2,0s0.1,0,0.2,0h0.9c2.4,0.1,4.7,0.2,7.2,0.2h1.1
|
||||
c0.1,0,0.2,0,0.2,0h10c2.2,0,4.5-0.1,6.7-0.2h0.9c0.1,0,0.1,0,0.2,0c0.1,0,0.1,0,0.2,0h0.7c2.5-0.1,4.9-0.3,7.3-0.5l0.9-0.1h0.4
|
||||
l0.7-0.1c3-0.3,6-0.6,8.7-1l0.6-0.1l2.8-27.8c1.5,1.6,3,3.4,4.3,5.3c-0.8,0.8-1.4,1.6-1.9,2.5c-1.1,1.9-1.3,3.6-0.4,4.8
|
||||
c0.6,0.9,1.8,1.4,3.2,1.4c0.1,0.7,0.2,1.6,0.4,2.5c0.3,1,0.6,2.2,1.1,3.3c0.9,2.2,1.3,4.5,1.2,6.9l-0.2,3.5c-0.3,1.1-1.9,7,1.4,8.9
|
||||
c0.6,0.4,1.4,0.5,2.2,0.5c1.1,0,2.2-0.3,3-0.9c0.5-0.4,0.9-0.8,1.1-1.3c0.7,0.7,1.6,1.3,2.8,1.3c0.1,0,0.1,0,0.2,0
|
||||
c1.4-0.1,2.6-0.9,3.3-2.2c0,0,0,0,0.1,0.1c1.4,0.7,2.7,1.1,3.8,1.1c0.7,0,1.4-0.2,2-0.5c0.5-0.3,1.2-0.8,1.8-2.4
|
||||
c0.7-1.8,1-4.5,1-8.1c0-4.4-0.6-8.8-1.6-13.2c1.5,0,3.4-0.4,5.2-1.5c2-1.3,4.9-4.3,4-6.7c-0.8-2-3.6-2.4-4.1-2.4
|
||||
c-2.1-0.2-3.6,0.6-4.8,1.4c-1.1,0.7-2.1,1.3-3.6,1.2c-1-0.1-1.5-0.4-2.2-0.9c-0.8-0.5-1.6-1.1-3.1-1.2h-1.5c0-0.7-0.1-1.4-0.5-1.9
|
||||
c-0.8-1.2-2.5-1.7-4.7-1.3c-0.1,0-0.1,0-0.2,0c-3.7-5.6-8.5-10.1-12-13l0.2-2.1c2.3,1.9,5,3.7,8.2,4.6c0.2,1.7,1.6,3.1,3.5,3.2
|
||||
c0.9,0,1.7-0.3,2.4-0.8c0.7,0.8,1.6,1.3,2.7,1.3h0.1c1.1,0,2.1-0.5,2.8-1.4c0.5,0.3,1.1,0.4,1.7,0.5c1.1,0,2.1-0.4,2.8-1.2
|
||||
c0.5,0.3,1.1,0.5,1.7,0.5c1.5,0,2.9-0.9,3.4-2.2c0.2,0,0.4,0.1,0.6,0.1c1.8,0.1,3.4-1.3,3.7-3c1.6-0.3,2.9-1.7,3-3.5v-0.1
|
||||
c0,0,0,0,0.1,0c1,0,1.9-0.3,2.6-1s1.1-1.6,1.1-2.5C158.6,136.9,158.2,135.9,157.4,135.3z M137.3,193.6c-0.5,1.3-1.2,2-2.3,2h-0.2
|
||||
c-1,0-1.9-1-2.3-1.5c0.2-1,0.2-2.2,0.3-3.7c0.1-3,0.1-6,0.1-8.9c0.6,0.1,1.3,0.2,2,0.2c1.3,0,2.7-0.2,4-0.8
|
||||
C139,183.7,138.7,189.7,137.3,193.6z M136.6,163.2c1.1,0.1,1.7,0.5,2.4,1s1.5,1,2.9,1.1c2,0.2,3.3-0.7,4.5-1.4
|
||||
c1.2-0.7,2.2-1.4,3.8-1.2c0.8,0.1,2.5,0.5,2.8,1.5c0.5,1.3-1.4,3.6-3.4,4.9c-1.6,1-3.4,1.3-4.7,1.3c-0.1-0.4-0.5-0.7-0.9-0.6
|
||||
c-0.4,0.1-0.7,0.5-0.6,0.9c1.3,4.8,1.9,9.7,1.9,14.4c0,5.1-0.7,8.4-2,9.1c-1.3,0.7-3.1,0-4.3-0.6c-0.1,0-0.1,0-0.2-0.1
|
||||
c1.4-4.5,1.7-10.9,1.5-13.4c1.1-0.7,2-1.5,2.9-2.6c0.3-0.3,0.2-0.8-0.1-1.1c-0.3-0.3-0.8-0.2-1.1,0.1c-1,1.3-2.2,2.2-3.6,2.8
|
||||
c-4.3,1.9-8.5-0.3-9.3-0.8c-0.4-0.2-0.8-0.1-1.1,0.3c-0.2,0.4-0.1,0.8,0.3,1.1c0.4,0.2,1.5,0.8,3,1.2c0,3.1,0,6.2-0.1,9.3
|
||||
c0,1.6-0.1,2.8-0.3,3.7c0,0,0,0,0,0.1c-0.2,0.8-0.5,1.3-1,1.7c-1,0.7-2.6,0.8-3.6,0.3c-1.9-1.1-1.1-5.6-0.6-7.2v-0.1l0.2-3.7
|
||||
c0.1-2.6-0.3-5.1-1.3-7.6c-0.4-1.1-0.8-2.1-1-3.1c-1.5-6.1,1-8.6,1.8-9.3c1.7-1.4,3.8-1.4,4-1.4c2.4,0,4.4,1.6,5.1,4.1
|
||||
c0.1,0.4,0.5,0.7,1,0.5c0.4-0.1,0.7-0.5,0.5-1c-0.5-1.6-1.3-2.9-2.5-3.8C134.6,163.3,135.6,163.1,136.6,163.2z M124.7,162.5
|
||||
c0.8-0.5,1.6-1,2.3-1.3h0.1c0-0.1,0.1-0.1,0.1-0.1c1.6-0.8,3.1-1.1,4.3-1.1c1,0,1.7,0.3,2,0.8c0.2,0.3,0.3,0.8,0.2,1.4
|
||||
c-0.6,0.2-1.2,0.5-1.8,0.8c-0.7-0.3-1.5-0.5-2.4-0.5c-0.5,0-2.9,0-5,1.7c-1.8,1.4-2.7,3.6-2.8,6.4v0.4c-0.9,0-1.5-0.3-1.9-0.7
|
||||
c-0.5-0.7-0.3-1.8,0.5-3.2C121.2,165.4,122.8,163.8,124.7,162.5z M78.8,58.3c9.4,0,19.1,3.7,27.5,10.5c3.8,3.1,7.1,6.7,9.7,10.4
|
||||
c2.6,3.8,4.4,7.7,5.2,11.4c0.6,2.9,0.8,5.6,0.6,8.2c-2.5-2.3-6.1-5.3-10.8-8c-0.1-1-0.3-2-0.6-2.9c-0.8-2.4-2.2-4.6-4.1-6.4
|
||||
c-1.6-1.4-3.1-2.3-4-2.8c-2.8-1.5-5.9-2.3-9.2-2.3c-3,0-5.4,0.6-6.9,1.1c-2.2,0.8-5.8,2-8.1,5.2c-2.3-0.2-4.7-0.2-7,0
|
||||
c-1.3,0.1-2.5,0.3-3.7,0.5c-0.2-0.3-0.4-0.6-0.7-0.8c-3-3.5-7.3-4-9.8-4.3c-0.9-0.1-1.8-0.2-2.7-0.2c-3.2,0-7.8,0.7-11.9,3.8
|
||||
c1.2-2.8,2.8-5.5,5-8C56.4,63.1,71.5,58.3,78.8,58.3z M108.5,101.2c0.7-1.2,1.1-2.2,1.3-2.7c0.3-0.8,0.8-2.2,1.1-4
|
||||
c0.1-0.6,0.2-1.3,0.2-1.9c4.8,2.9,8.3,6,10.6,8.2c-0.6,2.5-1.1,5.3-1.1,8.2C116.6,105.9,112.6,103.4,108.5,101.2z M116,107.6
|
||||
c0,5.9,1.2,12.1,5.1,16.5c1.5,1.8,0.4,4.8-1,6.2c0,0-0.1,0.1-0.2,0.1c-1.2,1.2-2.9,0.6-3.7-0.7c-4.7-8-6.6-17.1-6.7-26.3
|
||||
C111.7,104.7,113.9,106.1,116,107.6z M68.2,146.5c0,0.1,0.1,0.2,0.1,0.4c-0.4,0.3-0.8,0.5-1.2,0.7c-0.4,0.1-0.7,0.1-1.1,0.2
|
||||
c-1.7,0-3.4-1.5-4.7-4.2c-1.4-2.9-2.3-6.9-2.4-11.1c-0.1-2.9,0.3-5.6,0.9-8c-0.1,0.8-0.2,1.7-0.2,2.5c-0.2,5.8,2,10.5,4.8,10.6
|
||||
c2.6,0.1,4.8-3.9,5.2-9l-3.8-1c-1.1-0.3-1-1.9,0.1-2l3.7-0.5c-0.3-4-1.7-7.2-3.6-8.2c1.5,0.2,3.1,1.7,4.3,4.2
|
||||
c1.4,2.9,2.3,6.9,2.4,11.1c0.1,2.7-0.2,5.3-0.8,7.6c-1.1,0.4-2.1,1.2-2.8,2.2C68.3,143.3,67.9,144.9,68.2,146.5z M79,106.3
|
||||
c1.1,1.2,2.5,1.9,3.8,2.3c0,0.1-0.1,0.1-0.1,0.2c-1,1.8-2.8,2.9-4.8,2.9c-2.2-0.1-4.2-1.6-4.9-3.9c1.8-0.1,3.1-0.9,3.3-1
|
||||
c0.4-0.2,0.5-0.7,0.2-1.1c-0.2-0.4-0.7-0.5-1.1-0.2c-0.1,0.1-2.3,1.4-4.8,0.4c-1.6-0.7-2.4-2.1-2.7-3c0.3-0.7,0.4-1.5,0.5-2.3
|
||||
c1.4-0.2,2.7-0.9,3.6-2c0.1,0,0.1,0,0.2,0c0.4,0,0.8,0,1.1-0.1c1.6-0.4,2.9-1.5,3.7-3c0,0,0,0,0-0.1c0.3,0.9,0.6,1.5,0.8,1.9
|
||||
c0.9,1.9,2.1,3.7,3.5,5.2c1.2,1.2,2.6,2.3,4.2,3.3c0.4,0.2,0.7,0.4,1,0.6c-0.3,0.3-0.7,0.6-1.1,0.7c-1.6,0.6-3.8-0.1-5.4-1.8
|
||||
c-0.3-0.3-0.8-0.3-1.1,0C78.7,105.5,78.7,106,79,106.3z M65.5,115.3c-0.2,0-0.3,0-0.5,0.1l-0.2-4c0.7-0.3,1.4-0.9,2-1.6
|
||||
c0.8-1.1,1.3-2.4,1.3-3.8c0.5,0.5,1.1,1,1.9,1.3c0.4,0.2,0.9,0.3,1.3,0.4v0.1c0.4,1.8,1.5,3.3,3,4.3l0.4,27.4h-1.2
|
||||
c0.5-2.3,0.7-4.7,0.7-7.3C74.1,122.6,70.2,115.2,65.5,115.3z M59.9,108.3c-0.1-0.1-0.1-0.1-0.2-0.2c0.8-0.5,1.9-1.2,3-2.2h0.7
|
||||
c0.7,0,1.5-0.1,2.3-0.6c0.3-0.2,0.6-0.4,0.9-0.6v0.1c0.2,1.4-0.1,2.8-0.9,3.8c-0.6,0.8-1.4,1.3-2.2,1.4
|
||||
C62.2,110.3,60.8,109.6,59.9,108.3z M60.6,111c-0.7,0.9-1.7,1.4-2.8,1.4c-0.9,0-1.8-0.3-2.4-0.8c-0.3-0.4-0.6-0.8-0.7-1.4
|
||||
c1.7-0.4,2.9-1,3.7-1.4c0,0,0.1,0,0.1-0.1c0,0.2,0,0.3,0.1,0.5C59.2,110,59.8,110.6,60.6,111z M64.5,104.3c0.8-0.9,1.6-1.8,2.2-2.8
|
||||
c-0.3,1.2-0.9,2.2-1.8,2.6C64.8,104.2,64.7,104.2,64.5,104.3z M69.9,96.2c-0.3-0.2-0.5-0.4-0.7-0.6c0,0,0,0,0-0.1
|
||||
c0-0.2,0.1-0.5,0.2-0.8c2.1-0.2,4.2-0.4,6.4-0.4c0,0.1,0,0.1-0.1,0.2c-0.6,1.1-1.6,2-2.7,2.2C72,97.1,70.8,96.9,69.9,96.2z M76.2,92
|
||||
c0.1,0.3,0.1,0.6,0.2,0.9c-2.4,0-4.6,0.1-6.8,0.4c0.1-0.9,0.2-1.9,0.1-3.1c-0.1-2.1-0.6-4-1.4-5.6c1-0.1,1.9-0.3,2.9-0.3
|
||||
c2-0.2,4-0.2,6,0C76.1,86.5,75.8,89.1,76.2,92z M68.7,97.3c0.1,0.1,0.2,0.2,0.3,0.2c0.4,0.2,0.7,0.4,1.1,0.5c-0.6,0.4-1.3,0.7-2,0.8
|
||||
C68.3,98.4,68.5,97.8,68.7,97.3z M67.7,95.3c-1.8,8.2-9.2,11.8-9.9,12.1c-1.2,0.6-3.7,1.7-6.9,1.7c-1.1,0-2.3-0.1-3.5-0.5
|
||||
c-4.7-1.4-7-5-7.8-6.3c-0.8-1.3-3-4.7-2.6-9.3c0.6-5.2,4.2-8.4,5.6-9.5c4.1-3.5,8.9-4.2,11.9-4.2c1.1,0,2,0.1,2.5,0.2
|
||||
c2.5,0.3,6.2,0.8,8.8,3.8C69.4,87.6,68,93.6,67.7,95.3z M54.2,112.7c0.8,0.8,2,1.3,3.3,1.3c0.1,0,0.1,0,0.2,0c1.5-0.1,3-0.8,4-2
|
||||
c0.1-0.1,0.1-0.3,0.2-0.4c0.4,0.1,0.7,0.1,1.1,0.1l0.2,4.3c-3.5,2.1-5.9,8.6-5.8,16.5c0.1,4.5,1,8.6,2.5,11.8c1,2.1,2.3,3.6,3.7,4.4
|
||||
c-1.3,0.6-2.7,1.4-4.4,2.3c-1.3,0.7-1.9,1.2-2.4,1.5c-0.2,0.1-0.3,0.2-0.5,0.3L54.1,123l-0.9-12.2C53.4,111.5,53.7,112.1,54.2,112.7
|
||||
z M70.4,142.9c0.7-1,1.7-1.6,2.9-1.9c1.7-0.3,3.4,0.4,4.4,1.7c0.3,0.3,0.7,0.4,1.1,0.2c0.3-0.3,0.4-0.7,0.2-1.1
|
||||
c-0.6-0.8-1.4-1.5-2.3-1.9l-0.5-27.1c0.5,0.2,1.1,0.3,1.7,0.3c0.1,0,0.1,0,0.2,0c2.4,0,4.8-1.4,6-3.7c0.1-0.2,0.1-0.5,0-0.7h0.3
|
||||
c0.4,0,0.8-0.1,1.1-0.1l-0.1,7.4c-3.9,1.5-6.7,8.4-6.5,16.9c0.1,4.5,1,8.6,2.5,11.8c0.6,1.2,1.3,2.2,2,3c-0.7-0.1-1.4-0.2-2.1-0.2
|
||||
c-0.4,0-1,0-1.7,0.1c-0.1-0.1-0.2-0.3-0.3-0.3c-0.4-0.2-0.9-0.1-1,0.3c-0.6,1.2-1.8,2.1-3.1,2.3c-2.4,0.5-4.7-1.1-5.2-3.6
|
||||
C69.5,145.1,69.8,143.9,70.4,142.9z M82.7,144c-1.4-2.9-2.3-6.9-2.4-11.1c-0.1-2.9,0.3-5.6,0.9-8c-0.1,0.8-0.2,1.7-0.2,2.5
|
||||
c-0.2,5.8,2,10.5,4.8,10.6c2.6,0.1,4.8-3.9,5.2-9l-3.8-1c-1.1-0.3-1-1.9,0.1-2l3.7-0.5c-0.3-4-1.7-7.2-3.6-8.2
|
||||
c1.5,0.2,3.1,1.7,4.3,4.2c1.4,2.9,2.3,6.9,2.4,11.1c0.1,4.2-0.6,8.2-1.9,11.2c-1.2,2.8-2.9,4.4-4.6,4.4
|
||||
C85.8,148.2,84,146.7,82.7,144z M93.5,144.4c1.4-3.2,2.2-7.4,2.1-11.9c-0.2-9.3-3.8-16.6-8.4-16.9l0.1-7.9c0.1-0.1,0.2-0.2,0.3-0.3
|
||||
c0.4,2.1,1.9,3.9,3.9,4.4c0.5,0.1,0.9,0.2,1.4,0.2h0.6c0.5,0.7,1.2,1.2,1.9,1.6c0.4,0.2,0.8,0.3,1.2,0.3l-1.3,32.3l-0.4,7.2
|
||||
c-0.8-0.4-1.2-0.6-1.7-1c-0.5-0.4-1.1-0.8-2.4-1.5c-0.9-0.5-1.7-1-2.4-1.4C90.4,149.2,92.2,147.4,93.5,144.4z M89.1,107.4
|
||||
c0.9,0.3,1.6,0.5,1.9,0.6c0.9,0.2,2.7,0.6,4.8,0.6h0.6c-1,1.5-2.9,2.2-4.5,1.8C90.5,110,89.5,108.8,89.1,107.4z M96,112.2
|
||||
c-0.4-0.2-0.6-0.4-0.9-0.6c1-0.4,1.9-1.1,2.5-2.1c0.3-0.3,0.2-0.7,0-1c1.1-0.1,2.1-0.4,3.1-0.7c0.5,0.8,0.7,1.7,0.5,2.4
|
||||
c-0.2,0.8-0.8,1.4-1.7,1.8C98.4,112.5,97,112.6,96,112.2z M96.8,146.4l1.6-32.4c0.6-0.1,1.2-0.3,1.8-0.5c1.3-0.6,2.2-1.6,2.5-2.8
|
||||
c0.5,3.3,1,6.4,1.7,9.3c0.8,3.2,1.7,6.1,2.7,8.6l-1.2,17.4l-0.7,9.2c-1,0.2-1.7,0.4-2.4,0.4c-2.4,0.2-4.4-0.5-6.2-1.3L96.8,146.4z
|
||||
M101.9,105.6c-2.1,1.1-4.3,1.5-6.1,1.5c-1.9,0-3.5-0.4-4.4-0.6c-0.7-0.2-8.7-2.2-12.1-9.9c-0.7-1.6-3.2-7.2-0.4-12.1
|
||||
c2-3.5,5.5-4.7,7.9-5.5c0.9-0.3,3.4-1.1,6.4-1.1c2.5,0,5.5,0.5,8.4,2.1c1.5,0.8,5.8,3.2,7.4,8.2c1.4,4.4-0.1,8.2-0.7,9.6
|
||||
C107.8,99.3,106.2,103.3,101.9,105.6z M43.7,75.9c-1.6,2.4-2.8,4.9-3.7,7.5c0,0.1-0.1,0.2-0.1,0.3c-0.6,0.6-1.2,1.3-1.8,2.1
|
||||
c-1.6,2.2-2.5,4.5-2.8,7.1c-0.2,2.2,0.1,4.4,0.9,6.6c0.7,1.8,1.5,3,1.9,3.7c0.3,0.4,0.7,1,1.3,1.8c-2.3,4.8-4.9,8.3-7.8,10.8
|
||||
c-0.4,0.3-0.8,0.7-1.2,1c-0.3-0.8-0.6-1.6-0.8-2.4c3-1.9,4.6-4.4,3.9-6.6c-0.3-1.2-1.3-2.1-2.7-2.7c0.5-0.5,1.1-1.1,1.5-1.7
|
||||
c2.3-3.2,4-9.2,0.7-12.8c-1.1-1.2-2.6-1.9-4.5-2.2C33.7,84.3,38.7,80.1,43.7,75.9z M17.1,79.2c0,0,1.6-1.6,3.2-1.4
|
||||
c1.4,0.2,2.6,2.3,3.4,4.1c0.7,1.6,1.3,4.5,1.6,6.5c-1.5,0.2-3.1,1.3-5.2,3.1c-1.2-2-3.1-5.5-3.6-7.2C16,82.1,15.9,80.4,17.1,79.2z
|
||||
M13.4,86.1c0.5-0.9,1.4-1.5,1.7-1.6v0.1c0.5,1.9,2.7,5.5,4,7.8c-0.8,0.7-1.7,1.5-2.6,2.4C13.6,91.4,12.4,88,13.4,86.1z M7.8,89.9
|
||||
c0.7-1.3,1.6-1.8,2.2-2.1c0.5-0.2,1-0.2,1.5-0.2c0,2.4,1.3,5.3,3.8,8.2c-1.2,1.1-1.6,1.9-1.6,2.8c0,0.3,0,0.7,0.1,1
|
||||
c-1.1,0.1-2.3-0.4-2.5-0.5c-1.6-0.7-2.9-2.2-3.6-4.1C7.1,93.2,7.1,91.3,7.8,89.9z M16.5,115.7c-1.9-0.3-3.1-1.1-3.4-2.2
|
||||
c-0.4-1.5,1-3.5,3.6-5.1l-0.8-1.3c-0.6,0.3-1.1,0.7-1.5,1.1c-0.5-0.1-1-0.2-1.5-0.4c-1-0.4-3-1.6-4.1-4.8c1-0.7,1.9-1.4,2.9-2.1
|
||||
c0.5,0.1,1.2,0.3,1.8,0.3c0.3,0,0.6,0,0.9-0.1c0.1,0.2,0.2,0.3,0.4,0.5c1.2,1.3,3.2,2.2,5,2.3c3,0.1,5.9-2.2,7-5.5l-1.5-0.5
|
||||
c-0.9,2.7-3.2,4.5-5.5,4.4c-1.4,0-3-0.7-3.9-1.8c-0.4-0.4-0.8-1.1-0.8-1.8c0-0.6,0.4-1.1,2.2-2.8c4.2-3.9,6.4-5.8,8.2-6.1
|
||||
c0.7-0.1,4.1-0.5,6.1,1.7c2.4,2.6,1.5,7.6-0.8,10.8c-2.1,2.9-5.2,3.9-6.9,4.3l0.3,1.5c1.2-0.3,3-0.8,4.9-2c1.4,0.4,2.4,1.1,2.6,2
|
||||
c0.3,1-0.3,2.4-1.7,3.7c-1.6,1.4-3.8,2.6-6.4,3.4C21.2,115.9,18.6,116.1,16.5,115.7z M25.7,140.9c-5.9-0.1-11.5-4.7-14.2-8.8
|
||||
c-1.4-2.2-2.1-4.3-1.6-5.5c0.5-1.1,2.2-1.4,4.9-1.8c1.6-0.2,3.4-0.5,5.3-1.1c0.5-0.1,0.9-0.3,1.4-0.4c0.1,0,0.2-0.1,0.4-0.1
|
||||
c3.5,6.9,8.5,11.4,11.2,13.3c0.4,0.4,0.9,0.7,1.4,1C31.5,139.8,28.7,140.9,25.7,140.9z M41.8,129.9c-1.5-0.8-3.8-2.4-6.1-4.8
|
||||
c-1.4-1.5-3-3.5-4.3-6c0.6-0.4,1.2-0.8,1.8-1.3c2.5-2.1,4.8-4.9,6.8-8.5l2.1,20.3C42,129.7,41.9,129.8,41.8,129.9z M41.4,107.1
|
||||
c1.4,1.2,3.2,2.4,5.4,3.1c1.3,0.4,2.6,0.6,4,0.6h0.7l1,12.4l2.2,30.7c-2,0.9-4.4,2-7.2,1.8c-0.4,0-0.7-0.1-1.1-0.1L41.4,107.1z
|
||||
M49.4,184.9l-2.1-20.6c0.8,0.4,1.8,0.7,2.9,1c2,0.4,3.7,0.4,5.3,0.2l1.4,20.3C54.3,185.5,51.8,185.2,49.4,184.9z M59.1,185.9
|
||||
L57.4,165c0.1,0,0.3-0.1,0.4-0.1c0.2,0,0.3-0.1,0.5-0.1c0.9-0.2,1.9-0.4,3.1-0.7c-0.3,0.3-0.6,0.6-0.5,1.2c0.1,0.8,0.8,1.1,1.1,1.2
|
||||
c0.6,0.3,1.4,0.4,2.4,0.4c0.3,0,0.7,0,1.1-0.1l0.8,19.5C63.8,186.3,61.4,186.2,59.1,185.9z M68.2,186.6l-1-19.8
|
||||
c0.7-0.2,1.6-0.5,2.5-1c0,0,0,0,0.1,0c0,0,0,0,0,0c0.4-0.2,0.9-0.5,1.3-0.7c0,0,0,0,0,0c0.1,0,0.1-0.1,0.2-0.1
|
||||
c0.8,1.7,2.2,3,3.8,3.5l0,0l0.3,18.4C73,186.7,70.6,186.7,68.2,186.6z M84,186.8h-6.6l-0.2-18.3c1.4-0.2,2.8-1.2,3.7-2.5
|
||||
c0.5,0.2,1,0.3,1.4,0.5l0.8,0.2c0,0,0.1,0,0.1,0c0,0,0,0,0.1,0c0.3,0.1,0.7,0.1,1,0.2L84,186.8z M93.3,186.6
|
||||
c-2.2,0.1-4.5,0.2-6.7,0.2l0.2-19.8c0.5-0.1,1-0.2,1.4-0.4c0.2-0.1,0.9-0.5,1.1-1.2c0.1-0.5-0.2-0.9-0.5-1.2
|
||||
c1.1,0.3,2.2,0.5,3.1,0.7c0.2,0,0.3,0.1,0.5,0.1c0.7,0.1,1.4,0.3,2.1,0.4L93.3,186.6z M92.5,163.4c-0.2,0-0.3-0.1-0.5-0.1
|
||||
c-1.3-0.3-3.2-0.6-5-1.4c-0.2-0.1-0.5-0.1-0.7,0c-0.2,0.1-0.4,0.3-0.4,0.6c-0.1,0.7,0.3,1.3,0.6,1.7c0.3,0.4,0.7,0.7,0.9,0.9
|
||||
c-0.4,0.2-1.5,0.3-2.9,0.2c-0.3,0-0.7-0.1-1-0.1c-0.1,0-0.2,0-0.2-0.1c0,0,0,0,0,0c-1.5-0.3-3.6-1.1-5.7-2.9c-0.7-0.6-1.4-1.4-2-2.2
|
||||
l-0.6-0.9l-0.6,0.9c-0.6,0.8-1.3,1.5-2,2.2c-1.3,1.1-2.6,1.9-3.8,2.4c-0.6,0.2-1.2,0.4-1.7,0.6c-0.1,0-0.2,0.1-0.3,0.1c0,0,0,0,0,0
|
||||
c0,0,0,0,0,0c-1.9,0.4-3.3,0.2-3.8-0.1c0.2-0.2,0.5-0.5,0.9-0.9c0.4-0.5,0.8-1.1,0.6-1.7c0-0.2-0.2-0.4-0.4-0.6
|
||||
c-0.2-0.1-0.5-0.1-0.7,0c-1.9,0.8-3.7,1.2-5,1.4c-0.2,0-0.4,0.1-0.5,0.1c-2.2,0.4-4.4,0.9-7.1,0.3c-2.6-0.6-4.3-1.7-5-2.2
|
||||
c-2.7-1.8-4.4-4.2-5.3-6.1c0.7,0.2,1.4,0.4,2.1,0.6c2.5,0.6,3.7,1,5.1,1.1h0.9c3,0,5.5-1.1,7.5-2.1c1.1-0.5,1.5-0.8,2.1-1.2
|
||||
c0.5-0.3,1.1-0.7,2.3-1.5c2.7-1.5,4.5-2.5,6.3-3c0.4,0,0.8-0.1,1.3-0.3c0.4-0.1,0.9-0.1,1.3-0.1h0.3c1.1,1.5,2.9,2.5,4.8,2.5
|
||||
c0.4,0,0.7,0,1.1-0.1c1.4-0.3,2.7-1,3.6-2.2c1-0.2,1.8-0.2,2.3-0.2c2.8,0,4.9,1.1,8.9,3.4c1.2,0.7,1.8,1.1,2.3,1.5
|
||||
c0.6,0.4,1,0.7,2.1,1.2c2.2,1,4.9,2.3,8.3,2.1c1.3-0.1,2.6-0.4,5.1-1.1c0.7-0.2,1.4-0.4,2.1-0.6c-1,1.9-2.6,4.2-5.3,6.1
|
||||
c-0.8,0.4-2.5,1.6-5.1,2.1C96.9,164.3,94.8,163.9,92.5,163.4z M102.6,185.9c-2.4,0.2-4.9,0.4-7.4,0.5l0.8-20.9
|
||||
c1.2,0.1,2.5,0,3.9-0.3c1.9-0.4,3.4-1.2,4.5-1.8L102.6,185.9z M115.2,156.6l-0.1,0.3l-2.9,27.9c-2.5,0.4-5.1,0.7-7.8,0.9l1.7-23.5
|
||||
c2.9-2.2,4.6-4.8,5.5-6.8c0.3-0.6,0.1-1.2-0.5-1.6c-0.3-0.2-0.7-0.3-1.1-0.1c-0.9,0.3-1.8,0.5-2.7,0.7c-0.3,0.1-0.5,0.1-0.7,0.2
|
||||
l0.6-8.7l1.1-14.3c0.8,1.5,1.6,2.8,2.5,3.9c1.8,2.2,3.7,4.4,5.8,6.5L115.2,156.6z M129.9,149.7c-0.8,0-1.5-0.5-1.8-1.2
|
||||
c1.1,0.2,2.2,0.4,3.5,0.4C131.2,149.4,130.6,149.7,129.9,149.7z M136.9,149c-0.3,0.8-1.1,1.3-1.9,1.2c-0.8,0-1.6-0.5-1.9-1.3
|
||||
c1.3-0.1,2.7-0.2,4-0.5L136.9,149z M151.1,143.4C151.1,143.4,151,143.4,151.1,143.4h-0.9v0.8c0,0.1,0,0.1,0,0.2c0,1.1-0.9,2-2.1,2
|
||||
c0,0,0,0-0.1,0c-0.3,0-0.6-0.1-0.8-0.2l-1-0.5l-0.1,1.1c-0.1,1-1,1.8-2.1,1.8c-0.5,0-1-0.2-1.4-0.6l-0.8-0.7l-0.5,0.9
|
||||
c-0.4,0.7-1.1,1.1-1.9,1.1c-0.5,0-1-0.2-1.4-0.6l-0.4-0.4c2.7-0.6,5.5-1.6,8.2-3c2.6-1.3,5-3,7-4.7c0.1,0.2,0.1,0.5,0.1,0.8
|
||||
C153.1,142.5,152.2,143.4,151.1,143.4z M132.4,147.3h-0.5c-4-0.1-7.7-1.4-11.1-4c-3.2-2.2-5.7-5.1-8.6-8.7
|
||||
c-2.4-2.9-4.7-8.3-6.3-15.1c-1-4-1.7-8.5-2.2-13.2c1.8-1.1,3-2.5,4-3.9c0.1,0,0.1,0.1,0.2,0.1c0,9.9,2,19.9,7.1,28.6
|
||||
c1.2,2.2,3.8,2.8,5.7,1c0.1-0.1,0.2-0.2,0.2-0.2c1.2-1.3,2-3.1,2.4-4.8c0.2-1.5,0.3-3-0.7-4.3c-3.4-3.7-4.6-8.9-4.7-14
|
||||
c1,0.7,1.9,1.4,2.8,2.1c0.2,3.8,1.1,7.6,3.5,10.7c3.7,4.9,10.1,7.4,19.2,7.5c3.4,0,6.4,0.4,8.7,1c2.3,0.7,3.7,1.6,4.1,2.7
|
||||
c0.3,0.9-0.2,2.2-1.3,3.7C151.5,141.1,141.8,147.3,132.4,147.3z M156.3,139.2c-0.4,0.4-0.9,0.6-1.5,0.6c-0.3,0-0.5-0.1-0.8-0.2
|
||||
c0.7-0.7,1.4-1.5,1.9-2.2c0.3-0.2,0.4-0.5,0.6-0.8c0.2,0.4,0.4,0.8,0.4,1.2C156.9,138.3,156.7,138.9,156.3,139.2z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 37 KiB |
45
orville-docsite/site-builder/index.md
Normal file
45
orville-docsite/site-builder/index.md
Normal file
@ -0,0 +1,45 @@
|
||||
---
|
||||
title: Home
|
||||
---
|
||||
|
||||
Orville's goal is to provide a powerful API for applications to access
|
||||
PostgreSQL databases with minimal use of sophisticated language techniques or
|
||||
extensions. It strikes a balance between enforcing type-safety in database
|
||||
interactions where it is reasonable and presenting type signatures that are
|
||||
minimally complicated.
|
||||
|
||||
## Why Orville?
|
||||
|
||||
Orville is not meant to replace existing PostgreSQL libraries in the Haskell
|
||||
ecosystem, but to complement them. It has the power to satisfy most experienced
|
||||
Haskell developers but strives to remain approachable to newcomers despite
|
||||
this. Orville's API is rich enough to be used in production on large and
|
||||
sophisticated applications, but avoids complicated type-level programming. If
|
||||
your application is too large to reasonably write all your SQL statements by
|
||||
hand yet doesn't require absolute type-safety between your custom SQL
|
||||
statements, their result sets and the Haskell types they decode into, Orville
|
||||
may be the right choice for you.
|
||||
|
||||
## Feature Overview
|
||||
|
||||
* Rich API for marshalling Haskell types to and from SQL
|
||||
* High-level APIs for common CRUD operations
|
||||
* Optional automatic schema migrations
|
||||
* Optional API for executing complex data loads across multiple tables without ever writing an N+1 query by accident
|
||||
* Progressive escape hatches to let you dig deeper when you need to
|
||||
|
||||
## Tutorials
|
||||
|
||||
See the tutorials, in order of increasing complexity:
|
||||
|
||||
$for(tutorials)$
|
||||
* <a href="$url$">$title$</a>$endfor$
|
||||
|
||||
Additional documentation is available in the Haddocks.
|
||||
|
||||
## Just show me some code!
|
||||
|
||||
Ok! Here's a very simple application that inserts some entities of a `Pet`
|
||||
model and finds one of them based on its name.
|
||||
|
||||
$sample("hero/src/Main.hs")$
|
21
orville-docsite/site-builder/site-builder.cabal
Normal file
21
orville-docsite/site-builder/site-builder.cabal
Normal file
@ -0,0 +1,21 @@
|
||||
name: site-builder
|
||||
version: 0.1.0.0
|
||||
build-type: Simple
|
||||
cabal-version: >= 1.10
|
||||
|
||||
executable site
|
||||
main-is: site.hs
|
||||
build-depends: base == 4.*
|
||||
, hakyll == 4.16.*
|
||||
, attoparsec
|
||||
, bytestring
|
||||
, containers
|
||||
, data-default
|
||||
, dlist
|
||||
, filepath
|
||||
, non-empty-text
|
||||
, pandoc
|
||||
, skylighting
|
||||
, text
|
||||
ghc-options: -threaded -rtsopts -with-rtsopts=-N
|
||||
default-language: Haskell2010
|
450
orville-docsite/site-builder/site.hs
Normal file
450
orville-docsite/site-builder/site.hs
Normal file
@ -0,0 +1,450 @@
|
||||
--------------------------------------------------------------------------------
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
import qualified Control.Concurrent.MVar as MVar
|
||||
import qualified Data.ByteString.Lazy as BSL
|
||||
import Data.Default (def)
|
||||
import qualified Data.Foldable as Fold
|
||||
import qualified Data.List as List
|
||||
import qualified Data.Set as Set
|
||||
import Data.Ord (comparing)
|
||||
import Data.Monoid (mappend)
|
||||
import Hakyll
|
||||
import Text.Pandoc.Highlighting (Style, breezeDark, styleToCss)
|
||||
import Text.Pandoc.Options (ReaderOptions(..), WriterOptions(..))
|
||||
import qualified Text.Read as Read
|
||||
import qualified Skylighting
|
||||
import qualified System.FilePath as FilePath
|
||||
|
||||
import qualified Data.Attoparsec.Text as AttoText
|
||||
import qualified Data.Char as Char
|
||||
import qualified Data.Map.Strict as Map
|
||||
import qualified Data.NonEmptyText as NET
|
||||
import qualified Data.Text as T
|
||||
import qualified Data.Text.IO as TIO
|
||||
import qualified Data.DList as DList
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
config :: Configuration
|
||||
config =
|
||||
def
|
||||
{ previewHost = "0.0.0.0"
|
||||
}
|
||||
|
||||
pandocCodeStyle :: Style
|
||||
pandocCodeStyle =
|
||||
breezeDark
|
||||
|
||||
renderPandocInStyle :: Item String -> Compiler (Item String)
|
||||
renderPandocInStyle =
|
||||
renderPandocWith
|
||||
defaultHakyllReaderOptions
|
||||
(defaultHakyllWriterOptions
|
||||
{ writerHighlightStyle = Just pandocCodeStyle
|
||||
})
|
||||
|
||||
main :: IO ()
|
||||
main = do
|
||||
hakyllWith config $ do
|
||||
snippetCache <- preprocess newSnippetCache
|
||||
|
||||
match "images/*" $ do
|
||||
route idRoute
|
||||
compile copyFileCompiler
|
||||
|
||||
match "css/*" $ do
|
||||
route idRoute
|
||||
compile compressCssCompiler
|
||||
|
||||
create ["css/syntax.css"] $ do
|
||||
route idRoute
|
||||
compile (makeItem $ styleToCss pandocCodeStyle)
|
||||
|
||||
match "contact.md" $ do
|
||||
route $ setExtension "html"
|
||||
compile $ pandocCompiler
|
||||
>>= applyDefaultLayout snippetCache defaultContext
|
||||
>>= relativizeUrls
|
||||
|
||||
let
|
||||
tutorialsRoute =
|
||||
composeRoutes
|
||||
(gsubRoute "tutorials/" (const ""))
|
||||
(setExtension "html")
|
||||
|
||||
match "tutorials/**" $ do
|
||||
route tutorialsRoute
|
||||
compile $
|
||||
getResourceBody
|
||||
>>= applyAsTemplate (pageCtx snippetCache)
|
||||
>>= renderPandocInStyle
|
||||
>>= loadAndApplyTemplate "templates/post.html" (pageCtx snippetCache)
|
||||
>>= saveSnapshot navLinksSnapshot
|
||||
>>= applyDefaultLayout snippetCache (pageCtx snippetCache)
|
||||
>>= relativizeUrls
|
||||
|
||||
match "index.md" $ do
|
||||
route $ setExtension "html"
|
||||
compile $ do
|
||||
navContext <- loadNavContex snippetCache
|
||||
getResourceBody
|
||||
>>= applyAsTemplate (mconcat [navContext, pageCtx snippetCache])
|
||||
>>= renderPandocInStyle
|
||||
>>= applyDefaultLayout snippetCache defaultContext
|
||||
>>= relativizeUrls
|
||||
|
||||
match "templates/*" $ compile templateBodyCompiler
|
||||
|
||||
create ["check-all-snippets-used"] $ do
|
||||
-- This rule doesn't have a route, which causes no actual file to get
|
||||
-- create for the site, which is what we want.
|
||||
compile $ do
|
||||
-- Depend on index since it gets compiled last. This way the other
|
||||
-- files will populate the snippet cache before we check if all
|
||||
-- snippets are used.
|
||||
_index <- load "index.md" :: Compiler (Item String)
|
||||
recompilingUnsafeCompiler $ failIfAnySnippetsUnused snippetCache
|
||||
makeItem ("Never seen" :: String)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
applyDefaultLayout ::
|
||||
SnippetCache ->
|
||||
Context a ->
|
||||
Item a ->
|
||||
Compiler (Item String)
|
||||
applyDefaultLayout snippetCache itemContext item = do
|
||||
navContext <- loadNavContex snippetCache
|
||||
loadAndApplyTemplate
|
||||
"templates/default.html"
|
||||
(mappend navContext itemContext)
|
||||
item
|
||||
|
||||
{- |
|
||||
Nav links are generated from a named snapshot of the content items to
|
||||
avoid the dependency cycle introduced by an item needing a nav link to
|
||||
itself.
|
||||
-}
|
||||
navLinksSnapshot :: String
|
||||
navLinksSnapshot =
|
||||
"navLinks"
|
||||
|
||||
loadNavContex :: SnippetCache -> Compiler (Context a)
|
||||
loadNavContex snippetCache = do
|
||||
tutorials <- inNavOrder =<< loadAllSnapshots "tutorials/*" navLinksSnapshot
|
||||
pure $
|
||||
listField "tutorials" (pageCtx snippetCache) (return tutorials)
|
||||
|
||||
inNavOrder :: (MonadMetadata m, MonadFail m) => [Item a] -> m [Item a]
|
||||
inNavOrder =
|
||||
let
|
||||
pairWithNavOrder item = do
|
||||
navOrder <- getNavOrder (itemIdentifier item)
|
||||
pure (item, navOrder)
|
||||
in
|
||||
\items ->
|
||||
fmap
|
||||
(map fst . List.sortBy (comparing snd))
|
||||
(traverse pairWithNavOrder items)
|
||||
|
||||
|
||||
getNavOrder :: (MonadMetadata m, MonadFail m) => Identifier -> m Int
|
||||
getNavOrder identifier = do
|
||||
metadata <- getMetadata identifier
|
||||
case lookupString "navOrder" metadata of
|
||||
Nothing -> fail $ "navOrder not specified for " <> show identifier
|
||||
Just navOrder -> either fail pure (Read.readEither navOrder)
|
||||
|
||||
|
||||
pageCtx :: SnippetCache -> Context String
|
||||
pageCtx snippetCache =
|
||||
mconcat
|
||||
[ dateField "date" "%B %e, %Y"
|
||||
, sampleField snippetCache
|
||||
, defaultContext
|
||||
]
|
||||
|
||||
sampleField :: SnippetCache -> Context a
|
||||
sampleField snippetCache =
|
||||
functionField "sample" $ \args item -> do
|
||||
case args of
|
||||
[path] ->
|
||||
mkMarkdownCodeBlock path =<<
|
||||
recompilingUnsafeCompiler (TIO.readFile ("../samples/" <> path))
|
||||
|
||||
[path, snippetName] -> do
|
||||
case NET.fromText (T.pack snippetName) of
|
||||
Nothing ->
|
||||
fail $ "Snippet name may not be empty"
|
||||
Just nonEmptySnippetName -> do
|
||||
errOrSnippet <-
|
||||
recompilingUnsafeCompiler $
|
||||
lookupSnippet
|
||||
snippetCache
|
||||
("../samples/" <> path)
|
||||
nonEmptySnippetName
|
||||
|
||||
case errOrSnippet of
|
||||
Left err -> fail $ "Error loading snippet from " <> path <> ": " <> err
|
||||
Right snippet -> mkMarkdownCodeBlock path snippet
|
||||
|
||||
_ -> fail "Wrong number of arguments passed to function sample()"
|
||||
|
||||
mkMarkdownCodeBlock :: FilePath -> T.Text -> Compiler String
|
||||
mkMarkdownCodeBlock path body = do
|
||||
lang <- either fail pure (guessLang path)
|
||||
pure $
|
||||
unlines
|
||||
[ "<div class=\"codeblock-label\">" <> lang <> "</div>"
|
||||
, "```" <> lang
|
||||
, T.unpack body
|
||||
, "```"
|
||||
]
|
||||
|
||||
guessLang :: FilePath -> Either String String
|
||||
guessLang path =
|
||||
case FilePath.takeExtensions path of
|
||||
".hs" -> Right "haskell"
|
||||
".sh" -> Right "sh"
|
||||
".patch" -> Right "diff"
|
||||
".txt" -> Right "txt"
|
||||
ext -> Left $ "Unable to determine language for " <> path
|
||||
|
||||
type SnippetMap =
|
||||
Map.Map NET.NonEmptyText T.Text
|
||||
|
||||
type SnippetCache =
|
||||
MVar.MVar (Map.Map FilePath SnippetCacheEntry)
|
||||
|
||||
newSnippetCache :: IO SnippetCache
|
||||
newSnippetCache =
|
||||
MVar.newMVar Map.empty
|
||||
|
||||
failIfAnySnippetsUnused :: SnippetCache -> IO ()
|
||||
failIfAnySnippetsUnused snippetCache =
|
||||
MVar.withMVar snippetCache (Fold.traverse_ failIfAnySnippetsUnusedForEntry)
|
||||
|
||||
failIfAnySnippetsUnusedForEntry :: MonadFail m => SnippetCacheEntry -> m ()
|
||||
failIfAnySnippetsUnusedForEntry cacheEntry =
|
||||
let
|
||||
allKeys =
|
||||
Map.keysSet (snippetCacheEntrySnippets cacheEntry)
|
||||
|
||||
unusedKeys =
|
||||
Set.difference allKeys (snippetCacheEntryUsedSnippets cacheEntry)
|
||||
in
|
||||
if Set.null unusedKeys
|
||||
then pure ()
|
||||
else
|
||||
fail
|
||||
$ "Unused snippets found in "
|
||||
<> snippetCacheEntryPath cacheEntry
|
||||
<> ": "
|
||||
<> show unusedKeys
|
||||
|
||||
|
||||
lookupSnippet ::
|
||||
SnippetCache ->
|
||||
FilePath ->
|
||||
NET.NonEmptyText ->
|
||||
IO (Either String T.Text)
|
||||
lookupSnippet cacheVar path snippetName =
|
||||
if snippetName == hiddenSnippetName
|
||||
then
|
||||
pure . Left $
|
||||
"Snippets with the name "
|
||||
<> show hiddenSnippetName
|
||||
<> " cannot be referenced in templates."
|
||||
else
|
||||
MVar.modifyMVar cacheVar $ \cacheMap -> do
|
||||
errOrMapAndEntry <-
|
||||
case Map.lookup path cacheMap of
|
||||
Just cacheEntry ->
|
||||
pure . Right $ (cacheMap, cacheEntry)
|
||||
Nothing -> do
|
||||
errOrSnippets <- loadSnippetsFile path
|
||||
case errOrSnippets of
|
||||
Left err -> pure . Left $ err
|
||||
Right snippets ->
|
||||
let
|
||||
cacheEntry =
|
||||
SnippetCacheEntry
|
||||
{ snippetCacheEntryPath = path
|
||||
, snippetCacheEntrySnippets = snippets
|
||||
, snippetCacheEntryUsedSnippets = Set.empty
|
||||
}
|
||||
in
|
||||
pure . Right $
|
||||
( Map.insert path cacheEntry cacheMap
|
||||
, cacheEntry
|
||||
)
|
||||
|
||||
pure $
|
||||
case errOrMapAndEntry of
|
||||
Left err -> (cacheMap, Left err)
|
||||
Right (newMap, cacheEntry) ->
|
||||
let
|
||||
snippet =
|
||||
case Map.lookup snippetName (snippetCacheEntrySnippets cacheEntry) of
|
||||
Nothing -> Left $ "Snippet " <> show snippetName <> " not found in " <> path
|
||||
Just snippet -> Right snippet
|
||||
|
||||
newMapWithSnippetMarkedUsed =
|
||||
Map.adjust
|
||||
(markSnippetUsed snippetName)
|
||||
path
|
||||
newMap
|
||||
in
|
||||
( newMapWithSnippetMarkedUsed
|
||||
, snippet
|
||||
)
|
||||
|
||||
|
||||
data SnippetCacheEntry =
|
||||
SnippetCacheEntry
|
||||
{ snippetCacheEntryPath :: FilePath
|
||||
, snippetCacheEntrySnippets :: SnippetMap
|
||||
, snippetCacheEntryUsedSnippets :: Set.Set NET.NonEmptyText
|
||||
}
|
||||
|
||||
loadSnippetsFile :: FilePath -> IO (Either String SnippetMap)
|
||||
loadSnippetsFile =
|
||||
fmap (AttoText.parseOnly snippets) . TIO.readFile
|
||||
|
||||
markSnippetUsed :: NET.NonEmptyText -> SnippetCacheEntry -> SnippetCacheEntry
|
||||
markSnippetUsed snippetName cacheEntry =
|
||||
cacheEntry
|
||||
{ snippetCacheEntryUsedSnippets =
|
||||
Set.insert
|
||||
snippetName
|
||||
(snippetCacheEntryUsedSnippets cacheEntry)
|
||||
}
|
||||
|
||||
hiddenSnippetName :: NET.NonEmptyText
|
||||
hiddenSnippetName =
|
||||
NET.new 'h' "idden"
|
||||
|
||||
snippets :: AttoText.Parser SnippetMap
|
||||
snippets =
|
||||
let
|
||||
insertChunks snippetMarker chunks snippets =
|
||||
let
|
||||
name =
|
||||
snippetMarkerName snippetMarker
|
||||
in
|
||||
if name == hiddenSnippetName
|
||||
then pure snippets
|
||||
else
|
||||
if Map.member name snippets
|
||||
then
|
||||
fail $
|
||||
"Snippet names must be unique within their file. "
|
||||
<> show name
|
||||
<> " appears more than once."
|
||||
else
|
||||
pure $
|
||||
Map.insert
|
||||
name
|
||||
(T.concat . DList.toList $ chunks)
|
||||
snippets
|
||||
|
||||
go snippets chunks snippetMarker = do
|
||||
let
|
||||
snippetLeadingChar =
|
||||
NET.head
|
||||
. snippetMarkerPrefix
|
||||
$ snippetMarker
|
||||
|
||||
nextChunk <- AttoText.takeTill (== snippetLeadingChar)
|
||||
|
||||
let
|
||||
newChunks =
|
||||
DList.snoc chunks nextChunk
|
||||
|
||||
atEnd <- AttoText.atEnd
|
||||
|
||||
if atEnd
|
||||
then insertChunks snippetMarker newChunks snippets
|
||||
else do
|
||||
nextNameOrBrace <-
|
||||
AttoText.eitherP
|
||||
(snippetMarkerWithMatchingPrefix snippetMarker)
|
||||
(AttoText.string (T.singleton snippetLeadingChar))
|
||||
|
||||
case nextNameOrBrace of
|
||||
Left nextName -> do
|
||||
newSnippets <- insertChunks snippetMarker newChunks snippets
|
||||
go
|
||||
newSnippets
|
||||
DList.empty
|
||||
nextName
|
||||
|
||||
Right followingBrace -> do
|
||||
go
|
||||
snippets
|
||||
(DList.snoc newChunks followingBrace)
|
||||
snippetMarker
|
||||
in
|
||||
go Map.empty DList.empty =<< snippetMarkerWithAnyPrefix
|
||||
|
||||
|
||||
{- |
|
||||
|
||||
Parses a magic haskell comment marking the beginning of a snippet. The
|
||||
snippet name may consist of any non-space characters. E.G.
|
||||
|
||||
> {- SNIPPET: header -}
|
||||
|
||||
or
|
||||
|
||||
> -- SNIPPET: header
|
||||
|
||||
or
|
||||
|
||||
> # SNIPPET: header
|
||||
|
||||
-}
|
||||
snippetMarkerWithAnyPrefix :: AttoText.Parser SnippetMarker
|
||||
snippetMarkerWithAnyPrefix = do
|
||||
SnippetMarker
|
||||
<$> nonEmptyWhile (not . Char.isSpace)
|
||||
<*> snippetMarkerBody
|
||||
|
||||
snippetMarkerWithMatchingPrefix :: SnippetMarker -> AttoText.Parser SnippetMarker
|
||||
snippetMarkerWithMatchingPrefix marker = do
|
||||
SnippetMarker
|
||||
<$> nonEmptyString (snippetMarkerPrefix marker)
|
||||
<*> snippetMarkerBody
|
||||
|
||||
snippetMarkerBody :: AttoText.Parser NET.NonEmptyText
|
||||
snippetMarkerBody = do
|
||||
AttoText.skipWhile AttoText.isHorizontalSpace
|
||||
AttoText.string "SNIPPET:"
|
||||
AttoText.skipWhile AttoText.isHorizontalSpace
|
||||
name <- nonEmptyWhile (not . Char.isSpace)
|
||||
AttoText.skipWhile AttoText.isHorizontalSpace
|
||||
AttoText.skipWhile (not . isEOLChar)
|
||||
AttoText.endOfLine
|
||||
pure name
|
||||
|
||||
nonEmptyWhile :: (Char -> Bool) -> AttoText.Parser NET.NonEmptyText
|
||||
nonEmptyWhile predicate = do
|
||||
text <- AttoText.takeWhile1 predicate
|
||||
case NET.fromText text of
|
||||
Nothing -> fail "nonEmptyWhile: takeWhile1 returned empty text"
|
||||
Just nonEmptyText -> pure nonEmptyText
|
||||
|
||||
nonEmptyString :: NET.NonEmptyText -> AttoText.Parser NET.NonEmptyText
|
||||
nonEmptyString targetText =
|
||||
AttoText.string (NET.toText targetText)
|
||||
*> pure targetText
|
||||
|
||||
data SnippetMarker =
|
||||
SnippetMarker
|
||||
{ snippetMarkerPrefix :: NET.NonEmptyText
|
||||
, snippetMarkerName :: NET.NonEmptyText
|
||||
}
|
||||
|
||||
isEOLChar :: Char -> Bool
|
||||
isEOLChar c =
|
||||
c == '\n' || c == '\r'
|
2
orville-docsite/site-builder/templates/archive.html
Normal file
2
orville-docsite/site-builder/templates/archive.html
Normal file
@ -0,0 +1,2 @@
|
||||
Here you can find all my previous posts:
|
||||
$partial("templates/post-list.html")$
|
44
orville-docsite/site-builder/templates/default.html
Normal file
44
orville-docsite/site-builder/templates/default.html
Normal file
@ -0,0 +1,44 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="x-ua-compatible" content="ie=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Orville - $title$</title>
|
||||
<link rel="stylesheet" href="/css/syntax.css" />
|
||||
<link rel="stylesheet" href="/css/default.css" />
|
||||
</head>
|
||||
<body>
|
||||
<section class="leftbar">
|
||||
<header>
|
||||
<h1 class="logo">
|
||||
<a href="/">
|
||||
<img alt="Orville Logo" src="images/orville-waving-pennant.svg"/>
|
||||
</a>
|
||||
</h1>
|
||||
</header>
|
||||
|
||||
<nav>
|
||||
<h3><a href="/">Home</a></h3>
|
||||
|
||||
<h3>Tutorials</h3>
|
||||
$for(tutorials)$
|
||||
<a href="$url$">$title$</a>
|
||||
$endfor$
|
||||
|
||||
<h3>Other Links</h3>
|
||||
<a href="/contact.html">Contact</a>
|
||||
</nav>
|
||||
|
||||
<footer>
|
||||
Site proudly generated by
|
||||
<a href="http://jaspervdj.be/hakyll">Hakyll</a>
|
||||
</footer>
|
||||
</section>
|
||||
|
||||
<main role="main">
|
||||
<h1>$title$</h1>
|
||||
$body$
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
5
orville-docsite/site-builder/templates/post.html
Normal file
5
orville-docsite/site-builder/templates/post.html
Normal file
@ -0,0 +1,5 @@
|
||||
<article>
|
||||
<section>
|
||||
$body$
|
||||
</section>
|
||||
</article>
|
44
orville-docsite/site-builder/tutorials/getting-started.md
Normal file
44
orville-docsite/site-builder/tutorials/getting-started.md
Normal file
@ -0,0 +1,44 @@
|
||||
---
|
||||
title: Getting Started
|
||||
navOrder: 0
|
||||
---
|
||||
|
||||
Orville is a PostgreSQL client library, so it needs a working PostgreSQL server
|
||||
to work. This tutorial assumes that PostgreSQL in running on `localhost`,
|
||||
listening on port `5432` and that the user `postgres` exists with password
|
||||
`postgres`.
|
||||
|
||||
Orville itself depends on the native LibPQ library being installed to
|
||||
communicate with the server, so we need to install it before building
|
||||
everything. If you're on Debian-like distribution that uses `apt`, this command
|
||||
will do that.
|
||||
|
||||
$sample("install-packages.sh", "installLibPqClient")$
|
||||
|
||||
With that setup out of the way, let's make a new project for this demo:
|
||||
|
||||
$sample("getting-started/run.sh", "initProject")$
|
||||
|
||||
Now we need to edit `stack.yaml` and add `orville-postgresql` as an extra-dep.
|
||||
|
||||
$sample("getting-started/add-orville-extra-dep-to-stack-yaml.patch")$
|
||||
|
||||
Next, edit the `orville-getting-started.cabal` file to make it depend on
|
||||
Orville.
|
||||
|
||||
$sample("getting-started/add-orville-to-cabal-file.patch")$
|
||||
|
||||
Here is a minimal program that simply computes 1+1 on the database. Replace
|
||||
the `src/Main.hs` with it.
|
||||
|
||||
$sample("getting-started/Main.hs")$
|
||||
|
||||
All that's left is to build the executable and run it. When it runs it will
|
||||
connect to the PostgreSQL server, run the SQL to calculate `1 + 1` and then
|
||||
print the result out.
|
||||
|
||||
$sample("getting-started/run.sh", "buildAndExecute")$
|
||||
|
||||
This is the output you will see in the console if everything went as planned:
|
||||
|
||||
$sample("getting-started/expected-output.txt")$
|
86
orville-docsite/site-builder/tutorials/using-json.md
Normal file
86
orville-docsite/site-builder/tutorials/using-json.md
Normal file
@ -0,0 +1,86 @@
|
||||
---
|
||||
title: Using JSON
|
||||
navOrder: 4
|
||||
---
|
||||
|
||||
You'll need a Haskell project named `using-json` for this tutorial.
|
||||
The setup is identical to the setup in [Getting Started](getting-started.html)
|
||||
aside from the package name, so we'll avoid explaining it again here.
|
||||
|
||||
SQL has a rigid schema. Using JSON inside SQL allows for pockets of schemaless
|
||||
data, that is still queryable using PostgreSQL's built-in functionality.
|
||||
|
||||
This document explains how to use Orville with the JSONB data type that
|
||||
PostgreSQL natively supports.
|
||||
|
||||
Project initialization is similar to previous tutorials, but additional
|
||||
dependencies like Aeson have been added. Aeson is a JSON library for Haskell.
|
||||
|
||||
Here's the beginning of our `src/Main.hs` file.
|
||||
|
||||
$sample("using-json/src/Main.hs", "moduleHeader")$
|
||||
|
||||
Let's suppose we have an example entity with an ID, and some arbitrary JSON
|
||||
data in a column called 'tags'.
|
||||
|
||||
Note how `fooTagsField` below uses the `Value` type from the Aeson library.
|
||||
|
||||
Remember that the `Value` contains its own `Null` constructor, which is
|
||||
distinct from SQL's `NULL`. So we can have JSON nulls in this field, but no SQL
|
||||
nulls.
|
||||
|
||||
We could also use a custom type with `FromJSON`/`ToJSON` instances, since
|
||||
`jsonb` allows for that too. Aeson is not the focus of this document though.
|
||||
|
||||
$sample("using-json/src/Main.hs", "dataTypes")$
|
||||
|
||||
Before we can define the corresponding `SqlMarshaller`, we'll need to define the
|
||||
`aesonValueField` helper function. This is done `tryConvertSqlType` along with
|
||||
`jsonb` field to apply Aeson encoding and decode.
|
||||
|
||||
$sample("using-json/src/Main.hs", "aesonValueField")$
|
||||
|
||||
Let's define the `SqlMarshaller` and the table. This is standard stuff, no
|
||||
surprises here.
|
||||
|
||||
$sample("using-json/src/Main.hs", "tableDefinition")$
|
||||
|
||||
With all definitions done, we can write `main`. Orville will also use the parts
|
||||
of the `SqlType` during migration.
|
||||
|
||||
$sample("using-json/src/Main.hs", "mainFunction")$
|
||||
|
||||
We'll construct a JSON value using the Aeson library, which makes this look
|
||||
fairly verbose. But imagine that the `Array` value below was read from a file,
|
||||
or received over HTTP from a web browser.
|
||||
|
||||
$sample("using-json/src/Main.hs", "insertEntity")$
|
||||
|
||||
Using raw SQL, we can use PostgreSQL's built-in JSONB functions. Let's suppose
|
||||
we want a row returned for each of the values in the `Array` above.
|
||||
|
||||
| ID | Tag |
|
||||
| -- | --- |
|
||||
| 0 | 1 |
|
||||
| 0 | 2 |
|
||||
| 0 | 3 |
|
||||
|
||||
We can use an `SqlMarshaller` to produce a result like this, even though there
|
||||
is no table for the returned schema. The programmer must ensure correspondence
|
||||
of the SQL and the `SqlMarshaller`. If they don't match, an exception will be
|
||||
thrown.
|
||||
|
||||
We'll have the `SqlMarshaller` work with tuples and `marshallReadOnlyField`s.
|
||||
These allow for succintly defining a quick one-off `SqlMarshaller`.
|
||||
|
||||
$sample("using-json/src/Main.hs", "selectJSONArray")$
|
||||
|
||||
# Program output and test
|
||||
|
||||
This concludes this tutorial. You can build an execute this as usual:
|
||||
|
||||
$sample("using-json/run.sh","buildAndExecute")$
|
||||
|
||||
And you should see the following output:
|
||||
|
||||
$sample("using-json/expected-output.txt")$
|
58
orville-docsite/site-builder/tutorials/using-migrations.md
Normal file
58
orville-docsite/site-builder/tutorials/using-migrations.md
Normal file
@ -0,0 +1,58 @@
|
||||
---
|
||||
title: Using Migrations
|
||||
navOrder: 2
|
||||
---
|
||||
|
||||
## Adding a new non-nullable column
|
||||
|
||||
We'll show how, to add a new non-nullable column:
|
||||
|
||||
1. first, we create the initial version of the table without the 'new' column
|
||||
1. we then add the column, but only as *nullable*
|
||||
1. we make sure all values are present on the nullable column
|
||||
1. we then migrate the column to be non-nullable
|
||||
|
||||
You'll need a Haskell project named `using-migrations` for this tutorial.
|
||||
The setup is identical to the setup in [Getting Started](getting-started.html)
|
||||
aside from the package name, so we'll avoid explaining it again here. Instead
|
||||
we'll get straight on creating our `src/Main.hs` file.
|
||||
|
||||
$sample("using-migrations/src/Main.hs", "moduleHeaderAndTypes")$
|
||||
|
||||
Note how the following tables have the same SQL table names. Imagine that
|
||||
`table1` is the initial version of the table, which then is changed to
|
||||
`table2`, and so on. In practice, they can keep their Haskell names, since they
|
||||
won't need to co-exist, like they do in this document.
|
||||
|
||||
Orville's AutoMigration tool sees that the difference between the tables is,
|
||||
that the second table has an additional column, and it will generate and
|
||||
execute the DDL to add the column. It needs to be nullable, because the database won't
|
||||
have any values for it when it is added.
|
||||
|
||||
$sample("using-migrations/src/Main.hs", "tableDefinitions")$
|
||||
|
||||
Now let's add a `main` function invokes Orville's auto-migration tool to
|
||||
demonstrate making changes to the table.
|
||||
|
||||
$sample("using-migrations/src/Main.hs", "mainFunction")$
|
||||
|
||||
# Dropping a column
|
||||
|
||||
Orville won't automatically drop a column in the SQL database that isn't in the SqlMarshaller.
|
||||
The column will just be ignored.
|
||||
|
||||
We have to tell Orville explicitly about the columns that are safe to drop.
|
||||
This is done using the `dropColumns` combinator. Add this to the bottom of the
|
||||
`main` function:
|
||||
|
||||
$sample("using-migrations/src/Main.hs", "droppingColumns")$
|
||||
|
||||
# Conclusion
|
||||
|
||||
This concludes this tutorial. You can build an execute this as usual:
|
||||
|
||||
$sample("using-migrations/run.sh","buildAndExecute")$
|
||||
|
||||
And you should see the following output:
|
||||
|
||||
$sample("using-migrations/expected-output.txt")$
|
124
orville-docsite/site-builder/tutorials/using-plans.md
Normal file
124
orville-docsite/site-builder/tutorials/using-plans.md
Normal file
@ -0,0 +1,124 @@
|
||||
---
|
||||
title: Using Plans
|
||||
navOrder: 3
|
||||
---
|
||||
|
||||
You'll need a Haskell project named `using-plans` for this tutorial.
|
||||
The setup is identical to the setup in [Getting Started](getting-started.html)
|
||||
aside from the package name, so we'll avoid explaining it again here.
|
||||
|
||||
This example shows Plans. A Plan contains one or more SQL select statements,
|
||||
and when the first is run, its result is passed into the next.
|
||||
|
||||
The goal of plans is to prevent [n+1
|
||||
queries](https://secure.phabricator.com/book/phabcontrib/article/n_plus_one/),
|
||||
while still allowing a plan for a single item to be modified to execute against
|
||||
many items.
|
||||
|
||||
The code contains a detailed description, this document will just focus on
|
||||
examples.
|
||||
|
||||
This example has a table with students, and a table with classes they can take.
|
||||
|
||||
Because multiple students can be enrolled in multiple classes, it is a
|
||||
many-to-many relationship, which can be modelled with a separate table with two
|
||||
foreign keys on it.
|
||||
|
||||
These tables are defined in a manner similar to previous tutorials. Note the
|
||||
foreign keys on the `student_class` table. Begin your `src/Main.hs` like this:
|
||||
|
||||
$sample("using-plans/src/Main.hs", "moduleHeader")$
|
||||
|
||||
The three tables are declared, we'll proceed to the `main` function, which
|
||||
ensures some sample data is available, and then queries it in different ways.
|
||||
|
||||
Since we have foreign key constraints, we have to create and delete the data in
|
||||
an order that doesn't violate these constraints. Orville will throw a Haskell
|
||||
runtime exception upon execution of an SQL statement that violates a
|
||||
constraint.
|
||||
|
||||
$sample("using-plans/src/Main.hs", "mainFunction")$
|
||||
|
||||
Now that the data is available, let's first show off a simple plan which is
|
||||
already more powerful than `findEntity`. `findMaybeOne` takes the table, and
|
||||
the field to filter on, where as `findEntity` only works using the primary key.
|
||||
|
||||
The plan returned by `findMaybeOne` is like a parameterized query, it doesn't
|
||||
yet contain the actual value to filter for.
|
||||
|
||||
But upon execution, that value has to be provided, and it has to match the type
|
||||
of the Plan. `execute` is in MonadOrville just like `findEntity`, so from here
|
||||
on out, it is familiar territory.
|
||||
|
||||
$sample("using-plans/src/Main.hs", "findByStudentId")$
|
||||
|
||||
Just to demonstrate that you can filter on other fields too, let's search for a
|
||||
specific student using their name. Note that Orville doesn't have any
|
||||
type-level checks for whether there is an index on the field.
|
||||
|
||||
$sample("using-plans/src/Main.hs", "findByStudentName")$
|
||||
|
||||
A plan that takes a single argument and returns a single argument can be passed
|
||||
into `planList` to make it work on multiple values. Note how `execute` now
|
||||
takes a list instead of just a single value.
|
||||
|
||||
$sample("using-plans/src/Main.hs", "findByStudentNameList")$
|
||||
|
||||
Remember how a plan is just a list of SQL statements. This can used in a way
|
||||
similar to how `JOIN` is used in SQL.
|
||||
|
||||
The function `chain` is used to chain these statements together. After the
|
||||
first statement is executed and its result has been sent to the client, Orville
|
||||
allows for manipulating the value with `focusParam` before using it in a step.
|
||||
|
||||
For simplicity, we'll use `findOne` here which is like `findMaybeOne`, but
|
||||
throws exceptions when it fails to find something. In practice, make sure to
|
||||
only use `findOne` when there are constraints or there is otherwise certainty
|
||||
that a row exists.
|
||||
|
||||
The following plan will:
|
||||
1. find a student, given a name. Since this is the first step of the plan, the
|
||||
name is the input of the plan.
|
||||
1. using the ID of the student, find a row in `student_class` that matches on
|
||||
the `studentClassStudentIdField`. Note that Orville doesn't check that the
|
||||
fields are actually comparable, or that it makes sense to compare them!
|
||||
1. using the ID of the `StudentClass`, find a row in `class` that matches on the `class_id`.
|
||||
|
||||
$sample("using-plans/src/Main.hs", "findStudentAndClass")$
|
||||
|
||||
Expanding on the previous example, let's find all the names of the classes they
|
||||
attend, instead of just one. This also lets us avoid unsafe uses of `findOne`.
|
||||
This new version doesn't throw an exception if a student doesn't attend any
|
||||
classes, which would happen when the middle plan would fail. Because of the
|
||||
constraint, the final plan can't fail, so that `findOne` kept.
|
||||
|
||||
Let's extract the `Student -> [Class]` part to its own Plan, which we'll then
|
||||
use for the final expression. Note how the following definition is similar to
|
||||
the last two lines of the plan above.
|
||||
|
||||
$sample("using-plans/src/Main.hs", "studentsToClassesPlan")$
|
||||
|
||||
Remember how plans solve the n+1 problem by scaling easily from a single item
|
||||
to multiple items. This means that while `studentToClassesPlan` executes as two
|
||||
SQL statements, `planList studentToClassesPlan`, a version that works on
|
||||
multiple elements, *also* executes as two SQL statements.
|
||||
|
||||
You can verify this using `explain`, which shows the generated SQL. Note how
|
||||
both of these lists have two SQL statements in them:
|
||||
|
||||
$sample("using-plans/src/Main.hs", "explainStudentsToClassesPlan")$
|
||||
|
||||
With that in mind, let's use `studentToClassesPlan` with `findAll` and
|
||||
`planList` to get a list of classes for each matching student.
|
||||
|
||||
(we sort the inner lists below, such that the result is deterministic.)
|
||||
|
||||
$sample("using-plans/src/Main.hs", "executeStudentsToClassesPlan")$
|
||||
|
||||
You can build an execute this as usual:
|
||||
|
||||
$sample("using-plans/run.sh","buildAndExecute")$
|
||||
|
||||
And you should see the following output:
|
||||
|
||||
$sample("using-plans/expected-output.txt")$
|
@ -0,0 +1,67 @@
|
||||
---
|
||||
title: Using SqlMarshaller
|
||||
navOrder: 1
|
||||
---
|
||||
|
||||
The SQL Marshaller helps with converting from a Haskell record to SQL and back.
|
||||
This document also shows how the SQL marshaller in table definitions, which can
|
||||
then be used to generate Data Definition Language (DDL) queries to create
|
||||
tables in the database.
|
||||
|
||||
You'll need a Haskell project named `using-sql-marshaller` for this tutorial.
|
||||
The setup is identical to the setup in [Getting Started](getting-started.html)
|
||||
aside from the package name, so we'll avoid explaining it again here. Instead
|
||||
we'll get straight on creating our `src/Main.hs` file.
|
||||
|
||||
First, let's import the necessary modules. We use less 'internal' imports here
|
||||
than in the [Getting Started](getting-started.html) guide, because we are using
|
||||
a higher abstraction level.
|
||||
|
||||
$sample("using-sql-marshaller/src/Main.hs","moduleHeader")$
|
||||
|
||||
Next, let's declare some type aliases and the Haskell record itself. We'll
|
||||
enable storing values of this record in the SQL database.
|
||||
|
||||
$sample("using-sql-marshaller/src/Main.hs","dataTypes")$
|
||||
|
||||
To store the record in SQL, we need to define Orville FieldDefinitions for each
|
||||
of the fields of the record. A FieldDefinition maps to a column in SQL. Also
|
||||
note how the type is parameterized on whether it is nullable or not.
|
||||
|
||||
The strings passed in here are the actual SQL column names. FieldDefinitions
|
||||
help avoiding typos, since the Haskell compiler will fail compilation if the
|
||||
name of a FieldDefinition is misspelt.
|
||||
|
||||
$sample("using-sql-marshaller/src/Main.hs","fieldDefinitions")$
|
||||
|
||||
Now that the fields are defined, we can use them, togehter with the record
|
||||
field selector functions, to define the `SqlMarshaller`.
|
||||
|
||||
$sample("using-sql-marshaller/src/Main.hs","sqlMarshaller")$
|
||||
|
||||
We can use the marshaller to define the Orville table definition. This binding
|
||||
represents a table, but note that it doesn't necessarily exist in the SQL
|
||||
database until we start using it.
|
||||
|
||||
$sample("using-sql-marshaller/src/Main.hs","tableDefinition")$
|
||||
|
||||
Now let's write the main function, which does the following:
|
||||
1. auto migrates using the table defintion. It will match the Haskell record at
|
||||
the time of execution of the statement. The details of migration are out of
|
||||
scope for this article.
|
||||
1. deletes the Foo with ID 0, if it exists. Necessary to allow the program to
|
||||
be repeatedly executed, as primary keys can't be duplicated.
|
||||
1. inserts an example Foo object with ID 0.
|
||||
1. reads it back out using its ID 0. If an entity with the given ID doesn't
|
||||
exist, we'd get a Nothing here.
|
||||
1. prints the retrieved entity
|
||||
|
||||
$sample("using-sql-marshaller/src/Main.hs","mainFunction")$
|
||||
|
||||
The program is now complete, let's compile and run!
|
||||
|
||||
$sample("using-sql-marshaller/run.sh","buildAndExecute")$
|
||||
|
||||
Once it builds and runs successfully you should see the following output:
|
||||
|
||||
$sample("using-sql-marshaller/expected-output.txt")$
|
7
orville-docsite/stack-config.yaml
Normal file
7
orville-docsite/stack-config.yaml
Normal file
@ -0,0 +1,7 @@
|
||||
templates:
|
||||
params:
|
||||
author-email: maintainers@flipstone.com
|
||||
author-name: Flipstone Technology Partners, Inc
|
||||
category: sample
|
||||
copyright: ""
|
||||
github-username: flipstone
|
12
orville-docsite/stack.yaml
Normal file
12
orville-docsite/stack.yaml
Normal file
@ -0,0 +1,12 @@
|
||||
resolver:
|
||||
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/21/19.yaml
|
||||
|
||||
system-ghc: true
|
||||
install-ghc: false
|
||||
|
||||
packages:
|
||||
- site-builder
|
||||
|
||||
extra-deps:
|
||||
- hakyll-4.16.2.0
|
||||
- non-empty-text-0.2.0
|
27
orville-docsite/stack.yaml.lock
Normal file
27
orville-docsite/stack.yaml.lock
Normal file
@ -0,0 +1,27 @@
|
||||
# This file was autogenerated by Stack.
|
||||
# You should not edit this file by hand.
|
||||
# For more information, please see the documentation at:
|
||||
# https://docs.haskellstack.org/en/stable/lock_files
|
||||
|
||||
packages:
|
||||
- completed:
|
||||
hackage: hakyll-4.16.2.0@sha256:d98c0e2ed14ca2483ea52406f9c924e8fe383fd06c4c3b80ca5daf312f36c270,9997
|
||||
pantry-tree:
|
||||
sha256: 7e353fb6d50611d437c3c355ee5b50cc7b0e2ec805ee6a2deb89068d0840266f
|
||||
size: 9412
|
||||
original:
|
||||
hackage: hakyll-4.16.2.0
|
||||
- completed:
|
||||
hackage: non-empty-text-0.2.0@sha256:7d1c1f9a11c78c00c656269f940d2fcccd3c2eaf6b619f66bb83a9ea72decc1f,2438
|
||||
pantry-tree:
|
||||
sha256: 303f637d7ca4c4573c8d861282b07b172959c8d3f3aa531b0462f3d08b9669d1
|
||||
size: 400
|
||||
original:
|
||||
hackage: non-empty-text-0.2.0
|
||||
snapshots:
|
||||
- completed:
|
||||
sha256: fb482b8e2d5d061cdda4ba1da2957c012740c893a5ee1c1b99001adae7b1fbe7
|
||||
size: 640046
|
||||
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/21/19.yaml
|
||||
original:
|
||||
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/21/19.yaml
|
Loading…
Reference in New Issue
Block a user