Skip to content

Commit 710f208

Browse files
authored
Merge pull request #473 from integer32llc/categorization
Add Categorization!
2 parents 25f7342 + bffd16a commit 710f208

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+1393
-215
lines changed

Cargo.lock

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ env_logger = "0.3"
3535
rustc-serialize = "0.3"
3636
license-exprs = "^1.3"
3737
dotenv = "0.8.0"
38+
toml = "0.2"
3839

3940
conduit = "0.8"
4041
conduit-conditional-get = "0.8"

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,21 @@ follows:
123123
```
124124
cargo test
125125
```
126+
127+
## Categories
128+
129+
The list of categories available on crates.io is stored in
130+
`src/categories.toml`. To propose adding, removing, or changing a category,
131+
send a pull request making the appropriate change to that file as noted in the
132+
comment at the top of the file. Please add a description that will help others
133+
to know what crates are in that category.
134+
135+
For new categories, it's helpful to note in your PR description examples of
136+
crates that would fit in that category, and describe what distinguishes the new
137+
category from existing categories.
138+
139+
After your PR is accepted, the next time that crates.io is deployed the
140+
categories will be synced from this file.
126141
127142
## Deploying & Using a Mirror
128143

app/adapters/category-slug.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import ApplicationAdapter from './application';
2+
import Ember from 'ember';
3+
4+
export default ApplicationAdapter.extend({
5+
pathForType(modelName) {
6+
var decamelized = Ember.String.underscore(
7+
Ember.String.decamelize(modelName)
8+
);
9+
return Ember.String.pluralize(decamelized);
10+
}
11+
});

app/controllers/categories.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import Ember from 'ember';
2+
import PaginationMixin from '../mixins/pagination';
3+
4+
const { computed } = Ember;
5+
6+
export default Ember.Controller.extend(PaginationMixin, {
7+
queryParams: ['page', 'per_page', 'sort'],
8+
page: '1',
9+
per_page: 10,
10+
sort: 'alpha',
11+
12+
totalItems: computed.readOnly('model.meta.total'),
13+
14+
currentSortBy: computed('sort', function() {
15+
return (this.get('sort') === 'crates') ? '# Crates' : 'Alphabetical';
16+
}),
17+
});

app/controllers/category/index.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import Ember from 'ember';
2+
import PaginationMixin from '../../mixins/pagination';
3+
4+
const { computed } = Ember;
5+
6+
export default Ember.Controller.extend(PaginationMixin, {
7+
queryParams: ['page', 'per_page', 'sort'],
8+
page: '1',
9+
per_page: 10,
10+
sort: 'downloads',
11+
12+
totalItems: computed.readOnly('model.meta.total'),
13+
14+
currentSortBy: computed('sort', function() {
15+
return (this.get('sort') === 'downloads') ? 'Downloads' : 'Alphabetical';
16+
}),
17+
});

app/controllers/crate/version.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export default Ember.Controller.extend({
1919
currentVersion: computed.alias('model'),
2020
requestedVersion: null,
2121
keywords: computed.alias('crate.keywords'),
22+
categories: computed.alias('crate.categories'),
2223

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

@@ -49,6 +50,7 @@ export default Ember.Controller.extend({
4950
}),
5051

5152
anyKeywords: computed.gt('keywords.length', 0),
53+
anyCategories: computed.gt('categories.length', 0),
5254

5355
currentDependencies: computed('currentVersion.dependencies', function() {
5456
var deps = this.get('currentVersion.dependencies');

app/models/category-slug.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import DS from 'ember-data';
2+
3+
export default DS.Model.extend({
4+
slug: DS.attr('string'),
5+
});

app/models/category.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import DS from 'ember-data';
2+
3+
export default DS.Model.extend({
4+
category: DS.attr('string'),
5+
slug: DS.attr('string'),
6+
description: DS.attr('string'),
7+
created_at: DS.attr('date'),
8+
crates_cnt: DS.attr('number'),
9+
10+
subcategories: DS.attr(),
11+
12+
crates: DS.hasMany('crate', { async: true })
13+
});

app/models/crate.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export default DS.Model.extend({
2020
owners: DS.hasMany('users', { async: true }),
2121
version_downloads: DS.hasMany('version-download', { async: true }),
2222
keywords: DS.hasMany('keywords', { async: true }),
23+
categories: DS.hasMany('categories', { async: true }),
2324
reverse_dependencies: DS.hasMany('dependency', { async: true }),
2425

2526
follow() {

app/router.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ Router.map(function() {
3535
this.route('keyword', { path: '/keywords/:keyword_id' }, function() {
3636
this.route('index', { path: '/' });
3737
});
38+
this.route('categories');
39+
this.route('category', { path: '/categories/:category_id' }, function() {
40+
this.route('index', { path: '/' });
41+
});
42+
this.route('category_slugs');
3843
this.route('catchAll', { path: '*path' });
3944
});
4045

app/routes/categories.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import Ember from 'ember';
2+
3+
export default Ember.Route.extend({
4+
queryParams: {
5+
page: { refreshModel: true },
6+
sort: { refreshModel: true },
7+
},
8+
9+
model(params) {
10+
return this.store.query('category', params);
11+
},
12+
});

app/routes/category-slugs.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import Ember from 'ember';
2+
3+
export default Ember.Route.extend({
4+
queryParams: {
5+
page: { refreshModel: true },
6+
sort: { refreshModel: true },
7+
},
8+
9+
model(params) {
10+
return this.store.query('category-slug', params);
11+
},
12+
});

app/routes/category.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import Ember from 'ember';
2+
3+
export default Ember.Route.extend({
4+
model(params) {
5+
return this.store.find('category', params.category_id).catch(e => {
6+
if (e.errors.any(e => e.detail === 'Not Found')) {
7+
this.controllerFor('application').set('flashError', `Category '${params.category_id}' does not exist`);
8+
}
9+
});
10+
}
11+
});

app/routes/category/index.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import Ember from 'ember';
2+
3+
export default Ember.Route.extend({
4+
queryParams: {
5+
page: { refreshModel: true },
6+
sort: { refreshModel: true },
7+
},
8+
9+
model(params) {
10+
params.category = this.modelFor('category').id;
11+
return this.store.query('crate', params);
12+
},
13+
14+
setupController(controller, model) {
15+
controller.set('category', this.modelFor('category'));
16+
this._super(controller, model);
17+
},
18+
});

app/templates/categories.hbs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
{{ title 'Categories' }}
2+
3+
<div id='crates-heading'>
4+
<img class='logo crate' src="/assets/crate.png"/>
5+
<h1>All Categories</h1>
6+
</div>
7+
8+
<div id='results'>
9+
<div class='nav'>
10+
<span class='amt small'>
11+
Displaying
12+
<span class='cur'>{{ currentPageStart }}-{{ currentPageEnd }}</span>
13+
of <span class='total'>{{ totalItems }}</span> total results
14+
</span>
15+
</div>
16+
17+
<div class='sort'>
18+
<span class='small'>Sort by</span>
19+
{{#rl-dropdown-container class="dropdown-container"}}
20+
{{#rl-dropdown-toggle tagName="a" class="dropdown"}}
21+
<img class="sort" src="/assets/sort.png"/>
22+
{{ currentSortBy }}
23+
<span class='arrow'></span>
24+
{{/rl-dropdown-toggle}}
25+
26+
{{#rl-dropdown tagName="ul" class="dropdown" closeOnChildClick="a:link"}}
27+
<li>
28+
{{#link-to (query-params sort="alpha")}}
29+
Alphabetical
30+
{{/link-to}}
31+
</li>
32+
<li>
33+
{{#link-to (query-params sort="crates")}}
34+
# Crates
35+
{{/link-to}}
36+
</li>
37+
{{/rl-dropdown}}
38+
{{/rl-dropdown-container}}
39+
</div>
40+
</div>
41+
42+
<div class='white-rows'>
43+
{{#each model as |category|}}
44+
<div class='row'>
45+
<div class='desc'>
46+
<div class='info'>
47+
{{link-to category.category "category" category.slug}}
48+
<span class='small'>
49+
{{ format-num category.crates_cnt }}
50+
crates
51+
</span>
52+
</div>
53+
<div class='summary'>
54+
<span class='small'>
55+
{{ truncate-text category.description }}
56+
</span>
57+
</div>
58+
</div>
59+
</div>
60+
{{/each}}
61+
</div>
62+
63+
<div class='pagination'>
64+
{{#link-to (query-params page=prevPage) class="prev" rel="prev" title="previous page"}}
65+
<img class="left-pag" src="/assets/left-pag.png"/>
66+
{{/link-to}}
67+
{{#each pages as |page|}}
68+
{{#link-to (query-params page=page)}}{{ page }}{{/link-to}}
69+
{{/each}}
70+
{{#link-to (query-params page=nextPage) class="next" rel="next" title="next page"}}
71+
<img class="right-pag" src="/assets/right-pag.png"/>
72+
{{/link-to}}
73+
</div>

app/templates/category/error.hbs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{{ title 'Category Not Found' }}

app/templates/category/index.hbs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
{{ title category.category ' - Categories' }}
2+
3+
<div id='crates-heading'>
4+
<img class='logo crate' src="/assets/crate.png"/>
5+
<h1>{{ category.category }}</h1>
6+
</div>
7+
8+
{{#if category.subcategories }}
9+
<div id='subcategories'>
10+
<h2>Subcategories</h2>
11+
<div class='white-rows'>
12+
{{#each category.subcategories as |subcategory| }}
13+
<div class='row'>
14+
<div class='desc'>
15+
<div class='info'>
16+
{{link-to subcategory.category "category" subcategory.slug}}
17+
<span class='small'>
18+
{{ format-num subcategory.crates_cnt }}
19+
crates
20+
</span>
21+
</div>
22+
<div class='summary'>
23+
<span class='small'>
24+
{{ truncate-text subcategory.description }}
25+
</span>
26+
</div>
27+
</div>
28+
</div>
29+
{{/each}}
30+
</div>
31+
</div>
32+
{{/if}}
33+
34+
<h2>Crates</h2>
35+
<div id='results'>
36+
<div class='nav'>
37+
<span class='amt small'>
38+
Displaying
39+
<span class='cur'>{{ currentPageStart }}-{{ currentPageEnd }}</span>
40+
of <span class='total'>{{ totalItems }}</span> total results
41+
</span>
42+
</div>
43+
44+
<div class='sort'>
45+
<span class='small'>Sort by</span>
46+
{{#rl-dropdown-container class="dropdown-container"}}
47+
{{#rl-dropdown-toggle tagName="a" class="dropdown"}}
48+
<img class="sort" src="/assets/sort.png"/>
49+
{{ currentSortBy }}
50+
<span class='arrow'></span>
51+
{{/rl-dropdown-toggle}}
52+
53+
{{#rl-dropdown tagName="ul" class="dropdown" closeOnChildClick="a:link"}}
54+
<li>
55+
{{#link-to (query-params sort="alpha")}}
56+
Alphabetical
57+
{{/link-to}}
58+
</li>
59+
<li>
60+
{{#link-to (query-params sort="downloads")}}
61+
Downloads
62+
{{/link-to}}
63+
</li>
64+
{{/rl-dropdown}}
65+
{{/rl-dropdown-container}}
66+
</div>
67+
</div>
68+
69+
<div id='crates' class='white-rows'>
70+
{{#each model as |crate|}}
71+
{{crate-row crate=crate}}
72+
{{/each}}
73+
</div>
74+
75+
<div class='pagination'>
76+
{{#link-to (query-params page=prevPage) class="prev" rel="prev" title="previous page"}}
77+
<img class="left-pag" src="/assets/left-pag.png"/>
78+
{{/link-to}}
79+
{{#each pages as |page|}}
80+
{{#link-to (query-params page=page)}}{{ page }}{{/link-to}}
81+
{{/each}}
82+
{{#link-to (query-params page=nextPage) class="next" rel="next" title="next page"}}
83+
<img class="right-pag" src="/assets/right-pag.png"/>
84+
{{/link-to}}
85+
</div>

0 commit comments

Comments
 (0)