A Pinch of Indirection
This is a modal window.
The media could not be loaded, either because the server or network failed or because the format is not supported.
Formal Metadata
Title |
| |
Subtitle |
| |
Title of Series | ||
Number of Parts | 66 | |
Author | ||
License | CC Attribution 3.0 Germany: You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal purpose as long as the work is attributed to the author in the manner specified by the author or licensor. | |
Identifiers | 10.5446/55279 (DOI) | |
Publisher | ||
Release Date | ||
Language | ||
Production Year | 2016 |
Content Metadata
Subject Area | ||
Genre | ||
Abstract |
|
Plone Conference 201663 / 66
4
5
6
7
8
9
10
11
12
13
15
17
18
19
22
23
24
25
26
28
30
31
32
36
43
44
46
47
48
49
50
52
53
55
56
57
60
62
63
64
65
66
00:00
Planar graphEmulationRippingAnalogyRoundingExecution unitConvex hullBeat (acoustics)Hill differential equationHypermediaRootPlot (narrative)Plane (geometry)SoftwareSoftware developerComputing platformGamma functionKolmogorov complexityOpen sourceMaxima and minimaAnalogyContent (media)Software developerDifferent (Kate Ryan album)Software testingWeb 2.0Windows RegistryBitComplex (psychology)NumberCASE <Informatik>Online helpContext awarenessShared memoryPlanningHypermediaEmailElectronic mailing listMultiplication signBand matrixSoftware architectureCommitment schemeFront and back endsDegree (graph theory)Shift operatorCartesian coordinate systemInterface (computing)Object (grammar)Parameter (computer programming)Server (computing)Term (mathematics)Dependent and independent variablesQuicksortPerspective (visual)Software frameworkMereologySoftwareArithmetic meanImage registrationAbstractionBasis <Mathematik>CurveScatteringCuboidObject-oriented programmingMachine visionComputing platformHorizonContingency tableOpen sourcePublic domainData managementWeb applicationClient (computing)CloningCore dumpSound effectComputer animation
08:52
Kolmogorov complexityOpen sourceMereologyLimit (category theory)AuftragsspracheTerm (mathematics)TrailContext awarenessDependent and independent variablesContent (media)View (database)Event horizonPlane (geometry)Category of beingObject (grammar)Windows RegistryDebuggerConsistencyImperative programmingSoftwareComputer programmingAsynchronous Transfer ModeInterface (computing)Software testingImplementationFlow separationForm (programming)GUI widgetDesign by contractStorage area networkFormal languageData structureSet (mathematics)SequenceWindows RegistryConfiguration spaceState of matterSoftwareView (database)Design by contractContent management systemObject (grammar)Category of beingInterface (computing)Software developerFormal languageComputing platformFlow separationDebuggerGUI widgetImage registrationContext awarenessNumberAdaptive behaviorEvent horizonDefault (computer science)Java appletSoftware frameworkComputer programParameter (computer programming)Content (media)QuicksortWebsiteGoodness of fitSelf-organizationArithmetic meanMultiplication signDependent and independent variablesUtility softwareWritingField (computer science)ImplementationForm (programming)AbstractionVariable (mathematics)Software testingMereologyComplex (psychology)Inheritance (object-oriented programming)MultiplicationRoutingSynchronizationType theoryHTTP cookieCartesian coordinate systemGroup actionTerm (mathematics)Traverse (surveying)Service (economics)Distribution (mathematics)Different (Kate Ryan album)Physical systemPattern languageNetwork topologyEntire functionLibrary (computing)Library catalogINTEGRALCurveFile systemExterior algebraServer (computing)AeroelasticityData structureUnit testingOvalPlastikkarteBuildingDeclarative programmingComputer fileData managementCode
17:37
SequenceFormal languageData structureSet (mathematics)Uniqueness quantificationComputing platformGroup actionObject (grammar)Procedural programmingTraverse (surveying)WebsiteGame controllerAbstractionState of matterRepresentation (politics)Interface (computing)Software frameworkUniform resource locatorSoftwareUtility softwareHyperbolischer RaumParity (mathematics)Dirac equationMereologyObject (grammar)Wrapper (data mining)Interface (computing)Parameter (computer programming)Group actionGame controllerWritingSet (mathematics)Utility softwareSequenceMappingTraverse (surveying)LeakQuicksortCategory of beingWebsiteSoftware frameworkObject-oriented programmingRoutingSimilarity (geometry)Relational databaseCASE <Informatik>Electronic mailing listFunctional (mathematics)Latent heatAxiom of choiceLine (geometry)Computer fileState of matterEndliche ModelltheorieDegree (graph theory)MereologyMaterialization (paranormal)Procedural programmingVariety (linguistics)Web 2.0Server (computing)Content (media)Proxy serverAdaptive behaviorRow (database)Software testingEntire functionSoftwarePerspective (visual)Message passingArithmetic meanCodeUniform resource locatorDifferent (Kate Ryan album)PressureMoment (mathematics)Graph (mathematics)Structural loadTerm (mathematics)BitSound effectCloningPoint (geometry)View (database)Context awarenessSchwerpunktsystemPlanningFigurate numberAbstractionProjective planeComputing platformService (economics)Data type
26:22
Procedural programmingMereologyInflection pointInterface (computing)Design by contractCodeFundamental theorem of algebraObject (grammar)Mechanism designPlanar graphInheritance (object-oriented programming)Function (mathematics)Content (media)Group actionGUI widgetTransformation (genetics)Flow separationStrukturierte DatenPhysical systemData storage devicePlane (geometry)EmulationHost Identity ProtocolVolumeWeightFraction (mathematics)DecimalPopulation densityUtility softwareWebsiteTwin primeEmpennageMaxima and minimaTerm (mathematics)Windows RegistryLatent heatEvent horizonImage registrationDistribution (mathematics)Interface (computing)Object (grammar)GUI widgetSoftware developerLatent heatField (computer science)Complex (psychology)Type theoryBitFlow separationExecution unitState of matterDesign by contractImage registrationElectronic visual displaySound effectPhysical systemDefault (computer science)Context awarenessSocial classAdaptive behaviorSystem callImplementationString (computer science)Point (geometry)Content (media)NumberUtility softwareFile formatDifferent (Kate Ryan album)Standard deviationPiFunction (mathematics)Volume (thermodynamics)WeightDecimalVolumeFraction (mathematics)SoftwareRevision controlDebuggerWindows RegistryAxiom of choiceParameter (computer programming)Independence (probability theory)Web browserDistribution (mathematics)DampingSoftware frameworkSelf-organizationPressureLibrary (computing)Presentation of a groupSlide ruleTerm (mathematics)WebsiteFunctional (mathematics)CASE <Informatik>Inheritance (object-oriented programming)CodeFormal languageInstance (computer science)Form (programming)Data storage devicePopulation densityWritingCurveFloating pointMathematical optimizationCoprocessorMultiplication sign
35:08
Latent heatInterface (computing)GUI widgetEvent horizonImage registrationDistribution (mathematics)Software frameworkView (database)Context awarenessContent (media)Dependent and independent variablesUniform resource locatorFault-tolerant systemWeb browserType theoryDisintegrationService (economics)Message passingFluid staticsTransformation (genetics)CryptographyObject (grammar)Queue (abstract data type)Configuration spaceMeta elementEnumerated typeAxiom of choiceOpen sourceField (computer science)Time zoneWebsiteLocal ringUtility softwareFunction (mathematics)SchwerpunktsystemCache (computing)ForceScripting languageData structureQuadratic equationInstance (computer science)Information managementForm (programming)Client (computing)Library (computing)Pattern languageIRIS-TProcess (computing)CASE <Informatik>Utility softwareWeb 2.0Field (computer science)String (computer science)Theory of relativityRoutingView (database)Software frameworkWeb browserQueue (abstract data type)Variety (linguistics)INTEGRALElectronic visual displayContent (media)Service (economics)Fault-tolerant systemDifferent (Kate Ryan album)Data storage deviceTransformation (genetics)Fluid staticsValidity (statistics)Message passingSet (mathematics)Interface (computing)Form (programming)GUI widgetFlow separationImage registrationContext awarenessAdaptive behaviorCore dumpImplementationWebsiteRevision controlCache (computing)Complex (psychology)State of matterObject (grammar)Keyboard shortcutProjective planeLatent heatLink (knot theory)Pattern languageSlide ruleTime zoneInstance (computer science)Library (computing)Inheritance (object-oriented programming)Data structureAxiom of choiceMultiplication signProxy serverScripting languageConfiguration spaceWhiteboardOpen sourceAttribute grammarOnline helpAbstractionType theoryPoint (geometry)Enumerated typeLocal ringInternetworkingConstructor (object-oriented programming)CuboidElectronic mailing listRegulärer Ausdruck <Textverarbeitung>Installation artHoaxFunctional (mathematics)Reading (process)Uniform resource locatorBlogLimit (category theory)Uniformer RaumDynamical systemPerspective (visual)CloningFrequencySchwerpunktsystemServer (computing)Keilförmige AnordnungoutputEndliche Modelltheorie
43:53
Form (programming)Library (computing)GUI widgetPattern languageWindows RegistryEvent horizonDesign by contractFile formatSoftwareLatent heatInterface (computing)Image registrationDistribution (mathematics)Local ringAdaptive behaviorForm (programming)Event horizonComputer wormInterface (computing)Object (grammar)Open sourceSoftware testingImplementationDistribution (mathematics)Web 2.0HorizonSynchronizationControl flowCartesian coordinate systemLatent heatQuicksortVisualization (computer graphics)Goodness of fitPoint (geometry)Multiplication signPlotterCurveSocial classWordData managementEquivalence relationInjektivitätWeb-DesignerData modelTime seriesProgrammschleifeOrder (biology)Loop (music)IterationInformationCASE <Informatik>Different (Kate Ryan album)Windows RegistrySoftware developerDebuggerDescriptive statisticsField (computer science)Shape (magazine)SoftwareComputer programView (database)Mathematical optimizationContent (media)Server (computing)Computer animation
Transcript: English(auto-generated)
00:06
Welcome, thank you for coming. My name is Sean Upton. I work for the University of Utah School of Medicine, and I've been developing with Plone for a fair number of years. Spent most of my time working with Plone in the newspaper business and made a very
00:23
dramatic shift to doing something quite different, working in healthcare quality improvement over the last six years using Plone as a platform for building an application for managing distributed teams doing healthcare quality improvement. A lot of what we've built has been based upon component architecture.
00:43
A lot of what I've done in the past in various incarnations has been, I want to say I've lived several lives doing different things with components. I spent two years doing component architecture stuff that had absolutely nothing to do with Plone Hub 2 that was all about basically plumbing content for a newspaper newsroom from
01:04
one place to another. So basically we built a hub that was all burning on the back end. This wasn't web applications, this wasn't Plone, it was all the same technologies under the hood. So I want to talk a little bit about component architecture, component architectures in general, and I probably will offer a fair number of opinions here.
01:24
So hello Boston, what does this talk about? We're going to talk about components, that much should be obvious. Should talk about Plone, APIs, food, some or all of these things, there'll be lots of food puns, and there may be a few jokes as well that are unrelated, but the topic
01:45
in general, let's talk about components. There are some idioms and helpful ideas that, or I hope there's some ideas that I can present here that share sort of an ideology of what we do with components. This is my pragmatic perspective on things, this is what I think the component architecture
02:04
is about, this is why I like to use components in solving software problems. So maybe this is about software development, maybe this is about architecture, maybe this is about food, I'm not quite sure. There'll be plenty of cooking analogies. How many Plone conference ZCA talks have there been over the years?
02:22
I don't know, I would imagine that I can't count them on two hands. So what you're going to get from me might be some perspective, it might be opinion, you can call it what you want. There may be some JavaScript involved, and if you don't like what I have to say, run while you can, the door is open. So this is an old hat topic, but my argument is hey, some folks might say, we've been
02:46
talking about this for 15 years, but these things have durability, and I think the old hat beats the new hat. I mean, surely, everyone who's seen Indiana Jones knows that Indiana Jones hates the fascists, so we have some durability here, we have something that can stick, and it's not like
03:03
something that's going to rot in the fridge. So our old hat is 1.5 decades old, it's sturdy, it's something that we've depended upon, and it really, if you look at the ZCA, at the Zope component architecture itself, hasn't really changed that much in that decade and a half. There are things that are new that are rotting in the fridge.
03:22
Just look at the JavaScript framework world to get an idea of how things change so quickly. I have cilantro that rotted in my produce drawer, and this is something that's durable, that's sitting in the pantry, that's always available to me. So I'm talking about craftsmanship, I'm thinking about food, this is also, Boston is the
03:42
home of a public television show, This Old House, where they focus a lot on the craft of building things. So there's also a lot of American food media here in Boston, or either rooted in Boston, so plan for a lot of kitchen metaphors sprinkled throughout the discussion. I hope you can see what I did there.
04:03
Components are a very relevant part of our craft, and they're even relevant in the time when we have simplified APIs. I'll make some arguments to that effect, agree or disagree, we'll talk about it. The goals here, I want to talk about components in Python. I want to talk about it in Plone, and I also want to look at it in the context of foreign
04:23
cuisine, and in that sense I mean JavaScript. I've taken a lot of the idioms that I've felt are appropriate, not everything, but things that I've found in the component architecture on the server side and transformed them when I'm working on things on the client side. So aside from all the complexities of the JavaScript world, you can still rely upon
04:42
some of these comforts of the way that you solve some of these software architecture problems by using components, or at least some of the ideas that we have in component architecture. So we'll talk about some of those idioms. We'll talk about why this matters, or at least why I think it matters to me. This is subjective.
05:00
I'm not preaching truth here. We'll also try to get through and discuss a few workarounds in terms of debugging component registrations. We have a large push in the last few years for simplified APIs. We have an interesting push to try to make Plone easier to digest and more accessible
05:21
for a large number of people of various capabilities. And I want to make the case that there is not necessarily an either or between using something like Plone.API and using the component architecture itself. If you are an add-on developer, if you're developing for the core, you can probably mix those things, and we shouldn't necessarily have fear of one approach or the other.
05:43
They're somewhat orthogonal to each other to some degree. And I think it is necessary to be able to understand the component architecture unless everything else can be a leaky abstraction unless you really understand this stuff.
06:03
So I'm going to make the cooking analogy with this James Beard quote. I think that what he's saying is, don't be afraid to get into the kitchen. And the no silver bullet argument that complexity is OK that Fred Brooks makes, I think that if you have essential complexity, it's part of your problem domain,
06:23
it's OK to think about it. It's OK to use it. It's OK to leverage it. We have complexity. We have a component registry that's sometimes insanely hard to debug. But it also solves more problems than it creates. I'm going to be a little bit more contrarian than necessary about things,
06:41
about public APIs, especially procedural APIs, even though I use them, especially in tests and things where it saves me time. Disclaimers, I'm not sure what this talk is about. I mean, I know it's about the component architecture. I know it's about components in general. But this is not necessarily a development practice versus development
07:02
software architecture discussion. I think all these truths are contingent, meaning you're going to have subjective experience that's going to guide you to the degree to which you're comfortable using some of the approaches that I use and some of the approaches that are sort of native and idiomatic to the component architecture. I'm hoping to give you three things from this talk today.
07:22
The first is why and how I think the component architecture matters. I want to give a handful of scattered ideas of ways to make it easier when you get stuck and talk about things outside the box. Brief history, my first recollection of talking about components and this
07:41
big shift from my previous Zope experience was Birds of a Feather session at OSCON that Jim Fulton was talking about something that just, this sounded way different than what I was really thinking about in terms of object-oriented development, object publishing, and the way that we thought about Zope 2 in the early 2000s, very,
08:03
very different. And what was described to me about interfaces and what components were, it sounded a little foreign. It sounded exciting. I trusted that Jim had an incredible vision. But the motivation, if you want to look at any piece of Zope 2 code and
08:20
find the motivation, look at something like the OFS package in Zope 2. Look at the sheer number of mixins that are required for, say, object manager, which is what the basis of Plone Folders is. That learning curve requires you, the typical mailing list response in that era was read the source. OK, yeah, well, we'll do that.
08:40
But that is a large investment of time. It's a large investment of bandwidth. And it's a commitment to a large degree of rote memorization. So the idea was try to make these components smaller, use composition, prefer it to inheritance. These are patterns that came from places that this is not something
09:00
that we invented, we as a community invented. But Jim was wanting to apply these ideas to Zope. And I think that that was a very nice way of reconceptualizing what we're doing. Of course, what happened was Zope 3, which was the idea that we're going to toss everything and reinvent an entire
09:21
application server framework. And I don't think that necessarily solved the problem because it created additional complexity. But what we did as a community, and I mean that in a very large sense. I mean that for Plone folks, for Zope folks, for people who are in the Python community at large who use parts of this, was absorb the complexity that worked for them.
09:44
And so we have the ZCA. We have some components of the Zope toolkit inside what we have. So we went from bigger parts to smaller, pick what we want. We're not taking the whole soup pot of Zope 3. This is a buffet style approach to looking at development.
10:02
There's a cost to this. And there's a large number of packages. In terms of the package distributions in your eggs folder, in your Plone build out, it's very large. But these are all digestible pieces. And so the learning curve is broad, not deep. That's a trade-off. I think that it's a worthwhile trade-off.
10:22
And it's really eat what you want. So Twisted, for example, uses Zope.interface. And they use it in spite of the fact that there are alternatives that they could consider using, like Python abstract base classes. The holy trinity of components. Somebody might say, hey, components
10:42
are all about adapters and utilities. And that's really, from my standpoint, the way I approach things is I'm looking at schema and adapters and resources. I'm looking at schema first. I'm always looking at the state of content first. I'm not looking at necessarily action-based behaviors.
11:01
I'm not looking at this from what should happen from an imperative standpoint. I like to think declaratively. But that context, if you look at adapters, we have single-context adapters that do one thing with one context. Usually content might be something else, like a site. We have views that have multiple contexts.
11:21
So a view is the simplest example of a multi-adapter. It adapts two things, content and a request. It's brilliant. In my opinion, much more sophisticated and natural than what views are in most other Python frameworks, which are simply endpoints to routes. There's no context involved. And so adapters are a really interesting thing.
11:44
And the way that we do them seems really nice. Synchronous event subscribers are a type of adapter. Resources. I think about things in broad context. Content, utilities, request, response. These are things that typically have some kind of state.
12:01
So a request has state like form variables, cookies. We do things with content has state fields, for example. Utilities might or might not have state, depending upon what kind of utility it is. That state may come from the file system, for example, configuration that's global. Or it might come from the site, from a registry
12:21
or something like that. Schema is the first thing I write when I write applications. And the reason why is because I think in terms of nouns and not in terms of verbs, I think that when you have a content management system, your brain eventually gets trained to think that way.
12:40
So when I categorize objects, I'm thinking about this way. There are two broad categories of objects. There's those that you look up, and there are those that you don't. And when I mean look up, we've done this before. We have object publishing. We've looked up components by path. We've done this from the beginning. That's what Zope has done and what Plone
13:03
has done before there was any component architecture. Doing the same thing by component registry is a different way of looking up. You could say, hey, it's more flat. It's solving a different problem. Or you might do something like looking up something by traversal and a component registry, which is what views are doing.
13:22
There are certain kinds of objects that you don't need to look up, but they're a minority. Things like request objects, they're in the service of a look up, but they're not necessarily looked up themselves. They're just created on the fly. So we have indirection, and I think that's OK.
13:41
And what I mean we have indirection everywhere, what I mean is we have this component registry that you look in terms of the number of different possible adapter registrations in a Plone site for certain interfaces like widgets, for example. You can start looking into the number. The number goes into the hundreds.
14:00
So if you're in a PDB session and you're trying to go through and figure out exactly what it is, why it is that your widget is not overriding the default, you might be actually looking at hundreds of registrations, and that can be scary for some folks who want everything to be explicit and hard coded. I work with a Java programmer who has that philosophy,
14:23
and I don't blame him because sometimes it is a little bit scary. But my argument is we get something out of that. Even though you'll eventually end up in the debugger, we get flexibility. We have consistent idioms. We have the ability to do the same thing. We have the ability to have things be pluggable.
14:42
We have the ability to have contracts with interfaces. And we have the ability to work together. If software is social, those interfaces are contracts for folks who write add-ons to write something that we didn't anticipate. So components are sort of the mirepoix of good software.
15:01
And the registries that allow us to look them up are effectively the larger that we tap into to be able to do the kind of cooking that we do. So you don't have to use a registry to adapt an object from one interface to another. You simply just have to construct the adapter with the context of the thing it's adapting.
15:23
But having a registry keeps things tidy. The organization system may be as complex as a card catalog, but if you have a library, you certainly need that kind of thing. Some principles. So I call this recipe-driven development, meaning this is what I typically do
15:42
when I write an add-on to Plone. I write interfaces first, write documentation about how to use those interfaces. Often that comes in the form of doc tests. It might come in the form of unit tests or integration tests. Then I write the implementation, and then refine and adjust. And then break the build, it's going to happen.
16:02
I spend a lot of time waiting for integration tests to run over and over and over again for simple things that should just be simple. But in the end, I end up with something that verifies that my implementation works to my interface. And I'm writing tests to the interface, not to the implementation.
16:20
So if I want somebody to, if I expect somebody else is going to write something that's pluggable to what I'm working on, it makes a lot of sense to write your tests to your interfaces only. Design by contract. I like this phrase, but for some reason it's trademarked. I don't think, and I'm not an attorney, but I don't think it's a trademark worthy phrase.
16:43
Interfaces are contracts. And that contract is effectively a way of, it's a promise that we make to people who want to write something like, you want to write a widget that does something better than my widget. I have a default widget for entering dates. You might want something else.
17:02
And so the interface is the contract for that. We can use multiple implementations to fulfill those contracts. We can register things that override a default. We can plug things in. This is a good thing for widgets, for various kinds of separation of concerns, for testing. These are all things that I think are useful.
17:21
This is a corollary to writing components. And that is, we have idioms from our platform. We also have idioms from our language. In Python, we have native data structures that seem natural to use. And most folks that I've worked with that are Python developers that are working on a large team project, but they're fairly new to Python,
17:41
they don't necessarily write things that look like Python, to me, I mean, idiomatically. So you have something like mappings and sequences and sets. And these are native, simple, built-in data types. We do very similar things with B-trees and with persistent lists and other things
18:01
that we use in the ZODB. But in the end, you want to make components that look like those things. I'm making that argument because I like things as nouns and not as verbs. And what I don't want to end up with is, for example, the best example I can give is someone writing a 10,000 line
18:22
file that is basically a bunch of accessor and mutator methods that have hard-coded SQL inside them. So because everybody's thinking that the model isn't the thing, the controller is the thing, we put the m as a very capitalized thing in MVC and not necessarily looking at things from a procedural standpoint. So developing things with a resource-centric perspective.
18:43
And what I mean is that we're driving things by state, not by action. So things are place full, meaning we might have a path to get to a folder, and that folder has a specific contracted interface. So we say the folder is the responsible party for adding things to itself.
19:02
Plone.api may have a different opinion on that. That's OK. But from my perspective, we're doing things in a resource-centric way. We have some unique talents. The platform, we have traversal. We have used traversal for a variety of things. We have an FTP server, a web dev server.
19:20
We have all these things that are built into Zope 2 that take advantage of the fact that we're doing object publishing. And we really should write APIs that sort of reflect that kind of placefulness, in my personal opinion. Some people may disagree. The other thing about persistent objects that we get is that objects are first class. They're not proxies to something else
19:41
like a record in a relational database or something else. So having persistent objects means that we can have state, but we can also have things that massage and normalize that state on load or on save. That's very useful. So when we have these things, it sort of all lends itself to say the components we write should
20:01
be more like nouns than verbs. They have certain prescribed actions. But we think in terms of REST. We'll think in terms of resources on when you're developing locally, not necessarily just with services. So now I'm just going to be a little contrary here. This is probably what I would say about APIs.
20:25
There are purposes for APIs. You want what you see here, which is make things easy and accessible. This was lunch for my eight-year-old daughter. So that's nice. It's simple. It's great. But this is in the check stand line at Walmart.
20:44
I like the idea. I have a pressure cooker. I like the idea of one pot, one shot. Dump stuff into the pressure cooker, close it, press a button, and you've got some food. But cooking is typically more involved than that. So my argument is that when we have APIs, you have to make the choice as to which
21:01
is more pragmatic for your specific use case at the moment you're writing code. You have opportunities and costs for both procedural and state-driven APIs. The component architecture typically doesn't necessarily have an opinion one way or another about this.
21:20
But when you pair the component architecture with things like graph traversal in Zope, my sense is that the procedural API is a way to try to hide the CCA from you. And my argument would be eventually that abstraction leaks. But the procedural APIs have some benefit. They certainly simplify things.
21:41
But now the site is sort of a mega controller because the site is the only resource you're working with. In Ploned.API, that's typically the case. The other resources you get from Ploned.API are things like, let's say you're adding or getting a user, getting the authenticated user. You get an IPropertiedUser, a user object that implements IPropertiedUser.
22:01
And that uses property sheets, which is an old Zope 2 thing that we don't really like to work with. There are costs to that. We could write a wrapper. But the Ploned.API folks have good reasons for not using wrappers. The other argument would be, well, let's just try to figure out a way to replace
22:20
the thing in the core and make it simpler. And that would be the argument that I would make. But in any case, there's costs to these procedural APIs. We lose the opportunity to traverse to the thing that does the thing that we want to do. So we maybe want to add an item to a folder. We don't traverse to the folder first and say, hey, folder, add this child item, this document,
22:43
to yourself. So it seems a little less object oriented. That's neither here nor there. But there also are these leaky abstractions. We don't necessarily always do what we want. And we're losing the opportunity to represent what we're working with as a noun. Everything is represented as a verb. Do this, not get this.
23:02
And it has its own specific ways of manipulating its state. State driven APIs, components of idioms. I mean, we will write utilities and adapters that look like mappings. That's an idiomatic thing that we can do. We do do that, I think, in this community to a large degree. Components look like the state they represent.
23:22
There's a cost to that. You have to traverse to the resource you want versus the procedural API. That may not necessarily be a bad thing. And sometimes we might be using the folder as the resource that is responsible, the responsible party for creating new content as opposed to the site.
23:41
In my opinion, that's more object oriented. But again, like I said, that's very subjective. But sometimes component lookup is part of the cost of writing the state driven APIs as opposed to using something procedural. You're never doing an adapter lookup using Plone.API. You're never doing any kind of lookup using Plone.API. That's being done under the hood for you.
24:01
That's great. But you ought to be able to do a little bit, at least, of work with components in their raw materials form to be able to write software that is effective, useful, repurposable. And so I'd ask this possibly hyperbolic and possibly
24:20
stirring the pot question of, are procedural APIs sort of the frozen microwave meal of software design? They have their utility. Some can be good. But they're not always necessarily as satisfying. I bought a frozen pizza from the hotel last night and microwaved it in the hotel room. It was not very impressive.
24:42
Placeful interfaces, so like traversing to an item. They made clean URLs before all the folks from Django and other places were thinking about routes and mappings and slugs and things. The people who put Django together arguably didn't even know about why it was that Zope had clean URLs in the first place.
25:03
So one of the arguments they were making for their framework was, well, hey, we get better, cleaner, nicer looking URLs because we can slugify some piece of content that's backed by an ORM and we have some route to it. But the problem with that thinking is it sort of neglects the fact that Zope was there first
25:21
and came up with a more sophisticated, but also potentially more natural way of representing things than a view, which is simply just a function that is context-free. So anyway, the approach is from an API standpoint,
25:41
and they're not necessarily mutually exclusive. I'm using both. I use plundered API. I'm using it a lot more in my testing. I don't use plundered API necessarily for testing things like users because I have my own basic giant mapping simplification facade on top of all the entire pass CMF tool framework
26:02
everything that we do with users in Plone. So I wrote my own simplification. I wrote my own API because I like it better because it's a mapping of users and a mapping of groups, and everything's represented as what I think is idiomatic Python rather than having a procedural API. But I do use plundered API for things that I like.
26:21
I use it a lot in tests. I'm using it for simple things, and I think it's OK to mix them. And especially when they help with the learning curve, they're not necessarily inappropriate for experienced developers when they save you time. But at some point, you're probably going to want to reach into a more sophisticated toolbox.
26:44
So my point here is don't think the zoped component architecture is just for component ninjas or people who are core developers or what have you. I would say if you're writing add-ons, you want to have your kitchen appliances. I like my food processor, but sometimes it's nice to be able to make pie dome from scratch.
27:01
It's nice to be able to chop onions when I need to chop onions. In fact, my food processor crushes the onions, which makes the flavor worse. So I would say don't be afraid to get a little scarring because it teaches you something about the framework. You have this institutional learning that your organization is going to do about how to code and write software,
27:21
especially if you're working with others. And those scars will teach you how to write software in a way that is helpful in terms of contract. So I'm not going to judge if anyone wants to use simplified APIs.
27:43
I'm using them myself. I even bought a frozen chicken tikka meal from Trader Joe's last week. But I like to cook. So I make my own compromises, and I think you should, too. So I have a pressure cooker. I make my own stock. But I don't grind my own sausage. Because my reasoning is actually quite simple. It's easier to clean up.
28:02
So make choices that you're comfortable with when you're writing software. And your choices may evolve over time. But get comfortable with the zoop component architecture because it's only a handful of libraries. It's only a handful of idioms. And it's really not that complicated once you get the hang of things. So just enough is, what do we do with the component
28:23
architecture? My basic principle is, I think, the idea that objects have interfaces. That's what a component is. But they're also connected by interfaces. And that's Jim Fulton's language. This is from a presentation that he gave in 2004.
28:41
And this quotation, I think, is quite apt. You put them together, and the interface is basically the contract. It's fundamental to everything we're doing in Plone and should infuse how you think and write about code. That's my argument. It's a contract for both behavior and state. It provides for separation of concern.
29:01
So we can say, we have content types. We write these into dexterity. And all we're really caring about is we write some fields that represent what it is we want to store. And what it is we want to ask people to put in forms. And anything else beyond that, you separate that concern into something like an adapter that does heavy lifting.
29:22
So I'll give a few examples in subsequent slides. But my argument is that resources are going to be easier to work with when they're smaller. They're also going to be easier to work with when they're pluggable. And so if you separate these concerns, we can make global functionality site independent. We can do things like have, we can look up things that are useful for the site.
29:41
We can do object oriented development by composition, not by inheritance. We don't have the complexities of some of the older bits of ZOAP2 that we have to deal with still. But in any case, this is the simplest thing that you start out with. Write an interface that describes some state. So I have an ingredient in a recipe.
30:01
Let's suppose I have a recipe system. And what I really care about is my international audience. I'm not writing something that's going to say three cups of flour. Because the pastry chefs are going to get annoyed at me for using volumetric units. I'm not going to write something that says 12 ounces
30:21
of flour because there's folks who aren't using imperial units who are going to get grouchy with me. So let's say I want to localize this kind of stuff. I need some way to store, let's say I store each ingredient in a grid. I'm using like a data grid field or something like that. And this recipe is just about state. And the ingredient component of the recipe
30:42
is just describing state. We're describing the state in schema. These are simple fields. It's declarative. It's not imperative. And we're storing the state as content, and dexterity takes care of that for us. So we can have methods and interfaces like I can say, well, OK, let my content type have a display title method.
31:02
Now, I want to display two cups of flour, something to that effect. But there's debatable merit to putting it on the content type itself, on the implementation of the content type. In fact, in dexterity, the default is not to even have an implementation class that can do that for you.
31:21
So adapters are useful here. Let's say I have this ingredient formatter adapter. I simply just adapt. I take the context. It's a simple, very, very simple component. I take the context of the content that I want to work with, and I have some method. In this case, I'm using the under call to basically format a string.
31:42
But then I can make this something that could be plugged into a registry, and I could have more than just a default adapter. I could have an adapter that does the same things with fetching the dense, maybe have a global utility that has a density of ingredients. So I know how much I'm making pizza. I need to know how much, what the density of double zero
32:04
grind flour is in one cup. And I need to be able to take that density and have it in some globalized unit like grams per milliliter. I want to be able to turn it into metric if somebody's typed it in imperial Unix.
32:20
I want to be able to get fractions or decimals instead of a floating point. We had a floating point number for what we were talking about for quantity. So I want to be able to get weight and volume or vice versa. All of those specific concerns can be solved with an adapter. I don't have to make this one mega content type implementation class that does all of these things.
32:43
They're usually registered, though. And that's the way we plug these, and the way that I can say I write this recipe system, but you have this different way that you want to format it for your site. You want to reuse my add-on, but you want to do it in a slightly different way. You can write an adapter that displays things differently. So we typically declare what the adapter implements
33:02
and what it is adapting. And we can do that in Python or CCML. And we typically register an adapter usually as a global component in the component registry. As I mentioned, there's lots of adapters registered in Plon. I mean, there may be hundreds, thousands. I'm not sure exactly the number, but it's a large number.
33:22
But the lookup is actually pretty quick. It's a small cost for standardization. And so we typically do that by calling an interface, like I ingredient format or my ingredient, and then calling it. So if I had a default that simply just output a string with the units and the quantity and the ingredient name, that would be fine.
33:41
But if somebody wanted to do something that was internationalized, somebody wanted to do something that's localized to non-imperial units, somebody wants things in volumetric units, we could have something that pleases all those audiences. So the problem with these registrations is sometimes you need to figure out why it is your registration is not overriding some default.
34:02
The thing you need to do, the short version is get rid of this under-ZOPE interface, seeoptimizations.so, move it temporarily out of your zope.interface distribution because it prevents you from doing useful things in PDB. What that forces you to do is, once you've restarted your instance, you can use the Python implementation of the adapter
34:22
registry. And that is completely 100% debuggable. It's still a lot of work, I promise you. Get lots of coffee, it's going to be some work. But once you figure things out, you can do things like be more specific about subclassing. For example, using a browser, we use a browser layer interface to be able to have more specificity in terms of subclassing
34:43
so we know that our specific date widget adapter wins. That's basically the way that we've solved some problems by saying, we need to make our adapter registration more specific than the default adapter registration. That way, in all cases, we get the right widget.
35:03
But I had to figure that out by going through three hours of PDB hell. It happens. It's something that you occasionally have to do. But it's a small cost. It's infrequent. It's something that is worthwhile. If you get stuck, you can do it if you're comfortable with PDB. Browser views.
35:21
I think this is great. Other frameworks see views as functions, basically endpoints to URLs, the routes mapped to the views. We don't do that here. We think about views as completely about context. We're taking a view that is about the content that we're adapting and the request that it's adapting. We're taking a resource and a request.
35:40
It's placeful. It's graceful. And it's all about the content. It's not like capitalizing the C in MVC. So we're not making the view simply just some kind of glue. Global utilities are useful for site-agnostic things. We use these in Plone.
36:01
The core of Plone uses for vocabulary lookup. You can use them in add-ons for integration with other services. For example, if you have a message queue or a job queue where you need to be able to do something asynchronously, a global utility might be useful for that. Quick static transformation content if you don't need configuration that's stored in the site. You can use a global utility to get the site,
36:20
but you have to use zoop.component.hooks.get site to be able to do that. That's fine. But generally speaking, there's a variety of cases for global utilities. There's also one kind of thing that we often use global utilities for are vocabularies. Sometimes vocabularies can be static.
36:41
So if you write a through the web content type, oftentimes you make a choice field. The choice field might simply be three items. They're just three strings that you've typed out, and those become radio buttons. That's a reasonable way to approach vocabulary, but sometimes you might want a dynamic source. For example, a stock global source
37:01
might be looking up time zones from PyTZ. Something that's context local to your, like if you want to use the related items widget and you need a vocabulary, you need a source that provides only items in the navigation route. That's an example of something that is context local. So vocabularies are useful for enumeration.
37:22
They might be useful for validation. Sometimes you might just use it for validation. In the case of the related items widget, I think that it's typically only used for one of the, I'm, don't hold me to that. But in any case, sometimes it's used for validating that the thing that you've picked is actually in the set of available choices.
37:41
And sometimes it's just used to enumerate those choices for display purposes or for choosing in a form. For possible approaches to getting some kind of site global service functionality, back in the beginning of the early Zope component days, there was some difference between something
38:01
called a service and something called a utility. And those differences were very soon realized that there was unnecessary complexity. So from my perspective, there's four ways that you can do site global stuff. You can have a persistent component that is a utility that stores state in your site.
38:22
They're a pain to uninstall. But you can also have an adapter of site. And I'll talk about the differences, really quickly talk about the differences between those two approaches. You can use Plone.API in certain cases. And we had CMF tools which were actually storing state because they were themselves persistent objects. That approach is deprecated.
38:41
So if you need to store a state on the site, in the site, you probably want to choose between the first two of these choices. So persistent utilities will store things in the ZODB. But again, uninstalling becomes the big nightmare. And the reason why the community has generally shied away from this. But there's some good things you can do, like cache data across requests in volatile attributes.
39:03
And that means that you might be able to do something that's helpful from a performance standpoint that you can't do when you're having to construct an adapter every time that you have a request. Now there's no need for proxies. The methods and the data are all in one place. That's also good. You need to have something calling set site.
39:23
Usually a web request will do this for you. If you have a script, like a run script that you're running from the command line, you need to call zoop.component.hooks.setSite to be able to use any kind of persistent component. You need to do that for a lot of things, pretty much across the board. So if you're already doing it, it's probably not necessarily
39:40
the cost of this. These things get a lot of blame. I'm not necessarily saying that they're as bad as they've probably taken flack for. The adapter of site pattern is a different approach to this. It needs to be something that's really, really cheap to construct. You can't take a long time for your constructor to run. Because you'll need to maybe do this more than once.
40:00
You might be able to put it in an annotation on a request to be able to avoid doing it more than once if you're clever. But you don't need things like set site unless you're trying to get specific site state or you're not passing the site itself to the adapter. But in that case, because the adapter is constructed
40:20
with the site as its context, it has access to the site you're working with. There's no uninstallation issues if, with a very important caveat that you are using out of the box data structures inside things like annotations on the site. That can be good or bad depending upon your need. If you do something like subclass persistent list
40:40
and then somebody uninstalls your add-on, you will end up with a broken object that you need to deal with. It's not as bad as having a broken persistent utility, but it's something that you'll maybe have to deal with and have to clean up in your ZODB. Related technologies, briefly. Martin Fassen has a library called
41:01
Reg that is inspired by zop.interface and zop.component. I don't know a lot about it. I know that it exists. It's something that you can look into. He's blogged extensively about it. There is the Python's built-in abstract base classes. You could call it a popper's interface. I think there's several problems with it.
41:22
I could critique them. The biggest critique I have is that it only describes zop as a possible relationship. As a relationship, they're not necessarily. So we think of things like behaviors in dexterity. I even wrote a throwaway experiment to do per instance to be able to mark individual items of content to have
41:42
specific behaviors instead of content type global behaviors. And that's a plug-in I wrote four years ago called experimental.flavors. It probably doesn't work anymore. My point is that abstract base classes have this limitation, but there's probably a better critique that you can find
42:00
on the internet explaining the differences between interfaces and ABCs that Glyph Lefkowitz has written. And I think that if you're curious, read it. Outside of the server, we can take some of the ideas that we have, take them to a JavaScript context. We recently wrote a mock-up widget that is extending the pick a date widget to do keyboard
42:21
input, but we used an adapter pattern. So we basically adapted the existing widget. We constructed it, adapted it, modified it in place. And that was easier for us to modify in an add-on. That way, we're not using any kind of component registration or presumptions otherwise, but we used the adapter pattern. We said, OK, construct the basic widget, adapt it, do things to it, add additional.
42:43
So we basically had a nice clean separation of concerns where we added something. We enhanced the pick a date widget that ships with Plone 5. We've also done several incarnations of zoop.schema lookalikes in JavaScript for some of the projects that I've worked on. We're running out of time, so I have
43:01
a link to these in the slides. The slides will be up. I'll put them on SlideShare sometime later today or tomorrow. But you can see a basic example. I'll see if I can click to it. Try to quickly preview this.
43:24
Hold on. Firefox doesn't want to cooperate. Without me ending the slides, let me. Anyway, short version. We're actually planning to write a form library, a forms implementation. We do a lot with data collection and forms of various complexities.
43:41
Something similar to what PFG is doing, what Easyform's doing. But our internal form implementation is showing its age. We're actually going to be rewriting everything client-side. So we're going to be taking, basically, supermodel schema and rendering forms completely in JavaScript with the payload. Our payload is already JSON. So we're actually doing something now
44:02
where we're generating the interface on the server side, but we're sending things back in JSON. It makes sense to do it completely in JavaScript, and that's where we're going. And that probably means that we're going to leverage our JavaScript schema lookalike to be able to declare fields.
44:20
We actually used the idea of a zop.schema lookalike in JavaScript to write a specification for a vendor who is writing a data visualization piece for us in JavaScript for D3. So we actually had an existing JSON data feed that provided points for time series plots. And the JSON itself is not self-descriptive,
44:45
and JSON schema is somewhat limited. So we actually used something that looks a lot like zop.schema, but in JavaScript, to describe to the vendor what it is that we wanted. So we actually had a hard-coded specification of the data model written down in actual running JavaScript.
45:01
So that was helpful. So just to be clear, I'm looking at the word component. There's the word web components, the phrase coming to the fore in the JavaScript world. But I'm thinking of these things as completely distinct things. They can have some overlap, but they're really not related to each other, just to be clear.
45:22
But we can have schema development in JavaScript. We can even have component registries in JavaScript if we really wanted. I use adapters in JavaScript. I haven't done any kind of component registry for them. But I've also done things like synchronous event notification in JavaScript as an auxiliary to doing asynchronous callbacks, depending upon the appropriateness of the specific kind of solution.
45:44
So I'm working and looking at form applications as a real significant opportunity to do some interesting things with schema and adapters and events and that sort of thing in JavaScript. So I might have more to say next year on the subject.
46:01
My point, generally speaking, in food and software, there's always room for good cooking. And I would invite you now that you've seen all these food slides to go out and invest in the local economy, because it's lunch time. I had the privilege of giving this talk to you at this time. Questions?
46:21
Z-shaped learning curve, sure. It starts off shallow. And then there's a steep climb. And then once you've climbed that steep climb, it seems to be shallow again. I don't know if that's really S-shaped, Z-shaped, Z for Zope. But the thing is, the general idea
46:42
is that the learning curve was easy for Zope, because we had things like Z, in the olden days, we had things like Z classes and through the web development. Non-developers could very easily get into the mentality of developing an application quickly, because everything could be done through the web. And then we threw all that away.
47:04
We still have this, OK, now you need to grok OFS. And you've got to go read the source to figure out why it is that your copy and paste thing isn't working. And so now you're looking at OFS.copy support, and you're going, oh, this is crazy. This stuff is insane.
47:20
But hey, it works. It's withstood the test of time, but it's still complicated to have to read the source to get how this stuff works. So the idea is break things into smaller components. You're not going to do that for Zope 2. Things like copy and paste are fairly complicated. You look at what object manager is doing for managing folders, it's a pretty significant, hefty interface
47:42
to be able to program against. You could break that into smaller pieces. I think if the community started that over again, they would. Yes?
48:00
Sorry, what's that? Oh, sure. So this is expensive, looking up things that are expensive.
48:22
And it was thought a good idea to write it in C. So this is actually the look up, the loops for the look up involve determining an order, which is how many things that the adapter is adapting. So if it's a view, it's adapting two things, content and request.
48:41
So how many adapters do I have in order 2? Let's say I have 1,000 adapters that I have to loop through. That kind of iterative thing is very expensive if you do it in pure Python. So they optimize it by putting it in C. But the thing is that there's a point to which PDB just skips over a whole bunch of really useful information
49:01
for you if you still have this in your distribution for Zope.interface. If you move it out, move it into a temp folder or something like that, do your debugging and move it back after you're done, it still takes time to debug. But it gets rid of that obstacle of making it opaque to you.
49:33
The folks who wrote it made a very good effort in a lot of cases with stuff that is in Zope that was written in C
49:41
to have a one-to-one functional equivalent in Python. So I don't make any guarantees that that's necessarily the case, that there's not any kind of drift that's happened. But this stuff has been living for a long time. I'd be significantly doubtful that there's any functional difference aside from speed and debuggability
50:02
between the C implementation and the Python implementation. Anybody else? Thanks again. It's lunchtime.