« Home

Timeline

54 things I’ve done on the internet since 2013.

  1. twitter.com/javan/status/1388215254685990915
  2. bugs.webkit.org/show_bug.cgi?id=222657

    Bug 222657 — Media element never populates its UA shadow if it was initially created in a document without browsing context

    When input and media elements are adopted with document.adoptNode() from a foreign document (via DOMParser, for example) they lose their native shadow DOM roots and are left in a mostly broken state: <input>s lose their native UI, and <video> and <audio> elements lose their native controls.
    
    To reproduce, open the attached adopt-node-bug.html file in Safari and compare the correct middle column (imported node) to the incorrect right column (adopted node).
    
    Tested in Safari v14.0.3 (16610.4.3.1.4) on macOS v11.2.2, and mobile Safari on iOS v14.4.
  3. twitter.com/javan/status/1357800714018443270
    Javan Makhmali Javan Makhmali @javan

    This shit is totally unacceptable, @canary. I’ve wasted enough time completing your dark pattern maze. Cancel my account.

    708 143
  4. youtube.com/watch?v=hdiLqJc0ySc
  5. bugs.webkit.org/show_bug.cgi?id=211620
  6. bugs.chromium.org/p/chromium/issues/detail?id=1043564#c7

    Re: Bug 1043564 — Incorrect range calculated during a 'deleteSoftLineBackward' event

    Here is another example that demonstrates the issue. It works by canceling the "beforeinput" event and applying the target range to the selection to illustrate it.
    
    1. Open the attached "select-target-range.html" file in Chrome
    2. Place the cursor at the end of each line and press Cmd+Delete
    3. Observe the selection, which represents the target range
    4. Not that it doesn't match the actual range of text to be removed
  7. bugs.webkit.org/show_bug.cgi?id=203116

    Bug 203116 — Native text substitutions interfere with HTML <datalist> options resulting in crash

    To reproduce:
    
    1. Open System Preferences → Keyboard → Text and add entry to replace "cat" with "Cat"
    2. Open the attached datalist.html file in Safari
    3. Ensure that the menu option for Edit → Substitutions → Text Replacement is selected
    3. Type "cat" in the text field and wait for the "Cat" replacement overlay to appear
    4. Click any option from the expanded <datalist> menu and Safari will crash
    
    Tested on macOS 10.14.6 (18G103) with Safari 13.0.2 (14608.2.40.1.3) and Safari TP Release 94 (Safari 13.1, WebKit 14609.1.6.1). I believe this issue occurs in Safari 12.1 as well.
  8. weblog.rubyonrails.org/2019/8/15/Rails-6-0-final-release/

    Rails 6.0: Action Mailbox, Action Text, Multiple DBs, Parallel Testing, Webpacker by default, and Zeitwerk

    Dealing with incoming email, composing rich-text content, connecting to multiple databases, parallelizing test runs, integrating JavaScript with love, and rewriting the code loader. These are fundamental improvements to the fundamentals of working with the web and building fast and fresh applications. This is the kind of work we’ve been doing for the past fifteen years, and we’re still at it. THIS IS RAILS SIX!

  9. bugs.chromium.org/p/chromium/issues/detail?id=992862

    Bug 992862 — Cutting from a contentEditable element causes slow forced reflow

    Steps to reproduce the problem:
    1. Open the attached cut.html file and click "Cut All" (or, use the Edit → Cut menu)
    
    What is the expected behavior?
    The cut operation completes in a reasonable amount of time.
    
    What went wrong?
    The cut operation takes several seconds to complete. The Performance inspector reveals a very slow Layout task. See the attached .gif for a comparison by browser.
    
    Chrome: 5,177ms
    Firefox: 19ms
    Safari: 147ms
    
    Did this work before? N/A 
    
    Does this work in other browsers? N/A
    
    Chrome version: 76.0.3809.100  Channel: stable
    OS Version: OS X 10.14.6
    Flash Version:
  10. devchat.tv/js-jabber/jsj-376-trix-a-rich-text-editor-for-everyday-writing-with-javan-makhmali/

    JSJ 376: Trix: A Rich Text Editor for Everyday Writing with Javan Makhmali - Devchat.tv

    Today’s guest is Javan Makhmali, who works for Basecamp and helped develop Trix. Trix is a rich text editor for the web, made purposefully simple for everyday use instead of a full layout tool. Trix is not the same as Tiny MCE, and Javan discusses some of the differences. He talks about the benefits of using Trix over other native browser features for text editing. He talks about how Trix has simplified the work at Basecamp, especially when it came to crossing platforms. Javan talks more about how Trix differs from other text editors like Google Docs and contenteditable, how to tell if Trix is functioning correctly, and how it works with Markdown.

  11. twitter.com/javan/status/1151914387428564992
  12. github.com/github/eventlistener-polyfill/pull/13
  13. twitter.com/javan/status/1111329710842101760
  14. remoteruby.transistor.fm/31

    Joined by Javan Makhmali and Sam Stephenson | Remote Ruby

    Buckle up! In this episode, Javan Makhmali and Sam Stephenson join Jason and Chris. Sam and Javan are employees at Basecamp and also part of the mastermind behind many JavaScript libraries to come out of Basecamp: Turbolinks, Trix, and Stimulus. They each share how they got started programming, a brief insight into how they work at Basecamp, the transition from CoffeeScript to ES6, Typescript, as well as (wait for it...) upcoming features in Stimulus (🙌) and considerations for Turbolinks 6 (no feature promises made 😉). It was an absolute joy to chat with Javan and Sam. We very much enjoyed this episode and hope you will enjoy it, too.

  15. github.com/electron/electron/issues/16418

    Top of window with "hiddenInset" title bar becomes unresponsive after exiting fullscreen electron/electron#16418

    • Output of node_modules/.bin/electron --version: v4.0.1
    • Operating System (Platform and Version): macOS 10.14.2

    Expected Behavior
    The top of the window should continue responding to clicks after exiting from fullscreen.

    Actual behavior
    The top ~40px stops responding to clicks after exiting from fullscreen.

    To Reproduce

    1. Clone and run https://github.com/javan/electron-quick-start/tree/hidden-inset-mojave
    2. Click the black bar at the top of the window. It should turn purple while the mouse is down.
    3. Enter and exit fullscreen.
    4. Click the top of the window and note that it no longer turns purple.
    $ git clone https://github.com/javan/electron-quick-start.git -b hidden-inset-mojave
    $ npm install
    $ npm start
    

    Screenshots
    electron-fullscreen-clicks

    Additional Information
    This issue is quite similar to #14529.

  16. github.com/basecamp/trix/pull/580

    Input Events Level {1,2} basecamp/trix#580

    This PR improves the way Trix records input—particularly on mobile—by adding support for Level 2 InputEvents, which are currently implemented in Safari and Chrome, and in their companion webview implementations on iOS and Android. In all other browsers, Trix will continue to work exactly as it does today. Here's how:

    First, Trix tests for InputEvent support:

    supportsInputEvents: do ->
    typeof InputEvent isnt "undefined" and
    "inputType" of InputEvent.prototype and
    "data" of InputEvent.prototype

    Then, when a <trix-editor> initializes, its editor controller installs one of two input controllers:

    @inputController = new Trix["Level#{Trix.config.input.getLevel()}InputController"](@editorElement)
    @inputController.delegate = this
    @inputController.responder = @composition

    1. Trix.Level2InputController if input events are supported
    2. Trix.Level0InputController, which is equivalent to the current Trix.InputController, if input events are not supported

    Fixes #575
    Fixes #574
    Fixes #520
    Fixes #494
    Fixes #396
    Fixes #315
    Fixes #178
    Closes #564

  17. 5by5.tv/rubyonrails/247

    Ruby on Rails Podcast #247

    Action Text is a new framework coming to Rails 6 to make it easier to create, edit, and display rich text content within an app. Brittany invited Javan Makhmali, programmer at Basecamp, on to the show to get the scoop.

  18. twitter.com/javan/status/1057247269307727872
  19. twitter.com/javan/status/1044958902369103874
  20. github.com/electron/electron/issues/14529

    macOS Mojave: Unclickable region in windows with "hiddenInset" title bar electron/electron#14529

    Expected Behavior
    Clicking any region of the window should activate the element under the cursor.

    Actual behavior
    A small region in the top-center of the window isn't clickable.

    To Reproduce

    $ git clone https://github.com/javan/electron-quick-start.git -b hidden-inset-mojave
    $ npm install
    $ npm start

    Screenshots
    macOS Mojave 10.14 Beta (18A384a) 👎
    mojave

    macOS High Sierra 10.13.6 👍
    high-sierra

    Additional Information
    This issue impacts any application with critical navigation elements on the top of the page. For us (Basecamp 3), the "Hey!" menu is very difficult to click:
    screen shot 2018-09-10 at 1 36 50 pm

    <notch-joke> </notch-joke>

    It's likely that Mojave will be released this week during the Apple Special Event on September 12.

  21. twitter.com/javan/status/1027642135712018437
    Javan Makhmali Javan Makhmali @javan

    Kudos to @getsentry for revealing the culprit of a JavaScript error that would’ve been a serious head scratcher.

 jQuery was being injected by some browser extension(s), clobbering our own version and nuking the jQuery plugins we rely on. 💥

    17 4
  22. github.com/basecamp/trix/pull/523

    Image galleries basecamp/trix#523

    This pull request adds support for displaying groups of images in a grid, AKA image galleries. It works by automatically grouping adjacent image attachments into blocks, and styling the block's attachment elements to form a grid. The layout is 100% CSS-based so it can easily be customized or adjusted for different viewport sizes using media queries. Out of the box, Trix supports two and three column grids controlled by just a few styles in trix.css:

    .attachment-gallery {
    display: flex;
    flex-wrap: wrap;
    position: relative;
    margin: 0;
    padding: 0;
    .attachment {
    flex: 1 0 33%;
    padding: 0 0.5em;
    max-width: 33%;
    }
    &.attachment-gallery--2,
    &.attachment-gallery--4 {
    .attachment {
    flex-basis: 50%;
    max-width: 50%;
    }
    }
    }






    Additional changes

    1. Attachment toolbars now include file metadata, and their markup and has been revised to align more closely with <trix-toolbar>'s.
      screen shot 2018-07-25 at 9 43 48 am

    2. Text formatting can no longer be applied to attachments because, in part, the resulting HTML would break image gallery layouts. It also had weird/inconsistent results before and could easily happen accidentally (dropping images into bold text, for example).
      disabled-attributes

    3. Attachments with href attributes now render <a>s inside their <figure>s.

      <!-- BEFORE -->
      <a href="">
        <figure>
          <img src="">
          <figcaption></figcaption>
        </figure>
      </a>
      
      <!-- AFTER -->
      <figure>
        <a href="">
          <img src="">
          <figcaption></figcaption>
        </a>
      </figure>
  23. github.com/basecamp/trix/pull/516

    Custom Elements v1 (and v0) basecamp/trix#516

    This pull requests adds support for Custom Elements v1, the official standardized spec recognized by all major platforms. v1 has already landed in Chrome, Safari, and Opera, and development is underway in Firefox and Microsoft Edge.

    Custom Elements v1 and v0 are conceptually identical, although they don't share much in common API-wise. For better or worse, those API differences make v1 more difficult to adopt (unless you're only targeting modern browsers and delivering ES2015+ JavaScript code).

    v1 gotchas…

    Caveat: an unpolyfillable upgrade
    There are things that current V1 makes impossible to reproduce via plain JS and the most obvious is the JS instance upgrade. In older engines, extending any DOM native class is not enough to have a proper DOM instance.

    https://www.webreflection.co.uk/blog/2016/08/21/custom-elements-v1#caveat-an-unpolyfillable-upgrade

    ES5 vs ES2015
    The custom elements v1 spec is not compatible with ES5 style classes. This means ES2015 code compiled to ES5 will not work with a native implementation of Custom Elements.[0] While it's possible to force the custom elements polyfill to be used to workaround this issue (by setting (customElements.forcePolyfill = true; before loading the polyfill), you will not be using the UA's native implementation in that case.

    Since this is not ideal, we've provided an alternative: native-shim.js. Loading this shim minimally augments the native implementation to be compatible with ES5 code. We are also working on some future refinements to this approach that will improve the implementation and automatically detect if it's needed.

    [0] The spec requires that an element call the HTMLElement constructor. Typically an ES5 style class would do something like HTMLElement.call(this) to emulate super(). However, HTMLElement must be called as a constructor and not as a plain function, i.e. with Reflect.construct(HTMLElement, [], MyCEConstructor), or it will throw.

    https://github.com/webcomponents/custom-elements/blob/master/README.md#es5-vs-es2015


    In my testing, none of the v1 polyfills worked reliably in older versions of Safari or on older Android devices. They're also far more invasive, attempting to patch every possible means of content creation / insertion. Miss one (like Element#insertAdjacentHTML) and custom elements slip through without upgrading.

    v0 on the other hand, even though it's only officially supported in Chrome, has far better support thanks to several battle hardened, MutationObserver-based polyfills. And so, this pull requests preserves support for v0 too.

    To smooth over the v1/v0 API differences, this change introduces its own distinct naming convention for lifecycle callbacks: initialize(), connect(), disconnect() (thanks, Stimulus!), and automatically maps them to the supported custom element API.

    v1:

    initialize() → connectedCallback() (once)
    connect()    → connectedCallback()
    disconnect() → disconnectedCallback()
    

    v0 (polyfilled if necessary):

    initialize() → createdCallback() 
    connect()    → attachedCallback()
    disconnect() → detachedCallback()
    

    Fixes #495

  24. twitter.com/javan/status/1012351745043902464
  25. bugs.webkit.org/show_bug.cgi?id=6656#c18

    Re: Bug 6656 — Image loading continues when IMG elements or Image JavaScript objects are removed

    Hello, another Basecamp developer here with more details.
    
    We use `MutationObserver` to detect when images are added to the DOM and swap their "src" attribute with a tiny placeholder image. Then we restore the original "src" as images are scrolled into the viewport. This technique is commonly referred to as “lazy loading” and is intended to avoid unnecessary network requests for images that may never be viewed.
    
    Due to this WebKit issue, our approach doesn’t work in Safari because the original image is always loaded.
    
    Additionally (this may be a separate issue), cloning <img> elements causes them to load *again* even if the cloned node is detached from the DOM. For example, running `document.body.cloneNode(true)` reloads all of its images. This affects our Turbolinks (https://github.com/turbolinks/turbolinks) library, which stores “snapshots” of pages by cloning them.
    
    I made a video to help illustrate the problem: https://www.youtube.com/watch?v=p6bkcjoyP1M
    
    In Safari (left), every image on the page is loaded initially, and then loaded again when scrolled into view. Cloning <body> loads all of its images once more.
    
    In Chrome (right), only the first image loads initially and the rest are canceled. Cloning <body> makes no additional network requests.
    
    My example application and its source code are available here: https://glitch.com/~jealous-moon
    
    Thanks for your time!
  26. github.com/rails/rails/pull/32967

    Change Active Storage’s video preview format from PNG to JPG rails/rails#32967

    PNG is not an ideal format for storing video previews for the same reason it’s not ideal for storing photos. We don’t need lossless compression or the associated extra bytes. Switching to JPG results in files that are ~90% smaller and have a negligible difference in image quality.

    Before

    $ ffmpeg -i beach.m4v -y -vcodec png -vf thumbnail -vframes 1 -f image2 beach.png
    
    $ mediainfo beach.png
    Format      : PNG
    File size   : 1.78 MiB
    Width       : 1 920 pixels
    Height      : 1 080 pixels
    Bit depth   : 24 bits
    Compression : Lossless
    

    After

    $ ffmpeg -i beach.m4v -y -vframes 1 -f image2 beach.jpg
    
    $ mediainfo beach.jpg
    Format      : JPEG
    File size   : 82.2 KiB
    Width       : 1 920 pixels
    Height      : 1 080 pixels
    Bit depth   : 8 bits
    Compression : Lossy
    

    beach.m4v details
    $ mediainfo beach.m4v
    Format    : MPEG-4
    Codec ID  : M4V  (M4V /M4A /mp42/isom)
    File size : 37.4 MiB
    Duration  : 20 s 860 ms
    Width     : 1 920 pixels
    Height    : 1 080 pixels
    
  27. github.com/johnbeatty/trix_emoji/pull/1

    Refactor emoji converter johnbeatty/trix_emoji#1

    I made a some improvements to the emoji converter, and broke it up into several commits so you could see each change clearly. Hopefully you find this useful and not annoying. Thanks so much sharing your Stimulus tutorials! ✌️

  28. twitter.com/javan/status/984114017194168320
  29. twitter.com/javan/status/980842276850143233
  30. twitter.com/javan/status/966360950562582528
    Javan Makhmali Javan Makhmali @javan

    Rich text editors have come a long way in recent years, and there are dozens of great libraries now. Sadly, they all suffer on one platform: Android. It’s notoriously difficult to support. Chrome team doesn’t seem to care, and they’re making matters worse: bugs.chromium.org/p/chromium/iss…

    13 4
  31. bugs.chromium.org/p/chromium/issues/detail?id=764439#c9

    Re: Bug 764439 — composition event should be triggered on setComposingRegion

    Hello,
    
    I’m one of the maintainers of Trix (https://github.com/basecamp/trix), a rich text editor for the web. Trix, like many modern editors, listens for events to record input, and then converts that input into an editing operation on its internal document model. This process has always been challenging on Android due to its lack of key codes on keyboard events (this ol’ issue https://bugs.chromium.org/p/chromium/issues/detail?id=118639), but we’ve managed well enough by handling composition events.
    
    The change that fixed this issue is a significant departure from the current behavior of composition events, and problematic in a number ways:
    
    1. Composition events are dispatched every time you move the cursor but aren’t typing. 
    
    This alone quite a stretch from from the spec’s definition: “Composition Events provide a means for inputing text in a supplementary or alternate manner than by Keyboard Events” (https://www.w3.org/TR/uievents/#events-compositionevents). These composition events while moving the cursor also have no associated keyboard or inputs events, another deviation from the spec: “During the composition session, keydown and keyup events MUST still be sent” (https://www.w3.org/TR/uievents/#events-composition-key-events).
    
    2. The DOM selection during compositions now varies depending on the context.
    
    When moving the cursor, the selection during compositionupdate is always collapsed at the cursor’s position. When typing, the selection is the expanded range around the composed characters. This means we can’t reliably determine if the input is new or if it’s replacing a range of existing content.
    
    Description of the attached screenshots:
    
    cursor-movement.png: Shows the composition events dispatched after moving the cursor from left to right four times. Note how the compositionupdate data is identical, but the DOM selection is not.
    
    movement-then-typing.png: Shows the events dispatched when placing the cursor before "The", "quick", and "brown", and then typing "fox " at the end. This illustrates how the selection expands while typing, and how difficult it is to distinguish input from cursor movement generally.
    
    These screenshots come from http://jsfiddle.net/javan/mj13jj7b/embedded/result,js/ - a small test bed I made.
    
    Related issue: https://bugs.chromium.org/p/chromium/issues/detail?id=812674
    
    Thanks for your time!
  32. bugs.chromium.org/p/chromium/issues/detail?id=810895

    Bug 810895 — Programmatically focusing sticky positioned input elements incorrectly scrolls the page

    Steps to reproduce the problem:
    1. Open the attached HTML file
    2. Scroll to the bottom
    3. Click each of the links to focus the form controls
    
    What is the expected behavior?
    The page does not scroll
    
    What went wrong?
    The page scrolls to the element's unstuck position when calling its focus() method.
    
    This happens for all form controls except for <input type="text"> so https://bugs.chromium.org/p/chromium/issues/detail?id=740417 could be partially related.
    
    Did this work before? No 
    
    Does this work in other browsers? Yes
    
    Chrome version: 64.0.3282.140  Channel: stable
    OS Version: OS X 10.13.3
    Flash Version: 
    
    https://bugs.chromium.org/p/chromium/issues/detail?id=805800 may also be related.
  33. github.com/hotwired/stimulus/pull/61

    Linked Target Properties hotwired/stimulus#61

    Define a controller's target names and Stimulus automatically creates properties for accessing them:

    import { Controller } from "stimulus"
    
    export default class extends Controller {
      static targets = [ "source", "slide" ]
    
      initialize() {
        this.sourceTarget  // this.targets.find("source")
        this.sourceTargets // this.targets.findAll("source")
    
        this.slideTarget  // this.targets.find("slide")
        this.slideTargets // this.targets.findAll("slide")
      }
    }
  34. weblog.rubyonrails.org/2017/8/15/three-new-committers/
  35. github.com/rails/activestorage/pull/81
  36. bugs.webkit.org/show_bug.cgi?id=174165

    Bug 174165 — DataTransfer shouldn't contain text/html when performing Paste and Match Style

    The ClipboardData for paste events are identical when performing Paste (⌘V) and Paste and Match Style (⌥⇧⌘V). Because they're indistinguishable, it's impossible to programmatically handle pastes correctly in a rich text editor (like https://github.com/basecamp/trix, which I help maintain). There's no way to infer the desired format and pick an appropriate type.
    
    To reproduce, open the attached paste-inspector.html file in Safari and paste as instructed. Note that the logged paste events are identical and both contain "text/html" and "text/plain" types. Chrome and Firefox only return a "text/plain" type when performing Paste and Match Style, which I assume is correct.
    
    This issue is also present in previous versions of Safari.
    
    Screenshots
    Safari: https://cl.ly/291S1I3L2B25
    Chrome: https://cl.ly/3m3B1P0D0Q2E
    Firefox: https://cl.ly/3M3v2R2r2T2H
  37. github.com/puma/puma-dev/pull/101
  38. bugzilla.mozilla.org/show_bug.cgi?id=1332924

    Bug 1332924 — [contenteditable] Custom or inline element duplicated when deleting selection that spans different block element types

    Steps to Reproduce:
    
    1. Paste the following Data URI into your address bar:
    
       data:text/html;charset=utf-8,<x-foo contenteditable><div>ab</div><p>cd</p></x-foo>
    
    2. Place the cursor before "b" and select through "c" so that both letters are selected
    
    3. Press backspace
    
    Expected Results:
    
    <x-foo contenteditable>
      <div>ad</div>
    </x-foo>
    
    Actual Results:
    
    <x-foo contenteditable>
      <div>a</div>
    </x-foo>
    <x-foo contenteditable>
      <p>d</p>
    </x-foo>
    
    Additional details:
    
    Enabling dom.webcomponents.customelements.enabled and registering the element with document.registerElement has no affect.
    
    The same problem is reproducible using <span contenteditable> as the containing element (instead of <x-foo contenteditable>) so I suspect Firefox treats the contained elements as invalid block children of an inline parent. Styling the container with "display:block;" doesn't help, and in either case the contenteditable element should not be duplicated.
  39. github.com/rails/rails/issues/27570

    ActiveRecord::Relation#cache_key returns same key for different records with hm:t association rails/rails#27570

    has_many :through relations that return the same number of records and one common record with the max updated_at will generate the same cache_key. For example:

    class Access < ActiveRecord::Base; end
    class Person < ActiveRecord::Base; end
    class Bucket < ActiveRecord::Base
      has_many :people, through: :accesses
    end
    >> bucket.people.map(&:name)
    => ["Victor Cooper", "Jared Davis"]
    
    >> bucket.people.cache_key
    => "people/query-2343a4c885c823c01ef9c2eb358b73eb-2-20161229150259317823"

    Above, Victor Cooper has the max updated_at (20161229150259317823). If we remove Jared Davis and add a new person who's updated_at is less than Victor's, we'll get the same cache_key:

    >> bucket.reload.people.map(&:name)
    => ["Victor Cooper", "Amy Rivera"]
    
    >> bucket.people.cache_key
    => "people/query-2343a4c885c823c01ef9c2eb358b73eb-2-20161229150259317823"

    This was introduced in #20884. /cc @afcapel @sgrif

  40. youtube.com/watch?v=nwvourv3nSQ
  41. github.com/rails/rails/pull/25826
  42. github.com/rails/rails/pull/25817

    Fix adding implicitly rendered namespaced template digests to ETags rails/rails#25817

    Extends the fix from #25546 to namespaced controllers and their templates.

    New test failing without this change:

    $ bundle exec rake test TEST=test/controller/render_test.rb 
    Run options: --seed 59446
    
    # Running:
    
    .....S...............................F.......................
    
    Finished in 0.498034s, 122.4816 runs/s, 614.4159 assertions/s.
    
      1) Failure:
    NamespacedEtagRenderTest#test_etag_reflects_template_digest [.../actionpack/test/controller/render_test.rb:611]:
    Expected response to be a <200: ok>, but was a <304: Not Modified>.
    Expected: 200
      Actual: 304
    
    61 runs, 306 assertions, 1 failures, 0 errors, 1 skips
    

    /cc @jeremy

  43. github.com/rails/rails/pull/25411

    Fix digesting non-HTML templates with non-unique logical names rails/rails#25411

    This fixes a pathological-sounding-but-it-happened-to-us scenario that caused incorrect template digests for */* requests that render non-HTML (JSON, in our case) templates. The scene looks something like this:

    1. An appended view path: append_view_path(Rails.root.join("app/views/api"))
    2. Partials in this path with the same logical path as partials in app/views, but for different formats: app/views/api/todos/_todo.json.jbuilder and app/views/todos/_todo.html.erb, both "logically" known as todos/todo. Both of these partials have cache fragments.
    3. A controller action (schedules#show) with a corresponding JSON template (show.json.jbuilder) that in turn renders a collection of partials: json.todos @todos, partial: 'todos/todo', as: :todo.
    4. A client-side fetch requests to this action that doesn't set an Accept header so it defaults to */*.

    The JSON response is correctly returned, but the template digest it uses for caching is computed using the HTML templates. Altering _todo.json.jbuilder does not invalidate its cache, but altering _todo.html.erb does.

    We worked around the issue by adding an Accept: application/json header. Adding a .json format to the URL would have worked around it too.

    Here are the new tests failing without the changes to ActionView::Digestor:

    $ rake test TEST=test/template/digestor_test.rb
    Run options: --seed 2948
    
    # Running:
    
    ........F.............F...............
    
    Finished in 5.274653s, 7.2043 runs/s, 10.4272 assertions/s.
    
      1) Failure:
    TemplateDigestorTest#test_template_formats_of_nested_deps_with_non_default_rendered_format [/Users/javan/Projects/rails/actionview/test/template/digestor_test.rb:165]:
    Expected: [:json]
      Actual: [:json, :html]
    
    
      2) Failure:
    TemplateDigestorTest#test_different_formats_with_same_logical_template_names_results_in_different_digests [/Users/javan/Projects/rails/actionview/test/template/digestor_test.rb:287]:
    Expected "18824a8cf013bb976bc39b5809a262c4" to not be equal to "18824a8cf013bb976bc39b5809a262c4".
    
    38 runs, 55 assertions, 2 failures, 0 errors, 0 skips
    

    All green with the changes.

    /cc @jeremy @matthewd

  44. github.com/rails/jbuilder/pull/341

    Fix fragment_cache_key cache invalidation rails/jbuilder#341

    Jbuilder currently does not account for an app's controller-wide fragment_cache_keys when computing its cache keys.

    This change adds support, and also aligns Jbuilder's caching setup with ActionView's cache helpers and Abstract Controller's fragment caching and instrumentation.

    To test, I made a fresh app with a fragment_cache_key call that should invalidate the template's cache with every request.

    # config/environments/development.rb
    Rails.application.configure do
      config.x.counter = 0
      config.action_controller.perform_caching = true
      config.cache_store = :mem_cache_store
    end
    
    # app/controllers/widgets_controller.rb
    class WidgetsController < ApplicationController
      before_action { Rails.application.config.x.counter += 1 }
    
      fragment_cache_key { Rails.application.config.x.counter }
    
      def index
      end
    end
    
    # app/views/widgets/index.json.jbuilder
    json.cache! "unwavering-key" do
      json.config_x_counter Rails.application.config.x.counter
    end

    Requests using Jbuilder 2.5.0

    $ curl http://0.0.0.0:3000/widgets.json
    {"config_x_counter":1}
    
    $ curl http://0.0.0.0:3000/widgets.json
    {"config_x_counter":1} 
    Started GET "/widgets.json" for 127.0.0.1 at 2016-06-13 18:42:18 -0400
    Processing by WidgetsController#index as JSON
      Rendering widgets/index.json.jbuilder
      Rendered widgets/index.json.jbuilder (5.7ms)
    Completed 200 OK in 18ms (Views: 10.4ms | ActiveRecord: 0.0ms)
    
    Started GET "/widgets.json" for 127.0.0.1 at 2016-06-13 18:42:20 -0400
    Processing by WidgetsController#index as JSON
      Rendering widgets/index.json.jbuilder
      Rendered widgets/index.json.jbuilder (5.7ms)
    Completed 200 OK in 10ms (Views: 8.7ms | ActiveRecord: 0.0ms)
    

    Requests using this branch

    $ curl http://0.0.0.0:3000/widgets.json
    {"config_x_counter":1}
    
    $ curl http://0.0.0.0:3000/widgets.json
    {"config_x_counter":2}
    Processing by WidgetsController#index as JSON
      Rendering widgets/index.json.jbuilder
    Read fragment jbuilder/views/1/unwavering-key/c9b8ffbaac0ab06acdab1ba36772a5dc (2.7ms)
    Write fragment jbuilder/views/1/unwavering-key/c9b8ffbaac0ab06acdab1ba36772a5dc (0.5ms)
      Rendered widgets/index.json.jbuilder (6.5ms)
    Completed 200 OK in 19ms (Views: 11.1ms | ActiveRecord: 0.0ms)
    
    Started GET "/widgets.json" for 127.0.0.1 at 2016-06-13 18:39:53 -0400
    Processing by WidgetsController#index as JSON
      Rendering widgets/index.json.jbuilder
    Read fragment jbuilder/views/2/unwavering-key/c9b8ffbaac0ab06acdab1ba36772a5dc (0.5ms)
    Write fragment jbuilder/views/2/unwavering-key/c9b8ffbaac0ab06acdab1ba36772a5dc (0.7ms)
      Rendered widgets/index.json.jbuilder (6.5ms)
    Completed 200 OK in 11ms (Views: 9.5ms | ActiveRecord: 0.0ms)
    

    Note that the cache was correctly invalidated and the presence logging like you'd see when using cache in an .erb template.

    /cc @jeremy

  45. github.com/github/fetch/issues/340

    PUT and PATCH requests fail in Microsoft Edge (with native Fetch support) github/fetch#340

    This isn't an issue with your polyfill per se so feel free to close it any time. We recently noticed a number of exceptions in our app stemming from Fetch-intiated PUT requests in MS Edge that lacked a request body.

    I did some digging and found that Edge's PUT requests had Content-Length: 0 so the JSON.stringify'd data we send as the body wasn't making it to the server. I also found that PATCH requests fail entirely and are never transmitted.

    These requests succeed in every browser I could find that has native Fetch support. They also succeed in browsers that don't when using this polyfill.

    I put together small demo Rack app here: https://github.com/javan/fetch-put-patch-demo

    I'm curious if you think MS Edge is doing it right and most closely following the spec. And if not, should this polyfill step in and smooth over the inconsistency?

    Thanks for your time! ❤️

  46. youtube.com/watch?v=tmWlm0M6JSk
  47. twitter.com/javan/status/685182546498449408
  48. youtube.com/watch?v=n5MjAE-SN6c
  49. twitter.com/javan/status/647508073439866880
  50. github.com/prawnpdf/ttfunk/pull/22

    Parse bitmap data from sbix tables prawnpdf/ttfunk#22

    I set out to find an effective way of extracting emoji images from Apple's "Apple Color Emoji.ttf" font and found that the data necessary data is in the sbix table.

    This table provides access to bitmap data in a standard graphics format (such as PNG, JPEG, TIFF).

    emoji_font = Pathname.new("/System/Library/Fonts/Apple Color Emoji.ttf")
    ttf = TTFunk::File.open(emoji_font)
    
    char_code = "\u{1f4a9}".codepoints.first
    glyph_id = ttf.cmap.unicode.first.code_map[char_code]
    
    ttf.sbix.all_bitmap_data_for(glyph_id).each do |bitmap|
      filename = "glyph-#{glyph_id}-#{bitmap.ppem}.#{bitmap.type}"
      File.write(filename, bitmap.data.read)
    end

    glyph-569-160glyph-569-96glyph-569-64glyph-569-48glyph-569-40glyph-569-32glyph-569-20

  51. youtube.com/watch?v=mWo3oEwFFzM
  52. github.com/rails/rails/pull/16793

    Add support for Rack::ContentLength middleware rails/rails#16793

    Currently, Rails is not compatible with Rack::ContentLength because ActionDispatch::Response::RackBody instances don't respond to to_ary and, even though it's not part of the Rack spec, it's how Rack decides if a body has a knowable length. There's some discussion in rack/rack@2b3abc7, which was later reverted leading to #1160. And a little more in rack/rack@9495207.

    IMO, Rack should remove the to_ary check. This change provides immediate support and should continue to work if / when Rack does make the change.

  53. github.com/rails/rails/pull/12891
  54. github.com/mikel/mail/pull/509

    Fix decoding strings that contain '=?', but are not actually encoded mikel/mail#509

    I ran into an exception today when sending an email that contained "1+1=?" in the subject.

    Here's a test (not included in the commit) to simulate that failure:

    it "should allow setting the value of a field to a string that contains '?='"  do
      mail = Mail.new
      mail.charset = 'utf-8'
      mail.subject = "1+1=?"
      mail[:subject].encoded.should eq "1+1=?"
    end
    Failures:
    
      1) mail encoding using default encoding should allow setting the value of a field to a string that contains '?='
         Failure/Error: mail[:subject].encoded.should eq "1+1=?"
         NoMethodError:
           undefined method `+' for nil:NilClass
         # ./lib/mail/encodings.rb:295:in `block in collapse_adjacent_encodings'
         # ./lib/mail/encodings.rb:291:in `each'
         # ./lib/mail/encodings.rb:291:in `collapse_adjacent_encodings'
         # ./lib/mail/encodings.rb:120:in `value_decode'
         # ./lib/mail/encodings.rb:101:in `decode_encode'
         # ./lib/mail/fields/unstructured_field.rb:73:in `do_decode'
         # ./lib/mail/fields/unstructured_field.rb:55:in `decoded'
         # ./lib/mail/fields/unstructured_field.rb:122:in `fold'
         # ./lib/mail/fields/unstructured_field.rb:103:in `wrapped_value'
         # ./lib/mail/fields/unstructured_field.rb:69:in `do_encode'
         # ./lib/mail/fields/unstructured_field.rb:51:in `encoded'
         # ./lib/mail/field.rb:167:in `method_missing'
         # ./spec/mail/encoding_spec.rb:25:in `block (3 levels) in <top (required)>'
    

    This fixes the problem by checking more closely (with a regex) to see if the string actually is encoded before proceeding.