Skip to content

Instantly share code, notes, and snippets.

@carols10cents
Last active January 25, 2017 14:59
Show Gist options
  • Save carols10cents/b6d03a6da9f2ea950571f59fb19cc56e to your computer and use it in GitHub Desktop.
Save carols10cents/b6d03a6da9f2ea950571f59fb19cc56e to your computer and use it in GitHub Desktop.

Ember: IDK What I Am Doing

Goal

Display Travis and Appveyor badges on crates.io.

Considerations

  • I want to show the badges on an individual crate's page and on a list of crates.
  • I only want to do 1 ajax request for the data (and only do 1 SQL query on the backend).
  • The Travis and Appveyor badges have different data and are constructed in slightly different ways.
  • On a crate's page, I want the badges to display one per line, so within display: block elements.
  • On a page of a list of crates, I want the badges to display inline.
  • I always want the Travis badge to appear before the Appveyor badge, and I think that knowledge should be encoded in one spot in Ember code somewhere (i.e. not the backend)
  • We may add more badges in the future.

What I tried

Here's the full code in context so far (I don't like it yet) but the ember parts and my reasoning is below:

I got my API to return JSON like this for a list of crates (same format for crate show page, only one crate in that case):

"crates": [{
    "id": "rust_mixin",
    "name": "rust_mixin",
    "badges": [
        {
            "attributes": {
                "repository": "huonw/external_mixin"
            },
            "badge_type": "appveyor"
        },
        {
            "attributes": {
                "branch": "master",
                "repository": "huonw/external_mixin"
            },
            "badge_type": "travis-ci"
        }
    ]
}, {
    "id": "rust_mixin",
    "name": "rust_mixin",
    "badges": [
        {
            "attributes": {
                "repository": "huonw/external_mixin",
                "service": "bitbucket",
                "branch": "master"
            },
            "badge_type": "appveyor"
        }
    ]
}]

In app/models/crate.js, I added this, with the annotated badges to have each badge know how to find its component:

export default DS.Model.extend({
    // ... other stuff
    badges: DS.attr(),
    annotated_badges: Ember.computed.map('badges', function(badge) {
        badge.component_name = `badge-${badge.badge_type}`;
        return badge;
    }),
});

I created app/components/badge-appveyor.js:

import Ember from 'ember';

export default Ember.Component.extend({
    tagName: 'span',
    classNames: ['badge'],
});

and app/components/badge-travis-ci.js:

import Ember from 'ember';

export default Ember.Component.extend({
    tagName: 'span',
    classNames: ['badge'],
});

so that the markup for the badges wouldn't always be in display: block divs.

I put the markup for the two badges in:

  • app/templates/components/badge-appveyor.hbs
  • app/templates/components/badge-travis-ci.hbs

This seemed straightforward and nice.

I put the markup for all badges in app/templates/components/badges-ordered.hbs, I would have prefered to name this badges.hbs but components have to have at least one hyphen in the name since someday badge might be an HTML element????

I haven't actually implemented the ordering part yet since I'm not sure how best to do it.... right now I just have:

{{#each badges as |badge|}}
    {{component badge.component_name badge=badge}}
{{/each}}

I'd like this to look rubyish but I don't know quite how to make this actually work yet:

{{#each ['travis-ci', 'appveyor'] as |badge_type|}}
    {{#if badges[badge_type]}}
        {{component badge.component_name badge=badges[badge_type]}}
    {{/if}}
{{/each}}

I'm calling the badges-ordered component in the crates list, where I want the badges to appear inline:

{{badges-ordered badges=crate.annotated_badges}}

When I want each badge to be in a display: block element on the crate show page, I'm doing this, which also doesn't take into account the fixed ordering I want to do, which I would have to duplicate at this point:

{{#each crate.annotated_badges as |badge|}}
    <p>
        {{component badge.component_name badge=badge}}
    </p>
{{/each}}

I haven't figured out how to do that best yet. Maybe change the CSS of span.badge to be display: block on the crate show page???

Other Questions

  • Is this the best way to do this? I feel like I'm fighting against something else that Ember would rather I do, but I don't know what that is.

    • Is component the right thing to use? the /components/*.js files feel repetitive and ceremonial.
    • Should I be using a helper?
    • I tried having a model for badge, but I don't think Ember liked that the data was already there? That's when I was getting the "Assertion Failed: You need to pass a model name to the store's modelFor method" errors so I took it out...
  • If I were to add a new badge, I'd need to add its ordering to the badges-ordered component and create each of these:

    • app/templates/components/badge-new.hbs
    • app/components/badge-new.js I'd rather just have to add the ordering and create the template...
@locks
Copy link

locks commented Jan 20, 2017

components have to have at least one hyphen in the name since someday badge might be an HTML element????

Confirm :P The custom element spec mandates that naming to avoid possible future collisions, since non-dasherized names are reserved.

[edit] ci-badges? ;)

@locks
Copy link

locks commented Jan 20, 2017

{{#each ['travis-ci', 'appveyor'] as |badge_type|}}

You can do ember generate helper array, because the file generated passes the arguments through. Meaning you would do it like:

{{#each (array 'travis-ci', 'appveyor']) as |badge_type|}}

[edit] badge=badges[badge_type] is badge=(get badges badge_type)

@locks
Copy link

locks commented Jan 20, 2017

I personally would have a helper that knows how to map a badge name to the component name instead of having the annotated_badges CP.

@carols10cents
Copy link
Author

ember-i18n for display text
factor out things into helpers

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment