diff --git a/.dart_tool/pub/bin/sdk-version b/.dart_tool/pub/bin/sdk-version new file mode 100644 index 00000000..7ec1d6db --- /dev/null +++ b/.dart_tool/pub/bin/sdk-version @@ -0,0 +1 @@ +2.1.0 diff --git a/.dart_tool/pub/bin/test/test.dart.snapshot.dart2 b/.dart_tool/pub/bin/test/test.dart.snapshot.dart2 new file mode 100644 index 00000000..cffff604 Binary files /dev/null and b/.dart_tool/pub/bin/test/test.dart.snapshot.dart2 differ diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..6d5318af --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,2 @@ +# 2.0.0 +* Dart2 + Angel2 update. \ No newline at end of file diff --git a/README.md b/README.md index edcc143e..d7e67c23 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ # paginate -[![version 1.0.0+3](https://img.shields.io/badge/pub-v1.0.0+3-brightgreen.svg)](https://pub.dartlang.org/packages/angel_paginate) +[![Pub](https://img.shields.io/pub/v/angel_paginate.svg)](https://pub.dartlang.org/packages/angel_paginate) [![build status](https://travis-ci.org/angel-dart/paginate.svg)](https://travis-ci.org/angel-dart/paginate) -![coverage: 100%](https://img.shields.io/badge/coverage-100%25-green.svg) Platform-agnostic pagination library, with custom support for the [Angel framework](https://github.com/angel-dart/angel). @@ -11,7 +10,7 @@ In your `pubspec.yaml` file: ```yaml dependencies: - angel_paginate: ^1.0.0 + angel_paginate: ^2.0.0 ``` # Usage @@ -58,38 +57,4 @@ The entire Paginator API is documented, so check out the DartDocs. Paginators by default cache paginations, to improve performance as you shift through pages. This can be especially helpful in a client-side application where your UX involves a fast -response time, i.e. a search page. - -## Use With Angel -Naturally, a library named `angel_paginate` has special provisions for the -[Angel framework](https://github.com/angel-dart/angel). - -In `package:angel_paginate/server.dart`, a function called `paginate` generates -pagination service hooks for you. If the result of a hooked service event is an `Iterable`, -it will be paginated. This is convenient because it works with any data store, whether it -be MongoDB, RethinkDB, an in-memory store, or something else entirely. - -```dart -configureServer(Angel app) { - var service = app.service('api/foo') as HookedService; - service.afterIndexed.listen(paginate(itemsPerPage: 10)); -} -``` - -See `test/server_test.dart` for examples of usage with Angel. - -The pagination hook also allows you to provide a `page` and/or `$limit` in the query. -If the user provides a `page` in the query, it will return a pagination of the given page. - -Ex. `http://mysite.com/api/todos?page=4` - -A `$limit` can be used to override the `itemsPerPage` set in the `paginate` hook. If you -would like to set a maximum on the number of items per page, you can set `maxItemsPerPage` -in the `paginate` call. - -Ex. `http://mysite.com/api/todos?$limit=25` - -You can use these pagination functions to provide powerful search experiences on your websites. - -**NOTE**: If the paginated data is empty, expect `start_index` and `end_index` -to both be `-1`. \ No newline at end of file +response time, i.e. a search page. \ No newline at end of file diff --git a/example/main.dart b/example/main.dart new file mode 100644 index 00000000..eb83cd03 --- /dev/null +++ b/example/main.dart @@ -0,0 +1,15 @@ +import 'package:angel_paginate/angel_paginate.dart'; + +main() { + var iterable = [1, 2, 3, 4]; + var p = new Paginator(iterable); + + // Get the current page (default: page 1) + var page = p.current; + print(page.total); + print(page.startIndex); + print(page.data); // The actual items on this page. + p.next(); // Advance a page + p.back(); // Back one page + p.goToPage(10); // Go to page number (1-based, not a 0-based index) +} diff --git a/lib/angel_paginate.dart b/lib/angel_paginate.dart index 7d40937b..34c34a25 100644 --- a/lib/angel_paginate.dart +++ b/lib/angel_paginate.dart @@ -57,8 +57,8 @@ class Paginator { nextPage: _page >= last - 1 ? -1 : _page + 2, startIndex: it.isEmpty ? -1 : offset, endIndex: offset + it.length - 1, - itemsPerPage: itemsPerPage < _items.length ? itemsPerPage : _items - .length, + itemsPerPage: + itemsPerPage < _items.length ? itemsPerPage : _items.length, total: len); } @@ -110,7 +110,7 @@ class Paginator { /// Stores the result of a pagination. abstract class PaginationResult { factory PaginationResult.fromMap(Map map) => - new _PaginationResultImpl(map['data'], + new _PaginationResultImpl((map['data'] as Iterable).cast(), currentPage: map['current_page'], endIndex: map['end_index'], itemsPerPage: map['items_per_page'], @@ -147,12 +147,12 @@ class _PaginationResultImpl implements PaginationResult { _PaginationResultImpl(this._data, {this.currentPage, - this.endIndex, - this.itemsPerPage, - this.nextPage, - this.previousPage, - this.startIndex, - this.total}); + this.endIndex, + this.itemsPerPage, + this.nextPage, + this.previousPage, + this.startIndex, + this.total}); @override List get data => _cachedData ?? (_cachedData = new List.from(_data)); diff --git a/lib/server.dart b/lib/server.dart deleted file mode 100644 index a4311b2f..00000000 --- a/lib/server.dart +++ /dev/null @@ -1,49 +0,0 @@ -import 'package:angel_framework/angel_framework.dart'; -import 'angel_paginate.dart'; -export 'angel_paginate.dart'; - -/// Paginates the results of service events. -/// -/// Users can add a `page` to the query to display a certain page, i.e. `http://foo.com/api/todos?page=5`. -/// -/// Users can also add a `$limit` to the query to display more or less items than specified in [itemsPerPage] (default: `5`). -/// If [maxItemsPerPage] is set, then even if the query contains a `$limit` parameter, it will be limited to the maximum. -HookedServiceEventListener paginate( - {int itemsPerPage, int maxItemsPerPage}) { - return (HookedServiceEvent e) { - if (e.isBefore) throw new UnsupportedError( - '`package:angel_paginate` can only be run as an after hook.'); - if (e.result is! Iterable) return; - - int page = 1, - nItems = itemsPerPage; - - if (e.params.containsKey('query') && e.params['query'] is Map) { - var query = e.params['query'] as Map; - - if (query.containsKey('page')) { - try { - page = int.parse(query['page']?.toString()); - } catch (e) { - // Fail silently... - } - } - - if (query.containsKey(r'$limit')) { - try { - var lim = int.parse(query[r'$limit']?.toString()); - if (lim > 0 && (maxItemsPerPage == null || lim <= maxItemsPerPage)) - nItems = lim; - } catch (e) { - // Fail silently... - } - } - } - - - var paginator = new Paginator( - e.result, itemsPerPage: nItems) - ..goToPage(page); - e.result = paginator.current; - }; -} \ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml index ba8697e8..e57dcd15 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,13 +1,13 @@ name: angel_paginate -version: 1.0.0+3 +version: 2.0.0 description: Platform-agnostic pagination library, with custom support for the Angel framework. author: Tobe O homepage: https://github.com/angel-dart/paginate environment: - sdk: ">=1.19.0" + sdk: ">=2.0.0-dev <3.0.0" dependencies: - angel_framework: ^1.0.0 + angel_framework: ^2.0.0-alpha dev_dependencies: - angel_diagnostics: ^1.0.0 - angel_test: ^1.0.0 - test: ^0.12.15 \ No newline at end of file + angel_test: ^2.0.0 + logging: ^0.11.0 + test: ^1.0.0 \ No newline at end of file diff --git a/test/all_test.dart b/test/all_test.dart index c071834b..c9ba44be 100644 --- a/test/all_test.dart +++ b/test/all_test.dart @@ -1,10 +1,8 @@ import 'package:test/test.dart'; import 'bounds_test.dart' as bounds; import 'paginate_test.dart' as paginate; -import 'server_test.dart' as server; main() { group('bounds', bounds.main); group('paginate', paginate.main); - group('server', server.main); -} \ No newline at end of file +} diff --git a/test/bounds_test.dart b/test/bounds_test.dart index f5185fc0..b799f9bb 100644 --- a/test/bounds_test.dart +++ b/test/bounds_test.dart @@ -1,11 +1,11 @@ -import 'package:angel_client/angel_client.dart' as c; -import 'package:angel_diagnostics/angel_diagnostics.dart'; +import 'dart:convert'; import 'package:angel_framework/angel_framework.dart'; -import 'package:angel_paginate/server.dart'; +import 'package:angel_paginate/angel_paginate.dart'; import 'package:angel_test/angel_test.dart'; +import 'package:logging/logging.dart'; import 'package:test/test.dart'; -final List> DATA = [ +final List> mjAlbums = [ {'billie': 'jean'}, {'off': 'the_wall'}, {'michael': 'jackson'} @@ -13,37 +13,45 @@ final List> DATA = [ main() { TestClient client; - c.Service songService; setUp(() async { - var app = new Angel() - ..use('/api/songs', new AnonymousService(index: ([p]) async => DATA)); - var service = app.service('api/songs') as HookedService; - service.afterIndexed.listen(paginate(itemsPerPage: 2)); - await app.configure(logRequests()); + var app = new Angel(); + + app.get('/api/songs', (req, res) { + var p = Paginator(mjAlbums, itemsPerPage: mjAlbums.length); + p.goToPage(int.parse(req.queryParameters['page'] ?? '1')); + return p.current; + }); + client = await connectTo(app); - songService = client.service('api/songs'); + + app.logger = Logger('angel_paginate') + ..onRecord.listen((rec) { + print(rec); + if (rec.error != null) print(rec.error); + if (rec.stackTrace != null) print(rec.stackTrace); + }); }); tearDown(() => client.close()); test('limit exceeds size of collection', () async { - var response = await songService.index({ - 'query': { - r'$limit': DATA.length + 1 - } - }); + var response = await client.get(Uri( + path: '/api/songs', + queryParameters: {r'$limit': (mjAlbums.length + 1).toString()})); + + var page = new PaginationResult>.fromMap( + json.decode(response.body)); - var page = new PaginationResult>.fromMap(response); print('page: ${page.toJson()}'); - expect(page.total, DATA.length); - expect(page.itemsPerPage, DATA.length); + expect(page.total, mjAlbums.length); + expect(page.itemsPerPage, mjAlbums.length); expect(page.previousPage, -1); expect(page.currentPage, 1); expect(page.nextPage, -1); expect(page.startIndex, 0); - expect(page.endIndex, DATA.length - 1); - expect(page.data, DATA); + expect(page.endIndex, mjAlbums.length - 1); + expect(page.data, mjAlbums); }); } diff --git a/test/paginate_test.dart b/test/paginate_test.dart index c682e902..02a85b5c 100644 --- a/test/paginate_test.dart +++ b/test/paginate_test.dart @@ -2,8 +2,7 @@ import 'package:angel_paginate/angel_paginate.dart'; import 'package:test/test.dart'; // Count-down from 100, then 101 at the end... -final List DATA = new List.generate(100, (i) => 100 - i) - ..add(101); +final List DATA = new List.generate(100, (i) => 100 - i)..add(101); main() { group('cache', () { @@ -68,8 +67,7 @@ main() { }); test('third page', () { - var paginator = new Paginator(DATA) - ..goToPage(3); + var paginator = new Paginator(DATA)..goToPage(3); expect(paginator.pageNumber, 3); var r = paginator.current; print(r.toJson()); @@ -108,7 +106,7 @@ main() { do { print(' * Page #${paginator.pageNumber}: ${paginator.current.data}'); paginator.next(); - } while(paginator.canGoForward); + } while (paginator.canGoForward); }); test('empty collection', () { diff --git a/test/server_test.dart b/test/server_test.dart deleted file mode 100644 index ca714e7c..00000000 --- a/test/server_test.dart +++ /dev/null @@ -1,115 +0,0 @@ -import 'package:angel_client/angel_client.dart' as c; -import 'package:angel_diagnostics/angel_diagnostics.dart'; -import 'package:angel_framework/angel_framework.dart'; -import 'package:angel_paginate/server.dart'; -import 'package:angel_test/angel_test.dart'; -import 'package:test/test.dart'; - -final List DATA = new List.filled(50, {'foo': 'bar'}, growable: true) - ..addAll(new List.filled(25, {'bar': 'baz'})); - -main() { - group('no max', () { - Angel app; - TestClient client; - c.Service dataService; - - setUp(() async { - app = new Angel() - ..use('/data', new AnonymousService(index: ([p]) async => DATA)); - await app.configure(logRequests()); - var service = app.service('data') as HookedService; - service.afterIndexed.listen(paginate(itemsPerPage: 10)); - - client = await connectTo(app); - dataService = client.service('data'); - }); - - tearDown(() => client.close()); - - test('first page', () async { - var response = await dataService - .index() - .then((m) => new PaginationResult.fromMap(m)); - print(response.toJson()); - - expect(response.total, DATA.length); - expect(response.itemsPerPage, 10); - expect(response.startIndex, 0); - expect(response.endIndex, 9); - expect(response.previousPage, -1); - expect(response.currentPage, 1); - expect(response.nextPage, 2); - expect(response.data, DATA.take(10).toList()); - }); - - test('third page', () async { - var response = await dataService.index({ - 'query': {'page': 3} - }).then((m) => new PaginationResult.fromMap(m)); - print(response.toJson()); - - expect(response.total, DATA.length); - expect(response.itemsPerPage, 10); - expect(response.startIndex, 20); - expect(response.endIndex, 29); - expect(response.previousPage, 2); - expect(response.currentPage, 3); - expect(response.nextPage, 4); - expect(response.data, DATA.skip(20).take(10).toList()); - }); - - test('custom limit', () async { - var response = await dataService.index({ - 'query': {'page': 4, r'$limit': 5} - }).then((m) => new PaginationResult.fromMap(m)); - print(response.toJson()); - - expect(response.total, DATA.length); - expect(response.itemsPerPage, 5); - expect(response.startIndex, 15); - expect(response.endIndex, 19); - expect(response.previousPage, 3); - expect(response.currentPage, 4); - expect(response.nextPage, 5); - expect(response.data, DATA.skip(15).take(5).toList()); - }); - }); - - group('max 15', () { - Angel app; - TestClient client; - c.Service dataService; - - setUp(() async { - app = new Angel() - ..use('/data', new AnonymousService(index: ([p]) async => DATA)); - await app.configure(logRequests()); - var service = app.service('data') as HookedService; - service.afterIndexed.listen( - paginate(itemsPerPage: 10, maxItemsPerPage: 15)); - - client = await connectTo(app); - dataService = client.service('data'); - }); - - tearDown(() => client.close()); - - test('exceed max', () async { - var response = await dataService.index({ - 'query': {'page': 4, r'$limit': 30} - }).then((m) => new PaginationResult.fromMap(m)); - print(response.toJson()); - - // Should default to 10 items per page :) - expect(response.total, DATA.length); - expect(response.itemsPerPage, 10); - expect(response.startIndex, 30); - expect(response.endIndex, 39); - expect(response.previousPage, 3); - expect(response.currentPage, 4); - expect(response.nextPage, 5); - expect(response.data, DATA.skip(30).take(10).toList()); - }); - }); -}