OpenERP 6.1 - Web Framework Tutorial
Upcoming SlideShare
Loading in...5
×
 

OpenERP 6.1 - Web Framework Tutorial

on

  • 16,883 views

Technical Presentation of the new Web client and framework of OpenERP 6.1: architecture, technologies, API, best practices, Javascript pitfalls, etc. ...

Technical Presentation of the new Web client and framework of OpenERP 6.1: architecture, technologies, API, best practices, Javascript pitfalls, etc.

DON'T FORGET to scroll each slide to read the NOTES.

See also the related presentation about the changes at the server API/framework level:
http://www.slideshare.net/openobject/openerp-61-framework-changes

Statistics

Views

Total Views
16,883
Views on SlideShare
16,776
Embed Views
107

Actions

Likes
12
Downloads
674
Comments
0

3 Embeds 107

https://twitter.com 88
http://www.techgig.com 10
https://si0.twimg.com 9

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

OpenERP 6.1 - Web Framework Tutorial OpenERP 6.1 - Web Framework Tutorial Document Transcript

  • Upgrade Training Web Client
  • Migrating 6.0 web addons to 6.1
  • Migrating 6.0 web addons to 6.1
  • Rewriting 6.0 web addons to 6.1
  • There is no migration Everything has been rewritten from scratchBeen at the community days, probably seen it. Not been and looked at OpenERP Web (6.1),probably noticed.I doubt there’s a line left from the 6.0 codebase (we even split the project itself out), meaningthere isn’t a single API left either, meaning you’ll have to rewrite everything as well.
  • Technological changes 6.0 6.1 Python (+ javascript) Javascript Mako QWeb CherryPy Werkzeug Pages Single Page Interface Page requests JSON-RPCClient used to be mostly Python and HTML-ish templates, bulk of logic moved to javascript.* Template language QWeb, reminiscent of Kid/Genshi (though not inspired by them), XML-basedMore applicative interaction, more stateful, no page requests and full reloads.
  • cloc: 6.0 Python 16059Javascript 9782Templates 5905
  • cloc: 6.1Javascript 16925Templates 3693 CSS 3064 Python 1874
  • Deployment changes • Embedded mode, primary M.O. • Stand-alone mode for dev/testing • Official OpenERP bundle embeds* Web Client has become “just another” OpenERP addon, though remains a different project* Standalone mostly to avoid full server reload when writing Python code** Or to put proxy inbetween
  • Architecture
  • Network HTTP XML-RPC JSON-RPC OpenERP Web Client Browser HTML6.0’s network arch: typical web frontend, communicates with server via XML-RPC, with clientvia regular HTTP requests (e.g. link clicks) and HTML pages * Most client logic in the Web Client layer, not really a good fit as OpenERP has lots ofclient state * Tentative to migrate to more stateful browser-side (iframe then ~pjax), half-heartedand unsuccesful6.1 merges server and web’s python: web’s python becomes a JSON-RPC interface toOpenERP (+ some specific services), vast majority of logic moves to browser
  • Network HTTP XML-RPC JSON-RPC OpenERP Web Client Browser HTML6.0’s network arch: typical web frontend, communicates with server via XML-RPC, with clientvia regular HTTP requests (e.g. link clicks) and HTML pages * Most client logic in the Web Client layer, not really a good fit as OpenERP has lots ofclient state * Tentative to migrate to more stateful browser-side (iframe then ~pjax), half-heartedand unsuccesful6.1 merges server and web’s python: web’s python becomes a JSON-RPC interface toOpenERP (+ some specific services), vast majority of logic moves to browser
  • Layout
  • LayoutWeb Client
  • Layout Header Menu Menu(secondary)
  • Layout Action ManagerOpenERP’s actions (ir.actions) -> primary drivers of everything (pretty much)No visuals, dispatches to various children based on action executed (window, client) or doeswork itself and handles result (act_url, server, report_xml)
  • Layout View ManagerThat slide was hard workActual handling of window actions: parses view descriptions, initializes views and handlesinter-view communications, view switching, ...
  • LayoutView (form)
  • Layout View (not form)View (not form either)
  • Layout: summary• WebClient • Action Manager • View Manager (?) • Views (n) • Client Action (?) • ...
  • Reusable pieces Dashboard (form view)
  • Reusable pieces action manager & action manager view manager & client action & list view
  • WidgetsBase unit of (visual) work. All the blue boxes are “widgets” (a class of openerp web)Widget ~ MVC view~ Backbone.View~ NSView~ QAbstractItemView~ Spine.ControllerCouldn’t use “View” due to confusion potential with OpenERP views (list, form, ...)
  • Widget Responsibilities• Drawing on-screen (template rendering)• User events handling (DOM events)• Delegation
  • Widgets: templates• Built-in handling• Only trivial information• Widget DOM root set up during rendering
  • Widgets: events• No special provisions• Use regular DOM events handling
  • Widgets: delegation• Create children widgets to manage sub- sections• Implementation detail of the widget• Cooperative behavior between parent and children
  • Widgets lifecycle • Synchronous initialization (construction) • Template rendering • DOM insertion • Asynchronous initialization (secondary) • [life] • Cooperative destructionSee code (and detail of APIs) later* Difficulty of parent/child relationships is not leaving “dangling” events or children** Recursively stops children widgets** Removes widget from DOM** Removes widget from parent** Must explicitly stop children when “killing” them during normal parent life (e.g.ActionManager)
  • JavaScriptYour new frienemy.
  • $ var a; $ a = 3.42; $ a = "42"; $ a = true; $ var array = []; $ var obj = {}; $ "foo".indexOf(o); 1 $ (3).toString(); "3" $ [1, 2, 3, 4, 5].slice(1, 3); [2, 3]Base language has Python similarities:* Dynamic typing* Literal booleans, strings, numbers, arrays, ~hashmap* Object-oriented, methods on a lot of things
  • > if (condition) { > // action > } > while (condition) { > // body > } > for(init; end; each) { > // stuff > }Statements-heavy, close to C in its core syntax: if, while, for, return (explicit), ...
  • $ function f1 () { > // things > } $ b = f1; $ var anon = function () { > // body > }; $ [1, 2, 3].forEach(function (item) { > console.log(item); > }); $ var a = 4; $ var fn = function () { > a = 5; > }; $ a; 4 $ fn(); $ a; 5* First-class functions (& higher-order functions)* Anonymous functions* Full closure (read/write)
  • $ var r = /foo/; $ r.test("a foo"); true $ r.exec("a foo"); ["foo"]Additional stuff:* Regex literals (~Perl)** RegExp#test(String) -> Boolean** RegExp#exec(String) -> MatchArray** String#match(RegExp) -> MatchArray** String#search(RegExp) -> IndexNumber** String#replace(RegExp|String, String|Function) -> String** String#split([String|RexExp], [Number]) -> Array<String>
  • Not Python* Empty array, object is true (empty string is false)* objects only have string keys, few methods (and no map-like methods)* “for (i in array)” does not work “correctly”, “for (o in object)” is tricky, only iterates on key* ‘key in object’?* “weak” types (+/-; ==; ?)* objects v primitives; typeof v instanceof* Very small “standard library”: 10 global functions, 8 global constructors and the Math andJSON namespaces... and the DOM (in browsers)
  • DOM* Nodes & Elements
  • $ var root = document.getElementById(root); $ root [object HTMLDivElement] $ var p = document.createElement(p); $ p.setAttribute(title, "This is a paragraph"); $ root.appendChild(p); $ p.appendChild(document.createTextNode("Text Content")); $ p.textContent = "text content"; $ root.childNodes[0]; [object Text] $ root.childNodes[1] === p; true $ root.childNodes[0].nodeType; 3 $ root.childNodes[1].nodeType; 1 $ p.textContent; "text content" $ p.childNodes[0].data; "text content"* Very verbose creation and query API [insert node/JSDOM examples** HTML5 APIs/modest improvements (querySelector, querySelectorAll)
  • $ p.onclick = function () { > writeln("clicked paragraph"); > }; $ click(p);; clicked paragraph $ p.onclick = function () { > writeln("clicked paragraph 2"); > }; $ click(p); clicked paragraph 2 $ p.addEventListener(click, function () { > writeln("clicked paragraph"); > }); $ click(p); clicked paragraph $ p.addEventListener(click, function () { > writeln("clicked paragraph 2"); > }); $ click(p); clicked paragraph clicked paragraph 2* Events** “DOM0” simple but broken** DOM level 2 meh (add) to horrible (trigger)** MSIE < 9 incompatible (global event object, element.fireEvent(name[, options]))
  • $ var $root = $(#root).empty();$ $root;[[object HTMLDivElement]]$ var $p = $("<p>", {title: "This is a paragraph"});$ $p;[[object HTMLParagraphElement]]$ $root.append($p);$ $p.text("text content");$ $root.children("p");$ $root.find("p");$ $p.text();"text content"$ $p.click(function () { writeln("clickedparagraph"); });$ $p.click();clicked paragraph[[object HTMLParagraphElement]]$ $p.click(function () { writeln("clicked paragraph2"); });$ $p.click();clicked paragraphclicked paragraph 2[[object HTMLParagraphElement]]
  • Pitfallshttp://bonsaiden.github.com/JavaScript-Garden/
  • $ {> var glob = 3;> }$ glob;3 $ var undef;$ (function () { $ writeln(undef);> var loc = 42; undefined> })();$ loc;Error: Cant find variable: loc$ var shadow = 3;$ (function () {> var shadow = 4;> writeln(shadow);> })(); $ does_not_exist;4 Error: Cant find variable: does_not_exist$ shadow; $ (function () {3 > does_not_exist = 42;$ (function () { > })();> writeln(local); $ does_not_exist;> var local = 3; 42> writeln(local);> })();undefined3Declared but not defined -> undefined (oddly makes sense)Function scope (!C, !java)* Python has UnboundLocalError v NameError, JS has undefined v ErrorImplicit declaration -> global
  • $ var somevar = 3; $ somevar; 3 $ somevar = 4; $ somevar; $ var fns = []; 4 $ for (var i=0; i != 10; ++i) { $ (function () { > fns.push(function () { writeln(i); }); > somevar = 5; > } > })(); $ fns.forEach(function (f) { f(); }); $ somevar; 10 5 10 $ (function () { 10 > var somevar = 42; 10 > (function () { 10 > somevar = 36; 10 > writeln(somevar); 10 > })(); 10 > writeln(somevar); 10 > })(); 10 36 36 $ somevar; 5* Writable closures (not possible in Python 2)* Danger! closures in loops
  • $ writeln(null);null$ typeof null;"object"$ null instanceof Object; $ NaN;false NaN $ typeof NaN;$ writeln(undefined); "number"undefined $ NaN === NaN;$ typeof undefined; false"undefined" $ NaN == NaN;$ undefined === null; falsefalse $ isNaN(NaN);$ undefined == null; truetrue $ parseInt("foo");$ writeln((function () {})()); NaNundefined $ parseInt("1foo");$ writeln((function () { return; })()); 1undefined $ 1 - "foo";$ (function (a) { writeln(a); })(); NaNundefined $ +"foo";$ writeln(({}).does_not_exist); NaNundefined$ var no_such_variable;$ writeln(no_such_variable);undefinedPython -> None and exceptions (errors)JS -> null (None), undefined, NaN and exceptions.* null !== undefined, but null == undefined* typeof null === “object”* Access undefined property -> undefined** Access property set to undefined -> also undefined* NaN exists in Python but very rare, more common in JS due to weak typing e.g. +”foo”** NaN != NaN, use isNaN** typeof NaN === ‘number’
  • $ function Foo() {}; $ var foo = new Foo(); $ foo instanceof Foo; true $ var bar = Foo(); $ writeln(bar); undefined $ var a = function () { this.b = "b"; }; $ a.b = function () { this.c = "c"; }; $ a.b.c = function () {}; $ new a.b.c instanceof (a.b.c); true $ new a.b().c instanceof (a.b.c); false $ (new (function () { this.ok = true; })).ok; true $ var baz = new Foo;JavaScript OO is “interesting” (mostly bad kind)Not going to dwelve far into it, but knowledge of *constructors* useful as object layers aregenerally sugar over JS OO.* Constructors are functions used in special context** Any function can be used as a constructor (!= is useful a constructor)* new $type() vs $type()* $type an expression, ends at first parens by default (kinda weird)** new a.b.c -> new (a.b.c); new a.b().c -> (new a.b()).c** parens optional
  • $ this;[object DOMWindow]Actual output: $ var fn = function (f) { f(this); }[] $ var a = {b: fn};$ this === window; $ fn(function (t) {true > writeln(t === window); > }); true $ a.b(function (t) { > writeln(t === a); > }); true $ new fn(function (t) { > writeln(t instanceof fn); > }); true $ var o = {}; {} $ fn.call(o, function (t) { $ var c = {}; > writeln(t === o); $ c.b = a.b; > }); $ c.b(function (t) { true > writeln(t === c); $ fn.apply(o, [function (t) { > }); > writeln(t === o); true > }]); true* Call site decides of `this` in callee* default (global, function): “global object” (browsers: window)* Not transitive** b() -> `this` is window** a.b() -> `this` is a** new a.b -> `this` is instance of `a.b`** c.b = a.b; c.b() -> `this` is c** Function#call, Function#apply -> `this` is arg0
  • $ var a = { $ a = {> attr: 1, > attr: 1,> b: function () { > b: function () {> writeln(this.attr); > var self = this;> return function () { > return function () {> writeln(this.attr); > writeln(self.attr);> }; > };> } > }> }; > };$ var f = a.b(); $ var f = a.b();1 $ f();$ f(); 1undefined $ a = { > attr: 1, > b: function () { > var fn = function () { > writeln(this.attr); > }; > return fn.bind(this); > } > }; $ var f = a.b(); $ f(); 1* In closures, use alias (`var self = this`)** Function#bind, _.bind** Widget#proxy
  • JavaScript concurrency
  • • JS runtime is a reactor (pattern) • Event loop • Single threaded • Blocking (synchronous)
  • Actually not what you’ll have with network requests: page (or even browser) frozen, no dialogno nothing.
  • • Continuation Passing Style (CPS) • Call long-running operation • Pass “continuation” callback • Called whenever operation completes
  • Callback Mess 1. Forwarding (all methods must take 1/2 callbacks) 2. Composition (waiting on multiple events) 3. Post-hoc decisions (act on an event which may or may not have happened)* Lower evolutivity** Many methods need extra 2 params, harder to read &use** Harder to add new params (callback @end)** Params post-callbacks hard to read/unreadable** Costlier for methods which may become async*** sync -> async transformation horrible so want to have API correct early* Ad-hoc mess of flags &stuff* Need to complement with flags as well
  • Deferred
  • • AKA Futures, promises • (Success, Error) • Pipelining • Composition • Promises/A • jQuery.Deferred, #pipe, jQuery.when* Stateful, tri-state (pending, resolved, rejected)* Callback queues** Deferred#then** Deferred#done/Deferred#fail/Deferred#always* Single state change* $.Deferred#pipe: pipelining (async chains)* $.when: composition** Original: wrap value in deferred
  • Tooling
  • • Webkit Developer Tools / CDT • Firebug • Dragonfly • IE Developer ToolsActual capabilities vary, some things might work better in some tools than in other1. WDT/CDT ~ Firebug2. Dragonfly3. IE9 DevTools4. IE8 DevTools
  • Console* Can actually get CLI JS console (à la `python`): spidermonkey or nodejs* Access to page content (and libraries)* Test code snippets** jsfiddle** jsbin** tinker.io** ...* Interactively inspect objects
  • Console API``console`` object, functions for programmatic printing of information* Basic logging (log, debug, info, warn, error, exception)* Formatting (group/groupEnd)* Timers (time/timeEnd)* Profiling (profile/profileEnd)* trace* dir* count
  • Visual Debugger* Breakpoints (visual or programmatic)* Conditional breakpoints* call stack* Locals** CONSOLE
  • DOM Inspector* “Inspect Element” entry point* Edit DOM (as HTML text, add/remove/change attributes)* Visualize margin/padding/borders* CSS adjustments** Metrics section (overview)** enable/disable rules* DOM breakpoints (flaky?)
  • Network* List requests, time, latency, payload size* Request & response header* Request payload* Response payload (formatted JSON)
  • ProfilerNothing much to say, usual visual profiler, have to learn on your own
  • Console (again)command-line API http://getfirebug.com/wiki/index.php/Command_Line_API
  • OpenERP Web APIs
  • 6.1 Caveat• API stability (and the lack thereof)
  • • Python controller • form.Widget• RPC (connection, • Client actions dataset) • Translations• QWeb • underscore.js• Widget • Dialog• View • CrashManager
  • Classes & subclassing* JS OO somewhat similar to Python’s* Single-inheritance* No sugar for classes, plumbing a bit hard to get your head around and verbose** Helper: Class & Class.extend** this._super, doesn’t play well with async
  • QWeb
  • Usage• openerp.web.qweb• QWeb.render(template, context) => String• t-name• t-call
  • Logic• t-set t-value• t-if• t-foreach t-as
  • Output• t-esc • t-att• t-escf • t-att-*• t-raw • t-attf-*• t-rawf
  • API: WidgetClient actionsregistries (instance.web.client_actions)<iframe class="youtube-player" type="text/html" width="640" height="385" t-attf-src="http://youtube.com/embed/#{widget.params.id}" frameborder="0"></iframe>* sn2l2_v6Ur8 * pOA9PGYeP3E* oHg5SJYRHA0 * wmkoJGm6y6k* ZZ5LpwO-An4 * eQemvyyJ--g* qWkUFxItWmU * hXlzci1rKNM
  • • Web-oriented keys: css, js, qweb• ‘static’ folder• module pattern: private namespace, initialization code
  • • Widget#init :: * => ()• Widget#render :: () => String• Widget#start :: () => Deferred• Widget#stop :: () => ()
  • API: Search Fieldsmeetings
  • • Field • non-standard cycle • get_context, get_domain{instance.web.search.fields}* Weird cycle** Render everything (recursive, defaults)** Bind (element_id, in template context)** Start* dygraphs.com** dygraphs.com/dygraph-combined.js** new Dygraph(element, CSV_string)
  • API: Form Fieldshr -> email field{instance.web.form.widgets}Also weird cycle/management* $element set to parent (table cell), in start() (_super() call required)
  • • Field#value :: * • Field#set_value :: * => Deferred • Field#on_ui_change :: () => () • Field#update_dom :: () => () • Field#validate :: () => ()#value -> current value for the widget#set_value -> form sets value on widget#on_ui_change -> signal value change via UI to form* Should have set #value# update_dom -> resync non-value DOM (readonly, invalid, required)# validate -> set/unset “invalid”
  • Debugging
  • Debugging withOpenERP Web
  • Debugging OpenERP Web
  • Third-party EmbeddingInstall “share”Share button “link or embed”Put in HTML file on diskpython -mSimpleHTTPServer