Skip to content

Commit 45b8dbf

Browse files
committed
Auto merge of #1931 - bruceadams:newest-updated, r=jtgeibel
Newest updated This PR is working toward addressing #310. This is incomplete. There is a relevant test commented out because it fails.
2 parents 46972f2 + fb356c4 commit 45b8dbf

File tree

18 files changed

+151
-54
lines changed

18 files changed

+151
-54
lines changed

app/models/crate.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export default Model.extend({
88
created_at: attr('date'),
99
updated_at: attr('date'),
1010
max_version: attr('string'),
11+
newest_version: attr('string'),
1112

1213
description: attr('string'),
1314
homepage: attr('string'),

app/templates/components/crate-list.hbs renamed to app/templates/components/crate-list-name-only.hbs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
{{#each this.crates as |crate index|}}
33
<li>
44
<LinkTo @route="crate" @model={{crate.id}} class="name" data-test-crate-link={{index}}>
5-
<span>{{ crate.name }} ({{ crate.max_version }})</span>
5+
<span>{{ crate.name }}</span>
66
<div class='arrow-in-list'>
77
{{svg-jar "right-arrow"}}
88
</div>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<ol>
2+
{{#each this.crates as |crate index|}}
3+
<li>
4+
<LinkTo @route="crate.version" @models={{array crate.id crate.newest_version}} class="name" data-test-crate-link={{index}}>
5+
<span>{{ crate.name }} ({{ crate.newest_version }})</span>
6+
<div class='arrow-in-list'>
7+
{{svg-jar "right-arrow"}}
8+
</div>
9+
</LinkTo>
10+
</li>
11+
{{/each}}
12+
</ol>

app/templates/index.hbs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,19 +38,19 @@
3838
<div id='home-crates' class='crate-lists'>
3939
<section id='new-crates' data-test-new-crates aria-busy="{{this.dataTask.isRunning}}">
4040
<h2>New Crates</h2>
41-
<CrateList @crates={{this.model.new_crates}} />
41+
<CrateListNewest @crates={{this.model.new_crates}} />
4242
</section>
4343
<section id='most-downloaded' data-test-most-downloaded aria-busy="{{this.dataTask.isRunning}}">
4444
<h2>Most Downloaded</h2>
45-
<CrateList @crates={{this.model.most_downloaded}} />
45+
<CrateListNameOnly @crates={{this.model.most_downloaded}} />
4646
</section>
4747
<section id='just-updated' data-test-just-updated aria-busy="{{this.dataTask.isRunning}}">
4848
<h2>Just Updated</h2>
49-
<CrateList @crates={{this.model.just_updated}} />
49+
<CrateListNewest @crates={{this.model.just_updated}} />
5050
</section>
5151
<section id='most-recently-downloaded' data-test-most-recently-downloaded aria-busy="{{this.dataTask.isRunning}}">
5252
<h2>Most Recent Downloads</h2>
53-
<CrateList @crates={{this.model.most_recently_downloaded}} />
53+
<CrateListNameOnly @crates={{this.model.most_recently_downloaded}} />
5454
</section>
5555
<section id='keywords' data-test-keywords aria-busy="{{this.dataTask.isRunning}}">
5656
<h2>Popular Keywords <LinkTo @route="keywords">(see all)</LinkTo></h2>

mirage/factories/crate.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export default Factory.extend({
1616
homepage: () => faker.internet.url(),
1717
repository: () => faker.internet.url(),
1818
max_version: () => faker.system.semver(),
19+
newest_version: () => faker.system.semver(),
1920

2021
created_at: () => faker.date.past(),
2122
updated_at() {

mirage/fixtures/crates.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export default [
1717
id: 'kinetic-rust',
1818
keywords: ['Protocol', 'Kinetic', 'Storage'],
1919
max_version: '0.0.16',
20+
newest_version: '0.0.16',
2021
name: 'kinetic-rust',
2122
repository: 'https://github.com/icorderi/kinetic-rust/',
2223
updated_at: '2015-04-21T00:15:49Z',
@@ -41,6 +42,7 @@ export default [
4142
id: 'nanomsg',
4243
keywords: ['network'],
4344
max_version: '0.7.0-alpha',
45+
newest_version: '0.6.1',
4446
name: 'nanomsg',
4547
repository: 'https://github.com/thehydroimpulse/nanomsg.rs',
4648
updated_at: '2016-12-28T08:40:00Z',
@@ -70,6 +72,7 @@ export default [
7072
id: 'rust_mixin',
7173
keywords: ['rust', 'plugin', 'code-generation'],
7274
max_version: '0.0.1',
75+
newest_version: '0.0.1',
7376
name: 'rust_mixin',
7477
repository: 'https://github.com/huonw/external_mixin',
7578
updated_at: '2015-02-27T11:52:13Z',
@@ -142,6 +145,7 @@ export default [
142145
id: 'external_mixin',
143146
keywords: ['python', 'ruby', 'shell', 'plugin', 'code-generation'],
144147
max_version: '0.0.1',
148+
newest_version: '0.0.1',
145149
name: 'external_mixin',
146150
repository: 'https://github.com/huonw/external_mixin',
147151
updated_at: '2015-02-27T11:51:58Z',
@@ -157,6 +161,7 @@ export default [
157161
id: 'external_mixin_umbrella',
158162
keywords: ['plugin', 'code-generation'],
159163
max_version: '0.0.2',
164+
newest_version: '0.0.2',
160165
name: 'external_mixin_umbrella',
161166
repository: 'https://github.com/huonw/external_mixin',
162167
updated_at: '2015-02-27T11:52:30Z',
@@ -173,6 +178,7 @@ export default [
173178
id: 'Inflector',
174179
keywords: ['string', 'case', 'camel', 'snake', 'inflection'],
175180
max_version: '0.1.6',
181+
newest_version: '0.1.6',
176182
name: 'Inflector',
177183
repository: 'https://github.com/whatisinternet/inflector',
178184
updated_at: '2015-10-27T01:51:42Z',
@@ -188,6 +194,7 @@ export default [
188194
id: 'rs-es',
189195
keywords: ['elasticsearch', 'elastic'],
190196
max_version: '0.1.17',
197+
newest_version: '0.1.17',
191198
name: 'rs-es',
192199
repository: 'https://github.com/benashford/rs-es',
193200
updated_at: '2015-09-09T15:34:50Z',
@@ -203,6 +210,7 @@ export default [
203210
id: 'rust-crypto',
204211
keywords: ['Crypto', 'MD5', 'Sha1', 'Sha2', 'AES'],
205212
max_version: '0.2.34',
213+
newest_version: '0.2.34',
206214
name: 'rust-crypto',
207215
repository: 'https://github.com/DaGenix/rust-crypto/',
208216
updated_at: '2015-10-29T01:16:17Z',
@@ -218,6 +226,7 @@ export default [
218226
id: 'rust-htslib',
219227
keywords: ['htslib', 'bam', 'bioinformatics', 'pileup', 'sequencing'],
220228
max_version: '0.5.2',
229+
newest_version: '0.5.2',
221230
name: 'rust-htslib',
222231
repository: 'https://github.com/rust-bio/rust-htslib.git',
223232
updated_at: '2015-11-11T00:10:43Z',
@@ -233,6 +242,7 @@ export default [
233242
id: 'rustless',
234243
keywords: ['api', 'web', 'hyper', 'iron', 'rest'],
235244
max_version: '0.8.0',
245+
newest_version: '0.8.0',
236246
name: 'rustless',
237247
repository: 'https://crates.io/crates/rustless',
238248
updated_at: '2015-10-31T11:49:29Z',
@@ -248,6 +258,7 @@ export default [
248258
id: 'serde',
249259
keywords: ['serde', 'serialization'],
250260
max_version: '0.6.1',
261+
newest_version: '0.6.1',
251262
name: 'serde',
252263
repository: 'https://github.com/serde-rs/serde',
253264
updated_at: '2015-10-18T03:10:21Z',
@@ -263,6 +274,7 @@ export default [
263274
id: 'rusted_cypher',
264275
keywords: ['neo4j', 'database', 'query', 'cypher', 'graph'],
265276
max_version: '0.7.1',
277+
newest_version: '0.7.1',
266278
name: 'rusted_cypher',
267279
repository: 'https://github.com/livioribeiro/rusted-cypher',
268280
updated_at: '2015-11-07T17:26:55Z',
@@ -279,6 +291,7 @@ export default [
279291
id: 'zlib',
280292
keywords: [],
281293
max_version: '0.0.1',
294+
newest_version: '0.0.1',
282295
name: 'zlib',
283296
repository: null,
284297
updated_at: '2015-01-02T20:54:04Z',
@@ -295,6 +308,7 @@ export default [
295308
id: 'rustful',
296309
keywords: ['web', 'rest', 'framework', 'http', 'routing'],
297310
max_version: '0.5.0',
311+
newest_version: '0.5.0',
298312
name: 'rustful',
299313
repository: 'https://github.com/Ogeon/rustful',
300314
updated_at: '2015-09-19T21:10:27Z',
@@ -310,6 +324,7 @@ export default [
310324
id: 'postgres',
311325
keywords: ['database', 'sql'],
312326
max_version: '0.10.1',
327+
newest_version: '0.10.1',
313328
name: 'postgres',
314329
repository: 'https://github.com/sfackler/rust-postgres',
315330
updated_at: '2015-11-08T00:48:59Z',
@@ -325,6 +340,7 @@ export default [
325340
id: 'quickcheck',
326341
keywords: ['testing', 'quickcheck', 'property', 'shrinking', 'fuzz'],
327342
max_version: '0.2.24',
343+
newest_version: '0.2.24',
328344
name: 'quickcheck',
329345
repository: 'https://github.com/BurntSushi/quickcheck',
330346
updated_at: '2015-09-20T21:53:38Z',
@@ -340,6 +356,7 @@ export default [
340356
id: 'quickcheck_macros',
341357
keywords: ['testing', 'quickcheck', 'property', 'shrinking', 'fuzz'],
342358
max_version: '0.2.24',
359+
newest_version: '0.2.24',
343360
name: 'quickcheck_macros',
344361
repository: 'https://github.com/BurntSushi/quickcheck',
345362
updated_at: '2015-09-20T21:53:57Z',
@@ -356,6 +373,7 @@ export default [
356373
id: 'nc_rustlex',
357374
keywords: ['lexer', 'lexical', 'analyser', 'generator'],
358375
max_version: '0.3.1',
376+
newest_version: '0.3.1',
359377
name: 'nc_rustlex',
360378
repository: 'https://github.com/nicolas-cherel/rustlex',
361379
updated_at: '2015-08-25T19:15:35Z',
@@ -371,6 +389,7 @@ export default [
371389
id: 'nom',
372390
keywords: ['parser', 'parser-combinators', 'parsing', 'streaming', 'bit'],
373391
max_version: '1.0.1',
392+
newest_version: '1.0.1',
374393
name: 'nom',
375394
repository: 'https://github.com/Geal/nom',
376395
updated_at: '2015-11-22T22:00:41Z',

mirage/fixtures/versions.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
export default [
22
{
33
crate: 'nanomsg',
4-
created_at: '2016-12-27T08:40:00Z',
4+
created_at: '2016-12-20T07:30:00Z',
55
dl_path: '/api/v1/crates/nanomsg/0.7.0-alpha.1/download',
66
downloads: 260,
77
features: {
88
bundled: ['nanomsg-sys/bundled'],
99
},
1010
id: 40906,
1111
num: '0.7.0-alpha.1',
12-
updated_at: '2016-12-27T08:40:00Z',
12+
updated_at: '2016-12-20T07:30:00Z',
1313
yanked: false,
1414
license: 'MIT',
1515
crate_size: 912355,

mirage/serializers/crate.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export default BaseSerializer.extend({
1414
'keywords',
1515
'links',
1616
'max_version',
17+
'newest_version',
1718
'name',
1819
'repository',
1920
'updated_at',

src/controllers/krate/metadata.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
//! Endpoints that expose metadata about a crate
22
//!
3-
//! These endpoints provide data that could be obtained direclty from the
3+
//! These endpoints provide data that could be obtained directly from the
44
//! index or cached metadata which was extracted (client side) from the
55
//! `Cargo.toml` file.
66
77
use crate::controllers::frontend_prelude::*;
8+
89
use crate::models::{
910
Category, Crate, CrateCategory, CrateKeyword, CrateVersions, Keyword, RecentCrateDownloads,
1011
User, Version, VersionOwnerAction,
@@ -31,10 +32,10 @@ pub fn summary(req: &mut dyn Request) -> AppResult<Response> {
3132
versions
3233
.grouped_by(&krates)
3334
.into_iter()
34-
.map(|versions| Version::max(versions.into_iter().map(|v| v.num)))
35+
.map(|versions| Version::top(versions.into_iter().map(|v| (v.created_at, v.num))))
3536
.zip(krates)
36-
.map(|(max_version, krate)| {
37-
Ok(krate.minimal_encodable(&max_version, None, false, None))
37+
.map(|(top_versions, krate)| {
38+
Ok(krate.minimal_encodable(&top_versions, None, false, None))
3839
})
3940
.collect()
4041
};
@@ -142,7 +143,7 @@ pub fn show(req: &mut dyn Request) -> AppResult<Response> {
142143
let badges = badges::table
143144
.filter(badges::crate_id.eq(krate.id))
144145
.load(&*conn)?;
145-
let max_version = krate.max_version(&conn)?;
146+
let top_versions = krate.top_versions(&conn)?;
146147

147148
#[derive(Serialize)]
148149
struct R {
@@ -154,7 +155,7 @@ pub fn show(req: &mut dyn Request) -> AppResult<Response> {
154155
}
155156
Ok(req.json(&R {
156157
krate: krate.clone().encodable(
157-
&max_version,
158+
&top_versions,
158159
Some(ids),
159160
Some(&kws),
160161
Some(&cats),

src/controllers/krate/publish.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ pub fn publish(req: &mut dyn Request) -> AppResult<Response> {
168168
// Update all badges for this crate, collecting any invalid badges in
169169
// order to be able to warn about them
170170
let ignored_invalid_badges = Badge::update_crate(&conn, &krate, new_crate.badges.as_ref())?;
171-
let max_version = krate.max_version(&conn)?;
171+
let top_versions = krate.top_versions(&conn)?;
172172

173173
if let Some(readme) = new_crate.readme {
174174
render::render_and_upload_readme(
@@ -215,7 +215,7 @@ pub fn publish(req: &mut dyn Request) -> AppResult<Response> {
215215
};
216216

217217
Ok(req.json(&GoodCrate {
218-
krate: krate.minimal_encodable(&max_version, None, false, None),
218+
krate: krate.minimal_encodable(&top_versions, None, false, None),
219219
warnings,
220220
}))
221221
})

src/controllers/krate/search.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ pub fn search(req: &mut dyn Request) -> AppResult<Response> {
179179
.load::<Version>(&*conn)?
180180
.grouped_by(&crates)
181181
.into_iter()
182-
.map(|versions| Version::max(versions.into_iter().map(|v| v.num)));
182+
.map(|versions| Version::top(versions.into_iter().map(|v| (v.created_at, v.num))));
183183

184184
let badges = CrateBadge::belonging_to(&crates)
185185
.select((badges::crate_id, badges::all_columns))

src/models/krate.rs

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@ use url::Url;
88

99
use crate::app::App;
1010
use crate::email;
11-
use crate::util::{cargo_err, AppResult};
12-
11+
use crate::models::version::TopVersions;
1312
use crate::models::{
1413
Badge, Category, CrateOwner, CrateOwnerInvitation, Keyword, NewCrateOwnerInvitation, Owner,
1514
OwnerKind, ReverseDependency, User, Version,
1615
};
16+
use crate::util::{cargo_err, AppResult};
1717
use crate::views::{EncodableCrate, EncodableCrateLinks};
1818

1919
use crate::models::helpers::with_count::*;
@@ -204,7 +204,7 @@ impl<'a> NewCrate<'a> {
204204
}
205205

206206
impl Crate {
207-
/// SQL filter based on whether the crate's name loosly matches the given
207+
/// SQL filter based on whether the crate's name loosely matches the given
208208
/// string.
209209
///
210210
/// The operator used varies based on the input.
@@ -281,13 +281,13 @@ impl Crate {
281281

282282
pub fn minimal_encodable(
283283
self,
284-
max_version: &semver::Version,
284+
top_versions: &TopVersions,
285285
badges: Option<Vec<Badge>>,
286286
exact_match: bool,
287287
recent_downloads: Option<i64>,
288288
) -> EncodableCrate {
289289
self.encodable(
290-
max_version,
290+
top_versions,
291291
None,
292292
None,
293293
None,
@@ -300,7 +300,7 @@ impl Crate {
300300
#[allow(clippy::too_many_arguments)]
301301
pub fn encodable(
302302
self,
303-
max_version: &semver::Version,
303+
top_versions: &TopVersions,
304304
versions: Option<Vec<i32>>,
305305
keywords: Option<&[Keyword]>,
306306
categories: Option<&[Category]>,
@@ -339,7 +339,8 @@ impl Crate {
339339
keywords: keyword_ids,
340340
categories: category_ids,
341341
badges,
342-
max_version: max_version.to_string(),
342+
max_version: top_versions.highest.to_string(),
343+
newest_version: top_versions.newest.to_string(),
343344
documentation,
344345
homepage,
345346
exact_match,
@@ -384,16 +385,16 @@ impl Crate {
384385
}
385386
}
386387

387-
pub fn max_version(&self, conn: &PgConnection) -> AppResult<semver::Version> {
388+
/// Return both the newest (most recently updated) and
389+
/// highest version (in semver order) for the current crate.
390+
pub fn top_versions(&self, conn: &PgConnection) -> AppResult<TopVersions> {
388391
use crate::schema::versions::dsl::*;
389392

390-
let vs = self
391-
.versions()
392-
.select(num)
393-
.load::<String>(conn)?
394-
.into_iter()
395-
.map(|s| semver::Version::parse(&s).unwrap());
396-
Ok(Version::max(vs))
393+
Ok(Version::top(
394+
self.versions()
395+
.select((updated_at, num))
396+
.load::<(NaiveDateTime, semver::Version)>(conn)?,
397+
))
397398
}
398399

399400
pub fn owners(&self, conn: &PgConnection) -> AppResult<Vec<Owner>> {

0 commit comments

Comments
 (0)