add: purest_json lib
No files matched your search
@@ -0,0 +1,248 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>
|
||||
Basic Usage
|
||||
</title>
|
||||
<style>
|
||||
/* Page tweaks */
|
||||
.preview-page {
|
||||
margin-top: 64px;
|
||||
margin-bottom: 21px;
|
||||
}
|
||||
/* User-content tweaks */
|
||||
.timeline-comment-wrapper > .timeline-comment:after,
|
||||
.timeline-comment-wrapper > .timeline-comment:before {
|
||||
content: none;
|
||||
}
|
||||
/* User-content overrides */
|
||||
.discussion-timeline.wide {
|
||||
width: 920px;
|
||||
}
|
||||
</style>
|
||||
<link href="style.css" rel="stylesheet"/>
|
||||
</head>
|
||||
<body>
|
||||
<div class="page">
|
||||
<div class="preview-page" data-autorefresh-url="" id="preview-page">
|
||||
<main id="js-repo-pjax-container">
|
||||
<div class="clearfix new-discussion-timeline container-xl px-3 px-md-4 px-lg-5">
|
||||
<div class="repository-content">
|
||||
<div class="clearfix">
|
||||
<div class="Layout Layout--flowRow-until-md Layout--sidebarPosition-end Layout--sidebarPosition-flowRow-end">
|
||||
<div class="Layout-main">
|
||||
<div class="Box md Box--responsive" id="readme">
|
||||
<div class="Box-header d-flex border-bottom-0 flex-items-center flex-justify-between color-bg-default rounded-top-2">
|
||||
<div class="d-flex flex-items-center">
|
||||
<h2 class="Box-title">
|
||||
Basic Usage
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="Box-body px-5 pb-5">
|
||||
<article class="markdown-body entry-content container-lg" id="grip-content">
|
||||
<p>
|
||||
The following examples are in the patch
|
||||
<code>
|
||||
examples/purest-json-test.pd
|
||||
</code>
|
||||
.
|
||||
</p>
|
||||
<h2>
|
||||
<a aria-hidden="true" class="anchor" href="#querying-webservices" id="user-content-querying-webservices">
|
||||
<span aria-hidden="true" class="octicon octicon-link">
|
||||
</span>
|
||||
</a>
|
||||
Querying Webservices
|
||||
</h2>
|
||||
<p>
|
||||
You can use
|
||||
<code>
|
||||
[rest]
|
||||
</code>
|
||||
to query webservices. I will explain this querying the history of the Makefile in the repository. You will see the handling of arrays in this example.
|
||||
</p>
|
||||
<p>
|
||||
<a rel="noopener noreferrer nofollow" target="_blank">
|
||||
<img alt="Webservice Example" src="webservice-example.png" style="max-width: 100%;"/>
|
||||
</a>
|
||||
</p>
|
||||
<p>
|
||||
As you can see, the webservice from Github returns an object with only one array. This array is then parsed by an instance of
|
||||
<code>
|
||||
[json-decode]
|
||||
</code>
|
||||
.
|
||||
</p>
|
||||
<p>
|
||||
<code>
|
||||
data: symbol [{"sha":"c505f1bbc72a73ceff447686db4c3c33f3bf3488"\,"node_id":"C_kwDOACK909oAKGM1MDVmMWJiYzcyYTczY2VmZjQ0NzY4NmRiNGMzYzMzZjNiZjM0ODg"\,"commit":{"author":{"name":"Thomas\ Mayer"\,"email":"thomas@residuum.org"\,"date":"2022-05-06T00:31:32Z"}\,"committer":{"name":"Thomas\ Mayer"\,"email":"thomas@residuum.org"\,"date":"2022-05-06T00:31:32Z"}\,"message":"Replace\ [import]\ with\ [declare]\ in\ examples.\\n\\nFixes\ #61"\,"tree":{"sha":"1a3c399407bb25f3976166e13f996b930df3b531"\,"url":"https://api.github.com/repos/residuum/PuRestJson/git/trees/1a3c399407bb25f3976166e13f996b930df3b531"}\,"url":"https://api.github.com/repos/residuum/PuRestJson/git/commits/c505f1bbc72a73ceff447686db4c3c33f3bf3488"\,"comment_count":0\,"verification":{"verified":false\,"reason":"unsigned"\,"signature":null\,"payload":null}}\,"url":"https://api.github.com/repos/residuum/PuRestJson/commits/c505f1bbc72a73ceff447686db4c3c33f3bf3488"\,"html_url":"https://github.com/residuum/PuRestJson/commit/c505f1bbc72a73ceff4476
|
||||
</code>
|
||||
</p>
|
||||
<p>
|
||||
Each array member is then parsed individually by
|
||||
<code>
|
||||
[json-decode]
|
||||
</code>
|
||||
, i.e. each member of the object is then output as list on the middle outlet, followed by a bang on the left outlet.
|
||||
</p>
|
||||
<pre><code>array-decoded: sha c505f1bbc72a73ceff447686db4c3c33f3bf3488
|
||||
array-decoded: node_id C_kwDOACK909oAKGM1MDVmMWJiYzcyYTczY2VmZjQ0NzY4NmRiNGMzYzMzZjNiZjM0ODg
|
||||
array-decoded: commit {\ "author":\ {\ "name":\ "Thomas\ Mayer"\,\ "email":\ "thomas@residuum.org"\,\ "date":\ "2022-05-06T00:31:32Z"\ }\,\ "committer":\ {\ "name":\ "Thomas\ Mayer"\,\ "email":\ "thomas@residuum.org"\,\ "date":\ "2022-05-06T00:31:32Z"\ }\,\ "message":\ "Replace\ [import]\ with\ [declare]\ in\ examples.\\n\\nFixes\ #61"\,\ "tree":\ {\ "sha":\ "1a3c399407bb25f3976166e13f996b930df3b531"\,\ "url":\ "https:\\/\\/api.github.com\\/repos\\/residuum\\/PuRestJson\\/git\\/trees\\/1a3c399407bb25f3976166e13f996b930df3b531"\ }\,\ "url":\ "https:\\/\\/api.github.com\\/repos\\/residuum\\/PuRestJson\\/git\\/commits\\/c505f1bbc72a73ceff447686db4c3c33f3bf3488"\,\ "comment_count":\ 0\,\ "verification":\ {\ "verified":\ false\,\ "reason":\ "unsigned"\,\ "signature":\ null\,\ "payload":\ null\ }\ }
|
||||
array-decoded: url https://api.github.com/repos/residuum/PuRestJson/commits/c505f1bbc72a73ceff447686db4c3c33f3bf3488
|
||||
array-decoded: html_url https://github.com/residuum/PuRestJson/commit/c505f1bbc72a73ceff447686db4c3c33f3bf3488
|
||||
array-decoded: comments_url https://api.github.com/repos/residuum/PuRestJson/commits/c505f1bbc72a73ceff447686db4c3c33f3bf3488/comments
|
||||
array-decoded: author {\ "login":\ "residuum"\,\ "id":\ 136216\,\ "node_id":\ "MDQ6VXNlcjEzNjIxNg=="\,\ "avatar_url":\ "https:\\/\\/avatars.githubusercontent.com\\/u\\/136216?v=4"\,\ "gravatar_id":\ ""\,\ "url":\ "https:\\/\\/api.github.com\\/users\\/residuum"\,\ "html_url":\ "https:\\/\\/github.com\\/residuum"\,\ "followers_url":\ "https:\\/\\/api.github.com\\/users\\/residuum\\/followers"\,\ "following_url":\ "https:\\/\\/api.github.com\\/users\\/residuum\\/following{\\/other_user}"\,\ "gists_url":\ "https:\\/\\/api.github.com\\/users\\/residuum\\/gists{\\/gist_id}"\,\ "starred_url":\ "https:\\/\\/api.github.com\\/users\\/residuum\\/starred{\\/owner}{\\/repo}"\,\ "subscriptions_url":\ "https:\\/\\/api.github.com\\/users\\/residuum\\/subscriptions"\,\ "organizations_url":\ "https:\\/\\/api.github.com\\/users\\/residuum\\/orgs"\,\ "repos_url":\ "https:\\/\\/api.github.com\\/users\\/residuum\\/repos"\,\ "events_url":\ "https:\\/\\/api.github.com\\/users\\/residuum\\/events{\\/privacy}"\,\ "received_events
|
||||
array-decoded: committer {\ "login":\ "residuum"\,\ "id":\ 136216\,\ "node_id":\ "MDQ6VXNlcjEzNjIxNg=="\,\ "avatar_url":\ "https:\\/\\/avatars.githubusercontent.com\\/u\\/136216?v=4"\,\ "gravatar_id":\ ""\,\ "url":\ "https:\\/\\/api.github.com\\/users\\/residuum"\,\ "html_url":\ "https:\\/\\/github.com\\/residuum"\,\ "followers_url":\ "https:\\/\\/api.github.com\\/users\\/residuum\\/followers"\,\ "following_url":\ "https:\\/\\/api.github.com\\/users\\/residuum\\/following{\\/other_user}"\,\ "gists_url":\ "https:\\/\\/api.github.com\\/users\\/residuum\\/gists{\\/gist_id}"\,\ "starred_url":\ "https:\\/\\/api.github.com\\/users\\/residuum\\/starred{\\/owner}{\\/repo}"\,\ "subscriptions_url":\ "https:\\/\\/api.github.com\\/users\\/residuum\\/subscriptions"\,\ "organizations_url":\ "https:\\/\\/api.github.com\\/users\\/residuum\\/orgs"\,\ "repos_url":\ "https:\\/\\/api.github.com\\/users\\/residuum\\/repos"\,\ "events_url":\ "https:\\/\\/api.github.com\\/users\\/residuum\\/events{\\/privacy}"\,\ "received_events
|
||||
array-decoded: parents [\ {\ "sha":\ "b1c602e3d73a9088e951181bea0a500e49223c8e"\,\ "url":\ "https:\\/\\/api.github.com\\/repos\\/residuum\\/PuRestJson\\/commits\\/b1c602e3d73a9088e951181bea0a500e49223c8e"\,\ "html_url":\ "https:\\/\\/github.com\\/residuum\\/PuRestJson\\/commit\\/b1c602e3d73a9088e951181bea0a500e49223c8e"\ }\ ]
|
||||
array-done: bang
|
||||
...
|
||||
</code></pre>
|
||||
<h2>
|
||||
<a aria-hidden="true" class="anchor" href="#using-couchdb" id="user-content-using-couchdb">
|
||||
<span aria-hidden="true" class="octicon octicon-link">
|
||||
</span>
|
||||
</a>
|
||||
Using CouchDB
|
||||
</h2>
|
||||
<p>
|
||||
This example will show the usage of diverse HTTP request methods, GET, PUT, and DELETE. You will also see, how you can add data to PUT requests.
|
||||
</p>
|
||||
<p>
|
||||
<a rel="noopener noreferrer nofollow" target="_blank">
|
||||
<img alt="CouchDB Example" src="couchdb-example.png" style="max-width: 100%;"/>
|
||||
</a>
|
||||
</p>
|
||||
<p>
|
||||
A CouchDB running on the same computer is implied.
|
||||
</p>
|
||||
<p>
|
||||
Here is the output from the main example in examples/purest-json-test.pd with explanations.
|
||||
</p>
|
||||
<p>
|
||||
First, we create the database and get info about the database:
|
||||
</p>
|
||||
<pre><code>couchdb return: list ok 1
|
||||
couchdb return: list db_name test
|
||||
couchdb return: list doc_count 0
|
||||
couchdb return: list doc_del_count 0
|
||||
couchdb return: list update_seq 0
|
||||
couchdb return: list purge_seq 0
|
||||
couchdb return: list compact_running 0
|
||||
couchdb return: list disk_size 79
|
||||
couchdb return: list instance_start_time 1.31733e+15
|
||||
couchdb return: list disk_format_version 5
|
||||
couchdb return: list committed_update_seq 0
|
||||
</code></pre>
|
||||
<p>
|
||||
After that, we build a JSON document and store it in the database:
|
||||
</p>
|
||||
<pre><code>json-encoded data: symbol \{ "value": "440"\\, "duration": "1000"\\, "start": "10"\\, "name": "first note"\\, "var": [ "yabba"\\, "dabba"\\, "doo" ]\\, "on": "TRUE" \}
|
||||
decode json data: list value 440
|
||||
decode json data: list duration 1000
|
||||
decode json data: list start 10
|
||||
decode json data: list name first note
|
||||
decode json data: list var [ "yabba"\\, "dabba"\\, "doo" ]
|
||||
decode json data: list on 1
|
||||
couchdb return: list ok 1
|
||||
couchdb return: list id 1
|
||||
couchdb return: list rev 1-b7a3cb13c20812a4c7845e9a86f92099`
|
||||
</code></pre>
|
||||
<p>
|
||||
Then we query the database for the stored document and use it to drive a simple ramp:
|
||||
</p>
|
||||
<pre><code>id: 1
|
||||
couchdb return: list _id 1
|
||||
couchdb return: list _rev 1-b7a3cb13c20812a4c7845e9a86f92099
|
||||
couchdb return: list value 440
|
||||
couchdb return: list duration 1000
|
||||
couchdb return: list start 10
|
||||
name: first note
|
||||
couchdb return: list name first note
|
||||
couchdb return: list var [ "yabba"\\, "dabba"\\, "doo" ]
|
||||
couchdb return: list on 1
|
||||
</code></pre>
|
||||
<p>
|
||||
In the end we delete the database:
|
||||
</p>
|
||||
<pre><code>couchdb return: list ok 1
|
||||
</code></pre>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
function showCanonicalImages() {
|
||||
var images = document.getElementsByTagName('img');
|
||||
if (!images) {
|
||||
return;
|
||||
}
|
||||
for (var index = 0; index < images.length; index++) {
|
||||
var image = images[index];
|
||||
if (image.getAttribute('data-canonical-src') && image.src !== image.getAttribute('data-canonical-src')) {
|
||||
image.src = image.getAttribute('data-canonical-src');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function scrollToHash() {
|
||||
if (location.hash && !document.querySelector(':target')) {
|
||||
var element = document.getElementById('user-content-' + location.hash.slice(1));
|
||||
if (element) {
|
||||
element.scrollIntoView();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function autorefreshContent(eventSourceUrl) {
|
||||
var initialTitle = document.title;
|
||||
var contentElement = document.getElementById('grip-content');
|
||||
var source = new EventSource(eventSourceUrl);
|
||||
var isRendering = false;
|
||||
|
||||
source.onmessage = function(ev) {
|
||||
var msg = JSON.parse(ev.data);
|
||||
if (msg.updating) {
|
||||
isRendering = true;
|
||||
document.title = '(Rendering) ' + document.title;
|
||||
} else {
|
||||
isRendering = false;
|
||||
document.title = initialTitle;
|
||||
contentElement.innerHTML = msg.content;
|
||||
showCanonicalImages();
|
||||
}
|
||||
}
|
||||
|
||||
source.onerror = function(e) {
|
||||
if (e.readyState === EventSource.CLOSED && isRendering) {
|
||||
isRendering = false;
|
||||
document.title = initialTitle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.onhashchange = function() {
|
||||
scrollToHash();
|
||||
}
|
||||
|
||||
window.onload = function() {
|
||||
scrollToHash();
|
||||
}
|
||||
|
||||
showCanonicalImages();
|
||||
|
||||
var autorefreshUrl = document.getElementById('preview-page').getAttribute('data-autorefresh-url');
|
||||
if (autorefreshUrl) {
|
||||
autorefreshContent(autorefreshUrl);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,496 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>
|
||||
Compilation
|
||||
</title>
|
||||
<style>
|
||||
/* Page tweaks */
|
||||
.preview-page {
|
||||
margin-top: 64px;
|
||||
margin-bottom: 21px;
|
||||
}
|
||||
/* User-content tweaks */
|
||||
.timeline-comment-wrapper > .timeline-comment:after,
|
||||
.timeline-comment-wrapper > .timeline-comment:before {
|
||||
content: none;
|
||||
}
|
||||
/* User-content overrides */
|
||||
.discussion-timeline.wide {
|
||||
width: 920px;
|
||||
}
|
||||
</style>
|
||||
<link href="style.css" rel="stylesheet"/>
|
||||
</head>
|
||||
<body>
|
||||
<div class="page">
|
||||
<div class="preview-page" data-autorefresh-url="" id="preview-page">
|
||||
<main id="js-repo-pjax-container">
|
||||
<div class="clearfix new-discussion-timeline container-xl px-3 px-md-4 px-lg-5">
|
||||
<div class="repository-content">
|
||||
<div class="clearfix">
|
||||
<div class="Layout Layout--flowRow-until-md Layout--sidebarPosition-end Layout--sidebarPosition-flowRow-end">
|
||||
<div class="Layout-main">
|
||||
<div class="Box md Box--responsive" id="readme">
|
||||
<div class="Box-header d-flex border-bottom-0 flex-items-center flex-justify-between color-bg-default rounded-top-2">
|
||||
<div class="d-flex flex-items-center">
|
||||
<h2 class="Box-title">
|
||||
Compilation
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="Box-body px-5 pb-5">
|
||||
<article class="markdown-body entry-content container-lg" id="grip-content">
|
||||
<p>
|
||||
Compilation should be working on Linux, Windows with MinGW and Mac OS X. You will need header files of Puredata, as well as
|
||||
<a href="http://curl.haxx.se/" rel="nofollow">
|
||||
libcurl
|
||||
</a>
|
||||
,
|
||||
<a href="https://github.com/json-c/json-c">
|
||||
json-c
|
||||
</a>
|
||||
, and
|
||||
<a href="http://liboauth.sourceforge.net/" rel="nofollow">
|
||||
liboauth
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
<p>
|
||||
The package build script uses the Makefile template from
|
||||
<a href="https://github.com/pure-data/pd-lib-builder">
|
||||
pd-lib-builder
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
<p>
|
||||
To enable multi-instance support, add
|
||||
<code>
|
||||
multi=true
|
||||
</code>
|
||||
to the make command.
|
||||
</p>
|
||||
<p>
|
||||
If you have gotten the source code from git, you will first have to init the
|
||||
<a href="http://troydhanson.github.io/uthash/" rel="nofollow">
|
||||
uthash
|
||||
</a>
|
||||
and pd-lib-builder submodule by running
|
||||
<code>
|
||||
git submodule update --init
|
||||
</code>
|
||||
.
|
||||
</p>
|
||||
<p>
|
||||
OS X and Linux build scripts do not include dependencies in the resulting pd_linux or pd_darwin files. For making a distributable version of the library, there is a collection of bash scripts for the different platforms.
|
||||
</p>
|
||||
<p>
|
||||
The manual is generated from the
|
||||
<a href="https://github.com/residuum/PuRestJson/wiki">
|
||||
Github wiki
|
||||
</a>
|
||||
. To generate it as well, a copy of the wiki is necessary (get it via
|
||||
<code>
|
||||
git clone https://github.com/residuum/PuRestJson.wiki.git
|
||||
</code>
|
||||
). The python script is called
|
||||
<code>
|
||||
create_manual.py
|
||||
</code>
|
||||
and needs
|
||||
<a href="https://github.com/joeyespo/grip">
|
||||
grip
|
||||
</a>
|
||||
and
|
||||
<a href="https://pypi.python.org/pypi/BeautifulSoup" rel="nofollow">
|
||||
BeautifulSoup
|
||||
</a>
|
||||
. Install those via
|
||||
<code>
|
||||
pip install grip beautifulsoup lxml
|
||||
</code>
|
||||
.
|
||||
</p>
|
||||
<h2>
|
||||
<a aria-hidden="true" class="anchor" href="#circle-ci-builds---general-information" id="user-content-circle-ci-builds---general-information">
|
||||
<span aria-hidden="true" class="octicon octicon-link">
|
||||
</span>
|
||||
</a>
|
||||
Circle CI Builds - General Information
|
||||
</h2>
|
||||
<p>
|
||||
PuREST JSON is configured to build on
|
||||
<a href="https://app.circleci.com/pipelines/github/residuum/PuRestJson" rel="nofollow">
|
||||
Circle CI
|
||||
</a>
|
||||
. The bash scripts in the folder
|
||||
<code>
|
||||
circleci-scripts
|
||||
</code>
|
||||
may serve as secondary documentation for building:
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
<code>
|
||||
*/before_install.sh
|
||||
</code>
|
||||
sets up the build environment
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
*/script.sh
|
||||
</code>
|
||||
is the command to build the binaries
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
*/pack.sh
|
||||
</code>
|
||||
generates the manual and creates deken packages
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
upload.sh
|
||||
</code>
|
||||
uploads the resulting package to my personal ownCloud instance
|
||||
</li>
|
||||
</ul>
|
||||
<h2>
|
||||
<a aria-hidden="true" class="anchor" href="#linux" id="user-content-linux">
|
||||
<span aria-hidden="true" class="octicon octicon-link">
|
||||
</span>
|
||||
</a>
|
||||
Linux
|
||||
</h2>
|
||||
<p>
|
||||
As a guideline, look at the scripts in the
|
||||
<code>
|
||||
circleci-scripts/linux-amd64
|
||||
</code>
|
||||
folder, as the scripts in forder
|
||||
<code>
|
||||
linux-i386
|
||||
</code>
|
||||
are used to build 32 bit i386 packages on a 64 bit amd64 system.
|
||||
</p>
|
||||
<ol>
|
||||
<li>
|
||||
You need build-essentials, header files for puredata, libjson-c, libcurl4, liboauth. If you have Debian or a derivative (e.g. Ubuntu, Linux Mint), you can install the build utils and needed headers with
|
||||
<code>
|
||||
apt-get install build-essential puredata-dev libjson-c-dev libcurl4-openssl-dev liboauth-dev
|
||||
</code>
|
||||
. If you are not root, prefix the command with
|
||||
<code>
|
||||
sudo
|
||||
</code>
|
||||
.
|
||||
</li>
|
||||
<li>
|
||||
Drop the sources in a directory and run
|
||||
<code>
|
||||
make
|
||||
</code>
|
||||
. You will get files with a suffix of pd_linux for each object (json-decode.pd_linux, json-encode.pd_linux, rest.pd_linux, oauth.pd_linux). These are needed to use the library.
|
||||
</li>
|
||||
<li>
|
||||
(optional) To create a distributable version, you have to install patchelf, e.g.
|
||||
<code>
|
||||
apt-get install patchelf
|
||||
</code>
|
||||
. Run
|
||||
<code>
|
||||
bash dependencies/linux.sh *.pdlinux
|
||||
</code>
|
||||
. This will copy dependencies to the current folder and rewrite linking information in the files to point to these dependencies.
|
||||
</li>
|
||||
</ol>
|
||||
<h3>
|
||||
<a aria-hidden="true" class="anchor" href="#building-for-different-architectures" id="user-content-building-for-different-architectures">
|
||||
<span aria-hidden="true" class="octicon octicon-link">
|
||||
</span>
|
||||
</a>
|
||||
Building for Different Architectures
|
||||
</h3>
|
||||
<p>
|
||||
Building for different architectures is possible. I will outline a way to do it on Debian based systems, e.g. Ubuntu.
|
||||
</p>
|
||||
<ol>
|
||||
<li>
|
||||
Install debootstrap, e.g. via
|
||||
<code>
|
||||
apt-get install debootstrap
|
||||
</code>
|
||||
.
|
||||
</li>
|
||||
<li>
|
||||
Set up a directory as the root for your new chroot, e.g. for
|
||||
<code>
|
||||
/tmp/buildroot
|
||||
</code>
|
||||
on Ubuntu Trusty for i386 with
|
||||
</li>
|
||||
</ol>
|
||||
<pre><code> sudo debootstrap \
|
||||
--variant=buildd \
|
||||
--include=build-essential \
|
||||
--arch=i386 \
|
||||
trusty /tmp/buildroot http://archive.ubuntu.com/ubuntu/
|
||||
</code></pre>
|
||||
<ol start="3">
|
||||
<li>
|
||||
Install the basic build system in that directory, and install the necessary dependencies for that architecture e.g. with
|
||||
</li>
|
||||
</ol>
|
||||
<pre><code> echo deb http://archive.ubuntu.com/ubuntu/ \
|
||||
$DIST restricted universe multiverse \
|
||||
| sudo tee -a /tmp/buildroot/etc/apt/sources.list
|
||||
sudo chroot /tmp/buildroot bash -c "apt-get update"
|
||||
sudo chroot /tmp/buildroot bash -c "apt-get install -qq -y build-essential \
|
||||
puredata-dev libjson-c-dev libcurl4-nss-dev liboauth-dev"
|
||||
</code></pre>
|
||||
<ol start="4">
|
||||
<li>
|
||||
Copy the files to a subfolder in the chroot folder, e.g.
|
||||
<code>
|
||||
/tmp/buildroot/PuRestJson
|
||||
</code>
|
||||
</li>
|
||||
<li>
|
||||
Run
|
||||
<code>
|
||||
make
|
||||
</code>
|
||||
via chroot:
|
||||
</li>
|
||||
</ol>
|
||||
<pre><code> sudo chroot /tmp/buildroot bash -c "cd PuRestJson && make"
|
||||
</code></pre>
|
||||
<ol start="6">
|
||||
<li>
|
||||
(optional) To create a distributable version, you have to install patchelf, e.g.
|
||||
<code>
|
||||
sudo chroot /tmp/buildroot bash -c "apt-get install patchelf"
|
||||
</code>
|
||||
. Run
|
||||
<code>
|
||||
sudo chroot ${CHROOTDIR} bash -c "cd PuRestJson && bash ./dependencies/linux.sh *.pd_linux"
|
||||
</code>
|
||||
. This will copy dependencies to the /tmp/buildroot and rewrite linking information in the files to point to these dependencies.
|
||||
</li>
|
||||
</ol>
|
||||
<h2>
|
||||
<a aria-hidden="true" class="anchor" href="#windows-with-mingw" id="user-content-windows-with-mingw">
|
||||
<span aria-hidden="true" class="octicon octicon-link">
|
||||
</span>
|
||||
</a>
|
||||
Windows with MinGW
|
||||
</h2>
|
||||
<p>
|
||||
This is possible, but I have given up on it, and only cross-compile the library with MXE.
|
||||
</p>
|
||||
<h2>
|
||||
<a aria-hidden="true" class="anchor" href="#compilation-on-mac-os-x" id="user-content-compilation-on-mac-os-x">
|
||||
<span aria-hidden="true" class="octicon octicon-link">
|
||||
</span>
|
||||
</a>
|
||||
Compilation on Mac OS X
|
||||
</h2>
|
||||
<p>
|
||||
As a guideline, look at the scripts in the
|
||||
<code>
|
||||
circleci-scripts/osx
|
||||
</code>
|
||||
folder.
|
||||
</p>
|
||||
<ol>
|
||||
<li>
|
||||
Install
|
||||
<a href="https://developer.apple.com/xcode/" rel="nofollow">
|
||||
XCode
|
||||
</a>
|
||||
, and
|
||||
<a href="http://stackoverflow.com/a/9353468/124983" rel="nofollow">
|
||||
install the command line tools
|
||||
</a>
|
||||
to get
|
||||
<code>
|
||||
gcc
|
||||
</code>
|
||||
and
|
||||
<code>
|
||||
otool
|
||||
</code>
|
||||
.
|
||||
</li>
|
||||
<li>
|
||||
Install
|
||||
<a href="http://brew.sh/" rel="nofollow">
|
||||
Homebrew
|
||||
</a>
|
||||
to later install the dependencies.
|
||||
</li>
|
||||
<li>
|
||||
Install dependencies via Homebrew:
|
||||
</li>
|
||||
</ol>
|
||||
<pre><code> brew update
|
||||
brew install json-c curl liboauth --universal
|
||||
</code></pre>
|
||||
<ol start="4">
|
||||
<li>
|
||||
Find the path to Pd.
|
||||
</li>
|
||||
<li>
|
||||
Drop the sources in a directory and run
|
||||
<code>
|
||||
make pdincludepath=/path/to/pd/Contents/Resources/src
|
||||
</code>
|
||||
</li>
|
||||
<li>
|
||||
(optional) You are now able to use the library on a machine, if the dependencies are installed via Homebrew. To move the necessary dylibs to the current directory and change the links of the pd_darwin files, run
|
||||
<code>
|
||||
./dependencies/osx.sh
|
||||
</code>
|
||||
. This makes the whole folder distributable.
|
||||
</li>
|
||||
</ol>
|
||||
<h2>
|
||||
<a aria-hidden="true" class="anchor" href="#cross-compilation-for-windows-on-unix-like-systems-linux--bsd--mac-os-x-with-mxe" id="user-content-cross-compilation-for-windows-on-unix-like-systems-linux--bsd--mac-os-x-with-mxe">
|
||||
<span aria-hidden="true" class="octicon octicon-link">
|
||||
</span>
|
||||
</a>
|
||||
Cross-compilation for Windows on UNIX-like systems (Linux / *BSD / Mac OS X) with MXE
|
||||
</h2>
|
||||
<p>
|
||||
As a guideline, look at the scripts in the
|
||||
<code>
|
||||
circleci-scripts/windows32
|
||||
</code>
|
||||
folder.
|
||||
</p>
|
||||
<ol>
|
||||
<li>
|
||||
Get the development version of MXE from
|
||||
<a href="https://github.com/mxe/mxe">
|
||||
their Github repository
|
||||
</a>
|
||||
and setup MXE, so that the cross compilation tools are in your PATH environmental variable.
|
||||
</li>
|
||||
<li>
|
||||
Build the required libraries by running
|
||||
<code>
|
||||
make curl json-c liboauth pthreads
|
||||
</code>
|
||||
.
|
||||
</li>
|
||||
<li>
|
||||
Download the Windows version of Pd and unzip it in a folder. You should have the following structure of files:
|
||||
</li>
|
||||
</ol>
|
||||
<pre><code> pd/src/(source files including m_pd.h)
|
||||
pd/bin/pd.dll
|
||||
</code></pre>
|
||||
<ol start="4">
|
||||
<li>
|
||||
Run
|
||||
<code>
|
||||
make
|
||||
</code>
|
||||
with some environmental variables to tell the compiler to use the cross compiler, and where to find the Pd source and pd.dll. Here is an example from my setup:
|
||||
</li>
|
||||
</ol>
|
||||
<pre><code> #!/bin/bash
|
||||
MXE_DIR=/usr/lib/mxe
|
||||
MXE_GCC=${MXE_DIR}/usr/bin/i686-w64-mingw32.static-gcc
|
||||
PD_DIR=/tmp/pd
|
||||
|
||||
make \
|
||||
system=Windows \
|
||||
machine=i386 \
|
||||
uname=MINGW \
|
||||
pdincludepath=${PD_DIR}/src \
|
||||
pdbinpath=${PD_DIR}/bin \
|
||||
CC=${MXE_GCC} \
|
||||
arch.c.flags='-march=pentium4 -msse -msse2 -mfpmath=sse -I \
|
||||
"/usr/lib/mxe/usr/i686-w64-mingw32.static/include"' \
|
||||
ldflags='-L "/usr/lib/mxe/usr/i686-w64-mingw32.static/lib"'
|
||||
</code></pre>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
function showCanonicalImages() {
|
||||
var images = document.getElementsByTagName('img');
|
||||
if (!images) {
|
||||
return;
|
||||
}
|
||||
for (var index = 0; index < images.length; index++) {
|
||||
var image = images[index];
|
||||
if (image.getAttribute('data-canonical-src') && image.src !== image.getAttribute('data-canonical-src')) {
|
||||
image.src = image.getAttribute('data-canonical-src');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function scrollToHash() {
|
||||
if (location.hash && !document.querySelector(':target')) {
|
||||
var element = document.getElementById('user-content-' + location.hash.slice(1));
|
||||
if (element) {
|
||||
element.scrollIntoView();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function autorefreshContent(eventSourceUrl) {
|
||||
var initialTitle = document.title;
|
||||
var contentElement = document.getElementById('grip-content');
|
||||
var source = new EventSource(eventSourceUrl);
|
||||
var isRendering = false;
|
||||
|
||||
source.onmessage = function(ev) {
|
||||
var msg = JSON.parse(ev.data);
|
||||
if (msg.updating) {
|
||||
isRendering = true;
|
||||
document.title = '(Rendering) ' + document.title;
|
||||
} else {
|
||||
isRendering = false;
|
||||
document.title = initialTitle;
|
||||
contentElement.innerHTML = msg.content;
|
||||
showCanonicalImages();
|
||||
}
|
||||
}
|
||||
|
||||
source.onerror = function(e) {
|
||||
if (e.readyState === EventSource.CLOSED && isRendering) {
|
||||
isRendering = false;
|
||||
document.title = initialTitle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.onhashchange = function() {
|
||||
scrollToHash();
|
||||
}
|
||||
|
||||
window.onload = function() {
|
||||
scrollToHash();
|
||||
}
|
||||
|
||||
showCanonicalImages();
|
||||
|
||||
var autorefreshUrl = document.getElementById('preview-page').getAttribute('data-autorefresh-url');
|
||||
if (autorefreshUrl) {
|
||||
autorefreshContent(autorefreshUrl);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,844 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>
|
||||
Contributing and Code Documentation
|
||||
</title>
|
||||
<style>
|
||||
/* Page tweaks */
|
||||
.preview-page {
|
||||
margin-top: 64px;
|
||||
margin-bottom: 21px;
|
||||
}
|
||||
/* User-content tweaks */
|
||||
.timeline-comment-wrapper > .timeline-comment:after,
|
||||
.timeline-comment-wrapper > .timeline-comment:before {
|
||||
content: none;
|
||||
}
|
||||
/* User-content overrides */
|
||||
.discussion-timeline.wide {
|
||||
width: 920px;
|
||||
}
|
||||
</style>
|
||||
<link href="style.css" rel="stylesheet"/>
|
||||
</head>
|
||||
<body>
|
||||
<div class="page">
|
||||
<div class="preview-page" data-autorefresh-url="" id="preview-page">
|
||||
<main id="js-repo-pjax-container">
|
||||
<div class="clearfix new-discussion-timeline container-xl px-3 px-md-4 px-lg-5">
|
||||
<div class="repository-content">
|
||||
<div class="clearfix">
|
||||
<div class="Layout Layout--flowRow-until-md Layout--sidebarPosition-end Layout--sidebarPosition-flowRow-end">
|
||||
<div class="Layout-main">
|
||||
<div class="Box md Box--responsive" id="readme">
|
||||
<div class="Box-header d-flex border-bottom-0 flex-items-center flex-justify-between color-bg-default rounded-top-2">
|
||||
<div class="d-flex flex-items-center">
|
||||
<h2 class="Box-title">
|
||||
Contributing and Code Documentation
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="Box-body px-5 pb-5">
|
||||
<article class="markdown-body entry-content container-lg" id="grip-content">
|
||||
<p>
|
||||
If you want to contribute to the development of the software, every help is much appreciated.
|
||||
</p>
|
||||
<h2>
|
||||
<a aria-hidden="true" class="anchor" href="#bugs-and-wishes" id="user-content-bugs-and-wishes">
|
||||
<span aria-hidden="true" class="octicon octicon-link">
|
||||
</span>
|
||||
</a>
|
||||
Bugs And Wishes
|
||||
</h2>
|
||||
<p>
|
||||
If you found any bugs in the software or would like a feature implemented, please open a new issue on
|
||||
<a href="https://github.com/residuum/PuRestJson/issues">
|
||||
the bug tracker
|
||||
</a>
|
||||
or send an email to
|
||||
<a href="mailto:purest-json-bugs@ix.residuum.org">
|
||||
purest-json-bugs@ix.residuum.org
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
<h2>
|
||||
<a aria-hidden="true" class="anchor" href="#development" id="user-content-development">
|
||||
<span aria-hidden="true" class="octicon octicon-link">
|
||||
</span>
|
||||
</a>
|
||||
Development
|
||||
</h2>
|
||||
<p>
|
||||
Feel free to fork the project and submit pull requests, if you want to implement some features or fix some bugs. If possible, please open a new issue on
|
||||
<a href="https://github.com/residuum/PuRestJson/issues">
|
||||
the bug tracker
|
||||
</a>
|
||||
, so that noone else tries to fix or implement the same issue.
|
||||
</p>
|
||||
<p>
|
||||
If you need some help with writing externals for Pd, read the
|
||||
<a href="http://iem.at/pd/externals-HOWTO/" rel="nofollow">
|
||||
excellent how-to on that subject
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
<h3>
|
||||
<a aria-hidden="true" class="anchor" href="#code-structure" id="user-content-code-structure">
|
||||
<span aria-hidden="true" class="octicon octicon-link">
|
||||
</span>
|
||||
</a>
|
||||
Code structure
|
||||
</h3>
|
||||
<p>
|
||||
The whole project in written in C99. C code is in subfolder
|
||||
<code>
|
||||
src/
|
||||
</code>
|
||||
. Every object and the library itself has a .c and a corresponding .h file.
|
||||
<code>
|
||||
purest_json.c
|
||||
</code>
|
||||
is only used to setup the library.
|
||||
<code>
|
||||
#define
|
||||
</code>
|
||||
s are done only in the .h files.
|
||||
<code>
|
||||
purest_json.h
|
||||
</code>
|
||||
is
|
||||
<code>
|
||||
#include
|
||||
</code>
|
||||
d in all objects.
|
||||
</p>
|
||||
<p>
|
||||
Every object contains functions with a structure of
|
||||
<code>
|
||||
<OBJECT>_<FUNCTION>
|
||||
</code>
|
||||
with the exception of setup functions. Only functions that Pd needs to call are exported, and only exported functions are declared in the .h file, all other functions are static.
|
||||
</p>
|
||||
<p>
|
||||
Shared functions used by more than one object are in directory
|
||||
<code>
|
||||
src/inc
|
||||
</code>
|
||||
. The functions are prefixed with the name of the file, e.g.
|
||||
<code>
|
||||
ctw.c
|
||||
</code>
|
||||
includes functions named
|
||||
<code>
|
||||
ctw_<FUNCTION>
|
||||
</code>
|
||||
.
|
||||
</p>
|
||||
<h3>
|
||||
<a aria-hidden="true" class="anchor" href="#order-of-functions-and-declarations-in-c-files" id="user-content-order-of-functions-and-declarations-in-c-files">
|
||||
<span aria-hidden="true" class="octicon octicon-link">
|
||||
</span>
|
||||
</a>
|
||||
Order of Functions And Declarations in .c Files
|
||||
</h3>
|
||||
<ol>
|
||||
<li>
|
||||
<code>
|
||||
#include
|
||||
</code>
|
||||
s
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
t_class
|
||||
</code>
|
||||
declaration
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
struct
|
||||
</code>
|
||||
declarations
|
||||
</li>
|
||||
<li>
|
||||
prototypes for static functions
|
||||
</li>
|
||||
<li>
|
||||
implementation of static functions
|
||||
</li>
|
||||
<li>
|
||||
setup functions
|
||||
</li>
|
||||
<li>
|
||||
functions reacting to messages
|
||||
</li>
|
||||
<li>
|
||||
constructor function
|
||||
</li>
|
||||
<li>
|
||||
destructor function
|
||||
</li>
|
||||
</ol>
|
||||
<h3>
|
||||
<a aria-hidden="true" class="anchor" href="#names-of-setup-functions" id="user-content-names-of-setup-functions">
|
||||
<span aria-hidden="true" class="octicon octicon-link">
|
||||
</span>
|
||||
</a>
|
||||
Names of Setup Functions
|
||||
</h3>
|
||||
<p>
|
||||
When Pd tries to load an object, it is looking for the file with the correct platform specific suffix (.pd_linux, .pd_darwin, .dll, .pd_freebsd) and the same name, e.g. to create
|
||||
<code>
|
||||
[json-encode]
|
||||
</code>
|
||||
, Pd on Windows looks for a file
|
||||
<code>
|
||||
json-encode.dll
|
||||
</code>
|
||||
.
|
||||
</p>
|
||||
<p>
|
||||
In this library file, Pd looks for a setup function according, depending on the name of the object:
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
If the name contains a special character, that cannot be used in a C symbol (
|
||||
<code>
|
||||
-
|
||||
</code>
|
||||
,
|
||||
<code>
|
||||
~
|
||||
</code>
|
||||
,
|
||||
<code>
|
||||
.
|
||||
</code>
|
||||
, etc.), then this special character is converted to a hex representation using
|
||||
<code>
|
||||
sprintf(symname+i, "0x%02x", c);
|
||||
</code>
|
||||
where
|
||||
<code>
|
||||
c
|
||||
</code>
|
||||
is the special character. This resulting name is prefixed by
|
||||
<code>
|
||||
setup_
|
||||
</code>
|
||||
. So the function for creating
|
||||
<code>
|
||||
[json-encode]
|
||||
</code>
|
||||
must be called
|
||||
<code>
|
||||
setup_json0x2dencode
|
||||
</code>
|
||||
.
|
||||
</li>
|
||||
<li>
|
||||
If the name can be converted to a C symbol, then the setup function must be the object name suffixed with
|
||||
<code>
|
||||
_setup
|
||||
</code>
|
||||
. So the function for creating
|
||||
<code>
|
||||
[urlparams]
|
||||
</code>
|
||||
must be called
|
||||
<code>
|
||||
urlparams_setup
|
||||
</code>
|
||||
.
|
||||
</li>
|
||||
</ul>
|
||||
<h3>
|
||||
<a aria-hidden="true" class="anchor" href="#names-of-other-exported-functions" id="user-content-names-of-other-exported-functions">
|
||||
<span aria-hidden="true" class="octicon octicon-link">
|
||||
</span>
|
||||
</a>
|
||||
Names of Other Exported Functions
|
||||
</h3>
|
||||
<p>
|
||||
If an object has a hyphen (
|
||||
<code>
|
||||
-
|
||||
</code>
|
||||
) in its name, then this is replaced by an underscore (
|
||||
<code>
|
||||
_
|
||||
</code>
|
||||
)
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
Creation functions:
|
||||
<code>
|
||||
<OBJECT>_new
|
||||
</code>
|
||||
, e.g.
|
||||
<code>
|
||||
rest_new
|
||||
</code>
|
||||
.
|
||||
</li>
|
||||
<li>
|
||||
Destruction functions:
|
||||
<code>
|
||||
<OBJECT>_free
|
||||
</code>
|
||||
, e.g.
|
||||
<code>
|
||||
rest_free
|
||||
</code>
|
||||
. Not every object needs to clean up on destruction, do not create this function then.
|
||||
</li>
|
||||
<li>
|
||||
Generic message functions: If the function should be called based on the type of content, then the corresponding function is
|
||||
<code>
|
||||
<OBJECT>_<TYPE>
|
||||
</code>
|
||||
, e.g.
|
||||
<code>
|
||||
json_decode_symbol
|
||||
</code>
|
||||
,
|
||||
<code>
|
||||
json_encode_bang
|
||||
</code>
|
||||
.
|
||||
</li>
|
||||
<li>
|
||||
HTTP request messages
|
||||
<code>
|
||||
[GET(
|
||||
</code>
|
||||
,
|
||||
<code>
|
||||
[POST(
|
||||
</code>
|
||||
,
|
||||
<code>
|
||||
[PUT(
|
||||
</code>
|
||||
,
|
||||
<code>
|
||||
[DELETE(
|
||||
</code>
|
||||
,
|
||||
<code>
|
||||
[HEAD(
|
||||
</code>
|
||||
,
|
||||
<code>
|
||||
[PATCH(
|
||||
</code>
|
||||
,
|
||||
<code>
|
||||
[TRACE(
|
||||
</code>
|
||||
, and
|
||||
<code>
|
||||
[OPTIONS(
|
||||
</code>
|
||||
:
|
||||
<code>
|
||||
<OBJECT>_command
|
||||
</code>
|
||||
, e.g.
|
||||
<code>
|
||||
[GET(
|
||||
</code>
|
||||
on
|
||||
<code>
|
||||
[rest]
|
||||
</code>
|
||||
calls
|
||||
<code>
|
||||
rest_command
|
||||
</code>
|
||||
.
|
||||
</li>
|
||||
<li>
|
||||
Message functions: Every
|
||||
<code>
|
||||
[MESSAGE(
|
||||
</code>
|
||||
for the object has a corresponding function
|
||||
<code>
|
||||
<OBJECT>_<MESSAGE>
|
||||
</code>
|
||||
, e.g.
|
||||
<code>
|
||||
[cancel(
|
||||
</code>
|
||||
on
|
||||
<code>
|
||||
[oauth]
|
||||
</code>
|
||||
calls the function
|
||||
<code>
|
||||
oauth_cancel
|
||||
</code>
|
||||
.
|
||||
</li>
|
||||
</ul>
|
||||
<h3>
|
||||
<a aria-hidden="true" class="anchor" href="#names-of-static-functions" id="user-content-names-of-static-functions">
|
||||
<span aria-hidden="true" class="octicon octicon-link">
|
||||
</span>
|
||||
</a>
|
||||
Names of Static Functions
|
||||
</h3>
|
||||
<p>
|
||||
Static functions are prefixed with the name of the corresponding file without the .c extension, and an underscore. Function prototypes are declared in .c files before implemtations. Sometimes, the object names are abbreviated:
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
<code>
|
||||
json-decode.c
|
||||
</code>
|
||||
=>
|
||||
<code>
|
||||
jdec_
|
||||
</code>
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
json-encode.c
|
||||
</code>
|
||||
=>
|
||||
<code>
|
||||
jenc_
|
||||
</code>
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
oauth.c
|
||||
</code>
|
||||
=>
|
||||
<code>
|
||||
oauth_
|
||||
</code>
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
rest.c
|
||||
</code>
|
||||
=>
|
||||
<code>
|
||||
rest_
|
||||
</code>
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
urlparams.c
|
||||
</code>
|
||||
=>
|
||||
<code>
|
||||
urlp_
|
||||
</code>
|
||||
</li>
|
||||
</ul>
|
||||
<h3>
|
||||
<a aria-hidden="true" class="anchor" href="#content-of-included-files" id="user-content-content-of-included-files">
|
||||
<span aria-hidden="true" class="octicon octicon-link">
|
||||
</span>
|
||||
</a>
|
||||
Content of Included Files
|
||||
</h3>
|
||||
<p>
|
||||
All .c files that do not have a matching .h file contain only static functions and are
|
||||
<code>
|
||||
#include
|
||||
</code>
|
||||
d in files defining the objects. These source files are in subfolder
|
||||
<code>
|
||||
src/inc/
|
||||
</code>
|
||||
. They have the following properties:
|
||||
</p>
|
||||
<h4>
|
||||
<a aria-hidden="true" class="anchor" href="#ctwc" id="user-content-ctwc">
|
||||
<span aria-hidden="true" class="octicon octicon-link">
|
||||
</span>
|
||||
</a>
|
||||
ctw.c
|
||||
</h4>
|
||||
<p>
|
||||
ctw is short for
|
||||
<strong>
|
||||
c
|
||||
</strong>
|
||||
url
|
||||
<strong>
|
||||
t
|
||||
</strong>
|
||||
hread
|
||||
<strong>
|
||||
w
|
||||
</strong>
|
||||
rapper. As the name suggests, it contain functions for using threading for making requests with libcurl.
|
||||
</p>
|
||||
<p>
|
||||
This file is used in
|
||||
<code>
|
||||
[oauth]
|
||||
</code>
|
||||
and
|
||||
<code>
|
||||
[rest]
|
||||
</code>
|
||||
. In both objects, a
|
||||
<code>
|
||||
struct _ctw
|
||||
</code>
|
||||
is the first member, so that casting is possible.
|
||||
</p>
|
||||
<p>
|
||||
All functions are prefixed with
|
||||
<code>
|
||||
ctw_
|
||||
</code>
|
||||
.
|
||||
</p>
|
||||
<h4>
|
||||
<a aria-hidden="true" class="anchor" href="#kvpc" id="user-content-kvpc">
|
||||
<span aria-hidden="true" class="octicon octicon-link">
|
||||
</span>
|
||||
</a>
|
||||
kvp.c
|
||||
</h4>
|
||||
<p>
|
||||
kvp is short for
|
||||
<strong>
|
||||
k
|
||||
</strong>
|
||||
ey
|
||||
<strong>
|
||||
v
|
||||
</strong>
|
||||
alue
|
||||
<strong>
|
||||
p
|
||||
</strong>
|
||||
air. Three structs are defined in this file,
|
||||
<code>
|
||||
struct _v
|
||||
</code>
|
||||
,
|
||||
<code>
|
||||
struct _kvp
|
||||
</code>
|
||||
, and
|
||||
<code>
|
||||
struct _kvp_store
|
||||
</code>
|
||||
.
|
||||
<code>
|
||||
struct _kvp
|
||||
</code>
|
||||
is a key value pair, while
|
||||
<code>
|
||||
struct _kvp_store
|
||||
</code>
|
||||
includes a hash table of key value pair.
|
||||
<code>
|
||||
struct _v
|
||||
</code>
|
||||
is a value, containing a union of
|
||||
<code>
|
||||
t_float
|
||||
</code>
|
||||
,
|
||||
<code>
|
||||
char *
|
||||
</code>
|
||||
and
|
||||
<code>
|
||||
int
|
||||
</code>
|
||||
to distinguish between floats, strings and integers for JSON.
|
||||
</p>
|
||||
<p>
|
||||
This file is used in
|
||||
<code>
|
||||
[json-encode]
|
||||
</code>
|
||||
and
|
||||
<code>
|
||||
[urlparams]
|
||||
</code>
|
||||
. In both objects, a
|
||||
<code>
|
||||
struct _kvp_store
|
||||
</code>
|
||||
is the first member, so that casting is possible.
|
||||
</p>
|
||||
<p>
|
||||
All functions are prefixed with
|
||||
<code>
|
||||
kvp_
|
||||
</code>
|
||||
.
|
||||
</p>
|
||||
<h4>
|
||||
<a aria-hidden="true" class="anchor" href="#stringc" id="user-content-stringc">
|
||||
<span aria-hidden="true" class="octicon octicon-link">
|
||||
</span>
|
||||
</a>
|
||||
string.c
|
||||
</h4>
|
||||
<p>
|
||||
Utility functions for creating and freeing memory for
|
||||
<code>
|
||||
char *
|
||||
</code>
|
||||
and the corresponding
|
||||
<code>
|
||||
size_t
|
||||
</code>
|
||||
.
|
||||
</p>
|
||||
<p>
|
||||
This file is used in
|
||||
<code>
|
||||
[json-decode]
|
||||
</code>
|
||||
,
|
||||
<code>
|
||||
[json-encode]
|
||||
</code>
|
||||
,
|
||||
<code>
|
||||
[oauth]
|
||||
</code>
|
||||
,
|
||||
<code>
|
||||
[rest]
|
||||
</code>
|
||||
, and
|
||||
<code>
|
||||
[urlparams]
|
||||
</code>
|
||||
.
|
||||
</p>
|
||||
<p>
|
||||
All functions are prefixed with
|
||||
<code>
|
||||
string_
|
||||
</code>
|
||||
.
|
||||
</p>
|
||||
<h4>
|
||||
<a aria-hidden="true" class="anchor" href="#strlistc" id="user-content-strlistc">
|
||||
<span aria-hidden="true" class="octicon octicon-link">
|
||||
</span>
|
||||
</a>
|
||||
strlist.c
|
||||
</h4>
|
||||
<p>
|
||||
Utility functions for creating and freeing memory for a linked list of
|
||||
<code>
|
||||
char *
|
||||
</code>
|
||||
and the corresponding
|
||||
<code>
|
||||
size_t
|
||||
</code>
|
||||
.
|
||||
</p>
|
||||
<p>
|
||||
This file is used in
|
||||
<code>
|
||||
[oauth]
|
||||
</code>
|
||||
and
|
||||
<code>
|
||||
[rest]
|
||||
</code>
|
||||
.
|
||||
</p>
|
||||
<p>
|
||||
All functions are prefixed with
|
||||
<code>
|
||||
strlist_
|
||||
</code>
|
||||
.
|
||||
</p>
|
||||
<h3>
|
||||
<a aria-hidden="true" class="anchor" href="#reasons-for-using-c99" id="user-content-reasons-for-using-c99">
|
||||
<span aria-hidden="true" class="octicon octicon-link">
|
||||
</span>
|
||||
</a>
|
||||
Reasons for using C99
|
||||
</h3>
|
||||
<ul>
|
||||
<li>
|
||||
Variadic macros
|
||||
</li>
|
||||
<li>
|
||||
In older implementations
|
||||
<code>
|
||||
json_object_object_foreach
|
||||
</code>
|
||||
requires C99:
|
||||
<a href="https://github.com/json-c/json-c/pull/73">
|
||||
https://github.com/json-c/json-c/pull/73
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
C99 forces the memory layout for using casting to structs:
|
||||
<a href="http://www.open-std.org/jtc1/sc22/WG14/www/docs/n1256.pdf" rel="nofollow">
|
||||
§ 6.2.5, 20 of the standard
|
||||
</a>
|
||||
:
|
||||
</li>
|
||||
</ul>
|
||||
<blockquote>
|
||||
<p>
|
||||
A structure type describes a sequentially allocated nonempty set of member objects
|
||||
(and, in certain circumstances, an incomplete array), each of which has an optionally
|
||||
specified name and possibly distinct type.
|
||||
</p>
|
||||
</blockquote>
|
||||
<h3>
|
||||
<a aria-hidden="true" class="anchor" href="#special-cases" id="user-content-special-cases">
|
||||
<span aria-hidden="true" class="octicon octicon-link">
|
||||
</span>
|
||||
</a>
|
||||
Special Cases
|
||||
</h3>
|
||||
<ul>
|
||||
<li>
|
||||
<code>
|
||||
static char *string_remove_backslashes(char *source_string, size_t *memsize)
|
||||
</code>
|
||||
in
|
||||
<code>
|
||||
string.c
|
||||
</code>
|
||||
is surrounded by a
|
||||
<code>
|
||||
#ifndef NO_BACKSLASHES
|
||||
</code>
|
||||
. This function is not needed for
|
||||
<code>
|
||||
[urlparams]
|
||||
</code>
|
||||
, and therefore this is used to not get warnings on compilation.
|
||||
</li>
|
||||
<li>
|
||||
There are only a few platform specific
|
||||
<code>
|
||||
#define
|
||||
</code>
|
||||
s, those are all declared in
|
||||
<code>
|
||||
purest_json.h
|
||||
</code>
|
||||
.
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
[json-encode]
|
||||
</code>
|
||||
can contain arrays, while
|
||||
<code>
|
||||
[urlparams]
|
||||
</code>
|
||||
cannot, so a
|
||||
<code>
|
||||
#define
|
||||
</code>
|
||||
is used in
|
||||
<code>
|
||||
kvp.c
|
||||
</code>
|
||||
to remove array functions and struct members for
|
||||
<code>
|
||||
[urlparams]
|
||||
</code>
|
||||
.
|
||||
</li>
|
||||
</ul>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
function showCanonicalImages() {
|
||||
var images = document.getElementsByTagName('img');
|
||||
if (!images) {
|
||||
return;
|
||||
}
|
||||
for (var index = 0; index < images.length; index++) {
|
||||
var image = images[index];
|
||||
if (image.getAttribute('data-canonical-src') && image.src !== image.getAttribute('data-canonical-src')) {
|
||||
image.src = image.getAttribute('data-canonical-src');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function scrollToHash() {
|
||||
if (location.hash && !document.querySelector(':target')) {
|
||||
var element = document.getElementById('user-content-' + location.hash.slice(1));
|
||||
if (element) {
|
||||
element.scrollIntoView();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function autorefreshContent(eventSourceUrl) {
|
||||
var initialTitle = document.title;
|
||||
var contentElement = document.getElementById('grip-content');
|
||||
var source = new EventSource(eventSourceUrl);
|
||||
var isRendering = false;
|
||||
|
||||
source.onmessage = function(ev) {
|
||||
var msg = JSON.parse(ev.data);
|
||||
if (msg.updating) {
|
||||
isRendering = true;
|
||||
document.title = '(Rendering) ' + document.title;
|
||||
} else {
|
||||
isRendering = false;
|
||||
document.title = initialTitle;
|
||||
contentElement.innerHTML = msg.content;
|
||||
showCanonicalImages();
|
||||
}
|
||||
}
|
||||
|
||||
source.onerror = function(e) {
|
||||
if (e.readyState === EventSource.CLOSED && isRendering) {
|
||||
isRendering = false;
|
||||
document.title = initialTitle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.onhashchange = function() {
|
||||
scrollToHash();
|
||||
}
|
||||
|
||||
window.onload = function() {
|
||||
scrollToHash();
|
||||
}
|
||||
|
||||
showCanonicalImages();
|
||||
|
||||
var autorefreshUrl = document.getElementById('preview-page').getAttribute('data-autorefresh-url');
|
||||
if (autorefreshUrl) {
|
||||
autorefreshContent(autorefreshUrl);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,315 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>
|
||||
Using JSON Data as Pitch Generator
|
||||
</title>
|
||||
<style>
|
||||
/* Page tweaks */
|
||||
.preview-page {
|
||||
margin-top: 64px;
|
||||
margin-bottom: 21px;
|
||||
}
|
||||
/* User-content tweaks */
|
||||
.timeline-comment-wrapper > .timeline-comment:after,
|
||||
.timeline-comment-wrapper > .timeline-comment:before {
|
||||
content: none;
|
||||
}
|
||||
/* User-content overrides */
|
||||
.discussion-timeline.wide {
|
||||
width: 920px;
|
||||
}
|
||||
</style>
|
||||
<link href="style.css" rel="stylesheet"/>
|
||||
</head>
|
||||
<body>
|
||||
<div class="page">
|
||||
<div class="preview-page" data-autorefresh-url="" id="preview-page">
|
||||
<main id="js-repo-pjax-container">
|
||||
<div class="clearfix new-discussion-timeline container-xl px-3 px-md-4 px-lg-5">
|
||||
<div class="repository-content">
|
||||
<div class="clearfix">
|
||||
<div class="Layout Layout--flowRow-until-md Layout--sidebarPosition-end Layout--sidebarPosition-flowRow-end">
|
||||
<div class="Layout-main">
|
||||
<div class="Box md Box--responsive" id="readme">
|
||||
<div class="Box-header d-flex border-bottom-0 flex-items-center flex-justify-between color-bg-default rounded-top-2">
|
||||
<div class="d-flex flex-items-center">
|
||||
<h2 class="Box-title">
|
||||
Using JSON Data as Pitch Generator
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="Box-body px-5 pb-5">
|
||||
<article class="markdown-body entry-content container-lg" id="grip-content">
|
||||
<p>
|
||||
This tutorial will show you, how you can get data from a simple RESTful webservice and generate sound from it.
|
||||
</p>
|
||||
<p>
|
||||
In this tutorial, I will use the following notation:
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
<code>
|
||||
[my object]
|
||||
</code>
|
||||
: an object with the name "my-object".
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
[my own message(
|
||||
</code>
|
||||
: a message with the content "my own message".
|
||||
</li>
|
||||
</ul>
|
||||
<h2>
|
||||
<a aria-hidden="true" class="anchor" href="#getting-the-data" id="user-content-getting-the-data">
|
||||
<span aria-hidden="true" class="octicon octicon-link">
|
||||
</span>
|
||||
</a>
|
||||
Getting the Data
|
||||
</h2>
|
||||
<p>
|
||||
This example gets the conversion rates between US Dollar und Euro from the
|
||||
<a href="https://www.ecb.europa.eu/stats/policy_and_exchange_rates/euro_reference_exchange_rates/html/usd.xml" rel="nofollow">
|
||||
European central bank
|
||||
</a>
|
||||
, which on the server is converted from XML to JSON.
|
||||
</p>
|
||||
<p>
|
||||
<a rel="noopener noreferrer nofollow" target="_blank">
|
||||
<img alt="The patch" src="currency-full.png" style="max-width: 100%;"/>
|
||||
</a>
|
||||
</p>
|
||||
<p>
|
||||
The data from the script on the website looks something like this:
|
||||
</p>
|
||||
<pre><code>[
|
||||
{"day":"1999-01-04","rate":1.1789},
|
||||
{"day":"1999-01-05","rate":1.179},
|
||||
{"day":"1999-01-06","rate":1.1743},
|
||||
{"day":"1999-01-07","rate":1.1632}
|
||||
]
|
||||
</code></pre>
|
||||
<p>
|
||||
When the data is received, the returned value is fed into
|
||||
<code>
|
||||
[json-decode]
|
||||
</code>
|
||||
. As the symbol atom from
|
||||
<code>
|
||||
[rest]
|
||||
</code>
|
||||
will be a JSON array, we will get messages for each JSON object on the middle outlet followed by a bang on the left outlet.
|
||||
</p>
|
||||
<p>
|
||||
A sequence of messages for an object looks like this:
|
||||
</p>
|
||||
<pre><code>list day 1999-01-04
|
||||
list rate 1.1789
|
||||
</code></pre>
|
||||
<h2>
|
||||
<a aria-hidden="true" class="anchor" href="#storing-the-data" id="user-content-storing-the-data">
|
||||
<span aria-hidden="true" class="octicon octicon-link">
|
||||
</span>
|
||||
</a>
|
||||
Storing the Data
|
||||
</h2>
|
||||
<p>
|
||||
This section discusses the operations in the subpatch
|
||||
<code>
|
||||
[pd store-data]
|
||||
</code>
|
||||
.
|
||||
</p>
|
||||
<p>
|
||||
<a rel="noopener noreferrer nofollow" target="_blank">
|
||||
<img alt="pd process-data" src="currency-data-processing.png" style="max-width: 100%;"/>
|
||||
</a>
|
||||
</p>
|
||||
<p>
|
||||
In this subpatch, the incoming lists from
|
||||
<code>
|
||||
[json-decode]
|
||||
</code>
|
||||
are taken and packed into one list for each JSON object in the array.
|
||||
</p>
|
||||
<p>
|
||||
First, we remove the
|
||||
<code>
|
||||
list
|
||||
</code>
|
||||
prefix from each message, then pack those messages, and use the bang message from the left outlet of
|
||||
<code>
|
||||
[json-decode]
|
||||
</code>
|
||||
that is emitted after each decoded array member to trigger the output of
|
||||
<code>
|
||||
[pack]
|
||||
</code>
|
||||
. The transformed message for each JSON object then looks like:
|
||||
</p>
|
||||
<pre><code>0 1999-01-04 1.1789
|
||||
</code></pre>
|
||||
<p>
|
||||
Notes:
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
The date will not come out as a symbol. Conversion is done with a
|
||||
<code>
|
||||
[symbol]
|
||||
</code>
|
||||
object.
|
||||
</li>
|
||||
<li>
|
||||
Each output from
|
||||
<code>
|
||||
[pack f s f]
|
||||
</code>
|
||||
starts with a
|
||||
<code>
|
||||
0
|
||||
</code>
|
||||
, as we only use the bang on the first inlet of
|
||||
<code>
|
||||
[pack]
|
||||
</code>
|
||||
to trigger the output.
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
We then use
|
||||
<code>
|
||||
[text]
|
||||
</code>
|
||||
to store the value. We use a very high number for the position
|
||||
<code>
|
||||
1e+15
|
||||
</code>
|
||||
, to ensure that new values are appended without a counter before.
|
||||
</p>
|
||||
<h2>
|
||||
<a aria-hidden="true" class="anchor" href="#sequencing-the-values" id="user-content-sequencing-the-values">
|
||||
<span aria-hidden="true" class="octicon octicon-link">
|
||||
</span>
|
||||
</a>
|
||||
Sequencing the values
|
||||
</h2>
|
||||
<p>
|
||||
This section discusses the operations in the subpatch
|
||||
<code>
|
||||
[pd stepper]
|
||||
</code>
|
||||
.
|
||||
</p>
|
||||
<p>
|
||||
<a rel="noopener noreferrer nofollow" target="_blank">
|
||||
<img alt="pd stepper" src="currency-stepper.png" style="max-width: 100%;"/>
|
||||
</a>
|
||||
</p>
|
||||
<p>
|
||||
This subpatch starts a
|
||||
<code>
|
||||
[metro]
|
||||
</code>
|
||||
after all data is stored. This is basically just the sequencer example from the
|
||||
<code>
|
||||
[text]
|
||||
</code>
|
||||
help file.
|
||||
</p>
|
||||
<h2>
|
||||
<a aria-hidden="true" class="anchor" href="#generating-sound" id="user-content-generating-sound">
|
||||
<span aria-hidden="true" class="octicon octicon-link">
|
||||
</span>
|
||||
</a>
|
||||
Generating sound
|
||||
</h2>
|
||||
<p>
|
||||
Sound generation is the bottom part of the main patch.
|
||||
</p>
|
||||
<p>
|
||||
The exchange rates between US Dollar and Euro is varying very narrowly around 1. The value is multiplied by a fixed number and with an offset interpreted as MIDI notes, such that a change of 0.03 in the exchange rate will result in a change of the tone of 1 semitone.
|
||||
</p>
|
||||
<p>
|
||||
Different data might benefit from other scaling methods, linear, logarithmic, exponential, or poynomial.
|
||||
</p>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
function showCanonicalImages() {
|
||||
var images = document.getElementsByTagName('img');
|
||||
if (!images) {
|
||||
return;
|
||||
}
|
||||
for (var index = 0; index < images.length; index++) {
|
||||
var image = images[index];
|
||||
if (image.getAttribute('data-canonical-src') && image.src !== image.getAttribute('data-canonical-src')) {
|
||||
image.src = image.getAttribute('data-canonical-src');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function scrollToHash() {
|
||||
if (location.hash && !document.querySelector(':target')) {
|
||||
var element = document.getElementById('user-content-' + location.hash.slice(1));
|
||||
if (element) {
|
||||
element.scrollIntoView();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function autorefreshContent(eventSourceUrl) {
|
||||
var initialTitle = document.title;
|
||||
var contentElement = document.getElementById('grip-content');
|
||||
var source = new EventSource(eventSourceUrl);
|
||||
var isRendering = false;
|
||||
|
||||
source.onmessage = function(ev) {
|
||||
var msg = JSON.parse(ev.data);
|
||||
if (msg.updating) {
|
||||
isRendering = true;
|
||||
document.title = '(Rendering) ' + document.title;
|
||||
} else {
|
||||
isRendering = false;
|
||||
document.title = initialTitle;
|
||||
contentElement.innerHTML = msg.content;
|
||||
showCanonicalImages();
|
||||
}
|
||||
}
|
||||
|
||||
source.onerror = function(e) {
|
||||
if (e.readyState === EventSource.CLOSED && isRendering) {
|
||||
isRendering = false;
|
||||
document.title = initialTitle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.onhashchange = function() {
|
||||
scrollToHash();
|
||||
}
|
||||
|
||||
window.onload = function() {
|
||||
scrollToHash();
|
||||
}
|
||||
|
||||
showCanonicalImages();
|
||||
|
||||
var autorefreshUrl = document.getElementById('preview-page').getAttribute('data-autorefresh-url');
|
||||
if (autorefreshUrl) {
|
||||
autorefreshContent(autorefreshUrl);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,737 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>
|
||||
Using Pd as Twitter Client
|
||||
</title>
|
||||
<style>
|
||||
/* Page tweaks */
|
||||
.preview-page {
|
||||
margin-top: 64px;
|
||||
margin-bottom: 21px;
|
||||
}
|
||||
/* User-content tweaks */
|
||||
.timeline-comment-wrapper > .timeline-comment:after,
|
||||
.timeline-comment-wrapper > .timeline-comment:before {
|
||||
content: none;
|
||||
}
|
||||
/* User-content overrides */
|
||||
.discussion-timeline.wide {
|
||||
width: 920px;
|
||||
}
|
||||
</style>
|
||||
<link href="style.css" rel="stylesheet"/>
|
||||
</head>
|
||||
<body>
|
||||
<div class="page">
|
||||
<div class="preview-page" data-autorefresh-url="" id="preview-page">
|
||||
<main id="js-repo-pjax-container">
|
||||
<div class="clearfix new-discussion-timeline container-xl px-3 px-md-4 px-lg-5">
|
||||
<div class="repository-content">
|
||||
<div class="clearfix">
|
||||
<div class="Layout Layout--flowRow-until-md Layout--sidebarPosition-end Layout--sidebarPosition-flowRow-end">
|
||||
<div class="Layout-main">
|
||||
<div class="Box md Box--responsive" id="readme">
|
||||
<div class="Box-header d-flex border-bottom-0 flex-items-center flex-justify-between color-bg-default rounded-top-2">
|
||||
<div class="d-flex flex-items-center">
|
||||
<h2 class="Box-title">
|
||||
Using Pd as Twitter Client
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="Box-body px-5 pb-5">
|
||||
<article class="markdown-body entry-content container-lg" id="grip-content">
|
||||
<p>
|
||||
This tutorial will show you, how you can create your own Twitter client for Pd. You will learn how to use PuREST JSON for OAuth authentication, and you will be able to apply the methodology to create clients for other webservices.
|
||||
</p>
|
||||
<p>
|
||||
In the examples folder, you can find the patch
|
||||
<code>
|
||||
twitter-client.pd
|
||||
</code>
|
||||
. You will only need to fill in your credentials, that you will get with the patch itself.
|
||||
</p>
|
||||
<p>
|
||||
In this tutorial, I will use the following notation:
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
<code>
|
||||
[my object]
|
||||
</code>
|
||||
: an object with the name "my-object".
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
[my own message(
|
||||
</code>
|
||||
: a message with the content "my own message".
|
||||
</li>
|
||||
</ul>
|
||||
<h2>
|
||||
<a aria-hidden="true" class="anchor" href="#basic-explanation-of-oauth" id="user-content-basic-explanation-of-oauth">
|
||||
<span aria-hidden="true" class="octicon octicon-link">
|
||||
</span>
|
||||
</a>
|
||||
Basic Explanation of OAuth
|
||||
</h2>
|
||||
<p>
|
||||
With OAuth, a user can give an application (here: Puredata) access to a webservice (here: Twitter) without giving the application your login to the service.
|
||||
</p>
|
||||
<p>
|
||||
To do so, the application needs its own token and secret (client credentials, consumer key and consumer secret), as well as the user's (token credentials, access token and access token secret). The token credentials are different to the login name and password for the service.
|
||||
</p>
|
||||
<p>
|
||||
As the token credentials are different from login name and password, the webservice and / or user can restrict the application, so that it may only perform certain actions, but not all (e.g. only read access, not showing sensitive informations).
|
||||
</p>
|
||||
<p>
|
||||
When the application has both tokens and secrets, it needs to sign each request to the service with these.
|
||||
Most of these is done under the hood of
|
||||
<code>
|
||||
[oauth]
|
||||
</code>
|
||||
. What you will learn in this tutorial, is how to get your application token and secret, the user's token and secret, and how to read developer documentation. The latter skill is important for creating your own clients for other services.
|
||||
</p>
|
||||
<p>
|
||||
<strong>
|
||||
Caveat!
|
||||
</strong>
|
||||
This document makes some assumptions, which were true at the time of the last edit (May 2015): My client credentials are still valid, despite me giving it away in the example patch. Twitter have not changed their API, or the API is still supported.
|
||||
</p>
|
||||
<p>
|
||||
This document goes through the file
|
||||
<code>
|
||||
twitter-client.pd
|
||||
</code>
|
||||
in the examples step by step.
|
||||
</p>
|
||||
<h2>
|
||||
<a aria-hidden="true" class="anchor" href="#setting-up-your-application" id="user-content-setting-up-your-application">
|
||||
<span aria-hidden="true" class="octicon octicon-link">
|
||||
</span>
|
||||
</a>
|
||||
Setting Up Your Application
|
||||
</h2>
|
||||
<p>
|
||||
All information about the Twitter API can be found at
|
||||
<a href="https://dev.twitter.com/" rel="nofollow">
|
||||
https://dev.twitter.com/
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
<p>
|
||||
For this tutorial, we will use PuREST JSON as a registered application. This application has both read and write access. If you want to reliably use Pd as a Twitter client, and / or want to restrict your client to read access only, you will need to register your client.
|
||||
</p>
|
||||
<h3>
|
||||
<a aria-hidden="true" class="anchor" href="#registering-your-client" id="user-content-registering-your-client">
|
||||
<span aria-hidden="true" class="octicon octicon-link">
|
||||
</span>
|
||||
</a>
|
||||
Registering Your Client
|
||||
</h3>
|
||||
<p>
|
||||
Login at
|
||||
<a href="https://dev.twitter.com/" rel="nofollow">
|
||||
https://dev.twitter.com/
|
||||
</a>
|
||||
with your Twitter login. Click on the
|
||||
<em>
|
||||
create your application
|
||||
</em>
|
||||
and create your application. Leave the field
|
||||
<em>
|
||||
Callback URL
|
||||
</em>
|
||||
empty.
|
||||
</p>
|
||||
<p>
|
||||
After registering your application, you get your consumer key and secret. These are needed for any communication with twitter.
|
||||
</p>
|
||||
<p>
|
||||
On that page, you can also create your own access token and access token secret.
|
||||
</p>
|
||||
<h3>
|
||||
<a aria-hidden="true" class="anchor" href="#getting-access-token-and-access-token-secret" id="user-content-getting-access-token-and-access-token-secret">
|
||||
<span aria-hidden="true" class="octicon octicon-link">
|
||||
</span>
|
||||
</a>
|
||||
Getting Access Token and Access Token Secret
|
||||
</h3>
|
||||
<p>
|
||||
This section discusses the operations in the subpatch
|
||||
<code>
|
||||
[pd authorize]
|
||||
</code>
|
||||
.
|
||||
</p>
|
||||
<p>
|
||||
<a rel="noopener noreferrer nofollow" target="_blank">
|
||||
<img alt="pd authorize" src="twitter-authorize.png" style="max-width: 100%;"/>
|
||||
</a>
|
||||
</p>
|
||||
<p>
|
||||
You may skip this step, if you created your own access token and access token secret at the Twitter website.
|
||||
</p>
|
||||
<p>
|
||||
Initialize
|
||||
<code>
|
||||
[oauth]
|
||||
</code>
|
||||
with the base URL, consumer key, and consumer secret (the instance of
|
||||
<code>
|
||||
[oauth]
|
||||
</code>
|
||||
already uses the PuREST JSON credentials).
|
||||
</p>
|
||||
<p>
|
||||
Use
|
||||
<code>
|
||||
[POST /oauth/request_token(
|
||||
</code>
|
||||
method at
|
||||
<code>
|
||||
[oauth]
|
||||
</code>
|
||||
.
|
||||
<code>
|
||||
[oauth]
|
||||
</code>
|
||||
will issue a request to
|
||||
<a href="https://api.twitter.com/oauth/request_token" rel="nofollow">
|
||||
https://api.twitter.com/oauth/request_token
|
||||
</a>
|
||||
and sign the request with the consumer credentials.
|
||||
</p>
|
||||
<p>
|
||||
In your Pd console, you will hopefully get something similar to this:
|
||||
</p>
|
||||
<pre><code>data1: symbol oauth_token=(YOUR_TEMP_TOKEN)&amp;oauth_token_secret=(YOUR_TEMP_TOKEN_SECRET)&amp;oauth_callback_confirmed=true
|
||||
status1: list oauth bang
|
||||
</code></pre>
|
||||
<p>
|
||||
Now we have an temporary access token / secret pair. With this pair, we need to tell our user (YOU!) to go to Twitter and get a PIN to generate the real access token / secret pair.
|
||||
</p>
|
||||
<p>
|
||||
Open a web browser and go to
|
||||
<a href="https://api.twitter.com/oauth/authorize?oauth_token=(YOUR_TEMP_TOKEN)&amp;oauth_token_secret=(YOUR_TEMP_TOKEN_SECRET)&amp;oauth_callback_confirmed=true" rel="nofollow">
|
||||
https://api.twitter.com/oauth/authorize?oauth_token=(YOUR_TEMP_TOKEN)&amp;oauth_token_secret=(YOUR_TEMP_TOKEN_SECRET)&amp;oauth_callback_confirmed=true
|
||||
</a>
|
||||
. This will get you to a website, where you can authorize PuREST JSON for using the Twitter API. After authorization, you will see YOUR_PIN in large friendly letters printed on a web page. We will need these for the next step.
|
||||
</p>
|
||||
<p>
|
||||
Now you get a PIN. This PIN will enable you to create your access token and access token key.
|
||||
</p>
|
||||
<p>
|
||||
First, we have to tell
|
||||
<code>
|
||||
[oauth]
|
||||
</code>
|
||||
to use your new oauth access token and credentials. To do so, we will need to use the temporary access token / secret pair for signing the next OAuth request. We can do this either by editing the creation values of
|
||||
<code>
|
||||
[oauth]
|
||||
</code>
|
||||
or by sending a
|
||||
<code>
|
||||
[init BASE_URL CONSUMER_KEY CONSUMER_SECRET YOUR_TEMP_TOKEN YOUR_TEMP_TOKEN_SECRET(
|
||||
</code>
|
||||
to it. Use your return values from before.
|
||||
Now send the PIN to Twitter to finally generate the read access token / secret pair. Use
|
||||
<code>
|
||||
[POST access_token oauth_identifier=YOUR_PIN(
|
||||
</code>
|
||||
for
|
||||
<code>
|
||||
[oauth]
|
||||
</code>
|
||||
to do so.
|
||||
</p>
|
||||
<p>
|
||||
Hopefully, you get a message similar to this in your Pd console:
|
||||
</p>
|
||||
<pre><code>data1: symbol oauth_token=(YOUR_ACCESS_TOKEN)&amp;oauth_token_secret=(YOUR_ACCESS_TOKEN_SECRET)&amp;user_id=(YOUR_USER_ID)&amp;screen_name=(YOUR_TWITTER_SCREEN_NAME)
|
||||
</code></pre>
|
||||
<h3>
|
||||
<a aria-hidden="true" class="anchor" href="#test-your-twitter-client" id="user-content-test-your-twitter-client">
|
||||
<span aria-hidden="true" class="octicon octicon-link">
|
||||
</span>
|
||||
</a>
|
||||
Test Your Twitter Client
|
||||
</h3>
|
||||
<p>
|
||||
This section describes the subpatch
|
||||
<code>
|
||||
[pd simple-operations]
|
||||
</code>
|
||||
.
|
||||
</p>
|
||||
<p>
|
||||
<a rel="noopener noreferrer nofollow" target="_blank">
|
||||
<img alt="pd simple-operations" src="twitter-simple.png" style="max-width: 100%;"/>
|
||||
</a>
|
||||
</p>
|
||||
<p>
|
||||
You will need to tell
|
||||
<code>
|
||||
[oauth]
|
||||
</code>
|
||||
to use a) consumer key / secret pair and b) access token / secret pair. To do so, either set these (with the base url for all requests) in the creation arguments, or send these to it with the
|
||||
<code>
|
||||
[init(
|
||||
</code>
|
||||
message. Do this with the right instance of
|
||||
<code>
|
||||
[oauth]
|
||||
</code>
|
||||
.
|
||||
</p>
|
||||
<p>
|
||||
Now, get your home timeline by issuing
|
||||
<code>
|
||||
[GET /1.1/statuses/home_timeline.json(
|
||||
</code>
|
||||
to
|
||||
<code>
|
||||
[oauth]
|
||||
</code>
|
||||
. Does your timeline appear in the Pd console? Great!
|
||||
</p>
|
||||
<p>
|
||||
Now you can even send tweets from inside Pd. To do so, POST your status update. Two things must be considered: The message must be correctly URL encoded using
|
||||
<code>
|
||||
[urlparams]
|
||||
</code>
|
||||
. This example only sets the text, for more parameters,
|
||||
<a href="https://dev.twitter.com/rest/reference/post/statuses/update" rel="nofollow">
|
||||
see the documentation
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
<h2>
|
||||
<a aria-hidden="true" class="anchor" href="#lets-search-and-sonify-tweets" id="user-content-lets-search-and-sonify-tweets">
|
||||
<span aria-hidden="true" class="octicon octicon-link">
|
||||
</span>
|
||||
</a>
|
||||
Let's Search and Sonify Tweets
|
||||
</h2>
|
||||
<p>
|
||||
This section discusses the subpatch
|
||||
<code>
|
||||
[pd sonify-it]
|
||||
</code>
|
||||
and includes operations on JSON data.
|
||||
</p>
|
||||
<p>
|
||||
<a rel="noopener noreferrer nofollow" target="_blank">
|
||||
<img alt="pd sonify-it" src="twitter-sonify.png" style="max-width: 100%;"/>
|
||||
</a>
|
||||
</p>
|
||||
<p>
|
||||
As this example is quite complicated, this is seperated in diverse subpatches.
|
||||
</p>
|
||||
<h3>
|
||||
<a aria-hidden="true" class="anchor" href="#search-for-data" id="user-content-search-for-data">
|
||||
<span aria-hidden="true" class="octicon octicon-link">
|
||||
</span>
|
||||
</a>
|
||||
Search for Data
|
||||
</h3>
|
||||
<p>
|
||||
The
|
||||
<a href="https://dev.twitter.com/rest/public/search" rel="nofollow">
|
||||
search API is extensively documented
|
||||
</a>
|
||||
. In our case we search for tweets containing the hashtag
|
||||
<code>
|
||||
#Puredata
|
||||
</code>
|
||||
. This leads to an output similar to the following on the end of
|
||||
<code>
|
||||
[oauth]
|
||||
</code>
|
||||
.
|
||||
</p>
|
||||
<pre><code>\{"statuses":[\{"metadata":\{"iso_language_code":"en"\\,"result_type":"recent"\}\\,"created_at":"Thu Apr 09 21:33:41 +0000 1970"\\,"id":111\\,"id_str":"111"\\,"text":"#PureData
|
||||
</code></pre>
|
||||
<p>
|
||||
The next logical step is disecting the message and finding suitable data for sonification. This example settles on the tweet message, user name and display name.
|
||||
</p>
|
||||
<h3>
|
||||
<a aria-hidden="true" class="anchor" href="#transform-the-json-string-to-pd-symbols" id="user-content-transform-the-json-string-to-pd-symbols">
|
||||
<span aria-hidden="true" class="octicon octicon-link">
|
||||
</span>
|
||||
</a>
|
||||
Transform the JSON string to Pd symbols
|
||||
</h3>
|
||||
<p>
|
||||
This section discusses the subpatch
|
||||
<code>
|
||||
[pd data-transform]
|
||||
</code>
|
||||
.
|
||||
</p>
|
||||
<p>
|
||||
<a rel="noopener noreferrer nofollow" target="_blank">
|
||||
<img alt="Transforming the JSON data" src="twitter-json.png" style="max-width: 100%;"/>
|
||||
</a>
|
||||
</p>
|
||||
<p>
|
||||
We first feed the output of
|
||||
<code>
|
||||
[oauth]
|
||||
</code>
|
||||
to the first instance of
|
||||
<code>
|
||||
[json-decode]
|
||||
</code>
|
||||
. This will generate a sequence of lists on the middle output. After removing the list keyword using
|
||||
<code>
|
||||
[list trim]
|
||||
</code>
|
||||
, we can select only the symbol following the
|
||||
<code>
|
||||
statuses
|
||||
</code>
|
||||
beginning. This contains the status data.
|
||||
</p>
|
||||
<p>
|
||||
Using the second
|
||||
<code>
|
||||
[json-decode]
|
||||
</code>
|
||||
we decode the statuses. This is a JSON array, so we get a sequence of several objects that are decoded. We strip off the list keyword, and route our status members
|
||||
<code>
|
||||
text
|
||||
</code>
|
||||
and
|
||||
<code>
|
||||
user
|
||||
</code>
|
||||
.
|
||||
<code>
|
||||
user
|
||||
</code>
|
||||
is a JSON object itself, so we need another instance of
|
||||
<code>
|
||||
[json-decode]
|
||||
</code>
|
||||
to get
|
||||
<code>
|
||||
name
|
||||
</code>
|
||||
and
|
||||
<code>
|
||||
screen_name
|
||||
</code>
|
||||
from the object. As the output may contain spaces, instances of
|
||||
<code>
|
||||
[list2symbol]
|
||||
</code>
|
||||
or
|
||||
<code>
|
||||
[l2s]
|
||||
</code>
|
||||
from zexy are used to generate correct symbols.
|
||||
</p>
|
||||
<p>
|
||||
We pack these objects while not using the first inlet of
|
||||
<code>
|
||||
[pack]
|
||||
</code>
|
||||
for data. This gets connected to the left outlet of the second
|
||||
<code>
|
||||
[json-decode]
|
||||
</code>
|
||||
, so that after each array of the
|
||||
<code>
|
||||
statuses
|
||||
</code>
|
||||
array something like the following list is sent to the right outlet of the subpatch:
|
||||
</p>
|
||||
<pre><code>0 #Puredata\ is\ Turing\ complete!\ Yay Joe\ Doe joedoe1
|
||||
</code></pre>
|
||||
<p>
|
||||
Beware:
|
||||
<code>
|
||||
\
|
||||
</code>
|
||||
is the escape character in Pd, so that the space following it is not a separator between two atoms but part of the symbol. This translates to: The user
|
||||
<code>
|
||||
Joe Doe
|
||||
</code>
|
||||
with the Twitter handle
|
||||
<code>
|
||||
joedoe1
|
||||
</code>
|
||||
has tweeted
|
||||
<code>
|
||||
#Puredata is Turing complete! Yay
|
||||
</code>
|
||||
.
|
||||
</p>
|
||||
<p>
|
||||
After everything is analysed, the first
|
||||
<code>
|
||||
[json-decode]
|
||||
</code>
|
||||
is done and outputs a bang on its left outlet, which is connected to the left outlet of the subpatch.
|
||||
</p>
|
||||
<p>
|
||||
Directly after the subpatch,
|
||||
<code>
|
||||
[fifop]
|
||||
</code>
|
||||
from zexy is used to store all lists of atoms. Using the bang from the left outlet of the subpatch, we can retrieve the first list and process it, beginning with
|
||||
<code>
|
||||
[unpack]
|
||||
</code>
|
||||
ing the list. Notice, that the first outlet is discarded, as this contains always
|
||||
<code>
|
||||
0
|
||||
</code>
|
||||
.
|
||||
</p>
|
||||
<h3>
|
||||
<a aria-hidden="true" class="anchor" href="#sonifying-the-data" id="user-content-sonifying-the-data">
|
||||
<span aria-hidden="true" class="octicon octicon-link">
|
||||
</span>
|
||||
</a>
|
||||
Sonifying the Data
|
||||
</h3>
|
||||
<p>
|
||||
This is just a rough sketch of what else is going on in the patch.
|
||||
</p>
|
||||
<p>
|
||||
The tweet text is subsequently split into a sequence of the ASCII codes of the characters using
|
||||
<code>
|
||||
[spell]
|
||||
</code>
|
||||
from cyclone. Another FIFO buffer is used to store this sequence and is triggered by a metro. As the first ASCII codes 0x00 - 0x32 are non-printable characters, we do not generate a sound for those. The rest of the ASCII codes are interpreted as MIDI notes.
|
||||
</p>
|
||||
<p>
|
||||
For the name, we find the length of the symbol by using
|
||||
<code>
|
||||
[symbol2list]
|
||||
</code>
|
||||
or
|
||||
<code>
|
||||
[s2l]
|
||||
</code>
|
||||
from zexy with an empty separator. Counting the length of the resulting list with
|
||||
<code>
|
||||
[list length]
|
||||
</code>
|
||||
, we get the symbol length. The length of the name is used to determine the volume of the tweet sonification.
|
||||
</p>
|
||||
<p>
|
||||
Lastly we take the length of the username and use it to modify the frequency of an LFO.
|
||||
</p>
|
||||
<h2>
|
||||
<a aria-hidden="true" class="anchor" href="#whats-up-with-the-base-url-stuff-why-is-it-used-as-the-first-parameter-to-everything" id="user-content-whats-up-with-the-base-url-stuff-why-is-it-used-as-the-first-parameter-to-everything">
|
||||
<span aria-hidden="true" class="octicon octicon-link">
|
||||
</span>
|
||||
</a>
|
||||
What's up with the Base URL stuff? Why Is It Used as the First Parameter to Everything?
|
||||
</h2>
|
||||
<p>
|
||||
Consumer key and secret as well as access token and secret are sensitive data. Anybody getting the consumer key / secret pair in their hands can pose as that application to the webservice. Likewise, anybody getting their hand on the access token / secret pair
|
||||
<strong>
|
||||
can use the webservice with the provided accessibilty
|
||||
</strong>
|
||||
.
|
||||
That means, if you send your OAuth request to the wrong webservice provider, you will get a 404 error back - in the best of all cases.
|
||||
</p>
|
||||
<p>
|
||||
In the worst case, the wrong webservice provider is malicious and guesses, that you wanted to use Twitter and use your credentials to
|
||||
<strong>
|
||||
pose as your application with your access credentials
|
||||
</strong>
|
||||
.
|
||||
</p>
|
||||
<p>
|
||||
Now, if you set base URL to
|
||||
<a href="http://example.com" rel="nofollow">
|
||||
http://example.com
|
||||
</a>
|
||||
and issue a
|
||||
<code>
|
||||
[GET http://example.org/test(
|
||||
</code>
|
||||
,
|
||||
<code>
|
||||
[oauth]
|
||||
</code>
|
||||
will perform a GET request to
|
||||
<a href="http://example.comhttp://example.org/test" rel="nofollow">
|
||||
http://example.comhttp://example.org/test
|
||||
</a>
|
||||
, something not existant.
|
||||
</p>
|
||||
<h2>
|
||||
<a aria-hidden="true" class="anchor" href="#what-else-can-i-do-now-or-how-to-read-the-api-documentation" id="user-content-what-else-can-i-do-now-or-how-to-read-the-api-documentation">
|
||||
<span aria-hidden="true" class="octicon octicon-link">
|
||||
</span>
|
||||
</a>
|
||||
What Else Can I Do Now? Or: How to Read the API Documentation
|
||||
</h2>
|
||||
<p>
|
||||
This section assumes, that you are not a professional programmer or do not have much experience in reading API reference documents.
|
||||
</p>
|
||||
<p>
|
||||
Now, that we have a functional Twitter client, what are the possibilities? And how can I get information on how to use OAuth with other webservices?
|
||||
</p>
|
||||
<p>
|
||||
Well, first go to
|
||||
<a href="https://dev.twitter.com/docs" rel="nofollow">
|
||||
the documentation from Twitter
|
||||
</a>
|
||||
themselves. All you need to know is there.
|
||||
</p>
|
||||
<p>
|
||||
OK, just joking. As this library is called PuREST JSON and
|
||||
<code>
|
||||
[oauth]
|
||||
</code>
|
||||
is used for OAuth signed RESTful requests, we need to read the
|
||||
<a href="https://dev.twitter.com/docs/api/1.1" rel="nofollow">
|
||||
REST API documention
|
||||
</a>
|
||||
"only".
|
||||
</p>
|
||||
<h3>
|
||||
<a aria-hidden="true" class="anchor" href="#how-do-i-know-the-right-steps-to-get-my-login-information" id="user-content-how-do-i-know-the-right-steps-to-get-my-login-information">
|
||||
<span aria-hidden="true" class="octicon octicon-link">
|
||||
</span>
|
||||
</a>
|
||||
How Do I Know the Right Steps to Get My Login Information?
|
||||
</h3>
|
||||
<p>
|
||||
Take the documentation for any method, e.g.
|
||||
<a href="https://dev.twitter.com/docs/api/1.1/get/statuses/mentions_timeline" rel="nofollow">
|
||||
GET statuses/mentions_timeline
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
<p>
|
||||
On the right side, you see a section "Resource Information", which contains "Authentication: Requires user context" as a link.
|
||||
</p>
|
||||
<p>
|
||||
So, we click on that link, and go to a page that tells us about OAuth authentication. We will need to use OAuth, so there is the link "Using OAuth" that gets us to another overview page. We will need to get an access token, which leads us to
|
||||
<a href="https://dev.twitter.com/docs/auth/obtaining-access-tokens" rel="nofollow">
|
||||
this wonderful selection page
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
<p>
|
||||
What do we want to do? We have a (kind of) desktop app, that can't access a browser, so we will need to use ...
|
||||
<a href="https://dev.twitter.com/docs/auth/pin-based-authorization" rel="nofollow">
|
||||
PIN-based authorization
|
||||
</a>
|
||||
. And now, all the steps are linked from there.
|
||||
</p>
|
||||
<h3>
|
||||
<a aria-hidden="true" class="anchor" href="#how-do-i-read-a-documentation-entry" id="user-content-how-do-i-read-a-documentation-entry">
|
||||
<span aria-hidden="true" class="octicon octicon-link">
|
||||
</span>
|
||||
</a>
|
||||
How Do I Read A Documentation Entry?
|
||||
</h3>
|
||||
<p>
|
||||
OK, now that we have found our method to call, we need to know how to do our call.
|
||||
</p>
|
||||
<p>
|
||||
Let's say, you want to tweet from Pd and have already found the method
|
||||
<a href="https://dev.twitter.com/docs/api/1.1/post/statuses/update" rel="nofollow">
|
||||
POST statuses/update
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
<p>
|
||||
<a rel="noopener noreferrer nofollow" target="_blank">
|
||||
<img alt="Screenshot from Twitter website" src="twitter-documentation.png" style="max-width: 100%;"/>
|
||||
</a>
|
||||
</p>
|
||||
<p>
|
||||
The important parts are marked with yellow by me. On the bottom of the page you can see examples, that tell you how to use the method.
|
||||
</p>
|
||||
<p>
|
||||
That's all there is, there's no inherent magic.
|
||||
</p>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
function showCanonicalImages() {
|
||||
var images = document.getElementsByTagName('img');
|
||||
if (!images) {
|
||||
return;
|
||||
}
|
||||
for (var index = 0; index < images.length; index++) {
|
||||
var image = images[index];
|
||||
if (image.getAttribute('data-canonical-src') && image.src !== image.getAttribute('data-canonical-src')) {
|
||||
image.src = image.getAttribute('data-canonical-src');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function scrollToHash() {
|
||||
if (location.hash && !document.querySelector(':target')) {
|
||||
var element = document.getElementById('user-content-' + location.hash.slice(1));
|
||||
if (element) {
|
||||
element.scrollIntoView();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function autorefreshContent(eventSourceUrl) {
|
||||
var initialTitle = document.title;
|
||||
var contentElement = document.getElementById('grip-content');
|
||||
var source = new EventSource(eventSourceUrl);
|
||||
var isRendering = false;
|
||||
|
||||
source.onmessage = function(ev) {
|
||||
var msg = JSON.parse(ev.data);
|
||||
if (msg.updating) {
|
||||
isRendering = true;
|
||||
document.title = '(Rendering) ' + document.title;
|
||||
} else {
|
||||
isRendering = false;
|
||||
document.title = initialTitle;
|
||||
contentElement.innerHTML = msg.content;
|
||||
showCanonicalImages();
|
||||
}
|
||||
}
|
||||
|
||||
source.onerror = function(e) {
|
||||
if (e.readyState === EventSource.CLOSED && isRendering) {
|
||||
isRendering = false;
|
||||
document.title = initialTitle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.onhashchange = function() {
|
||||
scrollToHash();
|
||||
}
|
||||
|
||||
window.onload = function() {
|
||||
scrollToHash();
|
||||
}
|
||||
|
||||
showCanonicalImages();
|
||||
|
||||
var autorefreshUrl = document.getElementById('preview-page').getAttribute('data-autorefresh-url');
|
||||
if (autorefreshUrl) {
|
||||
autorefreshContent(autorefreshUrl);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
After Width: | Height: | Size: 69 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 22 KiB |
@@ -0,0 +1,215 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>
|
||||
Home
|
||||
</title>
|
||||
<style>
|
||||
/* Page tweaks */
|
||||
.preview-page {
|
||||
margin-top: 64px;
|
||||
margin-bottom: 21px;
|
||||
}
|
||||
/* User-content tweaks */
|
||||
.timeline-comment-wrapper > .timeline-comment:after,
|
||||
.timeline-comment-wrapper > .timeline-comment:before {
|
||||
content: none;
|
||||
}
|
||||
/* User-content overrides */
|
||||
.discussion-timeline.wide {
|
||||
width: 920px;
|
||||
}
|
||||
</style>
|
||||
<link href="style.css" rel="stylesheet"/>
|
||||
</head>
|
||||
<body>
|
||||
<div class="page">
|
||||
<div class="preview-page" data-autorefresh-url="" id="preview-page">
|
||||
<main id="js-repo-pjax-container">
|
||||
<div class="clearfix new-discussion-timeline container-xl px-3 px-md-4 px-lg-5">
|
||||
<div class="repository-content">
|
||||
<div class="clearfix">
|
||||
<div class="Layout Layout--flowRow-until-md Layout--sidebarPosition-end Layout--sidebarPosition-flowRow-end">
|
||||
<div class="Layout-main">
|
||||
<div class="Box md Box--responsive" id="readme">
|
||||
<div class="Box-header d-flex border-bottom-0 flex-items-center flex-justify-between color-bg-default rounded-top-2">
|
||||
<div class="d-flex flex-items-center">
|
||||
<h2 class="Box-title">
|
||||
Home
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="Box-body px-5 pb-5">
|
||||
<article class="markdown-body entry-content container-lg" id="grip-content">
|
||||
<p>
|
||||
PuREST JSON is a library for connecting Puredata (Pd) to HTTP services
|
||||
and encoding and decoding JSON data.
|
||||
</p>
|
||||
<p>
|
||||
The library can issue HTTP statements, so consumation of RESTful services is possible, e.g. CouchDB.
|
||||
</p>
|
||||
<h2>
|
||||
<a aria-hidden="true" class="anchor" href="#basic-information-for-users" id="user-content-basic-information-for-users">
|
||||
<span aria-hidden="true" class="octicon octicon-link">
|
||||
</span>
|
||||
</a>
|
||||
Basic Information for Users
|
||||
</h2>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="http://ix.residuum.org/pd/purest_json.html#download" rel="nofollow">
|
||||
Binary Downloads
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="Basic-Usage.html">
|
||||
Basic Usage
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<h2>
|
||||
<a aria-hidden="true" class="anchor" href="#usage-examples" id="user-content-usage-examples">
|
||||
<span aria-hidden="true" class="octicon octicon-link">
|
||||
</span>
|
||||
</a>
|
||||
Usage examples
|
||||
</h2>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="Using-JSON-Data-as-Pitch-Generator.html">
|
||||
Using JSON data as pitch generator
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="Using-Pd-as-Twitter-Client.html">
|
||||
Using Pd as Twitter Client
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://github.com/g-roma/freesound_pd">
|
||||
Previewing and downloading samples from Freesound.org
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<h2>
|
||||
<a aria-hidden="true" class="anchor" href="#detailed-documentation-for-objects" id="user-content-detailed-documentation-for-objects">
|
||||
<span aria-hidden="true" class="octicon octicon-link">
|
||||
</span>
|
||||
</a>
|
||||
Detailed Documentation for Objects
|
||||
</h2>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="json-decode-And-json-encode-JSON-Data-in-Pd.html">
|
||||
[json decode] and [json encode]: JSON Data in Pd
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="rest-And-oauth-Making-HTTP-Requests.html">
|
||||
[rest] and [oauth]: Making HTTP Requests
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="urlparams-Creating-Query-Strings.html">
|
||||
[urlparams]: Creating Query Strings
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<h2>
|
||||
<a aria-hidden="true" class="anchor" href="#developers" id="user-content-developers">
|
||||
<span aria-hidden="true" class="octicon octicon-link">
|
||||
</span>
|
||||
</a>
|
||||
Developers
|
||||
</h2>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="Compilation.html">
|
||||
Compilation
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="Contributing-and-Code-Documentation.html">
|
||||
Contributing and Code Documentation
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
function showCanonicalImages() {
|
||||
var images = document.getElementsByTagName('img');
|
||||
if (!images) {
|
||||
return;
|
||||
}
|
||||
for (var index = 0; index < images.length; index++) {
|
||||
var image = images[index];
|
||||
if (image.getAttribute('data-canonical-src') && image.src !== image.getAttribute('data-canonical-src')) {
|
||||
image.src = image.getAttribute('data-canonical-src');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function scrollToHash() {
|
||||
if (location.hash && !document.querySelector(':target')) {
|
||||
var element = document.getElementById('user-content-' + location.hash.slice(1));
|
||||
if (element) {
|
||||
element.scrollIntoView();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function autorefreshContent(eventSourceUrl) {
|
||||
var initialTitle = document.title;
|
||||
var contentElement = document.getElementById('grip-content');
|
||||
var source = new EventSource(eventSourceUrl);
|
||||
var isRendering = false;
|
||||
|
||||
source.onmessage = function(ev) {
|
||||
var msg = JSON.parse(ev.data);
|
||||
if (msg.updating) {
|
||||
isRendering = true;
|
||||
document.title = '(Rendering) ' + document.title;
|
||||
} else {
|
||||
isRendering = false;
|
||||
document.title = initialTitle;
|
||||
contentElement.innerHTML = msg.content;
|
||||
showCanonicalImages();
|
||||
}
|
||||
}
|
||||
|
||||
source.onerror = function(e) {
|
||||
if (e.readyState === EventSource.CLOSED && isRendering) {
|
||||
isRendering = false;
|
||||
document.title = initialTitle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.onhashchange = function() {
|
||||
scrollToHash();
|
||||
}
|
||||
|
||||
window.onload = function() {
|
||||
scrollToHash();
|
||||
}
|
||||
|
||||
showCanonicalImages();
|
||||
|
||||
var autorefreshUrl = document.getElementById('preview-page').getAttribute('data-autorefresh-url');
|
||||
if (autorefreshUrl) {
|
||||
autorefreshContent(autorefreshUrl);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,766 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>
|
||||
[json decode] And [json encode]: JSON Data in Pd
|
||||
</title>
|
||||
<style>
|
||||
/* Page tweaks */
|
||||
.preview-page {
|
||||
margin-top: 64px;
|
||||
margin-bottom: 21px;
|
||||
}
|
||||
/* User-content tweaks */
|
||||
.timeline-comment-wrapper > .timeline-comment:after,
|
||||
.timeline-comment-wrapper > .timeline-comment:before {
|
||||
content: none;
|
||||
}
|
||||
/* User-content overrides */
|
||||
.discussion-timeline.wide {
|
||||
width: 920px;
|
||||
}
|
||||
</style>
|
||||
<link href="style.css" rel="stylesheet"/>
|
||||
</head>
|
||||
<body>
|
||||
<div class="page">
|
||||
<div class="preview-page" data-autorefresh-url="" id="preview-page">
|
||||
<main id="js-repo-pjax-container">
|
||||
<div class="clearfix new-discussion-timeline container-xl px-3 px-md-4 px-lg-5">
|
||||
<div class="repository-content">
|
||||
<div class="clearfix">
|
||||
<div class="Layout Layout--flowRow-until-md Layout--sidebarPosition-end Layout--sidebarPosition-flowRow-end">
|
||||
<div class="Layout-main">
|
||||
<div class="Box md Box--responsive" id="readme">
|
||||
<div class="Box-header d-flex border-bottom-0 flex-items-center flex-justify-between color-bg-default rounded-top-2">
|
||||
<div class="d-flex flex-items-center">
|
||||
<h2 class="Box-title">
|
||||
[json decode] And [json encode]: JSON Data in Pd
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="Box-body px-5 pb-5">
|
||||
<article class="markdown-body entry-content container-lg" id="grip-content">
|
||||
<p>
|
||||
Pd does not have boolean values, and no notion of a NULL object, only lists, symbols (=strings) and floats (=numbers).
|
||||
</p>
|
||||
<p>
|
||||
To use the definitions from
|
||||
<a href="http://json.org/" rel="nofollow">
|
||||
json.org
|
||||
</a>
|
||||
this leads to the following mapping of JSON data to Pd:
|
||||
</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
JSON type
|
||||
</th>
|
||||
<th>
|
||||
Pd type
|
||||
</th>
|
||||
<th>
|
||||
JSON example
|
||||
</th>
|
||||
<th>
|
||||
Pd example
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
string
|
||||
</td>
|
||||
<td>
|
||||
symbol
|
||||
</td>
|
||||
<td>
|
||||
"my string, containing a comma"
|
||||
</td>
|
||||
<td>
|
||||
symbol my string\, containing a comma
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
number
|
||||
</td>
|
||||
<td>
|
||||
float
|
||||
</td>
|
||||
<td>
|
||||
1.01
|
||||
</td>
|
||||
<td>
|
||||
1.01
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
object
|
||||
</td>
|
||||
<td>
|
||||
see below
|
||||
</td>
|
||||
<td>
|
||||
{"key": "value"}
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
array
|
||||
</td>
|
||||
<td>
|
||||
see below
|
||||
</td>
|
||||
<td>
|
||||
[{"key": "value"}, {"other key": "other value"}]
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
boolean
|
||||
</td>
|
||||
<td>
|
||||
float
|
||||
</td>
|
||||
<td>
|
||||
true
|
||||
</td>
|
||||
<td>
|
||||
1
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
boolean
|
||||
</td>
|
||||
<td>
|
||||
float
|
||||
</td>
|
||||
<td>
|
||||
false
|
||||
</td>
|
||||
<td>
|
||||
0
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
null
|
||||
</td>
|
||||
<td>
|
||||
symbol
|
||||
</td>
|
||||
<td>
|
||||
null
|
||||
</td>
|
||||
<td>
|
||||
symbol
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>
|
||||
As you can infer from the table, values of
|
||||
<code>
|
||||
true
|
||||
</code>
|
||||
,
|
||||
<code>
|
||||
false
|
||||
</code>
|
||||
, or
|
||||
<code>
|
||||
null
|
||||
</code>
|
||||
cannot be constructed by
|
||||
<code>
|
||||
[json-encode]
|
||||
</code>
|
||||
, but can be read by the objects. A value of null yields a Pd value of an empty symbol.
|
||||
</p>
|
||||
<h2>
|
||||
<a aria-hidden="true" class="anchor" href="#json-decode" id="user-content-json-decode">
|
||||
<span aria-hidden="true" class="octicon octicon-link">
|
||||
</span>
|
||||
</a>
|
||||
<code>
|
||||
[json-decode]
|
||||
</code>
|
||||
</h2>
|
||||
<p>
|
||||
Feeding a symbol or list to
|
||||
<code>
|
||||
[json-decode]
|
||||
</code>
|
||||
will parse the values and generate an output. If you use a list, then all elements of the list will be converted to symbols and concatenated using a space as separator, so
|
||||
<code>
|
||||
symbol my value
|
||||
</code>
|
||||
will generate the same output as
|
||||
<code>
|
||||
list my value
|
||||
</code>
|
||||
.
|
||||
</p>
|
||||
<p>
|
||||
You must feed exactly one valid JSON object or array of objects as a symbol or list message to
|
||||
<code>
|
||||
[json-decode]
|
||||
</code>
|
||||
. Any other correct JSON data cannot be interpreted by the
|
||||
<code>
|
||||
[json-decode]
|
||||
</code>
|
||||
.
|
||||
</p>
|
||||
<h3>
|
||||
<a aria-hidden="true" class="anchor" href="#output-of-a-json-object" id="user-content-output-of-a-json-object">
|
||||
<span aria-hidden="true" class="octicon octicon-link">
|
||||
</span>
|
||||
</a>
|
||||
Output of a JSON object
|
||||
</h3>
|
||||
<p>
|
||||
JSON objects are interpreted by
|
||||
<code>
|
||||
[json-decode]
|
||||
</code>
|
||||
as key value stores. Each key/value pair outputs a list on the middle outlet, where the key is always converted to a symbol, while the value is converted according to the table above. Arrays and objects are converted to their string representation again and then output as a symbol.
|
||||
</p>
|
||||
<p>
|
||||
After all key/value are sent to the middle outlet, a
|
||||
<code>
|
||||
bang
|
||||
</code>
|
||||
is emitted from the left outlet.
|
||||
</p>
|
||||
<p>
|
||||
<strong>
|
||||
Example:
|
||||
</strong>
|
||||
</p>
|
||||
<p>
|
||||
JSON string:
|
||||
</p>
|
||||
<pre><code>{
|
||||
"id": 1,
|
||||
"active": true,
|
||||
"obsolete": false,
|
||||
"name": "PuREST JSON",
|
||||
"objects": ["json-decode", "json-encode", "urlparams", "rest", "oauth"],
|
||||
"author": {"name": "me", "age": "none of your business"}
|
||||
}
|
||||
</code></pre>
|
||||
<p>
|
||||
Output:
|
||||
</p>
|
||||
<ol>
|
||||
<li>
|
||||
middle:
|
||||
<code>
|
||||
list id 1
|
||||
</code>
|
||||
</li>
|
||||
<li>
|
||||
middle:
|
||||
<code>
|
||||
list active 1
|
||||
</code>
|
||||
</li>
|
||||
<li>
|
||||
middle:
|
||||
<code>
|
||||
list obsolete 0
|
||||
</code>
|
||||
</li>
|
||||
<li>
|
||||
middle:
|
||||
<code>
|
||||
list name PuREST JSON
|
||||
</code>
|
||||
</li>
|
||||
<li>
|
||||
middle:
|
||||
<code>
|
||||
list objects ["json-decode"\, "json-encode"\, "urlparams"\, "rest"\, "oauth"]
|
||||
</code>
|
||||
</li>
|
||||
<li>
|
||||
middle:
|
||||
<code>
|
||||
list author {"name": "me"\, "age": "none of your business"}
|
||||
</code>
|
||||
</li>
|
||||
<li>
|
||||
left:
|
||||
<code>
|
||||
bang
|
||||
</code>
|
||||
</li>
|
||||
</ol>
|
||||
<h3>
|
||||
<a aria-hidden="true" class="anchor" href="#output-of-an-array-of-json-objects" id="user-content-output-of-an-array-of-json-objects">
|
||||
<span aria-hidden="true" class="octicon octicon-link">
|
||||
</span>
|
||||
</a>
|
||||
Output of an Array of JSON objects
|
||||
</h3>
|
||||
<p>
|
||||
An array generates the same output as a series of JSON objects, including
|
||||
<code>
|
||||
bang
|
||||
</code>
|
||||
messages on the left outlet.
|
||||
</p>
|
||||
<p>
|
||||
<strong>
|
||||
Example:
|
||||
</strong>
|
||||
</p>
|
||||
<p>
|
||||
JSON string:
|
||||
</p>
|
||||
<pre><code>[
|
||||
{"key": "value", "id": 1},
|
||||
{"id": 2},
|
||||
{"id": 3}
|
||||
]
|
||||
</code></pre>
|
||||
<p>
|
||||
Output:
|
||||
</p>
|
||||
<ol>
|
||||
<li>
|
||||
middle:
|
||||
<code>
|
||||
list key value
|
||||
</code>
|
||||
</li>
|
||||
<li>
|
||||
middle:
|
||||
<code>
|
||||
list id 1
|
||||
</code>
|
||||
</li>
|
||||
<li>
|
||||
left:
|
||||
<code>
|
||||
bang
|
||||
</code>
|
||||
</li>
|
||||
<li>
|
||||
middle:
|
||||
<code>
|
||||
list id 2
|
||||
</code>
|
||||
</li>
|
||||
<li>
|
||||
left:
|
||||
<code>
|
||||
bang
|
||||
</code>
|
||||
</li>
|
||||
<li>
|
||||
middle:
|
||||
<code>
|
||||
list id 3
|
||||
</code>
|
||||
</li>
|
||||
<li>
|
||||
left:
|
||||
<code>
|
||||
bang
|
||||
</code>
|
||||
</li>
|
||||
</ol>
|
||||
<h3>
|
||||
<a aria-hidden="true" class="anchor" href="#error-handling" id="user-content-error-handling">
|
||||
<span aria-hidden="true" class="octicon octicon-link">
|
||||
</span>
|
||||
</a>
|
||||
Error handling
|
||||
</h3>
|
||||
<p>
|
||||
If the string fed into
|
||||
<code>
|
||||
[json-decode]
|
||||
</code>
|
||||
cannot be converted into a valid JSON object, then Pd will write an error to the console, and the right outlet will output a bang. Use that for error handling.
|
||||
</p>
|
||||
<h3>
|
||||
<a aria-hidden="true" class="anchor" href="#some-hints-for-data-processing" id="user-content-some-hints-for-data-processing">
|
||||
<span aria-hidden="true" class="octicon octicon-link">
|
||||
</span>
|
||||
</a>
|
||||
Some hints for data processing
|
||||
</h3>
|
||||
<ul>
|
||||
<li>
|
||||
Use
|
||||
<code>
|
||||
list trim
|
||||
</code>
|
||||
from list-abs for trimming the list selector off the output and then use
|
||||
<code>
|
||||
[route]
|
||||
</code>
|
||||
for the data. If you want to create new lists, feed them into
|
||||
<code>
|
||||
[pack]
|
||||
</code>
|
||||
.
|
||||
</li>
|
||||
<li>
|
||||
If you process an array of same object types, use the approach above and store the values in a FIFO buffer, e. g.
|
||||
<code>
|
||||
[fifop]
|
||||
</code>
|
||||
from zexy or
|
||||
<code>
|
||||
[list-fifo]
|
||||
</code>
|
||||
from list-abs.
|
||||
</li>
|
||||
<li>
|
||||
Do not expect JSON data to be ordered:
|
||||
<code>
|
||||
{"id": 1, "name: "my name"}
|
||||
</code>
|
||||
and
|
||||
<code>
|
||||
{"name": "my name", "id": 1}
|
||||
</code>
|
||||
are in fact the same object. Use an empty first inlet of
|
||||
<code>
|
||||
[pack]
|
||||
</code>
|
||||
and the
|
||||
<code>
|
||||
bang
|
||||
</code>
|
||||
from the left outlet to create your own lists.
|
||||
</li>
|
||||
</ul>
|
||||
<h2>
|
||||
<a aria-hidden="true" class="anchor" href="#json-encode" id="user-content-json-encode">
|
||||
<span aria-hidden="true" class="octicon octicon-link">
|
||||
</span>
|
||||
</a>
|
||||
<code>
|
||||
[json-encode]
|
||||
</code>
|
||||
</h2>
|
||||
<h3>
|
||||
<a aria-hidden="true" class="anchor" href="#reading-and-writing-json-files" id="user-content-reading-and-writing-json-files">
|
||||
<span aria-hidden="true" class="octicon octicon-link">
|
||||
</span>
|
||||
</a>
|
||||
Reading and Writing JSON files
|
||||
</h3>
|
||||
<p>
|
||||
<code>
|
||||
[json-encode]
|
||||
</code>
|
||||
can hold one JSON object at a time in its memory. Therefore, if you read JSON data from a file using the
|
||||
<code>
|
||||
[read(
|
||||
</code>
|
||||
message, the message needs exactly one additional parameter, the path to the file to read.
|
||||
</p>
|
||||
<p>
|
||||
The file must contain exactly one JSON object. If you want to read an array, you must wrap the array in an object.
|
||||
</p>
|
||||
<p>
|
||||
<strong>
|
||||
Example:
|
||||
</strong>
|
||||
</p>
|
||||
<p>
|
||||
<code>
|
||||
[{"key": "value"}, {"another key": "another value"}]
|
||||
</code>
|
||||
cannot be read, but
|
||||
<code>
|
||||
{"wrapping": [{"key": "value"}, {"another key": "another value"}]}
|
||||
</code>
|
||||
can.
|
||||
</p>
|
||||
<p>
|
||||
To write data to a file, use the
|
||||
<code>
|
||||
[write(
|
||||
</code>
|
||||
message. This message needs exactly one additional parameter, the path to the file to write the JSON object as string. It will overwrite existing content without asking for confirmation.
|
||||
</p>
|
||||
<h3>
|
||||
<a aria-hidden="true" class="anchor" href="#storing-data-with-add-and-array" id="user-content-storing-data-with-add-and-array">
|
||||
<span aria-hidden="true" class="octicon octicon-link">
|
||||
</span>
|
||||
</a>
|
||||
Storing Data with
|
||||
<code>
|
||||
[add(
|
||||
</code>
|
||||
And
|
||||
<code>
|
||||
[array(
|
||||
</code>
|
||||
</h3>
|
||||
<p>
|
||||
There are two messages for adding values to the JSON object stored in
|
||||
<code>
|
||||
[json-encode]
|
||||
</code>
|
||||
,
|
||||
<code>
|
||||
[add(
|
||||
</code>
|
||||
and
|
||||
<code>
|
||||
[array(
|
||||
</code>
|
||||
. Both need at least two additional values. The first parameter must be a symbol and represents the key. As Pd currently has no way to escape whitespace, there is no simple way to generate keys with space in it, a workaround is possible using
|
||||
<code>
|
||||
[makefilename]
|
||||
</code>
|
||||
from
|
||||
<a href="http://puredata.info/downloads/zexy" rel="nofollow">
|
||||
zexy
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
<p>
|
||||
If the message has exactly two additional parameters, and the second parameter is a float, then the value will be stored as a number. Otherwise all additional parameters will be concatenated with a space.
|
||||
</p>
|
||||
<p>
|
||||
If the additional parameters can be converted to a JSON object, then the resulting string will be converted thusly, otherwise, a string is stored.
|
||||
</p>
|
||||
<p>
|
||||
Adding a value with an already stored key will result in overwriting the existing value.
|
||||
</p>
|
||||
<p>
|
||||
<strong>
|
||||
Example:
|
||||
</strong>
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
<code>
|
||||
[add key 1(
|
||||
</code>
|
||||
will store
|
||||
<code>
|
||||
{"key":1}
|
||||
</code>
|
||||
.
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
[add key first second third(
|
||||
</code>
|
||||
will store
|
||||
<code>
|
||||
{"key":"first second third"}
|
||||
</code>
|
||||
.
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
[add key {"first":"second third"}(
|
||||
</code>
|
||||
will store
|
||||
<code>
|
||||
{"key":{first: "second third"}}
|
||||
</code>
|
||||
.
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
<code>
|
||||
[array(
|
||||
</code>
|
||||
will add data to an array with the name of the key. The members of the array will be sorted in the specified order. If a value with the same key exists, but was stored with
|
||||
<code>
|
||||
[add(
|
||||
</code>
|
||||
, the value will be discarded. A new
|
||||
<code>
|
||||
[add(
|
||||
</code>
|
||||
message with the same key will discard the stored array.
|
||||
</p>
|
||||
<p>
|
||||
<strong>
|
||||
Example:
|
||||
</strong>
|
||||
</p>
|
||||
<ol>
|
||||
<li>
|
||||
<code>
|
||||
[add id 1(
|
||||
</code>
|
||||
=>
|
||||
<code>
|
||||
{"id": 1}
|
||||
</code>
|
||||
.
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
[array id 2(
|
||||
</code>
|
||||
=>
|
||||
<code>
|
||||
{"id": [2]}
|
||||
</code>
|
||||
.
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
[array id 3(
|
||||
</code>
|
||||
=>
|
||||
<code>
|
||||
{"id": [2, 3]}
|
||||
</code>
|
||||
.
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
[add id 4(
|
||||
</code>
|
||||
=>
|
||||
<code>
|
||||
{"id": 4}
|
||||
</code>
|
||||
.
|
||||
</li>
|
||||
</ol>
|
||||
<h3>
|
||||
<a aria-hidden="true" class="anchor" href="#outputting-and-clearing-data" id="user-content-outputting-and-clearing-data">
|
||||
<span aria-hidden="true" class="octicon octicon-link">
|
||||
</span>
|
||||
</a>
|
||||
Outputting And Clearing Data
|
||||
</h3>
|
||||
<p>
|
||||
Issuing a
|
||||
<code>
|
||||
[bang(
|
||||
</code>
|
||||
message to
|
||||
<code>
|
||||
[json-encode]
|
||||
</code>
|
||||
will output the JSON object as a string on its only outlet. If no data is stored, an empty symbol is sent.
|
||||
</p>
|
||||
<p>
|
||||
You can clear the stored object with the
|
||||
<code>
|
||||
[clear(
|
||||
</code>
|
||||
method.
|
||||
</p>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
function showCanonicalImages() {
|
||||
var images = document.getElementsByTagName('img');
|
||||
if (!images) {
|
||||
return;
|
||||
}
|
||||
for (var index = 0; index < images.length; index++) {
|
||||
var image = images[index];
|
||||
if (image.getAttribute('data-canonical-src') && image.src !== image.getAttribute('data-canonical-src')) {
|
||||
image.src = image.getAttribute('data-canonical-src');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function scrollToHash() {
|
||||
if (location.hash && !document.querySelector(':target')) {
|
||||
var element = document.getElementById('user-content-' + location.hash.slice(1));
|
||||
if (element) {
|
||||
element.scrollIntoView();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function autorefreshContent(eventSourceUrl) {
|
||||
var initialTitle = document.title;
|
||||
var contentElement = document.getElementById('grip-content');
|
||||
var source = new EventSource(eventSourceUrl);
|
||||
var isRendering = false;
|
||||
|
||||
source.onmessage = function(ev) {
|
||||
var msg = JSON.parse(ev.data);
|
||||
if (msg.updating) {
|
||||
isRendering = true;
|
||||
document.title = '(Rendering) ' + document.title;
|
||||
} else {
|
||||
isRendering = false;
|
||||
document.title = initialTitle;
|
||||
contentElement.innerHTML = msg.content;
|
||||
showCanonicalImages();
|
||||
}
|
||||
}
|
||||
|
||||
source.onerror = function(e) {
|
||||
if (e.readyState === EventSource.CLOSED && isRendering) {
|
||||
isRendering = false;
|
||||
document.title = initialTitle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.onhashchange = function() {
|
||||
scrollToHash();
|
||||
}
|
||||
|
||||
window.onload = function() {
|
||||
scrollToHash();
|
||||
}
|
||||
|
||||
showCanonicalImages();
|
||||
|
||||
var autorefreshUrl = document.getElementById('preview-page').getAttribute('data-autorefresh-url');
|
||||
if (autorefreshUrl) {
|
||||
autorefreshContent(autorefreshUrl);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,22 @@
|
||||
|
||||
/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:0.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace, monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}*{box-sizing:border-box}input,select,textarea,button{font:13px/1.4 Helvetica, arial, nimbussansl, liberationsans, freesans, clean, sans-serif, "Segoe UI Emoji", "Segoe UI Symbol"}body{font:13px/1.4 Helvetica, arial, nimbussansl, liberationsans, freesans, clean, sans-serif, "Segoe UI Emoji", "Segoe UI Symbol";color:#333;background-color:#fff}a{color:#4183c4;text-decoration:none}a:hover,a:active{text-decoration:underline}hr,.rule{height:0;margin:15px 0;overflow:hidden;background:transparent;border:0;border-bottom:1px solid #ddd}hr:before,.rule:before{display:table;content:""}hr:after,.rule:after{display:table;clear:both;content:""}h1,h2,h3,h4,h5,h6{margin-top:15px;margin-bottom:15px;line-height:1.1}h1{font-size:30px}h2{font-size:21px}h3{font-size:16px}h4{font-size:14px}h5{font-size:12px}h6{font-size:11px}small{font-size:90%}blockquote{margin:0}.lead{margin-bottom:30px;font-size:20px;font-weight:300;color:#555}.text-muted{color:#999}.text-danger{color:#bd2c00}.text-emphasized{font-weight:bold;color:#333}ul,ol{padding:0;margin-top:0;margin-bottom:0}ol ol,ul ol{list-style-type:lower-roman}ul ul ol,ul ol ol,ol ul ol,ol ol ol{list-style-type:lower-alpha}dd{margin-left:0}tt,code{font-family:Consolas, "Liberation Mono", Menlo, Courier, monospace;font-size:12px}pre{margin-top:0;margin-bottom:0;font:12px Consolas, "Liberation Mono", Menlo, Courier, monospace}.container{width:980px;margin-right:auto;margin-left:auto}.container:before{display:table;content:""}.container:after{display:table;clear:both;content:""}.columns{margin-right:-10px;margin-left:-10px}.columns:before{display:table;content:""}.columns:after{display:table;clear:both;content:""}.column{float:left;padding-right:10px;padding-left:10px}.one-third{width:33.333333%}.two-thirds{width:66.666667%}.one-fourth{width:25%}.one-half{width:50%}.three-fourths{width:75%}.one-fifth{width:20%}.four-fifths{width:80%}.single-column{padding-right:10px;padding-left:10px}.table-column{display:table-cell;width:1%;padding-right:10px;padding-left:10px;vertical-align:top}fieldset{padding:0;margin:0;border:0}label{font-size:13px;font-weight:bold}.form-control,input[type="text"],input[type="password"],input[type="email"],input[type="number"],input[type="tel"],input[type="url"],textarea{min-height:34px;padding:7px 8px;font-size:13px;color:#333;vertical-align:middle;background-color:#fff;background-repeat:no-repeat;background-position:right center;border:1px solid #ccc;border-radius:3px;outline:none;box-shadow:inset 0 1px 2px rgba(0,0,0,0.075)}.form-control.focus,.form-control:focus,input[type="text"].focus,input[type="text"]:focus,.focused .drag-and-drop,input[type="password"].focus,input[type="password"]:focus,input[type="email"].focus,input[type="email"]:focus,input[type="number"].focus,input[type="number"]:focus,input[type="tel"].focus,input[type="tel"]:focus,input[type="url"].focus,input[type="url"]:focus,textarea.focus,textarea:focus{border-color:#51a7e8;box-shadow:inset 0 1px 2px rgba(0,0,0,0.075),0 0 5px rgba(81,167,232,0.5)}input.input-contrast,.input-contrast{backgrounLine truncated
|
||||
90%{opacity:0}100%{opacity:1}}@keyframes mini-nav-loader{0%,
|
||||
90%{opacity:0}100%{opacity:1}}@font-face{font-family:'octicons';src:url(data:font/eot;base64,PCFET0NUWVBFIGh0bWw+CjxodG1sPgogIDxoZWFkPgogICAgPG1ldGEgaHR0cC1lcXVpdj0iQ29udGVudC10eXBlIiBjb250ZW50PSJ0ZXh0L2h0bWw7IGNoYXJzZXQ9dXRmLTgiPgogICAgPG1ldGEgaHR0cC1lcXVpdj0iQ29udGVudC1TZWN1cml0eS1Qb2xpY3kiIGNvbnRlbnQ9ImRlZmF1bHQtc3JjICdub25lJzsgc2NyaXB0LXNyYyAnc2VsZic7IHN0eWxlLXNyYyAndW5zYWZlLWlubGluZSc7IGltZy1zcmMgZGF0YTo7IGNvbm5lY3Qtc3JjICdzZWxmJyI+CiAgICA8dGl0bGU+UGFnZSBub3QgZm91bmQgJm1pZGRvdDsgR2l0SHViPC90aXRsZT4KICAgIDxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyIgbWVkaWE9InNjcmVlbiI+CiAgICAgIGJvZHkgewogICAgICAgIGJhY2tncm91bmQtY29sb3I6ICNmMWYxZjE7CiAgICAgICAgbWFyZ2luOiAwOwogICAgICB9CiAgICAgIGJvZHksCiAgICAgIGlucHV0LAogICAgICBidXR0b24gewogICAgICAgIGZvbnQtZmFtaWx5OiAiSGVsdmV0aWNhIE5ldWUiLCBIZWx2ZXRpY2EsIEFyaWFsLCBzYW5zLXNlcmlmOwogICAgICB9CiAgICAgIC5jb250YWluZXIgeyBtYXJnaW46IDMwcHggYXV0byA0MHB4IGF1dG87IHdpZHRoOiA4MDBweDsgdGV4dC1hbGlnbjogY2VudGVyOyB9CgogICAgICBhIHsgY29sb3I6ICM0MTgzYzQ7IHRleHQtZGVjb3JhdGlvbjogbm9uZTsgZm9udC13ZWlnaHQ6IGJvbGQ7IH0KICAgICAgYTpob3ZlciB7IHRleHQtZGVjb3JhdGlvbjogdW5kZXJsaW5lOyB9CgogICAgICBoMyB7IGNvbG9yOiAjNjY2OyB9CiAgICAgIHVsIHsgbGlzdC1zdHlsZTogbm9uZTsgcGFkZGluZzogMjVweCAwOyB9CiAgICAgIGxpIHsKICAgICAgICBkaXNwbGF5OiBpbmxpbmU7CiAgICAgICAgbWFyZ2luOiAxMHB4IDUwcHggMTBweCAwcHg7CiAgICAgIH0KICAgICAgaW5wdXRbdHlwZT10ZXh0XSwKICAgICAgaW5wdXRbdHlwZT1wYXNzd29yZF0gewogICAgICAgIGZvbnQtc2l6ZTogMTNweDsKICAgICAgICBtaW4taGVpZ2h0OiAzMnB4OwogICAgICAgIG1hcmdpbjogMDsKICAgICAgICBwYWRkaW5nOiA3cHggOHB4OwogICAgICAgIG91dGxpbmU6IG5vbmU7CiAgICAgICAgY29sb3I6ICMzMzM7CiAgICAgICAgYmFja2dyb3VuZC1jb2xvcjogI2ZmZjsKICAgICAgICBiYWNrZ3JvdW5kLXJlcGVhdDogbm8tcmVwZWF0OwogICAgICAgIGJhY2tncm91bmQtcG9zaXRpb246IHJpZ2h0IGNlbnRlcjsKICAgICAgICBib3JkZXI6IDFweCBzb2xpZCAjY2NjOwogICAgICAgIGJvcmRlci1yYWRpdXM6IDNweDsKICAgICAgICBib3gtc2hhZG93OiBpbnNldCAwIDFweCAycHggcmdiYSgwLDAsMCwwLjA3NSk7CiAgICAgICAgLW1vei1ib3gtc2l6aW5nOiBib3JkZXItYm94OwogICAgICAgIGJveC1zaXppbmc6IGJvcmRlci1ib3g7CiAgICAgICAgLXdlYmtpdC10cmFuc2l0aW9uOiBhbGwgMC4xNXMgZWFzZS1pbjsKICAgICAgICB0cmFuc2l0aW9uOiBhbGwgMC4xNXMgZWFzZS1pbjsKICAgICAgICB2ZXJ0aWNhbC1hbGlnbjogbWlkZGxlOwogICAgICB9CiAgICAgIC5idG4gewogICAgICAgIHBvc2l0aW9uOiByZWxhdGl2ZTsKICAgICAgICBkaXNwbGF5OiBpbmxpbmUtYmxvY2s7CiAgICAgICAgcGFkZGluZzogNnB4IDEycHg7CiAgICAgICAgZm9udC1zaXplOiAxM3B4OwogICAgICAgIGZvbnQtd2VpZ2h0OiBib2xkOwogICAgICAgIGxpbmUtaGVpZ2h0OiAyMHB4OwogICAgICAgIGNvbG9yOiAjMzMzOwogICAgICAgIHdoaXRlLXNwYWNlOiBub3dyYXA7CiAgICAgICAgdmVydGljYWwtYWxpZ246IG1pZGRsZTsKICAgICAgICBjdXJzb3I6IHBvaW50ZXI7CiAgICAgICAgYmFja2dyb3VuZC1jb2xvcjogI0VFRTsKICAgICAgICBiYWNrZ3JvdW5kLWltYWdlOiAtd2Via2l0LWxpbmVhci1ncmFkaWVudCgjRkNGQ0ZDLCAjRUVFKTsKICAgICAgICBiYWNrZ3JvdW5kLWltYWdlOiBsaW5lYXItZ3JhZGllbnQoI0ZDRkNGQywgI0VFRSk7CiAgICAgICAgYmFja2dyb3VuZC1yZXBlYXQ6IHJlcGVhdC14OwogICAgICAgIGJvcmRlcjogMXB4IHNvbGlkICNkNWQ1ZDU7CiAgICAgICAgYm9yZGVyLXJhZGl1czogM3B4OwogICAgICAgIHVzZXItc2VsZWN0OiBub25lOwogICAgICAgIC13ZWJraXQtYXBwZWFyYW5jZTogbm9uZTsKICAgICAgfQoKICAgICAgLmJ0bjpmb2N1cywKICAgICAgaW5wdXRbdHlwZT10ZXh0XTpmb2N1cywKICAgICAgaW5wdXRbdHlwZT1wYXNzd29yZF06Zm9jdXMgewogICAgICAgIHRleHQtZGVjb3JhdGlvbjogbm9uZTsKICAgICAgICBib3JkZXItY29sb3I6ICM1MWE3ZTg7CiAgICAgICAgb3V0bGluZTogbm9uZTsKICAgICAgICBib3gtc2hhZG93OiAwIDAgNXB4IHJnYmEoODEsIDE2NywgMjMyLCAwLjUpOwogICAgICB9CgogICAgICAuYnRuOmhvdmVyLAogICAgICAuYnRuOmFjdGl2ZSB7CiAgICAgICAgdGV4dC1kZWNvcmF0aW9uOiBub25lOwogICAgICAgIGJhY2tncm91bmQtY29sb3I6ICNkZGQ7CiAgICAgICAgYmFja2dyb3VuZC1pbWFnZTogLXdlYmtpdC1saW5lYXItZ3JhZGllbnQoI2VlZSwgI2RkZCk7CiAgICAgICAgYmFja2dyb3VuZC1pbWFnZTogbGluZWFyLWdyYWRpZW50KCNlZWUsICNkZGQpOwogICAgICAgIGJhY2tncm91bmQtcmVwZWF0OiByZXBlYXQteDsKICAgICAgICBib3JkZXItY29sb3I6ICNjY2M7CiAgICAgIH0KCiAgICAgIC5idG46YWN0aXZlIHsKICAgICAgICBiYWNrZ3JvdW5kLWNvbG9yOiAjZGNkY2RjOwogICAgICAgIGJhY2tncm91bmQtaW1hZ2U6IG5vbmU7CiAgICAgICAgYm9yZGVyLWNvbG9yOiAjYjViNWI1OwogICAgICAgIGJveC1zaGFkb3c6IGluc2V0IDAgMnB4IDRweCByZ2JhKDAsIDAsIDAsIDAuMTUpOwogICAgICB9CgogICAgICAjYXV0aCB7CiAgICAgICAgcG9zaXRpb246IGFic29sdXRlOwogICAgICAgIHRvcDogMDsKICAgICAgICByaWdodDogMDsKICAgICAgICB6LWluZGV4OiA1MDsKICAgICAgICBtaW4taGVpZ2h0OiAzMnB4OwogICAgICAgIGJhY2tncm91bmQtY29sb3I6IHJnYmEoNTMsOTUsMTIwLC40KTsKICAgICAgICBwYWRkaW5nOiA3cHggMTBweDsKICAgICAgICBib3JkZXItYm90dG9tLXJpZ2h0LXJhZGl1czogMTBweDsKICAgICAgICBib3JkZXItYm90dG9tLWxlZnQtcmFkaXVzOiAxMHB4OwogICAgICAgIGJveC1zaGFkb3c6IDAgM3B4IDAgcmdiYSgwLCAwLCAwLCAwLjI4KTsKICAgICAgICBkaXNwbGF5OiBub25lOwogICAgICB9CiAgICAgICNhdXRoIGgxLCAjYXV0aCBwLCAjYXV0aCBsYWJlbCB7CiAgICAgICAgZGlzcGxheTogbm9uZTsKICAgICAgfQogICAgICAuYXV0aC1mb3JtLWJvZHkgewogICAgICAgIGRpc3BsYXk6IGlubGluZTsKICAgICAgfQogICAgICAjYXV0aCBpbnB1dFt0eXBlPXRleHRdLAogICAgICAjYXV0aCBpbnB1dFt0eXBlPXBhc3N3b3JkXSB7CiAgICAgICAgZmxvYXQ6IGxlZnQ7CiAgICAgICAgd2lkdGg6IDE3NXB4OwogICAgICAgIG1hcmdpbi1yaWdodDogOXB4OwogICAgICAgIGJvcmRlcjogMDsKICAgICAgICBiYWNrZ3JvdW5kLWNvbG9yOiAjZjVmNWY1OwogICAgICB9CiAgICAgICNhdXRoIGlucHV0W3R5cGU9dGV4dF06Zm9jdXMsCiAgICAgICNhdXRoIGlucHV0W3R5cGU9cGFzc3dvcmRdOmZvY3VzIHsKICAgICAgICBiYWNrZ3JvdW5kLWNvbG9yOiAjZmZmOwogICAgICAgIGJveC1zaGFkb3c6IDAgMCA1cHggcmdiYSgyNTUsMjU1LDI1NSwuNSk7Line truncated
|
||||
|
||||
.marketing .pagehead h1{font-size:30px}.marketing .pagehead p{margin-top:4px;margin-bottom:0;font-size:14px;color:#777}.marketing .pagehead ul.actions{margin-top:10px}.marketing h2 .secure{float:right;padding:1px 0;font-size:11px;font-weight:bold;text-transform:uppercase;color:#6cc644}.marketing .questions p{font-size:14px}.marketing-header{margin-bottom:40px}.marketing-header h1{margin-top:0;margin-bottom:0;font-size:42px;font-weight:300}.marketing-header .lead{color:#777;max-width:750px;margin:10px auto 0}.marketing-header .btn{margin-top:15px;padding:12px 20px;font-size:18px;font-weight:normal;border-radius:6px}.marketing-section{position:relative;padding-top:80px;padding-bottom:80px;border-bottom:1px solid #e5e5e5;text-align:center;font-size:16px;line-height:1.5}.marketing-section:before{display:table;content:""}.marketing-section:after{display:table;clear:both;content:""}.marketing-section h3{font-size:21px;font-weight:normal}.marketing-section-stripe{background-color:#f5f5f5}.marketing-hero-octicon{width:100px;height:100px;border-radius:50px;text-align:center;border:solid 1px #e5e5e5;margin:0 auto 15px}.marketing-hero-octicon .mega-octicon{color:#4183c4;font-size:48px;line-height:100px}.marketing-hero-octicon .octicon-checklist{position:relative;right:-3px}.marketing-grid{font-size:14px}.marketing-grid .column{padding:20px 25px 40px}.marketing-grid p{margin:0 auto;max-width:90%;color:#5a5a5a}.marketing-grid .mega-octicon{color:#4183c4}.read-it{padding-top:50px;text-align:center;border-top:1px solid #eee}.contact-form .input-block{margin-top:10px;margin-bottom:10px}.contact-form textarea{height:200px}.checklist{margin:20px 0;font-size:12px}.checklist>li{margin:15px 0 15px 18px;list-style-position:outside}.hanging-list li,.hanging-icon-list li{margin:10px 0;font-size:14px}.hanging-list li{margin-left:12px;list-style-position:inside}.hanging-icon-list li{padding-left:25px;list-style-type:none}.hanging-icon-list .octicon{float:left;margin-left:-20px;color:#999}.hanging-icon-list .octicon-check{color:#6cc644}.hanging-icon-list .octicon-x{color:#bd2c00}.logos-page h3{font-size:18px}.logos-download{position:relative;display:block;float:left;width:32%;height:290px;margin-bottom:30px;padding-top:20px;text-align:center;border:1px solid #ddd;border-radius:6px}.logos-download+.logos-download{margin-left:2%}.logos-download .gh-logo{margin-top:70px}.logos-download .gh-octocat{margin-top:10px}.logos-download-link{position:absolute;right:0;bottom:0;left:0;display:block;padding:15px 20px;font-size:16px;font-weight:bold;background-color:#f5f5f5;border-top:1px solid #ddd;border-radius:0 0 5px 5px}.logos-download-link .octicon{vertical-align:2px}.logos-download:hover{text-decoration:none}.logos-download:hover .logos-download-link{background-color:#eee}.nonprofit-head{padding:100px 0 120px;border-bottom:1px solid #eee;text-align:center;position:relative;overflow:hidden}.nonprofit-head .title{font-weight:300;font-size:30px;color:#777;margin-bottom:20px;display:inline-block;border-bottom:1px solid #ccc}.nonprofit-head .title .mega-octicon{color:#333}.nonprofit-head .logo{vertical-align:middle}.nonprofit h1{font-weight:300;font-size:28px;line-height:1.5em;position:relative}.nonprofit h2{font-weight:normal}.heart{width:12px;height:12px;background:#83d6c0;box-shadow:140px 30px 0 #efa, 120px -120px 0 #aded84, 220px -60px 0 #ded, 30px 240px 0 #ada, 60px -60px 0 #d76666, 60px -30px 0 #ff846f, 60px 0 0 #f9a7a7, 60px 30px 0 #ffc8c8, 60px 60px 0 #ffd8d8, 30px 60px 0 #baf2ca, 30px 30px 0 #98eaac, 30px 0 0 #80d896, 30px -30px 0 #6dd085, 30px -60px 0 #55be6f, 0 -60px 0 #4cc2a7, 0 -30px 0 #73d3b9, 0 30px 0 #93e3cd, 0 60px 0 #adf9e4, -30px 60px 0 #ffe1b9, -30px 30px 0 #ffd194, -30px 0 0 #ffc86f, -60px 0 0 #fd9ff0, -60px 30px 0 #ffbaf7, -60px 60px 0 #fccdf7, -180px 60px 0 #9df;position:absolute;left:50%;top:40%;margin-left:400px;-webkit-transform:rotate(45deg);-ms-transform:rotate(45deg);transform:rotate(45deg)}.heart.left{margin-left:-400px;-webkit-transform:rotate(-45deg),scaleX(-1);-ms-transform:rotate(-45deg),scaleX(-1);transform:rotate(-45deg),scaleX(-1)}.octo-earth{position:absolute;left:50%;bottom:-150px;margin-left:-120px;-webkit-animation:rotate 20s infinite linear;animation:rotate 20s infinite linear}@-webkit-keyframes rotate{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(-360deg);transform:rotate(-360deg)}}@keyframes rotate{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(-360deg);transform:rotate(-360deg)}}.nonprofit-steps{margin-left:30px;font-size:20px;font-weight:300}.nonprofit-steps li{margin-bottom:10px}.nonprofit-steps ul{margin:15px 0 0 20px;list-style:square}.nonprofit-section{padding:50px 0;background:#f5f5f5}.nonprofit-section h1{text-align:center}.nonprofit-section .dialog{width:640px;background:#fff;margin:30px auto 0;padding:30px}.nonprofit-section .dialog h2:first-child{margin-top:0}.nonprofit-section .dialog p:last-child{margin-bottom:0}.dialog.eLine truncated
|
||||
/* Page tweaks */
|
||||
/* Discussion tweaks */
|
||||
.discussion-timeline.wide {
|
||||
width: 920px;
|
||||
}
|
||||
.timeline-comment-wrapper > .timeline-comment:after,
|
||||
.timeline-comment-wrapper > .timeline-comment:before {
|
||||
content: none;
|
||||
}
|
||||
.preview-page {
|
||||
margin-top: 0;
|
||||
}
|
||||
.Box-title {
|
||||
padding: 0 30px;
|
||||
font-size: 2.5em;
|
||||
}
|
||||
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 119 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 31 KiB |
@@ -0,0 +1,232 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>
|
||||
[urlparams]: Creating Query Strings
|
||||
</title>
|
||||
<style>
|
||||
/* Page tweaks */
|
||||
.preview-page {
|
||||
margin-top: 64px;
|
||||
margin-bottom: 21px;
|
||||
}
|
||||
/* User-content tweaks */
|
||||
.timeline-comment-wrapper > .timeline-comment:after,
|
||||
.timeline-comment-wrapper > .timeline-comment:before {
|
||||
content: none;
|
||||
}
|
||||
/* User-content overrides */
|
||||
.discussion-timeline.wide {
|
||||
width: 920px;
|
||||
}
|
||||
</style>
|
||||
<link href="style.css" rel="stylesheet"/>
|
||||
</head>
|
||||
<body>
|
||||
<div class="page">
|
||||
<div class="preview-page" data-autorefresh-url="" id="preview-page">
|
||||
<main id="js-repo-pjax-container">
|
||||
<div class="clearfix new-discussion-timeline container-xl px-3 px-md-4 px-lg-5">
|
||||
<div class="repository-content">
|
||||
<div class="clearfix">
|
||||
<div class="Layout Layout--flowRow-until-md Layout--sidebarPosition-end Layout--sidebarPosition-flowRow-end">
|
||||
<div class="Layout-main">
|
||||
<div class="Box md Box--responsive" id="readme">
|
||||
<div class="Box-header d-flex border-bottom-0 flex-items-center flex-justify-between color-bg-default rounded-top-2">
|
||||
<div class="d-flex flex-items-center">
|
||||
<h2 class="Box-title">
|
||||
[urlparams]: Creating Query Strings
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="Box-body px-5 pb-5">
|
||||
<article class="markdown-body entry-content container-lg" id="grip-content">
|
||||
<p>
|
||||
<code>
|
||||
[urlparams]
|
||||
</code>
|
||||
is an object for creating query strings. It takes a series of key/value pairs at its inlet and outputs these as one symbol. Special characters in both key and value are URL encoded.
|
||||
</p>
|
||||
<h2>
|
||||
<a aria-hidden="true" class="anchor" href="#storing-data-with-add" id="user-content-storing-data-with-add">
|
||||
<span aria-hidden="true" class="octicon octicon-link">
|
||||
</span>
|
||||
</a>
|
||||
Storing Data with
|
||||
<code>
|
||||
[add(
|
||||
</code>
|
||||
</h2>
|
||||
<p>
|
||||
The
|
||||
<code>
|
||||
[add(
|
||||
</code>
|
||||
message needs at least two parameters: key and value. Any additional parameters are concatenated to the value with a space. A key/value pair is output with a
|
||||
<code>
|
||||
=
|
||||
</code>
|
||||
separator, while all key/value pairs are concatenated with a
|
||||
<code>
|
||||
&
|
||||
</code>
|
||||
. Special characters will be URL encoded.
|
||||
</p>
|
||||
<p>
|
||||
Adding a value with an already stored key will result in overwriting the existing value.
|
||||
</p>
|
||||
<p>
|
||||
<strong>
|
||||
Examples:
|
||||
</strong>
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
<code>
|
||||
[add key 1(
|
||||
</code>
|
||||
will store
|
||||
<code>
|
||||
key=1
|
||||
</code>
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
[add name My Name(
|
||||
</code>
|
||||
will store
|
||||
<code>
|
||||
name=My%20Name
|
||||
</code>
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
[add key 1(
|
||||
</code>
|
||||
,
|
||||
<code>
|
||||
[add name My Name(
|
||||
</code>
|
||||
,
|
||||
<code>
|
||||
[add key 2(
|
||||
</code>
|
||||
will store
|
||||
<code>
|
||||
key=2&name=My%20Name
|
||||
</code>
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
[add key @me with #hashtag(
|
||||
</code>
|
||||
will store
|
||||
<code>
|
||||
key=%40me%20with%20%23hashtag
|
||||
</code>
|
||||
</li>
|
||||
</ul>
|
||||
<h2>
|
||||
<a aria-hidden="true" class="anchor" href="#outputting-and-clearing-data" id="user-content-outputting-and-clearing-data">
|
||||
<span aria-hidden="true" class="octicon octicon-link">
|
||||
</span>
|
||||
</a>
|
||||
Outputting And Clearing Data
|
||||
</h2>
|
||||
<p>
|
||||
Issuing a
|
||||
<code>
|
||||
[bang(
|
||||
</code>
|
||||
message to
|
||||
<code>
|
||||
[urlparams]
|
||||
</code>
|
||||
will output the stored key/value pairs as string on its only outlet. If no data is stored, an empty symbol is sent.
|
||||
</p>
|
||||
<p>
|
||||
You can clear the stored object with the
|
||||
<code>
|
||||
[clear(
|
||||
</code>
|
||||
method.
|
||||
</p>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
function showCanonicalImages() {
|
||||
var images = document.getElementsByTagName('img');
|
||||
if (!images) {
|
||||
return;
|
||||
}
|
||||
for (var index = 0; index < images.length; index++) {
|
||||
var image = images[index];
|
||||
if (image.getAttribute('data-canonical-src') && image.src !== image.getAttribute('data-canonical-src')) {
|
||||
image.src = image.getAttribute('data-canonical-src');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function scrollToHash() {
|
||||
if (location.hash && !document.querySelector(':target')) {
|
||||
var element = document.getElementById('user-content-' + location.hash.slice(1));
|
||||
if (element) {
|
||||
element.scrollIntoView();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function autorefreshContent(eventSourceUrl) {
|
||||
var initialTitle = document.title;
|
||||
var contentElement = document.getElementById('grip-content');
|
||||
var source = new EventSource(eventSourceUrl);
|
||||
var isRendering = false;
|
||||
|
||||
source.onmessage = function(ev) {
|
||||
var msg = JSON.parse(ev.data);
|
||||
if (msg.updating) {
|
||||
isRendering = true;
|
||||
document.title = '(Rendering) ' + document.title;
|
||||
} else {
|
||||
isRendering = false;
|
||||
document.title = initialTitle;
|
||||
contentElement.innerHTML = msg.content;
|
||||
showCanonicalImages();
|
||||
}
|
||||
}
|
||||
|
||||
source.onerror = function(e) {
|
||||
if (e.readyState === EventSource.CLOSED && isRendering) {
|
||||
isRendering = false;
|
||||
document.title = initialTitle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.onhashchange = function() {
|
||||
scrollToHash();
|
||||
}
|
||||
|
||||
window.onload = function() {
|
||||
scrollToHash();
|
||||
}
|
||||
|
||||
showCanonicalImages();
|
||||
|
||||
var autorefreshUrl = document.getElementById('preview-page').getAttribute('data-autorefresh-url');
|
||||
if (autorefreshUrl) {
|
||||
autorefreshContent(autorefreshUrl);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
After Width: | Height: | Size: 20 KiB |