diff --git a/Gemfile b/Gemfile index 5a35cf1..cfc7df4 100644 --- a/Gemfile +++ b/Gemfile @@ -34,10 +34,15 @@ gem 'bootsnap', '>= 1.1.0', require: false gem 'faker' -gem 'cache_crispies', '~> 1.0.1' +gem 'cache_crispies', '~> 1.1.2' gem 'fast_jsonapi' +gem 'blueprinter' + +gem 'thread_safe' +gem 'active_model_serializers', '~> 0.10.0' + gem 'redis' group :development, :test do diff --git a/Gemfile.lock b/Gemfile.lock index b1e305c..0e4edef 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -37,6 +37,11 @@ GEM erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.1, >= 1.2.0) + active_model_serializers (0.10.10) + actionpack (>= 4.1, < 6.1) + activemodel (>= 4.1, < 6.1) + case_transform (>= 0.2) + jsonapi-renderer (>= 0.1.1.beta1, < 0.3) activejob (6.0.2) activesupport (= 6.0.2) globalid (>= 0.3.6) @@ -61,11 +66,12 @@ GEM archive-zip (0.12.0) io-like (~> 0.3.0) bindex (0.8.1) + blueprinter (0.25.1) bootsnap (1.4.5) msgpack (~> 1.0) builder (3.2.4) byebug (11.0.1) - cache_crispies (1.0.1) + cache_crispies (1.1.2) oj (~> 3.7) railties (>= 5.0.0, < 6.1) capybara (3.29.0) @@ -76,12 +82,14 @@ GEM rack-test (>= 0.6.3) regexp_parser (~> 1.5) xpath (~> 3.2) + case_transform (0.2) + activesupport childprocess (3.0.0) chromedriver-helper (2.1.1) archive-zip (~> 0.10) nokogiri (~> 1.8) - concurrent-ruby (1.1.5) - crass (1.0.5) + concurrent-ruby (1.1.7) + crass (1.0.6) erubi (1.9.0) execjs (2.7.0) faker (2.9.0) @@ -91,32 +99,33 @@ GEM ffi (1.11.3) globalid (0.4.2) activesupport (>= 4.2.0) - i18n (1.7.0) + i18n (1.7.1) concurrent-ruby (~> 1.0) io-like (0.3.0) jbuilder (2.9.1) activesupport (>= 4.2.0) + jsonapi-renderer (0.2.2) listen (3.1.5) rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) ruby_dep (~> 1.2) - loofah (2.4.0) + loofah (2.7.0) crass (~> 1.0.2) nokogiri (>= 1.5.9) mail (2.7.1) mini_mime (>= 0.1.1) marcel (0.3.3) mimemagic (~> 0.3.2) - method_source (0.9.2) + method_source (1.0.0) mimemagic (0.3.3) mini_mime (1.0.2) mini_portile2 (2.4.0) - minitest (5.13.0) + minitest (5.14.2) msgpack (1.3.1) nio4r (2.5.2) - nokogiri (1.10.8) + nokogiri (1.10.10) mini_portile2 (~> 2.4.0) - oj (3.10.0) + oj (3.10.14) public_suffix (4.0.1) puma (3.12.6) rack (2.2.3) @@ -184,7 +193,7 @@ GEM thor (1.0.1) thread_safe (0.3.6) tilt (2.0.10) - tzinfo (1.2.5) + tzinfo (1.2.7) thread_safe (~> 0.1) uglifier (4.2.0) execjs (>= 0.3.0, < 3) @@ -198,15 +207,17 @@ GEM websocket-extensions (0.1.5) xpath (3.2.0) nokogiri (~> 1.8) - zeitwerk (2.2.2) + zeitwerk (2.4.0) PLATFORMS ruby DEPENDENCIES + active_model_serializers (~> 0.10.0) + blueprinter bootsnap (>= 1.1.0) byebug - cache_crispies (~> 1.0.1) + cache_crispies (~> 1.1.2) capybara (>= 2.15) chromedriver-helper faker @@ -221,6 +232,7 @@ DEPENDENCIES spring spring-watcher-listen (~> 2.0.0) sqlite3 + thread_safe tzinfo-data uglifier (>= 1.3.0) web-console (>= 3.3.0) diff --git a/README.md b/README.md index 7df5813..f66b327 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,16 @@ ab -n 15 -c 1 "http://localhost:3042/courses/fast_jsonapi" ab -n 15 -c 1 "http://localhost:3042/courses/jbuilder" ``` +**Blueprinter** +```shell +ab -n 15 -c 1 "http://localhost:3042/courses/blueprinter" +``` + +**ActiveModelSerializers (0.10 with Attributes Adapter)** +```shell +ab -n 15 -c 1 "http://localhost:3042/courses/active_model_serializer" +``` + Latest Results -------------- @@ -82,7 +92,7 @@ Versions: ### Cache Crispies (cached) ``` -This is ApacheBench, Version 2.3 <$Revision: 1826891 $> +This is ApacheBench, Version 2.3 <$Revision: 1843412 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ @@ -94,36 +104,36 @@ Server Hostname: localhost Server Port: 3042 Document Path: /courses/cache_crispies_cached -Document Length: 887062 bytes +Document Length: 890509 bytes Concurrency Level: 1 -Time taken for tests: 1.235 seconds +Time taken for tests: 1.045 seconds Complete requests: 15 Failed requests: 0 -Total transferred: 13312665 bytes -HTML transferred: 13305930 bytes -Requests per second: 12.14 [#/sec] (mean) -Time per request: 82.350 [ms] (mean) -Time per request: 82.350 [ms] (mean, across all concurrent requests) -Transfer rate: 10524.76 [Kbytes/sec] received +Total transferred: 13364370 bytes +HTML transferred: 13357635 bytes +Requests per second: 14.35 [#/sec] (mean) +Time per request: 69.677 [ms] (mean) +Time per request: 69.677 [ms] (mean, across all concurrent requests) +Transfer rate: 12487.27 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 0 0.0 0 0 -Processing: 55 82 23.5 76 162 -Waiting: 55 81 23.5 76 162 -Total: 56 82 23.5 76 162 +Processing: 63 69 10.0 66 95 +Waiting: 63 69 10.0 65 95 +Total: 64 70 10.0 66 95 Percentage of the requests served within a certain time (ms) - 50% 76 - 66% 77 - 75% 83 - 80% 87 - 90% 89 - 95% 162 - 98% 162 - 99% 162 - 100% 162 (longest request) + 50% 66 + 66% 66 + 75% 70 + 80% 71 + 90% 92 + 95% 95 + 98% 95 + 99% 95 + 100% 95 (longest request) ``` _Note that caching is enabled for these serializers, so it's not really a fair comparison to the others below, but we're including it here anyway_ @@ -133,50 +143,47 @@ This is ApacheBench, Version 2.3 <$Revision: 1826891 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ -Benchmarking localhost (be patient).....done - - Server Software: Server Hostname: localhost Server Port: 3042 Document Path: /courses/cache_crispies -Document Length: 887062 bytes +Document Length: 890509 bytes Concurrency Level: 1 -Time taken for tests: 7.741 seconds +Time taken for tests: 7.680 seconds Complete requests: 15 Failed requests: 0 -Total transferred: 13312665 bytes -HTML transferred: 13305930 bytes -Requests per second: 1.94 [#/sec] (mean) -Time per request: 516.065 [ms] (mean) -Time per request: 516.065 [ms] (mean, across all concurrent requests) -Transfer rate: 1679.46 [Kbytes/sec] received +Total transferred: 13364370 bytes +HTML transferred: 13357635 bytes +Requests per second: 1.95 [#/sec] (mean) +Time per request: 511.986 [ms] (mean) +Time per request: 511.986 [ms] (mean, across all concurrent requests) +Transfer rate: 1699.41 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 0 0.0 0 0 -Processing: 437 516 59.0 517 611 -Waiting: 436 515 59.1 517 611 -Total: 437 516 59.0 517 611 +Processing: 399 512 63.8 542 596 +Waiting: 399 511 63.8 542 596 +Total: 399 512 63.8 542 596 Percentage of the requests served within a certain time (ms) - 50% 510 - 66% 546 - 75% 572 - 80% 587 - 90% 588 - 95% 611 - 98% 611 - 99% 611 - 100% 611 (longest request) + 50% 539 + 66% 550 + 75% 563 + 80% 577 + 90% 593 + 95% 596 + 98% 596 + 99% 596 + 100% 596 (longest request) ``` _Note that caching is not enabled for these serializers, so there's no cheating going on there_ ### Fast JSONAPI ``` -This is ApacheBench, Version 2.3 <$Revision: 1826891 $> +This is ApacheBench, Version 2.3 <$Revision: 1843412 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ @@ -188,42 +195,42 @@ Server Hostname: localhost Server Port: 3042 Document Path: /courses/fast_jsonapi -Document Length: 1255879 bytes +Document Length: 1258072 bytes Concurrency Level: 1 -Time taken for tests: 12.530 seconds +Time taken for tests: 12.196 seconds Complete requests: 15 Failed requests: 0 -Total transferred: 18844920 bytes -HTML transferred: 18838185 bytes -Requests per second: 1.20 [#/sec] (mean) -Time per request: 835.350 [ms] (mean) -Time per request: 835.350 [ms] (mean, across all concurrent requests) -Transfer rate: 1468.71 [Kbytes/sec] received +Total transferred: 18877815 bytes +HTML transferred: 18871080 bytes +Requests per second: 1.23 [#/sec] (mean) +Time per request: 813.083 [ms] (mean) +Time per request: 813.083 [ms] (mean, across all concurrent requests) +Transfer rate: 1511.56 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max -Connect: 0 0 0.0 0 0 -Processing: 769 835 50.7 863 897 -Waiting: 769 835 50.7 862 896 -Total: 770 835 50.7 863 897 +Connect: 0 0 0.1 0 0 +Processing: 799 813 11.5 814 834 +Waiting: 798 812 11.5 813 834 +Total: 799 813 11.5 814 834 Percentage of the requests served within a certain time (ms) - 50% 859 - 66% 874 - 75% 885 - 80% 889 - 90% 895 - 95% 897 - 98% 897 - 99% 897 - 100% 897 (longest request) + 50% 813 + 66% 815 + 75% 823 + 80% 824 + 90% 832 + 95% 834 + 98% 834 + 99% 834 + 100% 834 (longest request) ``` _Note that because Fast JSON API only supports the JSON:API standard, the exact format doesn't match the others. But all of the same data should still be included in the response._ ### Jbuilder ``` -This is ApacheBench, Version 2.3 <$Revision: 1826891 $> +This is ApacheBench, Version 2.3 <$Revision: 1843412 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ @@ -235,36 +242,127 @@ Server Hostname: localhost Server Port: 3042 Document Path: /courses/jbuilder -Document Length: 887062 bytes +Document Length: 890509 bytes + +Concurrency Level: 1 +Time taken for tests: 17.045 seconds +Complete requests: 15 +Failed requests: 0 +Total transferred: 13364370 bytes +HTML transferred: 13357635 bytes +Requests per second: 0.88 [#/sec] (mean) +Time per request: 1136.317 [ms] (mean) +Time per request: 1136.317 [ms] (mean, across all concurrent requests) +Transfer rate: 765.70 [Kbytes/sec] received + +Connection Times (ms) + min mean[+/-sd] median max +Connect: 0 0 0.0 0 0 +Processing: 993 1136 162.5 1049 1378 +Waiting: 993 1136 162.5 1049 1377 +Total: 993 1136 162.5 1049 1378 + +Percentage of the requests served within a certain time (ms) + 50% 1049 + 66% 1077 + 75% 1348 + 80% 1355 + 90% 1362 + 95% 1378 + 98% 1378 + 99% 1378 +``` + +### Blueprinter +``` +This is ApacheBench, Version 2.3 <$Revision: 1843412 $> +Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ +Licensed to The Apache Software Foundation, http://www.apache.org/ + +Benchmarking localhost (be patient).....done + + +Server Software: +Server Hostname: localhost +Server Port: 3042 + +Document Path: /courses/blueprinter +Document Length: 890497 bytes + +Concurrency Level: 1 +Time taken for tests: 9.779 seconds +Complete requests: 15 +Failed requests: 0 +Total transferred: 13364190 bytes +HTML transferred: 13357455 bytes +Requests per second: 1.53 [#/sec] (mean) +Time per request: 651.904 [ms] (mean) +Time per request: 651.904 [ms] (mean, across all concurrent requests) +Transfer rate: 1334.65 [Kbytes/sec] received + +Connection Times (ms) + min mean[+/-sd] median max +Connect: 0 0 0.0 0 0 +Processing: 517 652 85.0 646 822 +Waiting: 517 651 85.0 645 821 +Total: 517 652 85.0 646 822 + +Percentage of the requests served within a certain time (ms) + 50% 634 + 66% 659 + 75% 729 + 80% 752 + 90% 762 + 95% 822 + 98% 822 + 99% 822 + 100% 822 (longest request) +``` + +### ActiveModelSerializers (0.10 with "Attributes Adapter") +``` +This is ApacheBench, Version 2.3 <$Revision: 1843412 $> +Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ +Licensed to The Apache Software Foundation, http://www.apache.org/ + +Benchmarking localhost (be patient).....done + + +Server Software: +Server Hostname: localhost +Server Port: 3042 + +Document Path: /courses/active_model_serializer +Document Length: 929777 bytes Concurrency Level: 1 -Time taken for tests: 16.607 seconds +Time taken for tests: 13.817 seconds Complete requests: 15 Failed requests: 0 -Total transferred: 13312665 bytes -HTML transferred: 13305930 bytes -Requests per second: 0.90 [#/sec] (mean) -Time per request: 1107.140 [ms] (mean) -Time per request: 1107.140 [ms] (mean, across all concurrent requests) -Transfer rate: 782.84 [Kbytes/sec] received +Total transferred: 13953390 bytes +HTML transferred: 13946655 bytes +Requests per second: 1.09 [#/sec] (mean) +Time per request: 921.155 [ms] (mean) +Time per request: 921.155 [ms] (mean, across all concurrent requests) +Transfer rate: 986.18 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 0 0.0 0 0 -Processing: 1020 1107 75.8 1153 1198 -Waiting: 1019 1107 75.7 1152 1198 -Total: 1020 1107 75.8 1153 1198 +Processing: 841 921 76.7 906 1040 +Waiting: 841 921 76.7 906 1040 +Total: 841 921 76.7 906 1041 Percentage of the requests served within a certain time (ms) - 50% 1150 - 66% 1161 - 75% 1177 - 80% 1182 - 90% 1197 - 95% 1198 - 98% 1198 - 99% 1198 - 100% 1198 (longest request) + 50% 904 + 66% 985 + 75% 997 + 80% 999 + 90% 1034 + 95% 1041 + 98% 1041 + 99% 1041 + 100% 1041 (longest request) ``` Contributing diff --git a/app/ams_serializers/course_ams_serializer.rb b/app/ams_serializers/course_ams_serializer.rb new file mode 100644 index 0000000..1760bc8 --- /dev/null +++ b/app/ams_serializers/course_ams_serializer.rb @@ -0,0 +1,16 @@ +class CourseAmsSerializer < ActiveModel::Serializer + attributes( + :id, + :title, + :minutes, + :published, + :created_at, + :updated_at + ) + + has_many :slides, serializer: SlideAmsSerializer, if: -> { object.published? } + + def id + object.id.to_s + end +end diff --git a/app/ams_serializers/slide_ams_serializer.rb b/app/ams_serializers/slide_ams_serializer.rb new file mode 100644 index 0000000..c94e766 --- /dev/null +++ b/app/ams_serializers/slide_ams_serializer.rb @@ -0,0 +1,13 @@ +class SlideAmsSerializer < ActiveModel::Serializer + attributes( + :id, + :content, + :order, + :created_at, + :updated_at + ) + + def id + object.id.to_s + end +end diff --git a/app/blueprints/course_blueprint.rb b/app/blueprints/course_blueprint.rb new file mode 100644 index 0000000..82410f9 --- /dev/null +++ b/app/blueprints/course_blueprint.rb @@ -0,0 +1,15 @@ +class CourseBlueprint < Blueprinter::Base + identifier :id do |course, _options| + course.id.to_s + end + + fields( + :title, + :minutes, + :published, + :created_at, + :updated_at + ) + + association :slides, blueprint: SlideBlueprint, if: ->(_field_name, course, _options) { course.published? } +end diff --git a/app/blueprints/slide_blueprint.rb b/app/blueprints/slide_blueprint.rb new file mode 100644 index 0000000..dd347a6 --- /dev/null +++ b/app/blueprints/slide_blueprint.rb @@ -0,0 +1,12 @@ +class SlideBlueprint < Blueprinter::Base + identifier :id do |slide, _options| + slide.id.to_s + end + + fields( + :content, + :order, + :created_at, + :updated_at + ) +end diff --git a/app/controllers/courses_controller.rb b/app/controllers/courses_controller.rb index d84632c..7518c8d 100644 --- a/app/controllers/courses_controller.rb +++ b/app/controllers/courses_controller.rb @@ -23,6 +23,14 @@ def fast_jsonapi ) end + def blueprinter + render(json: CourseBlueprint.render_as_json(@courses)) + end + + def active_model_serializer + render json: @courses, each_serializer: CourseAmsSerializer, root: false + end + private def set_courses diff --git a/app/views/courses/jbuilder.json.jbuilder b/app/views/courses/jbuilder.json.jbuilder index 4e53a72..5a9476c 100644 --- a/app/views/courses/jbuilder.json.jbuilder +++ b/app/views/courses/jbuilder.json.jbuilder @@ -6,8 +6,8 @@ json.courses @courses do |course| json.created_at course.created_at.iso8601 json.updated_at course.updated_at.iso8601 if course.published? - json.slides course.slides do |slide| - json.partial! 'slides/slide', slide: slide + json.slides do + json.array!(course.slides, partial: 'slides/slide', as: :slide) end end end diff --git a/config/initializers/blueprinter.rb b/config/initializers/blueprinter.rb new file mode 100644 index 0000000..dc207f2 --- /dev/null +++ b/config/initializers/blueprinter.rb @@ -0,0 +1,7 @@ +require 'oj' # you can skip this if OJ has already been required. + +Blueprinter.configure do |config| + config.generator = Oj # default is JSON + config.datetime_format = ->(datetime) { datetime.nil? ? datetime : datetime.iso8601 } + +end diff --git a/config/routes.rb b/config/routes.rb index bdb93d4..c01faaa 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -5,6 +5,8 @@ get 'cache_crispies' get 'cache_crispies_cached' get 'fast_jsonapi' + get 'blueprinter' + get 'active_model_serializer' end end end