Skip to content

Commit

Permalink
Merge pull request #473 from integer32llc/categorization
Browse files Browse the repository at this point in the history
Add Categorization!
  • Loading branch information
alexcrichton authored Dec 29, 2016
2 parents 25f7342 + bffd16a commit 710f208
Show file tree
Hide file tree
Showing 50 changed files with 1,393 additions and 215 deletions.
10 changes: 10 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ env_logger = "0.3"
rustc-serialize = "0.3"
license-exprs = "^1.3"
dotenv = "0.8.0"
toml = "0.2"

conduit = "0.8"
conduit-conditional-get = "0.8"
Expand Down
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,21 @@ follows:
```
cargo test
```

## Categories

The list of categories available on crates.io is stored in
`src/categories.toml`. To propose adding, removing, or changing a category,
send a pull request making the appropriate change to that file as noted in the
comment at the top of the file. Please add a description that will help others
to know what crates are in that category.

For new categories, it's helpful to note in your PR description examples of
crates that would fit in that category, and describe what distinguishes the new
category from existing categories.

After your PR is accepted, the next time that crates.io is deployed the
categories will be synced from this file.

## Deploying & Using a Mirror

Expand Down
11 changes: 11 additions & 0 deletions app/adapters/category-slug.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import ApplicationAdapter from './application';
import Ember from 'ember';

export default ApplicationAdapter.extend({
pathForType(modelName) {
var decamelized = Ember.String.underscore(
Ember.String.decamelize(modelName)
);
return Ember.String.pluralize(decamelized);
}
});
17 changes: 17 additions & 0 deletions app/controllers/categories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import Ember from 'ember';
import PaginationMixin from '../mixins/pagination';

const { computed } = Ember;

export default Ember.Controller.extend(PaginationMixin, {
queryParams: ['page', 'per_page', 'sort'],
page: '1',
per_page: 10,
sort: 'alpha',

totalItems: computed.readOnly('model.meta.total'),

currentSortBy: computed('sort', function() {
return (this.get('sort') === 'crates') ? '# Crates' : 'Alphabetical';
}),
});
17 changes: 17 additions & 0 deletions app/controllers/category/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import Ember from 'ember';
import PaginationMixin from '../../mixins/pagination';

const { computed } = Ember;

export default Ember.Controller.extend(PaginationMixin, {
queryParams: ['page', 'per_page', 'sort'],
page: '1',
per_page: 10,
sort: 'downloads',

totalItems: computed.readOnly('model.meta.total'),

currentSortBy: computed('sort', function() {
return (this.get('sort') === 'downloads') ? 'Downloads' : 'Alphabetical';
}),
});
2 changes: 2 additions & 0 deletions app/controllers/crate/version.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export default Ember.Controller.extend({
currentVersion: computed.alias('model'),
requestedVersion: null,
keywords: computed.alias('crate.keywords'),
categories: computed.alias('crate.categories'),

sortedVersions: computed.readOnly('crate.versions'),

Expand Down Expand Up @@ -49,6 +50,7 @@ export default Ember.Controller.extend({
}),

anyKeywords: computed.gt('keywords.length', 0),
anyCategories: computed.gt('categories.length', 0),

currentDependencies: computed('currentVersion.dependencies', function() {
var deps = this.get('currentVersion.dependencies');
Expand Down
5 changes: 5 additions & 0 deletions app/models/category-slug.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import DS from 'ember-data';

export default DS.Model.extend({
slug: DS.attr('string'),
});
13 changes: 13 additions & 0 deletions app/models/category.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import DS from 'ember-data';

export default DS.Model.extend({
category: DS.attr('string'),
slug: DS.attr('string'),
description: DS.attr('string'),
created_at: DS.attr('date'),
crates_cnt: DS.attr('number'),

subcategories: DS.attr(),

crates: DS.hasMany('crate', { async: true })
});
1 change: 1 addition & 0 deletions app/models/crate.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export default DS.Model.extend({
owners: DS.hasMany('users', { async: true }),
version_downloads: DS.hasMany('version-download', { async: true }),
keywords: DS.hasMany('keywords', { async: true }),
categories: DS.hasMany('categories', { async: true }),
reverse_dependencies: DS.hasMany('dependency', { async: true }),

follow() {
Expand Down
5 changes: 5 additions & 0 deletions app/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ Router.map(function() {
this.route('keyword', { path: '/keywords/:keyword_id' }, function() {
this.route('index', { path: '/' });
});
this.route('categories');
this.route('category', { path: '/categories/:category_id' }, function() {
this.route('index', { path: '/' });
});
this.route('category_slugs');
this.route('catchAll', { path: '*path' });
});

Expand Down
12 changes: 12 additions & 0 deletions app/routes/categories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import Ember from 'ember';

export default Ember.Route.extend({
queryParams: {
page: { refreshModel: true },
sort: { refreshModel: true },
},

model(params) {
return this.store.query('category', params);
},
});
12 changes: 12 additions & 0 deletions app/routes/category-slugs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import Ember from 'ember';

export default Ember.Route.extend({
queryParams: {
page: { refreshModel: true },
sort: { refreshModel: true },
},

model(params) {
return this.store.query('category-slug', params);
},
});
11 changes: 11 additions & 0 deletions app/routes/category.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import Ember from 'ember';

export default Ember.Route.extend({
model(params) {
return this.store.find('category', params.category_id).catch(e => {
if (e.errors.any(e => e.detail === 'Not Found')) {
this.controllerFor('application').set('flashError', `Category '${params.category_id}' does not exist`);
}
});
}
});
18 changes: 18 additions & 0 deletions app/routes/category/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import Ember from 'ember';

export default Ember.Route.extend({
queryParams: {
page: { refreshModel: true },
sort: { refreshModel: true },
},

model(params) {
params.category = this.modelFor('category').id;
return this.store.query('crate', params);
},

setupController(controller, model) {
controller.set('category', this.modelFor('category'));
this._super(controller, model);
},
});
73 changes: 73 additions & 0 deletions app/templates/categories.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
{{ title 'Categories' }}

<div id='crates-heading'>
<img class='logo crate' src="/assets/crate.png"/>
<h1>All Categories</h1>
</div>

<div id='results'>
<div class='nav'>
<span class='amt small'>
Displaying
<span class='cur'>{{ currentPageStart }}-{{ currentPageEnd }}</span>
of <span class='total'>{{ totalItems }}</span> total results
</span>
</div>

<div class='sort'>
<span class='small'>Sort by</span>
{{#rl-dropdown-container class="dropdown-container"}}
{{#rl-dropdown-toggle tagName="a" class="dropdown"}}
<img class="sort" src="/assets/sort.png"/>
{{ currentSortBy }}
<span class='arrow'></span>
{{/rl-dropdown-toggle}}

{{#rl-dropdown tagName="ul" class="dropdown" closeOnChildClick="a:link"}}
<li>
{{#link-to (query-params sort="alpha")}}
Alphabetical
{{/link-to}}
</li>
<li>
{{#link-to (query-params sort="crates")}}
# Crates
{{/link-to}}
</li>
{{/rl-dropdown}}
{{/rl-dropdown-container}}
</div>
</div>

<div class='white-rows'>
{{#each model as |category|}}
<div class='row'>
<div class='desc'>
<div class='info'>
{{link-to category.category "category" category.slug}}
<span class='small'>
{{ format-num category.crates_cnt }}
crates
</span>
</div>
<div class='summary'>
<span class='small'>
{{ truncate-text category.description }}
</span>
</div>
</div>
</div>
{{/each}}
</div>

<div class='pagination'>
{{#link-to (query-params page=prevPage) class="prev" rel="prev" title="previous page"}}
<img class="left-pag" src="/assets/left-pag.png"/>
{{/link-to}}
{{#each pages as |page|}}
{{#link-to (query-params page=page)}}{{ page }}{{/link-to}}
{{/each}}
{{#link-to (query-params page=nextPage) class="next" rel="next" title="next page"}}
<img class="right-pag" src="/assets/right-pag.png"/>
{{/link-to}}
</div>
1 change: 1 addition & 0 deletions app/templates/category/error.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{{ title 'Category Not Found' }}
85 changes: 85 additions & 0 deletions app/templates/category/index.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
{{ title category.category ' - Categories' }}

<div id='crates-heading'>
<img class='logo crate' src="/assets/crate.png"/>
<h1>{{ category.category }}</h1>
</div>

{{#if category.subcategories }}
<div id='subcategories'>
<h2>Subcategories</h2>
<div class='white-rows'>
{{#each category.subcategories as |subcategory| }}
<div class='row'>
<div class='desc'>
<div class='info'>
{{link-to subcategory.category "category" subcategory.slug}}
<span class='small'>
{{ format-num subcategory.crates_cnt }}
crates
</span>
</div>
<div class='summary'>
<span class='small'>
{{ truncate-text subcategory.description }}
</span>
</div>
</div>
</div>
{{/each}}
</div>
</div>
{{/if}}

<h2>Crates</h2>
<div id='results'>
<div class='nav'>
<span class='amt small'>
Displaying
<span class='cur'>{{ currentPageStart }}-{{ currentPageEnd }}</span>
of <span class='total'>{{ totalItems }}</span> total results
</span>
</div>

<div class='sort'>
<span class='small'>Sort by</span>
{{#rl-dropdown-container class="dropdown-container"}}
{{#rl-dropdown-toggle tagName="a" class="dropdown"}}
<img class="sort" src="/assets/sort.png"/>
{{ currentSortBy }}
<span class='arrow'></span>
{{/rl-dropdown-toggle}}

{{#rl-dropdown tagName="ul" class="dropdown" closeOnChildClick="a:link"}}
<li>
{{#link-to (query-params sort="alpha")}}
Alphabetical
{{/link-to}}
</li>
<li>
{{#link-to (query-params sort="downloads")}}
Downloads
{{/link-to}}
</li>
{{/rl-dropdown}}
{{/rl-dropdown-container}}
</div>
</div>

<div id='crates' class='white-rows'>
{{#each model as |crate|}}
{{crate-row crate=crate}}
{{/each}}
</div>

<div class='pagination'>
{{#link-to (query-params page=prevPage) class="prev" rel="prev" title="previous page"}}
<img class="left-pag" src="/assets/left-pag.png"/>
{{/link-to}}
{{#each pages as |page|}}
{{#link-to (query-params page=page)}}{{ page }}{{/link-to}}
{{/each}}
{{#link-to (query-params page=nextPage) class="next" rel="next" title="next page"}}
<img class="right-pag" src="/assets/right-pag.png"/>
{{/link-to}}
</div>
Loading

0 comments on commit 710f208

Please sign in to comment.