High Fidelity
Don't you just love movies where the characters and their situations remind you of your own life? No movie can replicate your life verbatim, but those that have realistic characters in realistic situations speaking realistic dialogue are my favorites. That's why "High Fidelity" works so well for me. It's movie with a rather simply, calm story about a guy who pines over lost loves and discusses pop music history with his friends and it's fascinating.
The film works on two levels: as a breezy portrait of modern-day GenX and GenYers who haven't really grown up yet and can still cherish, savor and appreciate spending most of the their time hanging with friends and rapping about their favorite CDs, TV shows and movies. So many people believe it's the little things in life that bring people together and make relationships work and the theme of "High Fidelity" exemplifies this when the main character Rob (John Cusack), is pining over his latest breakup and says, "It's not what YOU'RE like, it's WHAT you like."
Cusack delivers one of his finest performances to date playing a character that's an assemblage of virtually every other characters he's played before. He also co-wrote and co-produced the film which works to his advantage all that much more. Rob Gordon is a GenXer in his late 20's who owns a used record store called Championship Vinyl. Business isn't exactly booming and most of the customers are personal friends of Rob's and his two employees': the hyperactive, bossy music snob Barry (Jack Black) and the quiet, low-key, almost masochistic Dick (Todd Louiso). Barry insults potential customers and guilts surviving ones into buying more vinyl albums ("What do you mean you don't own 'Blonde on Blonde'!? For God's sake don't tell anyone!" he says as he stacks another record on a stack a schmuck customer's carrying thereby totally obscuring his vision).
"I can't fire these guys. I hired them for only three days a week and they just kept showing up. That was four years ago," says Rob.
As the film opens Rob's in the middle of a break-up with longtime girlfriend Laura (Iben Hjejle). They've been living together and she's slowly moving her stuff out. Where's she's going to and why she's leaving aren't clear. The film doesn't go into a lot of detail and drama over their relationship, not at first at least. Instead, it's a catalyst for Rob to tell us all about himself - how he is doomed to be left, rejected and heartbroken. He launches into a "Top Five" list of his all-time most painful breakups, from the girl he went out with for three days in a row in seventh grade to the present day.
The first half to two-thirds of the film is lighthearted character humor with the occasional check back to Rob and Laura's relationship, or what's left of it. The film does an excellent job in balancing these two separate stories of Rob's life within and outside of his relationship. There's so many great scenes of Rob hanging with Dick and Barry at bars and clubs making lists of the best songs ever and pondering about what it'd be like to date a musician who might include one of their jokes in the liner notes of their album. There's also flashbacks to the other breakups on his list, which stay within the confines of the lighthearted story and never get too serious despite the fact they're all about rejection. The only really "serious" aspects to the film are when Rob and Laura talk and argue about whether they can patch things up and what the future holds. The story will take a very serious turn as it winds down, teetering on melodrama but makes amends by having a slightly unusual but charming ending.
If I had to cite the film for any major flaws it would be for how it deals with Rob and Laura's post-relationship relationship. This is a romantic comedy to be sure, which means a touch of the intense is a given but the screenplay is a just a bit choppy. The reason they broke up has to do with Rob having slept with someone else and Laura having an abortion without even telling him. Little detail is revealed about this aspect which I think is a necessity. If they were such a great couple why did Rob cheat and who did he do it with? And what's the deal with this new guy Laura's seeing? He's a New Age guru named Ian who eats a lot of organic food, listens to "whatever world music is popular this week" and supposedly has the best sex in the world (played by Tim Robbins with tongue firmly in cheek). For a film that's so relatable this seems just a bit out of place, although when we examine Rob's other breakups he's always been dumped for a guy who's a total jerk.
I guess it's not the point, really. The payoff comes not in Rob's efforts to get Laura back (however unsuccessful and/or funny they may be), but in those moments where he opens up inside and says the reasons he REALLY misses Laura. Of course they involve the little things she did and other such characteristics which keeps with the film's theme. These romantic aspects to "High Fidelity" work on so many more levels and are so much more appreciable than most dramas try to do so much more forcibly (i.e. "The English Patient," "Bounce").
It's difficult to say what the best aspect to "High Fidelity" is. I love how the characters talk about real things and have real problems. I love the production design that makes every single set in the film look like a real place (the fact most of it was shot on location may be a reason why). And I absolutely adore the technique of having Rob speak directly to the camera, not just in scenes where he's alone in a room but scenes where there's people all around him. Everyone wants to have an anonymous outlet and we play that role for Rob. There's also a some smart fantasy scenes such as when Rob dreams of confronting Ian or when he asks Bruce Springsteen if he's one of "The Boss's" songs come to life.
If there's any justice young men around the world will realize "High Fidelity" is probably the perfect film to watch on a date because it will inspire so much conversation. As Rob said, it's not what you're like, it's WHAT you like and I can't imagine anyone not liking this film.
GRADE: A
[Critic's note: This is a re-write of a capsule review I initially wrote back in April. It's the best movie I've seen so far this year and I thought I should give it a proper full-length review if I name it the best movie of 2000. So I rented it and realized it was even better than I had given it credit for. So here's a proper, honest review. The original capsule review can be read here.]
Think of it as a really mature Kevin Smith flick (but not TOO mature), there's tons of discussions analyzing pop culture and music and relationships with an added dash of brash comedy and the like. The comedy and the drama (the main plot about Cusack's recent break-up with his long-time girlfriend and his attempt to win her back) balance nicely - so that you have one scene of serious human interaction and the next is banter about "Top five side A's, track 1's."
It's a cute movie that has more to offer than the typical Hollywood romantic comedy. Probably one of the best [first] date flicks to be released in a long time.
***** Chad'z Movie Page is back! In-depth reviews of nearly all mainstream films playing at your local cineplex. There's also reviews of a few classic films and some of your personal favorites.
The review above was posted to the
rec.arts.movies.reviews newsgroup (de.rec.film.kritiken for German reviews).
The Internet Movie Database accepts no responsibility for the contents of the
review and has no editorial control. Unless stated otherwise, the copyright
belongs to the author.
Please direct comments/criticisms of the review to relevant newsgroups.
Broken URLs inthe reviews are the responsibility of the author.
The formatting of the review is likely to differ from the original due
to ASCII to HTML conversion.
Related links: index of all rec.arts.movies.reviews reviews


Importance of a team health check
Great success and achievements do not come overnight, and are rarely the result of a person alone, but rather by a group of individuals what we commonly call a “team”.
The ability to work together, as a true team, is far better than the expertise of each individual.
But what makes a team great?
For instance, Google did its own research and found these 5 characteristics:
- Psychological safety: Can we take risks on this team without feeling insecure or embarrassed?
- Dependability: Can we count on each other to do high-quality work on time?
- Structure & clarity: Are goals, roles, and execution plans on our team clear?
- Meaning of work: Are we working on something that is personally important for each of us?
- Impact of work: Do we fundamentally believe that the work we’re doing matters?
The Team Health Check is a great way to take the temperature of a team. And that’s a starting point for improvement.
Goals of a Team Health Check
By doing regular checks, teams should be able to:
- Increase trust and psychological safety within the team
- Have better relationships between teammates
- Create a culture of constructive criticism and feedback
- Raise issues and fix them quickly
- Share their feelings more easily and frequently
How to do a Team Health Check
There are many ways to run a Team Health Check.
Use a Niko-Niko Calendar
A Niko-Niko Calendar is a simple calendar with everybody’s name on it. At the end of the day, each team member is invited to share their mood of the day, sticking smilies happy :-), straight :-, or frowning :-( or color stickers on it.
Create an Emotional Seismograph with the team
During, one of your retrospectives, try the Emotional Seismograph format.
It’s an easy and fun way to get an overview of your team’s health.
Run Team Health Check like Spotify
The Team Health Check has been popularized by Spotify with their Squad Health Check model, where people are asked to score 11 different areas (Easy to release, Suitable process, Tech quality, Value, Speed, Mission, Fun, Learning, Support, Pawns or players) on three levels:
- green: good
- yellow: some problems
- red: really bad
Want To Improve Your Agile Retrospectives?
Download our free Retrospective Outcomes Canvas
Get our free retro canvasHow to leverage a Team Health Check
As James Willis, an Agile coach, told us, “Simply doing a health check things will not change anything, just like looking at a metric every day, won’t improve it”. So, how do you leverage your Team Health Check?
Share the results openly with your entire team.
It can be at a weekly, monthly or quarterly meeting, during a retrospective, …  But of course, it’s not enough.
Discuss the issues
If your team is in a bad mood or if a team member is constantly down, you need to raise the issue and try to find a solution. Talking about the “bad” results of a Team Health Check at a team meeting is the perfect opportunity to identify issues and discuss potential solutions. You can trust your team to know what they need to feel better.
Celebrate successes
Team Health Checks aren’t only issues alerts: they can demonstrate that your team is currently in a great mood. Like for bad moods, taking the time to discuss it with your team will help you understand the causes and better know your team. It can also be a great opportunity to celebrate successes or good news.
Going further: Suggest improvements from a Team Health Check
As proposed by Aaron Gallagher from his article about Team Health Check, you can combine a Team Health Check with a Perfection Game.
“[During a Perfection Game], people have to rate the value that they received on a scale from 1 to 10, based on how much value they think they could add themselves by improving the product or service. For example, when there is nothing that they think they can improve, they should rate with a 10. If they think that they could make it twice as valuable, they should give it a 5.
(…)
If somebody rates the value as 5, (s)he will have to provide suggestions on how to make it a 10. People can only withhold points if they provide suggestions to improve it.”
From Ben Linders, https://www.benlinders.com/2014/getting-feedback-with-the-perfection-game/
Starting from a Team Health Check, Aaron proposes to think about what we could do so that people would feel better in the workplace.
Your turn!
I’m still amazed by the fact that however simple the concept of Team Health Check is, it’s still rarely used by team leaders. So, start now with your first Team Health Check, and if you fall in love with it, use TeamMood to do it on a regular basis.
Run Your First Team Health Check
Thank you to James Willis for his previous comments and review of the article.
Illustrations from https://undraw.co


It represents a modern, sophisticated, and elegant woman, a woman with a vision for the future of our planet and its preservation.


Socket.io-file 2.x File Upload Example
Select file and click upload button to upload.
Multiple upload also supports.


Golden Child tease preview of 'Lately' from upcoming 'RE:BOOT' album
by germainejGolden Child have revealed a preview of their track "Lately" from their upcoming 'RE:BOOT' album.
The music trailer reveals a dance track with electric guitar elements and an epic feel from their upcoming first full album 'RE:BOOT'. As previously reported, Golden Child are returning with a more mature, charismatic concept in chic black suits.
'RE:BOOT' is set for release on November 18 KST. What do you think of the music trailer?


i may reblog this every day for the rest of my life. or maybe i’ll just start a new tumblr called Dolph and His Action Nips.
it’s done
Promises vs Eventual Values
A few times on Twitter, I’ve complained that Promises are a poor conceptual stand-in for Eventual Values.
A colleague of mine pointed out that I tweet this or something like it every few months. He’s correct, I do.
The responses usually flow in saying something to the effect of “Well,
if you’re not sure if a thing is a Promise or not, just use
Promise.resolve(thing)
and now you’re guaranteed to get either the
Promise, or a Promise that resolves to the thing.”
And it’s true, that is “a” solution of sorts. It solves the “I don’t know if this is a Promise or not” problem by ensuring that you definitely do have a Promise, and not a normal value.
My problem, though, is that I’d really like to write code that interacts with values, and not Promises, and leave the machinery to the computer to work out.
Eventual Values
An Eventual Value is a value that is not yet resolved. However, by design, it is mostly indistinguishable from a “normal” value. Here’s an example:
// promise code
function add5 (x) {
return x + 5
}
function add5Promise (x) {
return Promise.resolve(x).then(function (x) {
return add5(x)
})
}
function someNumberLater () {
return new Promise(function (resolve) {
databaseConnector.get('numeric value').then(function (x) {
resolve(x)
})
})
}
add5Promise(someNumberLater()).then(function (xplus5) {
console.log('the number plus 5 is %d', xplus5)
})
With Eventual Values, this would look like the following:
// eventual value code
function add5 (x) {
return x + 5
}
function someNumberLater () {
databaseConnector.get('numeric value'))
}
console.log('the number plus 5 is %d', add5(someNumberLater()))
If you imagine getting a few numbers, any of which might be as yet unresolved, it’s even more annoying:
// promise code
function addThreeNumbers (x, y, z) {
return Promise.all([x, y, z]).then(function (numbers) {
return Promise.resolve(numbers[0] + numbers[1] + numbers[3])
})
}
// eventual code
function addThreeNumbers (x, y, z) {
return x + y + z
}
Criteria
- Eventual Values can be interacted with like normal values.
- If an Eventual Value is part of a simple value operation, then that expression resolves to a new Eventual Value which resolves when all its Eventual Values are resolved.
That’s it. You interact with Eventual Values as if they’re normal synchronous values, and the machine takes care of waiting where it’s necessary and appropriate.
But Zalgo!
Indeed, this is Zalgo’s purest form. But let’s clarify exactly what’s wrong with the maybe-sync anti-pattern.
The problem with Zalgo APIs is that they’re hard to reason about. Given code like this:
someApi(function () {
console.log('foo')
})
console.log('bar')
In a sync callback API, you know that this will always print out
foo\nbar
. In an async callback API, you know that this will always
print out bar\nfoo
. This predictability is important for human
brains.
This is why the built-in dezalgo of Promises A+ is so important.
new Promise(function (resolve) {
console.log('foo')
resolve()
})
console.log('bar')
This code must either (a) always print foo\nbar
, or (b) always print
bar\nfoo
. Since Promise resolution can be async, it must be
async all the time, or else this predictability constraint is
violated.
With Eventual Values, Zalgo is not a problem, because whether things happen synchronously or asynchronously, they always happen in the same predictable order.
function getFoo () {
return new Eventual(function (resolve) {
setTimeout(function () {
resolve('foo')
})
})
}
function getBar () {
return new Eventual(function (resolve) { resolve('foo') })
}
console.log(getFoo())
console.log(getBar())
In this case, it will always print foo\nbar
, even though foo
is
resolved later, and bar
is resolved immediately. Eventual Values
behave like synchronous values.
Introspection, Operators, Etc.
It’d be nice to have some magic isEventual
operator that could tell
you if a thing was already resolved or not.
Or even a wasEventual
operator to tell you whether this thing you
have was ever waited upon.
But mostly, while it’s great to be able to introspect programs, it’s even better not to have to need to introspect programs. We don’t have memory address introspection in JavaScript, and no one seems to mind. Inspecting Eventual Values should be akin to peering into the dark machinery of the VM; something that’s useful once in a while, and certainly interesting from an academic point of view, but not in the normal day to day activities of the typical JavaScript programmer.
Language design is hard. It may be that there’s some really good
reason for at least having a special operator or something to say
“Yes, I would like any Eventual Values to be resolved before calling
this function.” Maybe use functionE
instead of function
, I don’t
know. I don’t care. I just want to stop having to stick my nose in
the machinery. Ideally, we’d almost never have to care, because it
only matters at the very edges of a program, when data is sent to the
DOM, or written to a file or socket or terminal.
Implementation
This cannot be implemented in userland, nor should they be. Eventual Values are a language feature. Promises are an API, and easy to implement in userland.
Promises will never grow into Eventuals, because they are fundamentally different things, even though they implement a similar pattern.
Looking Async
I’ve argued in the past, quite forcefully, that synchronous code should look synchronous, and async code should look async. But again, like Zalgo, let’s not conflate “a good rule of thumb for API design” with “a language feature that would make our lives better”.
If you dig one layer deeper, you find that the only reason asynchronous code needs to look asynchronous is that we continually rely upon our human brains to deal with the timing and synchronicity of our programs.

Anti-Promises
Promises are neat. They’re a terse and expressive way to chain together a series of potentially asynchronous actions in a way that’s relatively easy for a meat brain to make sense of.
Also, it’s quite nice how they invert control differently than callbacks. With a callback, the caller creates and passes a token to the API, with the contract that the API provider will use that token to indicate done-ness. With Promises, the API provider creates and passes a token to the caller, with the contract that the API provider will use that token to indicate done-ness.
It’s a subtle difference, but one that many people find easier to reason about. That’s fine.
However, if you have a function that takes a list of arguments, any of
which may potentially be promises, and then needs to only act once all
of those promises are resolved, it gets tedious quite fast. Promises
tend to expose quite a lot of boilerplate to the user in these types
of situations. And, since the only straightforward solution is to
either turn all things into Promises, or manually check each for
Promise-ness, one potentially ends up introducing a lot of
nextTick()
delays unnecessarily.
I don’t dislike Promises. But I do long for Eventual Values, and Promises are not those.


Day 1 Reminder
Human Interaction and Organisation
Case Studies
http://martinsumner.github.io/presentations/operations_iwc_d2.html#/
Parallels from Agile for Improvement
Terminology is simplistic
Needs are user-driven and evolving
Measuring flow is important
Need to be adaptable to confusing and unexpected events
Unavailability - related to process, organisation and bias
Direct communication with users should not be forgotten
http://martinsumner.github.io/presentations/operations_iwc_d2.html#/
'They were all creating work for each other'
Rivals or subordinates
ITIL's 17 Service Management Practices
See also Conway's Law
Genchi Genbutsu (現地現物)
Structure is defined by flow not function
Management support flow
Leadership ensure Kaizen (改善)
Iteration and Feedback Loops
Stop
Build something better/worse from scratch
Make things incrementally better/worse
Magnitude and directness
Relative priority
Ham and eggs
Alignment of activity with purpose
Freedom from context switching
Intrinsic motivation not allusion to incompetence
Interference is correlated to confidence
Confidence via assertion or openness
The problem of power when powerless
Judgement on perception
How much to communicate?
How many to communicate with?
Collaboration and distance
Challenge is part of collaboration
Triage is a fallacy?
Agile Document Lifecycle
Remove mundane tasks
Increase reliability to mundane tasks
Need for collaboration with the machine
The fight for attention and awareness
Independent (manual) exploratory tests
Pyramid of test
Inverted pyramid of test
Although not as bad as ....
For 3 months the application has been unusable after 2pm
https://aws.amazon.com/message/41926/
https://www.usenix.org/system/files/nsdi20-paper-brooker.pdf
“ Engineer Bob Ebeling in October 1985 wrote a memo—titled "Help!" so others would read it ”
“ I am appalled. I am appalled by your recommendation. ”
“ a second conference call was scheduled with only NASA & Thiokol management, excluding the engineers. For reasons that are unclear, Thiokol management disregarded its own engineers' warnings and now recommended that the launch proceed as scheduled; NASA did not ask why.  ”
“ When one of the dogs goes lame, and begins to slow the others down, they are shot. They are then chopped up and fed to the other dogs. The survivors work harder, not only because they’ve had a meal, but also because they have seen what will happen should they themselves go lame. ”
“ It can only go live when you guarrantee it can never fail. ”
I have the controls
I don't have control of the airplane any more now
The inputs cancelled each other out and triggered a "dual input" warning
Soon after this, Robert said to himself, "climb" four times. Bonin heard this and replied, "But I've been at maximum nose-up for a while!" When Captain Dubois heard this, he realized Bonin was causing the stall, and shouted, "No no no, don't climb! No No No!"
Documentation as a signal
That's not my improvement
“ Can we shut down the system to change a database config setting? ”
“ We don't really have monitoring, no-one knows where Brendan is ”
“ The database won't re-start - we'll get back to you ”
“ The problem is they're using an old kernel that doens't understand NUMA ”
The emergence of the borg
Take time over everything else
Retention identified as critical issue
Do what everyone wants - and everyone leaves
“ There are a wealth of benefits that come from moving large systems like e-RS and DoS to the cloud. Costs are lowered, reducing pressure on the public purse ”
Failure to plan for migration failure
API abuse
The network decision
The six month promise
Magnitude and directness for technical debt
When operations is the project
Assume every interaction is the potential start of an incident


The PlanGenerator class is used to generate the plans between defined positions. Currently there are 6 positions or angles for each side defined:
L - Low position: The gripping position of the object in a box
Distance to L depends on the offset from the box to the object and the gripping depth
F - Front position: Similar to L but used for placing the item. It is the same for all items.
P - Prepare position: The position above F, depending on the offset between F and the dimensions of the box
I - Initial position: Initial position of an arm. Often used as starting point
O - Hand Over position: Specific position to pass items from one hand to the other.
Generates plans for the consignment2 scenario
| Parameters: | side (str) – creates plans for the given side |
|---|
General function for plan generation, currently used by the consignment scenario.
Creates the plans and most of the time also the reversed plans for:
Computes the plans F to P and P to F and saves the angles of the P “position” for the box
| Parameters: |
|---|
Computes the plans H to O and O to H
| Parameters: |
|---|
Computes the plans L to H and H to L and returns the angles of the H “positions”
| Parameters: | |
|---|---|
| Returns: | angles of the computed H positions |
| Return type: | list(dict({str:float})) |
Computes the plans O to P and P to O
| Parameters: |
|
|---|
Moves the arm to the P angles computed by getFP
| Parameters: | |
|---|---|
| Returns: | True if the motion was successful |
| Return type: | bool |


---
layout: home
author_profile: true
---
I'm a postdoctoral fellow in the lab of [Dr. Isabelle Peretz](http://www.peretzlab.ca/), affiliated with the Department of Psychology at the University of Montreal, the [International Laboratory for Brain, Music, and Sound Research (BRAMS)](https://www.brams.org), and the [Centre for Research on Brain, Language, and Music (CRBLM)](http://crblm.ca/).


{% load static %}
This graph is generated using the views.metal_cdf function. It looks for all the traces of type vastplace_example, and all the sensor they contain. It computes the CDF of all the values of the sensors of type 'metal' it discovered
This graph is generated using the views.metal_bar function. It looks for all the traces of type vastplace_example, and all the sensor they contain. It computes the bar graph of values depending on wether they are above or below 30000
This graph is generated using the views.metal_step function. It looks for all the traces of type vastplace_example, and all the sensor they contain. It then represent the value of the sensor depending on its timestamp
This item is the response to a rest request. It looks for all the traces of type vastplace_example, and all the sensor they contain. It stores the number of element in a dictionnary depending on wether they are above or below 30000
This map is generated by calling the views.tile_map function. It looks for all the traces of type vastplace_example, and all the sensor they contain. It then locates the sensors of type 'metal' using the GPS measurements in the trace and locates them in tiles. In each tile, the average sensor value is calculated, and displayed as letters and colorcode
This map is generated by calling the views.cell_map function. It looks for all the traces of type vastplace_example, and all the sensor they contain. It then attributes the sensors of type 'metal' to the closest geographic feature in the area, using openstreetmap data. In allocates the measurements to cells, and creates a colorscale depending on wether the interpolated value of each cell is closer from 6000 or 60000


Save Big On The Unlocked Google Pixel 3 & Pixel 3 XL
Today, you can save big on both the Google Pixel 3 and Pixel 3 XL. With the Pixel 3 coming in at $349 and the Pixel 3 XL coming in at $399. This is actually lower than these are supposed to be on Black Friday. Making this a really great time to pick up a new Pixel 3.
The Pixel 3 and 3 XL were released last year by Google. These aren't the latest and greatest any longer, but still worth picking up. Since these were released as flagship smartphones, they do still stand tall with the other smartphones on the market today.
These sport a Snapdragon 845 chipset, 4GB of RAM and 64GB of storage. The smaller Pixel 3 has a 5.5-inch display with a 1080p resolution, while the Pixel 3 XL has a 6.3-inch Quad HD+ display. The other major difference is the battery size, where the Pixel 3 has a 2915mAh battery and the Pixel 3 XL sports a larger 3730mAh battery pack.
Advertisement
Both of these smartphones are still getting updates, and will for at least another year. Google promises two years of updates, but if the original Pixel is anything to go by, it will likely get it for a bit longer than that.
But more importantly, it is getting camera updates still. Which includes the improved Night Sight and Astrophotography modes that came with the Pixel 4 just a couple months ago. That makes this a really great smartphone to pick up. Seeing as it does still have the best smartphone camera on the market. And it's not even a close race right now.
The Pixel 3 and Pixel 3 XL are both unlocked. These can be used on all four US carriers. That includes AT&T, Sprint, T-Mobile and Verizon.
Advertisement
You can pick up the Google Pixel 3, or the Pixel 3 XL through this sale over at Amazon right now by clicking here.
Google Pixel 3 - Amazon - $349
Google Pixel 3 XL - Amazon - $449
Advertisement


This is the old site for Steve's Utilities
Click Here
to go to the New Site
Click Here
for the Original Old Website


| ReactNativeScratch |
|
50% |
5/10 |
100% |
0/0 |
16.67% |
1/6 |
55.56% |
5/9 |
| ReactNativeScratch/src/components/general |
|
100% |
15/15 |
75% |
6/8 |
100% |
3/3 |
100% |
15/15 |
| ReactNativeScratch/src/components/ui |
|
100% |
88/88 |
83.82% |
57/68 |
100% |
17/17 |
100% |
81/81 |
| ReactNativeScratch/src/constants |
|
100% |
0/0 |
50% |
1/2 |
100% |
0/0 |
100% |
0/0 |
| ReactNativeScratch/src/containers/Launch |
|
88.89% |
8/9 |
100% |
0/0 |
80% |
4/5 |
100% |
8/8 |
| ReactNativeScratch/src/containers/auth |
|
100% |
2/2 |
100% |
0/0 |
100% |
1/1 |
100% |
2/2 |
| ReactNativeScratch/src/containers/auth/Login |
|
34.38% |
11/32 |
0% |
0/8 |
35.71% |
5/14 |
36.67% |
11/30 |
| ReactNativeScratch/src/containers/recipes |
|
62.5% |
10/16 |
66.67% |
4/6 |
60% |
3/5 |
62.5% |
10/16 |
| ReactNativeScratch/src/containers/recipes/Browse |
|
23.53% |
8/34 |
12.5% |
2/16 |
20% |
3/15 |
22.58% |
7/31 |
| ReactNativeScratch/src/containers/recipes/Card |
|
11.36% |
5/44 |
6.38% |
3/47 |
10% |
1/10 |
11.9% |
5/42 |
| ReactNativeScratch/src/containers/recipes/Listing |
|
24.24% |
8/33 |
20% |
3/15 |
14.29% |
2/14 |
26.67% |
8/30 |
| ReactNativeScratch/src/lib |
|
9.6% |
17/177 |
0% |
0/145 |
2.7% |
1/37 |
10.63% |
17/160 |
| ReactNativeScratch/src/redux/recipes |
|
36.36% |
4/11 |
14.29% |
1/7 |
40% |
2/5 |
40% |
4/10 |
| ReactNativeScratch/src/redux/router |
|
75% |
3/4 |
66.67% |
2/3 |
100% |
1/1 |
75% |
3/4 |
| ReactNativeScratch/src/redux/sidemenu |
|
88.89% |
8/9 |
60% |
3/5 |
100% |
4/4 |
88.89% |
8/9 |
| ReactNativeScratch/src/redux/user |
|
20.59% |
7/34 |
11.76% |
2/17 |
29.41% |
5/17 |
25% |
7/28 |
| ReactNativeScratch/src/theme |
|
100% |
11/11 |
60% |
6/10 |
100% |
1/1 |
100% |
11/11 |


--- title: 'PhoneGap 2.x-3.x from PhoneGap Day EU 2012' author: Steve Gill format: html tags: - Event - Video - PhoneGap Day ---
Thank you to everyone who attended PhoneGap Day EU and made it such a huge success. Here's the next video in our series.
Brian LeRoux presents about what is upcoming for PhoneGap and Cordova post 2.0. If you are interested in the future of PhoneGap, this is a talk you don't want to miss!
Learn more by watching the recording from PhoneGap Day EU below and view Brian's slides!


Help
Rave Web Service Helper
Account information
Request Type
GET
POST
Reset
Download
Download Lock
Confirm Account Information
GET Type
Clinical Data
Metadata
Host
Study OID
Form OID
Data Type
regular
raw
Post Contents
Request


Overview
Shlaer-Mellor models are composed of domains which encapsulate classes arranged in class diagrams. A class may have a state machine which sequences the dynamic lifecycle of the class. Domain functions provide synchronous services to be invoked by other communicating domains. Action language defines the processing which occurs in domain functions, state actions and class operations.
The following tutorial walks a new user through the steps of creating a Shlaer-Mellor model in BridgePoint. BridgePoint uses UML notation to represent Shlaer-Mellor elements. A UML Component represents a domain. Inter-domain communication is modeled through UML Interfaces. Terminators are modeled as Ports containing Operations which serve to represent terminator services.
The domain being modeled is an abstract arithmetic logic unit (ALU) of a simple hand-held calculator.
The tutorial will follow a logical sequence. Note that it is fine to define the functions and interfaces before the class diagram and state machines.
-
xtUML Project
-
Domain Component
-
Class Diagram
-
State Machines
-
Domain Functions
-
Interfaces
xtUML Project
-
Create an Eclipse xtUML Project with the name of the new domain ('ALU' in this case) by right-clicking in the Model Explorer view (in empty space) and selecting New > xtUML Project.
-
Click Next to set the model compiler to WASL Exporter.
Domain Component
-
Create a top-level package (with the same name as the domain) by right-clicking on the project in the Model Explorer view and selecting New > Package.
-
Inside the top-level domain package, create a component with the same name. This can be done by right-clicking on the ALU package and selecting New > Components > Component. Or, after opening the Graphical Editor (canvas) of the ALU package by double-clicking on it and then by clicking on Component in the Components drawer of the Palette and drawing the component on the editor canvas (by clicking and dragging the mouse cursor) of the package.
Class Diagram
-
Inside the domain component, create a package called 'classes'. Do this by right-clicking on the ALU component and New > Package and give the package the name 'classes'.
-
Double-click on the classes package to open the class diagram canvas.
-
Inside the classes package, create classes 'Calculator', 'Display', 'Memory', and 'Operation' by clicking on Class in the Classes drawer of the Palette and then drawing each class on the canvas.
-
After you create each class, select the class (by clicking on it) to set the class key-letters in the Properties view to 'CALC', 'DISP', 'MEM' and 'OP' respectively.
|
Note
|
The class key letters default to be the same as the class name. |
Move a class around by clicking and dragging it. Arrange the class diagram sensibly in anticipation of the associations to be added between related classes.
Resize the classes by clicking on the class to select it and then clicking and dragging a corner or a side.
Attributes
-
In the Model Explorer right click on the Calculator class and New > Attribute. Name the attribute 'calc_id'.
-
Right click on the calc_id attribute and Add to Identifier… and add this attribute to the primary identifier (*1).
-
Add another attribute called 'current_op'.
-
Right click on the Memory class and add an attribute called 'value'.
-
Right click on the value attribute and Set Type…. In the Type Selection window choose real as the data type for this attribute.
Add other attributes and practice adding and removing attributes from identifiers. Create an attribute and set up a secondary identifier.
Associations
-
Establish a one-to-one association between the Calculator and Display classes by clicking on the Classes drawer in the Palette and then clicking on the Association widget.
-
Notice the cursor turn into a little "plug" when you move it onto the canvas.
-
Click inside the Calculator class and drag to inside the Display class and then "let go".
-
Fill out the Edit association edit form.
-
Make the Association Number '1'.
-
Edit Calculator Unconditionally prints_answer_on One Display.
-
Edit Display Unconditionally displays_value_for One Calculator.
-
Leave the Association check box empty.
-
Change Use identifier from to select class Calculator.
-
Finish.
-
-
If the association is drawn as a diagonal straight line ("oblique routing"), this can be changed by right-clicking on the association and selecting Routing > Rectilinear Routing.
-
In the Model Explorer view under the Display class, right click on the newly added calc_id referential attribute and Add to Identifier… and make this attribute the primary identifier (*1).
-
-
Establish a 1:1 unconditional association between the Calculator and Memory classes such that:
-
Calculator Unconditionally stores_value_for_calculation_in One Memory.
-
Memory Unconditionally stores_value_for One Calculator.
-
Use identifier from Calculator.
-
In the Model Explorer view under the Memory class, add the newly formed calc_id referential attribute to the primary identifier.
-
-
Establish an associative relationship between the Memory and Display classes using the Operation class as the associative class.
-
Select the Association widget from within the Classes drawer of the Palette.
-
Draw the association from the Memory class to the Display class.
-
Supply (symmetric) role phrase 'combines_with' to both role phrase boxes.
-
Check the Associative box and select Operation as the Associative class.
-
Check the Formalized box and select both primary identifiers.
-
Combine the duplicate referential attributes in the Operation class by right-clicking on the first calc_id and Combine With… and then select the other calc_id attribute in the Combine With chooser.
-
Association lines can be moved and bent using the mouse and clicking on end-points or bend-points.
Role phrases can be repositioned by dragging them with the mouse.
Operations
-
Add an operation called 'init' to the Calculator class by right clicking on the Calculator class in the Model Explorer view or by right-clicking on the Calculator class on the canvas and New > Operation.
-
Add an operation called 'store' to the Memory class.
-
Add a parameter to the store operation by right-clicking on the store operation in the Model Explorer view and New > Parameter and giving it the name 'value'.
-
Set the type of the value parameter by right-clicking on it and Set Type… and choosing real.
-
-
-
Add an operation called 'evaluate' to the Operation class.
-
Add an operation called 'clearDisplay' to the Display class.
-
Add action language to the operations by double-clicking on the operations in the Model Explorer view. An action language editor will be opened.
State Machines
-
Add a state machine to the Display class by right-clicking on the class and New > Instance State Machine.
-
Double-click on the Display class to edit the newly created state machine.
-
Inside the Display state machine, create a state by clicking on State in the Palette and drawing a state on the canvas. Give it the name 'displaying_memory_value'.
-
Add another state called 'toggling_sign' in the same way.
-
Add an event to the Display state machine by right clicking on the state machine canvas or by right-clicking on Instance State Machine under the Display class in the Model Explorer view. Then New > Event. Name the event 'keypress'.
-
Add a transition from the displaying_memory_value state to the toggling_sign state.
-
Select Transition from the Palette.
-
Drag the mouse cursor from inside the displaying_memory_value state to the inside of the toggling_sign state.
-
Right-click on the newly added transition and Assign Event…. Choose the keypress event from the selection box.
-
Arrange and bend the transition and adjust the position of the event as desired.
-
-
Add action language to the states by double-clicking on the states on the canvas or in the Model Explorer view. An action language editor will be opened.
Domain Functions
-
Inside the domain component, create a package called 'functions'. Do this by right-clicking on the ALU component and New > Package and give the package the name 'functions'.
-
Inside the 'functions' package, New > Function to add domain functions 'clear' and 'key'.
-
Right click on the function 'key' and New > Parameter to add parameter 'code'.
Scenarios
-
Inside the domain component, create a package called 'scenarios' the same way that you created the 'functions' package.
-
Inside the scenarios package, New > Function to add initialization function 'init'.
Interfaces
Interfaces define the signatures for all inbound messages (domain services) and outbound messages (terminator services). Data types of parameters on messages must be shared between domains. Thus, these shared data types are defined outside the component together with the interfaces.
-
Inside the top-level domain package (ALU), create a package 'Shared'. This package will be a peer to the domain component 'ALU'.
Shared Data Types
-
Inside the Shared package, New > Types > User DataType. Name the new type 'keyscan'.
-
Be sure that keyscan is based on type integer. This can be confirmed in the Properties view when the keyscan type is selected.
-
Terminator Interfaces
-
Add an interface called 'disp' by right-clicking on the Shared package and New > Components > Interface.
-
Add an operation to the disp interface by right-clicking on disp and New > Operation. Give the operation a name of 'result'.
-
Ensure the result Message Direction is ToProvider when the result parameter is selected. This can be confirmed in the Properties view when the keyscan type is selected.
-
Add New > Parameter 'value' to the result operation.
-
Set Type… of parameter 'value' to real.
-
-
Add Operation 'error'.
-
Ensure error Message Direction is ToProvider.
-
Add parameter 'message'.
-
Set Type… of message to string.
-
-
Domain Service Interface
-
Add an interface called 'ALU' in the same way that you added other interfaces to the Shared package.
-
Publish domain functions into the ALU interface.
-
In the Model Explorer view, navigate to the functions package containing the domain functions defined for this component.
-
Multi-select the key and clear functions by clicking on one and the holding the shift key while clicking on the other.
-
Right-click and Publish To Interface… and click OK.
-
Choose the 'ALU' interface in the Publish To Interface Selection window.
-
Terminator and Domain Ports
-
Create ports for the terminator and domain interfaces by opening the top-level domain package and adding them.
-
Double-click on the top-level ALU package so that you can see the ALU component on the canvas editor view. Resize and rearrange as needed.
-
Provide the domain services port.
-
In the Palette select Provided Interface from within the Components drawer.
-
Click and drag from inside the ALU Component to just outside the component boundary.
-
Right-click on the newly added port (interface reference) and Formalize. Select the ALU interface from the Interface chooser.
-
-
To provide the terminator services port.
-
In the Palette select Required Interface from within the Components drawer.
-
Click and drag from inside the ALU Component to just outside the component bounary.
-
Right-click on the newly added port and Formalize. Select the disp interface from the Interface chooser.
-
-
Rename the ports from their default names, 'Port1' and 'Port2', to match the domain names (key letters) ('ALU' and 'disp').
-
Generate WASL
-
There are two ways to generate WASL from the graphical user interface. (There are other ways using a command line interface (CLI).)
-
Right-click on the Eclipse xtUML Project and Build Project. See the export log scroll in the Console view.
-
Click on the Eclipse xtUML Project to be sure it is selected. Then click the Hammer icon in the Tool Bar. See the export log scroll in the Console view.
-
Open the Resource Perspective (via the widget indicated) or via Window > Perspective > Open Perspective > Other… > Resource, and navigate to the
/wasl
folder of the project to browse the generated WASL.
-


EugeneLab
Design
How-To
Contact
The EugeneLab Development Team
Jenhan Tao
Front-End Developer
Erik Lindgren
Front-End Developer
Ernst Oberortner
Project Lead
Douglas Densmore
Principle Investigator


For four centuries the noise of controversy has raged round the cradle of Typography. Volumes have been written, lives have been spent, fortunes have been wasted, communities have been stirred, societies have been organised, a literature has been developed, to find an answer to the famous triple question: “When, where, and by whom was found out the unspeakably useful art of printing books?” And yet the world to-day is little nearer a finite answer to the question than it was when Ulric Zel indited his memorable narrative to the Cologne Chronicle in 1499. Indeed, the dust of battle has added to, rather than diminished, the mysterious clouds which envelope the problem, and we are tempted to seek refuge in an agnosticism which almost refuses to believe that printing ever had an inventor.
It would be neither suitable nor profitable to encumber an investigation of that part of the History of Typography which relates to the types and type-making of the fifteenth century by any attempt to discuss the vexed question of the Invention of the Art. The man who invented Typography was doubtless the man who invented movable types. Where the one is discovered, we have also found the other. But, meanwhile, it is possible to avail ourselves of whatever evidence exists as to the nature of the types he and his successors used, and as to the methods by which those types were produced, and possibly to arrive at some conclusions respecting the earliest practices of the Art of Typefounding in the land and in the age in which it first saw the light.
No one has done more to clear the way for a free investigation of all questions relating to the origin of printing than Dr. Van der Linde, in his able essay, The Haarlem Legend, The Haarlem Legend of the Invention of Printing by Lourens Janszoon Coster, critically examined. From the Dutch by J. H. Hessels, with an introduction and classified list of the Costerian Incunabula. London, 1871. 8vo. which, while disposing ruthlessly of the fiction of Coster’s invention, lays down the important principle, too often neglected by writers on the subject, that the essence of Typography consists in the mobility of the types, and that, therefore, it is not a development of the long practised art of printing from fixed blocks, but an entirely distinct invention.
The principle is so important, and Dr. Van der Linde’s words are so emphatic, that we make no apology for quoting them:―
“I cannot repeat often enough that, when we speak of Typography and its invention, nothing is meant, or rather nothing must be meant, but printing with loose (separate, moveable) types (be they letters, musical notes, or other figures), which therefore, in distinction from letters cut on wooden or metal plates, may be put together or separated according to inclination. One thing therefore is certain: he who did not invent printing with moveable types, did, as far as Typography goes, invent nothing. What material was used first of all in this invention; of what metal the first letters, the patrices (engraved punches) and matrices were made; by whom and when the leaden matrices and brass patrices were replaced by brass matrices and steel patrices; . . . . . all this belongs to the secondary question of the technical execution of the principal idea: multiplication of books by means of multiplication of letters, multiplication of letters by means of their durability, and repeated use of the same letters, i.e., by means of the independence (looseness) of each individual letter (moveableness).”—P. 19.
If this principle be adopted—and we can hardly imagine it questioned—it will be obvious that a large class of works which usually occupy a prominent place in inquiries into the origin of Printing, have but slight bearing on the history of Typography. The block books of the fifteenth century had little direct connection with the art that followed and eclipsed them.Xylography did not become extinct for more than half a century after the invention of Typography. The last block book known was printed in Venice in 1510. In the one respect of marking the early use of printing for the instruction of mankind, the block books and the first works of Typography proper claim an equal interest; but, as regards their mechanical production, the one feature they possess in common is a quality shared also by the playing-cards, pictures, seals, stamps, brands, and all the other applications of the principle of impression which had existed in one form or another from time immemorial.
It is reasonable to suppose that the first idea of movable type may have been suggested to the mind of the inventor by a study of the works of a xylographic printer, and an observation of the cumbrous and wearisome method by which his books were produced. The toil involved in first painfully tracing the characters and figures, reversed, on the wood, then of engraving them, and, finally, of printing them with the frotton, would appear—in the case, at any rate, of the small school-books, for the production of which this process was largely resorted to—scarcely less tedious than copying the required number by the deft pen of a scribe. And even if, at a later period, the bookmakers so far facilitated their labours as to write their text in the ordinary manner on prepared paper, or with prepared ink, and so transfer their copy, after the manner of the Chinese, on to the wood, the labour expended in proportion to the result, and the uselessness of the blocks when once their work was done, would doubtless impress an inventive genius with a sense of dissatisfaction and impatience. We can imagine him examining the first page of an Abecedarium, on which would be engraved, in three lines, with a clear space between each character, the letters of the alphabet, and speculating, as Cicero had speculated centuries before,Hic ego non mirer esse quemquam qui sibi persuadeat . . . . mundum effici . . . . ex concursione fortuitâ! Hoc qui existimet fieri potuisse, non intelligo cur non idem putet si innumerabiles unius et viginti formæ litterarum, vel aureæ, vel qualeslibet, aliquò conjiciantur, posse ex his in terram excussis, annales Ennii, ut deinceps legi possint, effici” (De Nat. Deor., lib. ii). Cicero was not the only ancient writer who entertained the idea of mobile letters. Quintilian suggests the use of ivory letters for teaching children to read while playing: “Eburneas litterarum formas in ludum offere” (Inst. Orat., i, cap. 1); and Jerome, writing to Læta, propounds the same idea: “Fiant ei (Paulæ) litteræ vel buxeæ vel eburneæ, et suis nominibus appellentur. Ludat in eis ut et lusus ipse eruditio fiat.” on the possibilities presented by the combination in indefinite variety of those twenty-five symbols. Being a practical man as well as a theorist, we may suppose he would attempt to experiment on the little wood block in his hand, and by sawing off first the lines, and then some of the letters in the lines, attempt to arrange his little types into a few short words. A momentous experiment, and fraught with the greatest revolution the world has ever known!
No question has aroused more interest, or excited keener discussion in the history of printing, than that of the use of movable wooden types as a first stage in the passage from Xylography to Typography. Those who write on the affirmative side of the question profess to see in the earlier typographical works, as well as in the historical statements handed down by the old authorities, the clearest evidence that wooden types were used, and that several of the most famous works of the first printers were executed by their means.
As regards the latter source of their confidence, it is at least remarkable that no single writer of the fifteenth century makes the slightest allusion to the use of wooden types. Indeed, it was not till Bibliander, in 1548,In Commentatione de ratione communi omnium linguarum et literarum. Tiguri, 1548, p. 80. first mentioned and described them, that anything professing to be a record on the subject existed. “First they cut their letters,” he says, “on wood blocks the size of an entire page, but because the labour and cost of that way was so great, they devised movable wooden types, perforated and joined one to the other by a thread.”
The legend, once started, found no lack of sponsors, and the typographical histories of the sixteenth century and onward abound with testimonies confirmatory more or less of Bibliander’s statement. Of these testimonies, those only are worthy of attention which profess to be based on actual inspection of the alleged perforated wooden types. SpecklinIn Chronico Argentoratensi, m.s. ed. Jo. Schilterus, p. 442.  Angelo Roccha,De Bibliothecâ Vaticanâ. Romæ, 1591, p. 412. “Characteres enim a primis illis inventoribus non ita eleganter et expedite, ut a nostris fieri solet, sed filo in litterarum foramen immisso connectebantur, sicut Venetiis id genus typos me vidisse memini.” in 1591, vouches for the existence of similar letters (though he does not say whether wood or metal) at Venice. Paulus Pater,De Germaniæ Miraculo, etc. Lipsiæ, 1710, p. 10. “ . . . . ligneos typos, ex buxi frutice, perforatos in medio, ut zonâ colligari unâ jungique commode possint, ex Fausti officina reliquos, Moguntiæ aliquando me conspexisse memini.” in 1710, stated that he had once seen some belonging to Fust at Mentz; Bodman, as late as 1781, saw the same types in a worm-eaten condition at Mentz; while Fischer,Essai sur les Monumens Typographiques de Jean Gutenburg. Mayence, an 10, 1802, p. 39. in 1802, stated that these precious relics were used as a sort of token of honour to be bestowed on worthy apprentices on the occasion of their finishing their term.
This testimony proves nothing beyond the fact that at Strasburg, Venice, and Mentz there existed at some time or other certain perforated wooden types which tradition ascribed to the first printers. But on the question whether any book was ever printed with such type, it is wholly inconclusive. It is possible to believe that certain early printers, uninitiated into the mystery of the punch and matrix, may have attempted to cut themselves wooden types, which, when they proved untractable under the press, they perforated and strung together in lines; but it is beyond credit that any such rude experiment ever resulted in the production of a work like the Speculum.
It is true that many writers have asserted it was so. Fournier, a practical typographer, insists upon it from the fact that the letters vary among themselves in a manner which would not be the case had they been cast from a matrix in a mould. But, to be consistent, Fournier is compelled (as Bernard points out) to postpone the use of cast type till after the Gutenberg Bible and Mentz Psalter, both of which works display the same irregularities. And as the latest edition of the Psalter, printed in the old types, appeared in 1516, it would be necessary to suppose that movable wood type was in vogue up to that date. No one has yet demonstrated, or attempted seriously to demonstrate, the possibility of printing a book like the Speculum in movable wooden type. All the experiments hitherto made, even by the most ardent supporters of the theory, have been woful failures. LabordeDébuts de l’ Imprimerie à Strasbourg. Paris, 1840, p. 72. admits that to cut the 3,000 separate letters required for the Letters of Indulgence, engraved by him, would cost 450 francs; and even he, with the aid of modern tools to cut up his wooden cubes, can only show four widely spaced lines. WetterErfindung der Buchdruckerkunst. Mainz, 1836. Album, tab. ii. shows a page printed from perforated and threaded wooden typesThe history of these “fatal, unhistorical wooden types” is worth recording for the warning of the over-credulous typographical antiquary. Wetter, writing his book in 1836, and desirous to illustrate the feasibility of the theory, “spent,” so Dr. Van der Linde writes, “really the amount of ten shillings on having a number of letters made of the wood of a pear-tree, only to please Trithemius, Bergellanus, and Faust of Aschaffenburg. ; but these, though of large size, only prove by their “naughty caprioles” the absurdity of supposing that the “unleaded” Speculum, a quarternion of which would require 40,000 distinct letters, could have been produced in 1440 by a method which even the modern cutting and modern presswork of 1836 failed to adapt to a single page of large-sized print.
John Enschedé, the famous Haarlem typefounder, though a strong adherent to the Coster legend, was compelled to admit the practical impossibility, in his day at any rate, of producing a single wood type which would stand the test of being mathematically square; nor would it be possible to square it after being cut. “No engraver,” he remarks, “is able to cut separate letters in wood in such a manner that they retain their quadrature (for that is the main thing of the line in type-casting).” Van der Linde, Haarlem Legend. Lond., p. 72 Admitting for a moment that some printer may have succeeded in putting together a page of these wooden types, without the aid of leads, into a chase: how can it be supposed that after their exposure to the warping influences of the sloppy ink and tight pressure during the impression, they could ever have survived to be distributed and recomposed into another forme?Skeen, in his Early Typography, Colombo, 1872, takes up the challenge thrown down by Dr. Van der Linde on the strength of Enschedé’s opinion, and shows a specimen of three letters cut in boxwood, pica size, one of which he exhibits again at the close of the book after 1,500 impressions. But the value of Skeen’s arguments and experiments is destroyed when he sums up with this absurd dictum: “Three letters are as good as 3,000 or 30,000 or 300,000 to demonstrate the fact that words are and can be, and that therefore pages and whole books may be (and therefore also that they may have been) printed from such separable wooden types.”—P. 424.
The claims set up on behalf of movable wood types as the means by which the Speculum or any other of the earliest books was printed, are not only historically unsupported, but the whole weight of practical evidence rejects them.
Dismissing them, therefore, from our consideration, a new theory confronts us, which at first blush seems to supply, if not a more probable, certainly a more possible, stepping-stone between Xylography and Typography. We refer to what Meerman, the great champion of this theory, calls the “sculpto-fusi” characters: types, that is, the shanks of which have been cast in a quadrilateral mould, and the “faces” engraved by hand afterwards.
Meerman and those who agree with him engage a large array of testimony on their side. In the reference of Celtis, in 1502, to Mentz as the city “quæ prima sculpsit solidos ære characteres,” they see a clear confirmation of their theory; as also in the frequent recurrence of the same word “sculptus” in the colophons of the early printers. Meerman, indeed, goes so far as to ingeniously explain the famous account of the invention given by Trithemius in 1514,Annales Hirsaugienses, ii, p. 421: “Post hæc inventis successerunt subtiliora, inveneruntque modum fundendi formas omnium Latini Alphabeti literarum quas ipsi matrices nominabant; ex quibus rursum æneos sive stanneos characteres fundebant, ad omnem pressuram sufficientes, quos prius manibus sculpebant.” Trithemius’ statement, as every student of typographical history is aware, has been made to fit every theory that has been propounded, but it is doubtful whether any other writer has stretched it quite as severely as Meerman in the above rendering of these few Latin lines. in the light of his theory, to mean that, after the rejection of the first wooden types, “the inventors found out a method of casting the bodies only (fundendi formas) of all the letters of the Latin alphabet from what they called matrices, on which they cut the face of each letter; and from the same kind of matrices a method was in time discovered of casting the complete letters (æneos sive stanneos characteres) of sufficient hardness for the pressure they had to bear, which letters before—that is, when the bodies only were cast—they were obliged to cut.”Origines Typographicæ, Gerardo Meerman auctore. Hagæ Com., 1765. Append., p. 47.
After this bold flight of translation, it is not surprising to find that Meerman claims that the Speculum was printed in “sculpto-fusi” types, although in the one page of which he gives a facsimile there are nearly 1,700 separate types, of which 250 alone are e’s.
Schoepflin, claiming the same invention for the Strasburg printers, believes that all the earliest books printed there were produced by this means; and both Meerman and Schoepflin agree that engraved metal types were in use for many years after the invention of the punch and matrix, mentioning, among others so printed, the Mentz Psalter, the Catholicon of 1460, the Eggestein Bible of 1468, and even the Nideri Præceptorium, printed at Strasburg as late as 1476, as “literis in ære sculptis.”
Almost the whole historical claim of the engraved metal types, indeed, turns on the recurrence of the term “sculptus” in the colophons of the early printers. Jenson, in 1471, calls himself a “cutter of books” (librorum exsculptor). Sensenschmid, in 1475, says that the Codex Justinianus is “cut” (insculptus), and that he has “cut” (sculpsit) the work of Lombardus in Psalterium. Husner of Strasburg, in 1472, applies the term “printed with letters cut of metal” (exsculptis ære litteris) to the Speculum Durandi; and of the Præceptorium Nideri, printed in 1476, he says it is “printed in letters cut of metal by a very ingenious effort” (litteris exsculptis artificiali certe conatu ex ære). As Dr. Van der Linde points out, the use of the term in reference to all these books can mean nothing else than a figurative allusion to the first process towards producing the types, namely, the cutting of the punchThe constant recurrence in more modern typographical history of the expression “to cut matrices,” meaning of course to cut the punches necessary to form the matrices, bears out the same conclusion.; just as when Schoeffer, in 1466, makes his Grammatica Vetus Rhythmica say, “I am cast at Mentz” (At Moguntia sum fusus in urbe libellus), he means nothing more than a figurative allusion to the casting of the types.
The theory of the sculpto-fusi types appears to have sprung up on no firmer foundation than the difficulty of accounting for the marked irregularities in the letters of the earliest printed books, and the lack of a theory more feasible than that of movable wood type to account for it. The method suggested by Meerman seemed to meet the requirements of the case, and with the aid of the very free translation of Trithemius’ story, and the very literal translation of certain colophons, it managed to get a footing on the typographical records.
Mr. Skeen seriously applies himself to demonstrate how the shanks could be cast in clay moulds stamped with a number of trough-like matrices representing the various widths of the blanks required, and calculates that at the rate of four a day, 6,000 of these blanks could be engraved on the end by one man in five years, the whole weighing 100 lb. when finished! “No wonder,” Mr. Skeen naïvely observes, “that Fust at last grew impatient.” We must confess that there seems less ground for believing in the use of “sculpto-fusi” types as the means by which any of the early books were produced, than in the perforated wood types. The enormous labour involved, in itself renders the idea improbable. As M. Bernard says, “How can we suppose that intelligent men like the first printers would not at once find out that they could easily cast the face and body of their types together?”Origine et Débuts de l’Imprimerie en Europe. Paris, 1853, 8vo, i, 38. But admitting the possibility of producing type in this manner, and the possible obtuseness which could allow an inventor of printing to spend five years in laboriously engraving “shanks” enough for a single forme, the lack of any satisfactory evidence that such types were ever used, even experimentally, inclines us to deny them any place in the history of the origin of typography.
Putting aside, therefore, as improbable, and not proved, the two theories of engraved movable types, the question arises, Did typography, like her patron goddess, spring fully armed from the brain of her inventor? in other words, did men pass at a single stride from xylography to the perfect typography of the punch, the matrix, and the mould? or are we still to seek for an intermediate stage in some ruder and more primitive process of production? To this question we cannot offer a better reply than that contained in the following passage from Mr. Blades’s admirable life of Caxton.Life and Typography of William Caxton. London, 1861–3, 2 vols, 4to, ii, xxiv. “The examination of many specimens,” he observes, “has led me to conclude that two schools of typography existed together . . . The ruder consisted of those printers who practised their art in Holland and the Low Countries, . . . and who, by degrees only, adopted the better and more perfect methods of the . . . school founded in Germany by the celebrated trio, Gutenberg, Fust, and Schoeffer.”
It is impossible, we think, to resist the conclusion that all the earlier works of typography were the impression of cast metal types; but that the methods of casting employed were not always those of matured letter-founding, seems to us not only probable, but evident, from a study of the works themselves.
Mr. Theo. De Vinne, in his able treatise on the invention of printing,The Invention of Printing. New York, 1876. 8vo. speaking with the authority of a practical typographer, insists that the key to that invention is to be found, not in the press nor in the movable types, but in the adjustable type-mould, upon which, he argues, the existence of typography depends. While not prepared to go as far as Mr. De Vinne on this point, and still content to regard the invention of movable types as the real key to the invention of typography proper, we find in the mould not only the culminating achievement of the inventor, but also the key to the distinction between the two schools of early typography to which we have alluded.
The adjustable mould was undoubtedly the goal of the discovery, and those who reached it at once were the advanced typographers of the Mentz press. Those who groped after it through clumsy and tedious by-ways were the rude artists of the Donatus and Speculum.
In considering the primitive modes of type-casting, it must be frankly admitted that the inquirer stands in a field of pure conjecture. He has only negative evidence to assure him that such primitive modes undoubtedly did exist, and he searches in vain for any direct clue as to the nature and details of those methods.
We shall briefly refer to one or two theories which have been propounded, all with more or less of plausibility.
Casting in sand was an art not unknown to the silversmiths and trinket-makers of the fifteenth century, and several writers have suggested that some of the early printers applied this process to typefounding. M. BernardOrigine de l’Imprimerie, i, 40. considers that the types of the Speculum were sand-cast, and accounts for the varieties observable in the shapes of various letters, by explaining that several models would probably be made of each letter, and that the types when cast would, as is usual after sand-casting, require some touching up or finishing by hand. He shows a specimen of a word cast by himself by this process, which, as far as it goes, is a satisfactory proof of the possibility of casting letters in this way.Mr. Blades points out that there are no overhanging letters in the specimen. The necessity for such letters would be, we imagine, entirely obviated by the numerous combinations with which the type of the printers of the school abounded. The body is almost always large enough to carry ascending and descending sorts, and in width, a sort which would naturally overhang, is invariably covered by its following letter cast on the same piece. There are, indeed, many points in this theory which satisfactorily account for peculiarities in the appearance of books printed by the earliest rude Dutch School. Not only are the irregularities of the letters in body and line intelligible, but the specks between the lines, so frequently observable, would be accounted for by the roughness on the “shoulders” of the sand-cast bodies.It is well known that until comparatively recently the large “proscription letters” of our foundries, from three-line pica and upwards, were cast in sand. The practice died out at the close of last century.


Removing Players
Sometimes, you want to remove players after page load (in single page apps or modals, for instance). It's easy to manage, but there are some simple rules you need to follow.
Call .dispose()
To remove the html associated with your videojs player from the page always call the player's dispose()
method:
var oldPlayer = document.getElementById('my-player');
videojs(oldPlayer).dispose();
This method will:
- reset the internal state of videojs
- remove the player's dom from the page
Showing / Hiding a Player
For instance, if you have a modal that a player appears in, you should create the player when the modal pops up. When the modal hides, dispose the player. If you try to hide the Flash tech, things will go poorly. Even with other tech, calling dispose()
on a player that's not needed will free up resources for the browser.
Why Is This Needed?
VideoJS internally tracks all players and their associated data by html id attribute. If you plan to create new players with the same id as previously created players, you'll need to call the player's dispose() method to clear VideoJS's internal state before creating the new player.
Signs You Did It Wrong
TypeError: this.el_.vjs_getProperty is not a function
"VIDEOJS:" "Video.js: buffered unavailable on Hls playback technology element." TypeError: this.el_.vjs_getProperty is not a function
Stack trace:
...
If you encounter a console error in the browser similar to the above, you've probably forgotten to dispose()
a player before removing it from the dom. This would happen when using the contrib-hls plugin.


Documentation for the best project on GitHub.
To learn more about Assemble and registering custom helpers, visit assemble.io/docs/custom-helpers.
In lieu of a formal styleguide, take care to maintain the existing coding style. Add unit tests for any new or changed functionality. Lint and test your code using grunt.
(Nothing yet)


Proxy engine
Starting from the 1.5.3
release, otoroshi offers a new plugin that implements the next generation of the proxy engine. This engine has been designed based on our 5 years experience building, maintaining and running the previous one. It tries to fix all the drawback we may have encountered during those years and highly improve performances, user experience, reporting and debugging capabilities.
The new engine is fully plugin oriented in order to spend CPU cycles only on useful stuff. You can enable this plugin only on some domain names so you can easily A/B test the new engine. The new proxy engine is designed to be more reactive and more efficient generally. It is also designed to be very efficient on path routing where it wasn’t the old engines strong suit.
Starting from version 16.0.0
, this engine will be enabled by default on any new otoroshi cluster. In a future version, the engine will be enabled for any new or exisiting otoroshi cluster.
Enabling the new engine
By default, all freshly started Otoroshi instances have the new proxy engine enabled by default, for the other, to enable the new proxy engine on an otoroshi instance, just add the plugin in the global plugins
section of the danger zone, inject the default configuration, enable it and in domains
add the values of the desired domains (let say we want to use the new engine on api.foo.bar
. It is possible to use *.foo.bar
if that’s what you want to do).
The next time a request hits the api.foo.bar
domain, the new engine will handle it instead of the previous one.
{
"NextGenProxyEngine" : {
"enabled" : true,
"debug_headers" : false,
"reporting": true,
"domains" : [ "api.foo.bar" ],
"deny_domains" : [ ],
}
}
if you need to enable global plugin with the new engine, you can add the following configuration in the global plugins
configuration object
{
...
"ng": {
"slots": [
{
"plugin": "cp:otoroshi.next.plugins.W3CTracing",
"enabled": true,
"include": [],
"exclude": [],
"config": {
"baggage": {
"foo": "bar"
}
}
},
{
"plugin": "cp:otoroshi.next.plugins.wrappers.RequestSinkWrapper",
"enabled": true,
"include": [],
"exclude": [],
"config": {
"plugin": "cp:otoroshi.plugins.apikeys.ClientCredentialService",
"ClientCredentialService": {
"domain": "ccs-next-gen.oto.tools",
"expiration": 3600000,
"defaultKeyPair": "otoroshi-jwt-signing",
"secure": false
}
}
}
]
}
...
}
Entities
This plugin introduces new entities that will replace (one day maybe) service descriptors:
routes
: a unique routing rule based on hostname, path, method and headers that will execute a bunch of pluginsbackends
: a list of targets to contact a backend
Entities sync
A new behavior introduced for the new proxy engine is the entities sync job. To avoid unecessary operations on the underlying datastore when routing requests, a new job has been setup in otoroshi that synchronize the content of the datastore (at least a part of it) with an in-memory cache. Because of it, the propagation of changes between an admin api call and the actual result on routing can be longer than before. When a node creates, updates, or deletes an entity via the admin api, other nodes need to wait for the next poll to purge the old cached entity and start using the new one. You can change the interval between syncs with the configuration key otoroshi.next.state-sync-interval
or the env. variable OTOROSHI_NEXT_STATE_SYNC_INTERVAL
. The default value is 10000
and the unit is milliseconds
Because of entities sync, memory consumption of otoroshi will be significantly higher than previous versions. You can use otoroshi.next.monitor-proxy-state-size=true
config (or OTOROSHI_NEXT_MONITOR_PROXY_STATE_SIZE
env. variable) to monitor the actual memory size of the entities cache. This will produce the ng-proxy-state-size-monitoring
metric in standard otoroshi metrics
Automatic conversion
The new engine uses new entities for its configuration, but in order to facilitate transition between the old world and the new world, all the service descriptors
of an otoroshi instance are automatically converted live into routes
periodically. Any service descriptor
should still work as expected through the new engine while enjoying all the perks.
the experimental nature of the engine can imply unexpected behaviors for converted service descriptors
Routing
the new proxy engine introduces a new router that has enhanced capabilities and performances. The router can handle thousands of routes declarations without compromising performances.
The new route allow routes to be matched on a combination of
- hostname
- path
- header values
- where values can be
exact_value
, orRegex(value_regex)
, orWildcard(value_with_*)
- where values can be
- query param values
- where values can be
exact_value
, orRegex(value_regex)
, orWildcard(value_with_*)
- where values can be
patch matching works
- exactly
- matches
/api/foo
with/api/foo
and not with/api/foo/bar
- matches
- starting with value (default behavior, like the previous engine)
- matches
/api/foo
with/api/foo
but also with/api/foo/bar
- matches
path matching can also include wildcard paths and even path params
- plain old path:
subdomain.domain.tld/api/users
- wildcard path:
subdomain.domain.tld/api/users/*/bills
- named path params:
subdomain.domain.tld/api/users/:id/bills
- named regex path params:
subdomain.domain.tld/api/users/$id<[0-9]+>/bills
hostname matching works on
- exact values
subdomain.domain.tld
- wildcard values like
*.domain.tld
subdomain.*.tld
as path matching can now include named path params, it is possible to perform a ful url rewrite on the target path like
- input:
subdomain.domain.tld/api/users/$id<[0-9]+>/bills
- output:
target.domain.tld/apis/v1/basic_users/${req.pathparams.id}/all_bills
Plugins
the new route entity defines a plugin pipline where any plugin can be enabled or not and can be active only on some paths. Each plugin slot in the pipeline holds the plugin id and the plugin configuration.
You can also enable debugging only on a plugin instance instead of the whole route (see the debugging section)
{
...
"plugins" : [ {
"enabled" : true,
"debug" : false,
"plugin" : "cp:otoroshi.next.plugins.OverrideHost",
"include" : [ ],
"exclude" : [ ],
"config" : { }
}, {
"enabled" : true,
"debug" : false,
"plugin" : "cp:otoroshi.next.plugins.ApikeyCalls",
"include" : [ ],
"exclude" : [ "/openapi.json" ],
"config" : { }
} ]
}
you can find the list of built-in plugins here
Using legacy plugins
if you need to use legacy otoroshi plugins with the new engine, you can use several wrappers in order to do so
otoroshi.next.plugins.wrappers.PreRoutingWrapper
otoroshi.next.plugins.wrappers.AccessValidatorWrapper
otoroshi.next.plugins.wrappers.RequestSinkWrapper
otoroshi.next.plugins.wrappers.RequestTransformerWrapper
otoroshi.next.plugins.wrappers.CompositeWrapper
to use it, just declare a plugin slot with the right wrapper and in the config, declare the plugin
you want to use and its configuration like:
{
"plugin": "cp:otoroshi.next.plugins.wrappers.PreRoutingWrapper",
"enabled": true,
"include": [],
"exclude": [],
"config": {
"plugin": "cp:otoroshi.plugins.jwt.JwtUserExtractor",
"JwtUserExtractor": {
"verifier" : "$ref",
"strict" : true,
"namePath" : "name",
"emailPath": "email",
"metaPath" : null
}
}
}
Reporting
by default, any request hiting the new engine will generate an execution report with informations about how the request pipeline steps were performed. It is possible to export those reports as RequestFlowReport
events using classical data exporter. By default, exporting for reports is not enabled, you must enable the export_reporting
flag on a route
or service
.
{
"@id": "8efac472-07bc-4a80-8d27-4236309d7d01",
"@timestamp": "2022-02-15T09:51:25.402+01:00",
"@type": "RequestFlowReport",
"@product": "otoroshi",
"@serviceId": "service_548f13bb-a809-4b1d-9008-fae3b1851092",
"@service": "demo-service",
"@env": "prod",
"route": {
"_loc" : {
"tenant" : "default",
"teams" : [ "default" ]
},
"id" : "service_dev_d54f11d0-18e2-4da4-9316-cf47733fd29a",
"name" : "hey",
"description" : "hey",
"tags" : [ "env:prod" ],
"metadata" : { },
"enabled" : true,
"debug_flow" : true,
"export_reporting" : false,
"groups" : [ "default" ],
"frontend" : {
"domains" : [ "hey-next-gen.oto.tools/", "hey.oto.tools/" ],
"strip_path" : true,
"exact" : false,
"headers" : { },
"methods" : [ ]
},
"backend" : {
"targets" : [ {
"id" : "<IP_ADDRESS>:8081",
"hostname" : "<IP_ADDRESS>",
"port" : 8081,
"tls" : false,
"weight" : 1,
"protocol" : "HTTP/1.1",
"ip_address" : null,
"tls_config" : {
"certs" : [ ],
"trustedCerts" : [ ],
"mtls" : false,
"loose" : false,
"trustAll" : false
}
} ],
"target_refs" : [ ],
"root" : "/",
"rewrite" : false,
"load_balancing" : {
"type" : "RoundRobin"
},
"client" : {
"useCircuitBreaker" : true,
"retries" : 1,
"maxErrors" : 20,
"retryInitialDelay" : 50,
"backoffFactor" : 2,
"callTimeout" : 30000,
"callAndStreamTimeout" : 120000,
"connectionTimeout" : 10000,
"idleTimeout" : 60000,
"globalTimeout" : 30000,
"sampleInterval" : 2000,
"proxy" : { },
"customTimeouts" : [ ],
"cacheConnectionSettings" : {
"enabled" : false,
"queueSize" : 2048
}
}
},
"backend_ref" : null,
"plugins" : [ ]
},
"report": {
"id" : "ab73707b3-946b-4853-92d4-4c38bbaac6d6",
"creation" : "2022-02-15T09:51:25.402+01:00",
"termination" : "2022-02-15T09:51:25.408+01:00",
"duration" : 5,
"duration_ns" : 5905522,
"overhead" : 4,
"overhead_ns" : 4223215,
"overhead_in" : 2,
"overhead_in_ns" : 2687750,
"overhead_out" : 1,
"overhead_out_ns" : 1535465,
"state" : "Successful",
"steps" : [ {
"task" : "start-handling",
"start" :<PHONE_NUMBER>402,
"start_fmt" : "2022-02-15T09:51:25.402+01:00",
"stop" :<PHONE_NUMBER>402,
"stop_fmt" : "2022-02-15T09:51:25.402+01:00",
"duration" : 0,
"duration_ns" : 177430,
"ctx" : null
}, {
"task" : "check-concurrent-requests",
"start" :<PHONE_NUMBER>402,
"start_fmt" : "2022-02-15T09:51:25.402+01:00",
"stop" :<PHONE_NUMBER>402,
"stop_fmt" : "2022-02-15T09:51:25.402+01:00",
"duration" : 0,
"duration_ns" : 145242,
"ctx" : null
}, {
"task" : "find-route",
"start" :<PHONE_NUMBER>402,
"start_fmt" : "2022-02-15T09:51:25.402+01:00",
"stop" :<PHONE_NUMBER>403,
"stop_fmt" : "2022-02-15T09:51:25.403+01:00",
"duration" : 0,
"duration_ns" : 497119,
"ctx" : {
"found_route" : {
"_loc" : {
"tenant" : "default",
"teams" : [ "default" ]
},
"id" : "service_dev_d54f11d0-18e2-4da4-9316-cf47733fd29a",
"name" : "hey",
"description" : "hey",
"tags" : [ "env:prod" ],
"metadata" : { },
"enabled" : true,
"debug_flow" : true,
"export_reporting" : false,
"groups" : [ "default" ],
"frontend" : {
"domains" : [ "hey-next-gen.oto.tools/", "hey.oto.tools/" ],
"strip_path" : true,
"exact" : false,
"headers" : { },
"methods" : [ ]
},
"backend" : {
"targets" : [ {
"id" : "<IP_ADDRESS>:8081",
"hostname" : "<IP_ADDRESS>",
"port" : 8081,
"tls" : false,
"weight" : 1,
"protocol" : "HTTP/1.1",
"ip_address" : null,
"tls_config" : {
"certs" : [ ],
"trustedCerts" : [ ],
"mtls" : false,
"loose" : false,
"trustAll" : false
}
} ],
"target_refs" : [ ],
"root" : "/",
"rewrite" : false,
"load_balancing" : {
"type" : "RoundRobin"
},
"client" : {
"useCircuitBreaker" : true,
"retries" : 1,
"maxErrors" : 20,
"retryInitialDelay" : 50,
"backoffFactor" : 2,
"callTimeout" : 30000,
"callAndStreamTimeout" : 120000,
"connectionTimeout" : 10000,
"idleTimeout" : 60000,
"globalTimeout" : 30000,
"sampleInterval" : 2000,
"proxy" : { },
"customTimeouts" : [ ],
"cacheConnectionSettings" : {
"enabled" : false,
"queueSize" : 2048
}
}
},
"backend_ref" : null,
"plugins" : [ ]
},
"matched_path" : "",
"exact" : true,
"params" : { },
"matched_routes" : [ "service_dev_d54f11d0-18e2-4da4-9316-cf47733fd29a" ]
}
}, {
"task" : "compute-plugins",
"start" :<PHONE_NUMBER>403,
"start_fmt" : "2022-02-15T09:51:25.403+01:00",
"stop" :<PHONE_NUMBER>403,
"stop_fmt" : "2022-02-15T09:51:25.403+01:00",
"duration" : 0,
"duration_ns" : 105151,
"ctx" : {
"disabled_plugins" : [ ],
"filtered_plugins" : [ ]
}
}, {
"task" : "tenant-check",
"start" :<PHONE_NUMBER>403,
"start_fmt" : "2022-02-15T09:51:25.403+01:00",
"stop" :<PHONE_NUMBER>403,
"stop_fmt" : "2022-02-15T09:51:25.403+01:00",
"duration" : 0,
"duration_ns" : 26097,
"ctx" : null
}, {
"task" : "check-global-maintenance",
"start" :<PHONE_NUMBER>403,
"start_fmt" : "2022-02-15T09:51:25.403+01:00",
"stop" :<PHONE_NUMBER>403,
"stop_fmt" : "2022-02-15T09:51:25.403+01:00",
"duration" : 0,
"duration_ns" : 14132,
"ctx" : null
}, {
"task" : "call-before-request-callbacks",
"start" :<PHONE_NUMBER>403,
"start_fmt" : "2022-02-15T09:51:25.403+01:00",
"stop" :<PHONE_NUMBER>403,
"stop_fmt" : "2022-02-15T09:51:25.403+01:00",
"duration" : 0,
"duration_ns" : 56671,
"ctx" : null
}, {
"task" : "extract-tracking-id",
"start" :<PHONE_NUMBER>403,
"start_fmt" : "2022-02-15T09:51:25.403+01:00",
"stop" :<PHONE_NUMBER>403,
"stop_fmt" : "2022-02-15T09:51:25.403+01:00",
"duration" : 0,
"duration_ns" : 5207,
"ctx" : null
}, {
"task" : "call-pre-route-plugins",
"start" :<PHONE_NUMBER>403,
"start_fmt" : "2022-02-15T09:51:25.403+01:00",
"stop" :<PHONE_NUMBER>403,
"stop_fmt" : "2022-02-15T09:51:25.403+01:00",
"duration" : 0,
"duration_ns" : 39786,
"ctx" : null
}, {
"task" : "call-access-validator-plugins",
"start" :<PHONE_NUMBER>403,
"start_fmt" : "2022-02-15T09:51:25.403+01:00",
"stop" :<PHONE_NUMBER>403,
"stop_fmt" : "2022-02-15T09:51:25.403+01:00",
"duration" : 0,
"duration_ns" : 25311,
"ctx" : null
}, {
"task" : "enforce-global-limits",
"start" :<PHONE_NUMBER>403,
"start_fmt" : "2022-02-15T09:51:25.403+01:00",
"stop" :<PHONE_NUMBER>404,
"stop_fmt" : "2022-02-15T09:51:25.404+01:00",
"duration" : 0,
"duration_ns" : 296617,
"ctx" : {
"remaining_quotas" : {
"authorizedCallsPerSec" : 10000000,
"currentCallsPerSec" : 10000000,
"remainingCallsPerSec" : 10000000,
"authorizedCallsPerDay" : 10000000,
"currentCallsPerDay" : 10000000,
"remainingCallsPerDay" : 10000000,
"authorizedCallsPerMonth" : 10000000,
"currentCallsPerMonth" : 10000000,
"remainingCallsPerMonth" : 10000000
}
}
}, {
"task" : "choose-backend",
"start" :<PHONE_NUMBER>404,
"start_fmt" : "2022-02-15T09:51:25.404+01:00",
"stop" :<PHONE_NUMBER>404,
"stop_fmt" : "2022-02-15T09:51:25.404+01:00",
"duration" : 0,
"duration_ns" : 368899,
"ctx" : {
"backend" : {
"id" : "<IP_ADDRESS>:8081",
"hostname" : "<IP_ADDRESS>",
"port" : 8081,
"tls" : false,
"weight" : 1,
"protocol" : "HTTP/1.1",
"ip_address" : null,
"tls_config" : {
"certs" : [ ],
"trustedCerts" : [ ],
"mtls" : false,
"loose" : false,
"trustAll" : false
}
}
}
}, {
"task" : "transform-request",
"start" :<PHONE_NUMBER>404,
"start_fmt" : "2022-02-15T09:51:25.404+01:00",
"stop" :<PHONE_NUMBER>404,
"stop_fmt" : "2022-02-15T09:51:25.404+01:00",
"duration" : 0,
"duration_ns" : 506363,
"ctx" : null
}, {
"task" : "call-backend",
"start" :<PHONE_NUMBER>404,
"start_fmt" : "2022-02-15T09:51:25.404+01:00",
"stop" :<PHONE_NUMBER>407,
"stop_fmt" : "2022-02-15T09:51:25.407+01:00",
"duration" : 2,
"duration_ns" : 2163470,
"ctx" : null
}, {
"task" : "transform-response",
"start" :<PHONE_NUMBER>407,
"start_fmt" : "2022-02-15T09:51:25.407+01:00",
"stop" :<PHONE_NUMBER>407,
"stop_fmt" : "2022-02-15T09:51:25.407+01:00",
"duration" : 0,
"duration_ns" : 279887,
"ctx" : null
}, {
"task" : "stream-response",
"start" :<PHONE_NUMBER>407,
"start_fmt" : "2022-02-15T09:51:25.407+01:00",
"stop" :<PHONE_NUMBER>407,
"stop_fmt" : "2022-02-15T09:51:25.407+01:00",
"duration" : 0,
"duration_ns" : 382952,
"ctx" : null
}, {
"task" : "trigger-analytics",
"start" :<PHONE_NUMBER>407,
"start_fmt" : "2022-02-15T09:51:25.407+01:00",
"stop" :<PHONE_NUMBER>408,
"stop_fmt" : "2022-02-15T09:51:25.408+01:00",
"duration" : 0,
"duration_ns" : 812036,
"ctx" : null
}, {
"task" : "request-success",
"start" :<PHONE_NUMBER>408,
"start_fmt" : "2022-02-15T09:51:25.408+01:00",
"stop" :<PHONE_NUMBER>408,
"stop_fmt" : "2022-02-15T09:51:25.408+01:00",
"duration" : 0,
"duration_ns" : 0,
"ctx" : null
} ]
}
}
Debugging
with the new reporting capabilities, the new engine also have debugging capabilities built in. In you enable the debug_flow
flag on a route (or service), the resulting RequestFlowReport
will be enriched with contextual informations between each plugins of the route plugin pipeline
you can also use the Try it
feature of the new route designer UI to get debug reports automatically for a specific call
HTTP traffic capture
using the capture
flag, a TrafficCaptureEvent
is generated for each http request/response. This event will contains request and response body. Those events can be exported using data exporters as usual. You can also use the GoReplay file exporter that is specifically designed to ingest those events and create GoReplay files (.gor
)
this feature can have actual impact on CPU and RAM consumption
{
"@id": "d5998b0c4-cb08-43e6-9921-27472c7a56e0",
"@timestamp":<PHONE_NUMBER>115,
"@type": "TrafficCaptureEvent",
"@product": "otoroshi",
"@serviceId": "route_2b2670879-131c-423d-b755-470c7b1c74b1",
"@service": "test-server",
"@env": "prod",
"route": {
"id": "route_2b2670879-131c-423d-b755-470c7b1c74b1",
"name": "test-server"
},
"request": {
"id": "152250645825034725600000",
"int_id": 115,
"method": "POST",
"headers": {
"Host": "test-server-next-gen.oto.tools:9999",
"Accept": "*/*",
"Cookie": "fifoo=fibar",
"User-Agent": "curl/7.64.1",
"Content-Type": "application/json",
"Content-Length": "13",
"Remote-Address": "<IP_ADDRESS>:57660",
"Timeout-Access": "<function1>",
"Raw-Request-URI": "/",
"Tls-Session-Info": "Session(1651828041285|SSL_NULL_WITH_NULL_NULL)"
},
"cookies": [
{
"name": "fifoo",
"value": "fibar",
"path": "/",
"domain": null,
"http_only": true,
"max_age": null,
"secure": false,
"same_site": null
}
],
"tls": false,
"uri": "/",
"path": "/",
"version": "HTTP/1.1",
"has_body": true,
"remote": "<IP_ADDRESS>",
"client_cert_chain": null,
"body": "{\"foo\":\"bar\"}"
},
"backend_request": {
"url": "http://localhost:3000/",
"method": "POST",
"headers": {
"Host": "localhost",
"Accept": "*/*",
"Cookie": "fifoo=fibar",
"User-Agent": "curl/7.64.1",
"Content-Type": "application/json",
"Content-Length": "13"
},
"version": "HTTP/1.1",
"client_cert_chain": null,
"cookies": [
{
"name": "fifoo",
"value": "fibar",
"domain": null,
"path": "/",
"maxAge": null,
"secure": false,
"httpOnly": true
}
],
"id": "152260631569472064900000",
"int_id": 33,
"body": "{\"foo\":\"bar\"}"
},
"backend_response": {
"status": 200,
"headers": {
"Date": "Fri, 06 May 2022 09:20:01 GMT",
"Connection": "keep-alive",
"Set-Cookie": "foo=bar",
"Content-Type": "application/json",
"Transfer-Encoding": "chunked"
},
"cookies": [
{
"name": "foo",
"value": "bar",
"domain": null,
"path": null,
"maxAge": null,
"secure": false,
"httpOnly": false
}
],
"id": "152260631569472064900000",
"status_txt": "OK",
"http_version": "HTTP/1.1",
"body": "{\"headers\":{\"host\":\"localhost\",\"accept\":\"*/*\",\"user-agent\":\"curl/7.64.1\",\"content-type\":\"application/json\",\"cookie\":\"fifoo=fibar\",\"content-length\":\"13\"},\"method\":\"POST\",\"path\":\"/\",\"body\":\"{\\\"foo\\\":\\\"bar\\\"}\"}"
},
"response": {
"id": "152250645825034725600000",
"status": 200,
"headers": {
"Date": "Fri, 06 May 2022 09:20:01 GMT",
"Connection": "keep-alive",
"Set-Cookie": "foo=bar",
"Content-Type": "application/json",
"Transfer-Encoding": "chunked"
},
"cookies": [
{
"name": "foo",
"value": "bar",
"domain": null,
"path": null,
"maxAge": null,
"secure": false,
"httpOnly": false
}
],
"status_txt": "OK",
"http_version": "HTTP/1.1",
"body": "{\"headers\":{\"host\":\"localhost\",\"accept\":\"*/*\",\"user-agent\":\"curl/7.64.1\",\"content-type\":\"application/json\",\"cookie\":\"fifoo=fibar\",\"content-length\":\"13\"},\"method\":\"POST\",\"path\":\"/\",\"body\":\"{\\\"foo\\\":\\\"bar\\\"}\"}"
},
"user-agent-details": null,
"origin-details": null,
"instance-number": 0,
"instance-name": "dev",
"instance-zone": "local",
"instance-region": "local",
"instance-dc": "local",
"instance-provider": "local",
"instance-rack": "local",
"cluster-mode": "Leader",
"cluster-name": "otoroshi-leader-9hnv5HUXpbCZD7Ee"
}
openapi import
as the new router offers possibility to match exactly on a single path and a single method, and with the help of the service
entity, it is now pretty easy to import openapi document as route-compositions
entities. To do that, a new api has been made available to perform the translation. Be aware that this api DOES NOT save the entity and just return the result of the translation.
curl -X POST \
-H 'Content-Type: application/json' \
-u admin-api-apikey-id:admin-api-apikey-secret \
'http://otoroshi-api.oto.tools:8080/api/route-compositions/_openapi' \
-d '{"domain":"oto-api-proxy.oto.tools","openapi":"https://raw.githubusercontent.com/MAIF/otoroshi/master/otoroshi/public/openapi.json"}'


I’m sorry, but my website doesn’t work properly without JavaScript enabled, since it uses the Javascript framework Vue.js to manage its content. If you enable Javascript, all of our problems will go away. I promise I won’t mine any cryptocurrency on your computer.


This site is generated by Elm JavaScript. If you're seeing this you have JavaScript disabled. The game won't be much fun with just a static page. Turn on JavaScript to play.
© Copyright 2018 Gib Goy Games <GibGoyGames@gmail.com>