active_model_serializers 0.8.3 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (232) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE.md +29 -0
  3. data/.github/PULL_REQUEST_TEMPLATE.md +15 -0
  4. data/.gitignore +17 -0
  5. data/.rubocop.yml +104 -0
  6. data/.rubocop_todo.yml +167 -0
  7. data/.simplecov +110 -0
  8. data/.travis.yml +39 -24
  9. data/CHANGELOG.md +465 -6
  10. data/CONTRIBUTING.md +105 -0
  11. data/Gemfile +50 -1
  12. data/{MIT-LICENSE.txt → MIT-LICENSE} +3 -2
  13. data/README.md +102 -590
  14. data/Rakefile +93 -8
  15. data/active_model_serializers.gemspec +65 -23
  16. data/appveyor.yml +24 -0
  17. data/bin/bench +171 -0
  18. data/bin/bench_regression +316 -0
  19. data/bin/serve_benchmark +39 -0
  20. data/docs/ARCHITECTURE.md +126 -0
  21. data/docs/README.md +40 -0
  22. data/docs/STYLE.md +58 -0
  23. data/docs/general/adapters.md +245 -0
  24. data/docs/general/caching.md +52 -0
  25. data/docs/general/configuration_options.md +100 -0
  26. data/docs/general/deserialization.md +100 -0
  27. data/docs/general/getting_started.md +133 -0
  28. data/docs/general/instrumentation.md +40 -0
  29. data/docs/general/key_transforms.md +40 -0
  30. data/docs/general/logging.md +14 -0
  31. data/docs/general/rendering.md +255 -0
  32. data/docs/general/serializers.md +372 -0
  33. data/docs/how-open-source-maintained.jpg +0 -0
  34. data/docs/howto/add_pagination_links.md +139 -0
  35. data/docs/howto/add_root_key.md +51 -0
  36. data/docs/howto/outside_controller_use.md +58 -0
  37. data/docs/howto/passing_arbitrary_options.md +27 -0
  38. data/docs/howto/serialize_poro.md +32 -0
  39. data/docs/howto/test.md +152 -0
  40. data/docs/integrations/ember-and-json-api.md +112 -0
  41. data/docs/integrations/grape.md +19 -0
  42. data/docs/jsonapi/errors.md +56 -0
  43. data/docs/jsonapi/schema/schema.json +366 -0
  44. data/docs/jsonapi/schema.md +151 -0
  45. data/docs/rfcs/0000-namespace.md +106 -0
  46. data/docs/rfcs/template.md +15 -0
  47. data/lib/action_controller/serialization.rb +31 -36
  48. data/lib/active_model/serializable_resource.rb +11 -0
  49. data/lib/active_model/serializer/adapter/attributes.rb +15 -0
  50. data/lib/active_model/serializer/adapter/base.rb +16 -0
  51. data/lib/active_model/serializer/adapter/json.rb +15 -0
  52. data/lib/active_model/serializer/adapter/json_api.rb +15 -0
  53. data/lib/active_model/serializer/adapter/null.rb +15 -0
  54. data/lib/active_model/serializer/adapter.rb +24 -0
  55. data/lib/active_model/serializer/array_serializer.rb +9 -0
  56. data/lib/active_model/serializer/association.rb +19 -0
  57. data/lib/active_model/serializer/associations.rb +87 -220
  58. data/lib/active_model/serializer/attribute.rb +25 -0
  59. data/lib/active_model/serializer/attributes.rb +82 -0
  60. data/lib/active_model/serializer/belongs_to_reflection.rb +10 -0
  61. data/lib/active_model/serializer/caching.rb +333 -0
  62. data/lib/active_model/serializer/collection_reflection.rb +7 -0
  63. data/lib/active_model/serializer/collection_serializer.rb +64 -0
  64. data/lib/active_model/serializer/configuration.rb +35 -0
  65. data/lib/active_model/serializer/error_serializer.rb +10 -0
  66. data/lib/active_model/serializer/errors_serializer.rb +27 -0
  67. data/lib/active_model/serializer/field.rb +90 -0
  68. data/lib/active_model/serializer/fieldset.rb +31 -0
  69. data/lib/active_model/serializer/has_many_reflection.rb +10 -0
  70. data/lib/active_model/serializer/has_one_reflection.rb +10 -0
  71. data/lib/active_model/serializer/include_tree.rb +111 -0
  72. data/lib/active_model/serializer/links.rb +35 -0
  73. data/lib/active_model/serializer/lint.rb +146 -0
  74. data/lib/active_model/serializer/meta.rb +29 -0
  75. data/lib/active_model/serializer/null.rb +17 -0
  76. data/lib/active_model/serializer/reflection.rb +147 -0
  77. data/lib/active_model/serializer/singular_reflection.rb +7 -0
  78. data/lib/active_model/serializer/type.rb +25 -0
  79. data/lib/active_model/{serializers → serializer}/version.rb +1 -1
  80. data/lib/active_model/serializer.rb +158 -481
  81. data/lib/active_model_serializers/adapter/attributes.rb +76 -0
  82. data/lib/active_model_serializers/adapter/base.rb +83 -0
  83. data/lib/active_model_serializers/adapter/json.rb +21 -0
  84. data/lib/active_model_serializers/adapter/json_api/deserialization.rb +213 -0
  85. data/lib/active_model_serializers/adapter/json_api/error.rb +96 -0
  86. data/lib/active_model_serializers/adapter/json_api/jsonapi.rb +49 -0
  87. data/lib/active_model_serializers/adapter/json_api/link.rb +83 -0
  88. data/lib/active_model_serializers/adapter/json_api/meta.rb +37 -0
  89. data/lib/active_model_serializers/adapter/json_api/pagination_links.rb +62 -0
  90. data/lib/active_model_serializers/adapter/json_api/relationship.rb +52 -0
  91. data/lib/active_model_serializers/adapter/json_api/resource_identifier.rb +37 -0
  92. data/lib/active_model_serializers/adapter/json_api.rb +516 -0
  93. data/lib/active_model_serializers/adapter/null.rb +9 -0
  94. data/lib/active_model_serializers/adapter.rb +92 -0
  95. data/lib/active_model_serializers/callbacks.rb +55 -0
  96. data/lib/active_model_serializers/deprecate.rb +55 -0
  97. data/lib/active_model_serializers/deserialization.rb +13 -0
  98. data/lib/active_model_serializers/json_pointer.rb +14 -0
  99. data/lib/active_model_serializers/key_transform.rb +70 -0
  100. data/lib/active_model_serializers/logging.rb +122 -0
  101. data/lib/active_model_serializers/model.rb +49 -0
  102. data/lib/active_model_serializers/railtie.rb +46 -0
  103. data/lib/active_model_serializers/register_jsonapi_renderer.rb +65 -0
  104. data/lib/active_model_serializers/serializable_resource.rb +81 -0
  105. data/lib/active_model_serializers/serialization_context.rb +32 -0
  106. data/lib/active_model_serializers/test/schema.rb +138 -0
  107. data/lib/active_model_serializers/test/serializer.rb +125 -0
  108. data/lib/active_model_serializers/test.rb +7 -0
  109. data/lib/active_model_serializers.rb +32 -89
  110. data/lib/generators/rails/USAGE +6 -0
  111. data/lib/generators/rails/resource_override.rb +10 -0
  112. data/lib/generators/rails/serializer_generator.rb +36 -0
  113. data/lib/generators/rails/templates/serializer.rb.erb +8 -0
  114. data/lib/grape/active_model_serializers.rb +14 -0
  115. data/lib/grape/formatters/active_model_serializers.rb +15 -0
  116. data/lib/grape/helpers/active_model_serializers.rb +16 -0
  117. data/test/action_controller/adapter_selector_test.rb +53 -0
  118. data/test/action_controller/explicit_serializer_test.rb +134 -0
  119. data/test/action_controller/json/include_test.rb +167 -0
  120. data/test/action_controller/json_api/deserialization_test.rb +112 -0
  121. data/test/action_controller/json_api/errors_test.rb +41 -0
  122. data/test/action_controller/json_api/linked_test.rb +197 -0
  123. data/test/action_controller/json_api/pagination_test.rb +116 -0
  124. data/test/action_controller/json_api/transform_test.rb +181 -0
  125. data/test/action_controller/serialization_scope_name_test.rb +229 -0
  126. data/test/action_controller/serialization_test.rb +469 -0
  127. data/test/active_model_serializers/adapter_for_test.rb +208 -0
  128. data/test/active_model_serializers/json_pointer_test.rb +20 -0
  129. data/test/active_model_serializers/key_transform_test.rb +263 -0
  130. data/test/active_model_serializers/logging_test.rb +77 -0
  131. data/test/active_model_serializers/model_test.rb +9 -0
  132. data/test/active_model_serializers/railtie_test_isolated.rb +63 -0
  133. data/test/active_model_serializers/serialization_context_test_isolated.rb +58 -0
  134. data/test/active_model_serializers/test/schema_test.rb +130 -0
  135. data/test/active_model_serializers/test/serializer_test.rb +62 -0
  136. data/test/active_record_test.rb +9 -0
  137. data/test/adapter/deprecation_test.rb +100 -0
  138. data/test/adapter/json/belongs_to_test.rb +45 -0
  139. data/test/adapter/json/collection_test.rb +90 -0
  140. data/test/adapter/json/has_many_test.rb +45 -0
  141. data/test/adapter/json/transform_test.rb +93 -0
  142. data/test/adapter/json_api/belongs_to_test.rb +155 -0
  143. data/test/adapter/json_api/collection_test.rb +95 -0
  144. data/test/adapter/json_api/errors_test.rb +78 -0
  145. data/test/adapter/json_api/fields_test.rb +87 -0
  146. data/test/adapter/json_api/has_many_embed_ids_test.rb +43 -0
  147. data/test/adapter/json_api/has_many_explicit_serializer_test.rb +96 -0
  148. data/test/adapter/json_api/has_many_test.rb +144 -0
  149. data/test/adapter/json_api/has_one_test.rb +80 -0
  150. data/test/adapter/json_api/json_api_test.rb +35 -0
  151. data/test/adapter/json_api/linked_test.rb +392 -0
  152. data/test/adapter/json_api/links_test.rb +93 -0
  153. data/test/adapter/json_api/pagination_links_test.rb +166 -0
  154. data/test/adapter/json_api/parse_test.rb +137 -0
  155. data/test/adapter/json_api/relationship_test.rb +161 -0
  156. data/test/adapter/json_api/relationships_test.rb +199 -0
  157. data/test/adapter/json_api/resource_identifier_test.rb +85 -0
  158. data/test/adapter/json_api/resource_meta_test.rb +100 -0
  159. data/test/adapter/json_api/toplevel_jsonapi_test.rb +82 -0
  160. data/test/adapter/json_api/transform_test.rb +502 -0
  161. data/test/adapter/json_api/type_test.rb +61 -0
  162. data/test/adapter/json_test.rb +45 -0
  163. data/test/adapter/null_test.rb +23 -0
  164. data/test/adapter/polymorphic_test.rb +171 -0
  165. data/test/adapter_test.rb +67 -0
  166. data/test/array_serializer_test.rb +20 -73
  167. data/test/benchmark/app.rb +65 -0
  168. data/test/benchmark/benchmarking_support.rb +67 -0
  169. data/test/benchmark/bm_caching.rb +119 -0
  170. data/test/benchmark/bm_transform.rb +34 -0
  171. data/test/benchmark/config.ru +3 -0
  172. data/test/benchmark/controllers.rb +84 -0
  173. data/test/benchmark/fixtures.rb +219 -0
  174. data/test/cache_test.rb +485 -0
  175. data/test/collection_serializer_test.rb +110 -0
  176. data/test/fixtures/active_record.rb +78 -0
  177. data/test/fixtures/poro.rb +282 -0
  178. data/test/generators/scaffold_controller_generator_test.rb +24 -0
  179. data/test/generators/serializer_generator_test.rb +57 -0
  180. data/test/grape_test.rb +82 -0
  181. data/test/include_tree/from_include_args_test.rb +26 -0
  182. data/test/include_tree/from_string_test.rb +94 -0
  183. data/test/include_tree/include_args_to_hash_test.rb +64 -0
  184. data/test/lint_test.rb +49 -0
  185. data/test/logger_test.rb +18 -0
  186. data/test/poro_test.rb +9 -0
  187. data/test/serializable_resource_test.rb +83 -0
  188. data/test/serializers/association_macros_test.rb +36 -0
  189. data/test/serializers/associations_test.rb +295 -0
  190. data/test/serializers/attribute_test.rb +151 -0
  191. data/test/serializers/attributes_test.rb +52 -0
  192. data/test/serializers/caching_configuration_test_isolated.rb +170 -0
  193. data/test/serializers/configuration_test.rb +32 -0
  194. data/test/serializers/fieldset_test.rb +14 -0
  195. data/test/serializers/meta_test.rb +196 -0
  196. data/test/serializers/options_test.rb +21 -0
  197. data/test/serializers/read_attribute_for_serialization_test.rb +79 -0
  198. data/test/serializers/root_test.rb +21 -0
  199. data/test/serializers/serialization_test.rb +55 -0
  200. data/test/serializers/serializer_for_test.rb +134 -0
  201. data/test/support/custom_schemas/active_model_serializers/test/schema_test/my/index.json +6 -0
  202. data/test/support/isolated_unit.rb +79 -0
  203. data/test/support/rails5_shims.rb +47 -0
  204. data/test/support/rails_app.rb +45 -0
  205. data/test/support/schemas/active_model_serializers/test/schema_test/my/index.json +6 -0
  206. data/test/support/schemas/active_model_serializers/test/schema_test/my/show.json +6 -0
  207. data/test/support/schemas/custom/show.json +7 -0
  208. data/test/support/schemas/hyper_schema.json +93 -0
  209. data/test/support/schemas/render_using_json_api.json +43 -0
  210. data/test/support/schemas/simple_json_pointers.json +10 -0
  211. data/test/support/serialization_testing.rb +53 -0
  212. data/test/test_helper.rb +48 -23
  213. metadata +449 -43
  214. data/DESIGN.textile +0 -586
  215. data/Gemfile.edge +0 -9
  216. data/bench/perf.rb +0 -43
  217. data/cruft.md +0 -19
  218. data/lib/active_model/array_serializer.rb +0 -104
  219. data/lib/active_record/serializer_override.rb +0 -16
  220. data/lib/generators/resource_override.rb +0 -13
  221. data/lib/generators/serializer/USAGE +0 -9
  222. data/lib/generators/serializer/serializer_generator.rb +0 -42
  223. data/lib/generators/serializer/templates/serializer.rb +0 -19
  224. data/test/association_test.rb +0 -592
  225. data/test/caching_test.rb +0 -96
  226. data/test/generators_test.rb +0 -85
  227. data/test/no_serialization_scope_test.rb +0 -34
  228. data/test/serialization_scope_name_test.rb +0 -67
  229. data/test/serialization_test.rb +0 -392
  230. data/test/serializer_support_test.rb +0 -51
  231. data/test/serializer_test.rb +0 -1465
  232. data/test/test_fakes.rb +0 -217
data/Rakefile CHANGED
@@ -1,18 +1,103 @@
1
- #!/usr/bin/env rake
2
- require "bundler/gem_tasks"
3
- require "rake/testtask"
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+ begin
7
+ require 'simplecov'
8
+ rescue LoadError
9
+ end
10
+
11
+ Bundler::GemHelper.install_tasks
12
+
13
+ require 'yard'
14
+
15
+ namespace :yard do
16
+ YARD::Rake::YardocTask.new(:doc) do |t|
17
+ t.stats_options = ['--list-undoc']
18
+ end
19
+
20
+ desc 'start a gem server'
21
+ task :server do
22
+ sh 'bundle exec yard server --gems'
23
+ end
24
+
25
+ desc 'use Graphviz to generate dot graph'
26
+ task :graph do
27
+ output_file = 'doc/erd.dot'
28
+ sh "bundle exec yard graph --protected --full --dependencies > #{output_file}"
29
+ puts 'open doc/erd.dot if you have graphviz installed'
30
+ end
31
+ end
32
+
33
+ begin
34
+ require 'rubocop'
35
+ require 'rubocop/rake_task'
36
+ rescue LoadError
37
+ else
38
+ Rake::Task[:rubocop].clear if Rake::Task.task_defined?(:rubocop)
39
+ require 'rbconfig'
40
+ # https://github.com/bundler/bundler/blob/1b3eb2465a/lib/bundler/constants.rb#L2
41
+ windows_platforms = /(msdos|mswin|djgpp|mingw)/
42
+ if RbConfig::CONFIG['host_os'] =~ windows_platforms
43
+ desc 'No-op rubocop on Windows-- unsupported platform'
44
+ task :rubocop do
45
+ puts 'Skipping rubocop on Windows'
46
+ end
47
+ elsif defined?(::Rubinius)
48
+ desc 'No-op rubocop to avoid rbx segfault'
49
+ task :rubocop do
50
+ puts 'Skipping rubocop on rbx due to segfault'
51
+ puts 'https://github.com/rubinius/rubinius/issues/3499'
52
+ end
53
+ else
54
+ Rake::Task[:rubocop].clear if Rake::Task.task_defined?(:rubocop)
55
+ desc 'Execute rubocop'
56
+ RuboCop::RakeTask.new(:rubocop) do |task|
57
+ task.options = ['--rails', '--display-cop-names', '--display-style-guide']
58
+ task.fail_on_error = true
59
+ end
60
+ end
61
+ end
62
+
63
+ require 'rake/testtask'
4
64
 
5
- desc 'Run tests'
6
65
  Rake::TestTask.new(:test) do |t|
7
66
  t.libs << 'lib'
8
67
  t.libs << 'test'
9
68
  t.pattern = 'test/**/*_test.rb'
69
+ t.ruby_opts = ['-r./test/test_helper.rb']
70
+ t.ruby_opts << ' -w' unless ENV['NO_WARN'] == 'true'
10
71
  t.verbose = true
11
72
  end
12
73
 
13
- desc 'Benchmark'
14
- task :bench do
15
- load 'bench/perf.rb'
74
+ desc 'Run isolated tests'
75
+ task isolated: ['test:isolated']
76
+ namespace :test do
77
+ task :isolated do
78
+ desc 'Run isolated tests for Railtie'
79
+ require 'shellwords'
80
+ dir = File.dirname(__FILE__)
81
+ dir = Shellwords.shellescape(dir)
82
+ isolated_test_files = FileList['test/**/*_test_isolated.rb']
83
+ # https://github.com/rails/rails/blob/3d590add45/railties/lib/rails/generators/app_base.rb#L345-L363
84
+ _bundle_command = Gem.bin_path('bundler', 'bundle')
85
+ require 'bundler'
86
+ Bundler.with_clean_env do
87
+ isolated_test_files.all? do |test_file|
88
+ command = "-w -I#{dir}/lib -I#{dir}/test #{Shellwords.shellescape(test_file)}"
89
+ full_command = %("#{Gem.ruby}" #{command})
90
+ system(full_command)
91
+ end or fail 'Failures' # rubocop:disable Style/AndOr
92
+ end
93
+ end
94
+ end
95
+
96
+ if ENV['RAILS_VERSION'].to_s > '4.0' && RUBY_ENGINE == 'ruby'
97
+ task default: [:isolated, :test, :rubocop]
98
+ else
99
+ task default: [:test, :rubocop]
16
100
  end
17
101
 
18
- task :default => :test
102
+ desc 'CI test task'
103
+ task :ci => [:default]
@@ -1,24 +1,66 @@
1
- # -*- encoding: utf-8 -*-
2
-
3
- $:.unshift File.expand_path("../lib", __FILE__)
4
- require "active_model/serializers/version"
5
-
6
- Gem::Specification.new do |gem|
7
- gem.authors = ["José Valim", "Yehuda Katz"]
8
- gem.email = ["jose.valim@gmail.com", "wycats@gmail.com"]
9
- gem.description = %q{Making it easy to serialize models for client-side use}
10
- gem.summary = %q{Bringing consistency and object orientation to model serialization. Works great for client-side MVC frameworks!}
11
- gem.homepage = "https://github.com/rails-api/active_model_serializers"
12
-
13
- gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
14
- gem.files = `git ls-files`.split("\n")
15
- gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
- gem.name = "active_model_serializers"
17
- gem.require_paths = ["lib"]
18
- gem.version = ActiveModel::Serializer::VERSION
19
-
20
- gem.add_dependency 'activemodel', '>= 3.0'
21
- gem.add_development_dependency "rails", ">= 3.0"
22
- gem.add_development_dependency "pry"
23
- gem.add_development_dependency "minitest"
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'active_model/serializer/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'active_model_serializers'
8
+ spec.version = ActiveModel::Serializer::VERSION
9
+ spec.platform = Gem::Platform::RUBY
10
+ spec.authors = ['Steve Klabnik']
11
+ spec.email = ['steve@steveklabnik.com']
12
+ spec.summary = 'Conventions-based JSON generation for Rails.'
13
+ spec.description = 'ActiveModel::Serializers allows you to generate your JSON in an object-oriented and convention-driven manner.'
14
+ spec.homepage = 'https://github.com/rails-api/active_model_serializers'
15
+ spec.license = 'MIT'
16
+
17
+ spec.files = `git ls-files -z`.split("\x0")
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ['lib']
20
+ spec.executables = []
21
+
22
+ spec.required_ruby_version = '>= 2.0.0'
23
+
24
+ rails_versions = '>= 4.0'
25
+ spec.add_runtime_dependency 'activemodel', rails_versions
26
+ # 'activesupport', rails_versions
27
+ # 'builder'
28
+
29
+ spec.add_runtime_dependency 'actionpack', rails_versions
30
+ # 'activesupport', rails_versions
31
+ # 'rack'
32
+ # 'rack-test', '~> 0.6.2'
33
+
34
+ spec.add_runtime_dependency 'railties', rails_versions
35
+ # 'activesupport', rails_versions
36
+ # 'actionpack', rails_versions
37
+ # 'rake', '>= 0.8.7'
38
+
39
+ # 'activesupport', rails_versions
40
+ # 'i18n,
41
+ # 'tzinfo'
42
+ # 'minitest'
43
+ # 'thread_safe'
44
+
45
+ spec.add_development_dependency 'activerecord', rails_versions
46
+ # arel
47
+ # activesupport
48
+ # activemodel
49
+
50
+ # Soft dependency for pagination
51
+ spec.add_development_dependency 'kaminari', ' ~> 0.16.3'
52
+ spec.add_development_dependency 'will_paginate', '~> 3.0', '>= 3.0.7'
53
+
54
+ spec.add_development_dependency 'bundler', '~> 1.6'
55
+ spec.add_development_dependency 'simplecov', '~> 0.11'
56
+ spec.add_development_dependency 'timecop', '~> 0.7'
57
+ spec.add_development_dependency 'grape', ['>= 0.13', '< 1.0']
58
+ spec.add_development_dependency 'json_schema'
59
+ spec.add_development_dependency 'rake', ['>= 10.0', '< 12.0']
60
+
61
+ spec.post_install_message = <<-EOF
62
+ NOTE: The default key case for the JsonApi adapter has changed to dashed.
63
+ See https://github.com/rails-api/active_model_serializers/blob/master/docs/general/key_transforms.md
64
+ for more information on configuring this behavior.
65
+ EOF
24
66
  end
data/appveyor.yml ADDED
@@ -0,0 +1,24 @@
1
+ version: '{build}'
2
+
3
+ skip_tags: true
4
+
5
+ environment:
6
+ JRUBY_OPTS: "--dev -J-Xmx1024M --debug"
7
+ matrix:
8
+ - ruby_version: "Ruby21"
9
+ - ruby_version: "Ruby21-x64"
10
+ - ruby_version: "jruby-9.0.0.0"
11
+
12
+ cache:
13
+ - vendor/bundle
14
+
15
+ install:
16
+ - SET PATH=C:\%ruby_version%\bin;%PATH%
17
+ - gem install bundler
18
+ - bundle env
19
+ - bundle install --path=vendor/bundle --retry=3 --jobs=3
20
+
21
+ test_script:
22
+ - bundle exec rake ci
23
+
24
+ build: off
data/bin/bench ADDED
@@ -0,0 +1,171 @@
1
+ #!/usr/bin/env ruby
2
+ # ActiveModelSerializers Benchmark driver
3
+ # Adapted from
4
+ # https://github.com/ruby-bench/ruby-bench-suite/blob/8ad567f7e43a044ae48c36833218423bb1e2bd9d/rails/benchmarks/driver.rb
5
+ require 'bundler'
6
+ Bundler.setup
7
+ require 'json'
8
+ require 'pathname'
9
+ require 'optparse'
10
+ require 'digest'
11
+ require 'pathname'
12
+ require 'shellwords'
13
+ require 'logger'
14
+ require 'English'
15
+
16
+ class BenchmarkDriver
17
+ ROOT = Pathname File.expand_path(File.join('..', '..'), __FILE__)
18
+ BASE = ENV.fetch('BASE') { ROOT.join('test', 'benchmark') }
19
+ ESCAPED_BASE = Shellwords.shellescape(BASE)
20
+
21
+ def self.benchmark(options)
22
+ new(options).run
23
+ end
24
+
25
+ def self.parse_argv_and_run(argv = ARGV, options = {})
26
+ options = {
27
+ repeat_count: 1,
28
+ pattern: [],
29
+ env: 'CACHE_ON=on'
30
+ }.merge!(options)
31
+
32
+ OptionParser.new do |opts|
33
+ opts.banner = 'Usage: bin/bench [options]'
34
+
35
+ opts.on('-r', '--repeat-count [NUM]', 'Run benchmarks [NUM] times taking the best result') do |value|
36
+ options[:repeat_count] = value.to_i
37
+ end
38
+
39
+ opts.on('-p', '--pattern <PATTERN1,PATTERN2,PATTERN3>', 'Benchmark name pattern') do |value|
40
+ options[:pattern] = value.split(',')
41
+ end
42
+
43
+ opts.on('-e', '--env <var1=val1,var2=val2,var3=vale>', 'ENV variables to pass in') do |value|
44
+ options[:env] = value.split(',')
45
+ end
46
+ end.parse!(argv)
47
+
48
+ benchmark(options)
49
+ end
50
+
51
+ attr_reader :commit_hash, :base
52
+
53
+ # Based on logfmt:
54
+ # https://www.brandur.org/logfmt
55
+ # For more complete implementation see:
56
+ # see https://github.com/arachnid-cb/logfmtr/blob/master/lib/logfmtr/base.rb
57
+ # For usage see:
58
+ # https://blog.codeship.com/logfmt-a-log-format-thats-easy-to-read-and-write/
59
+ # https://engineering.heroku.com/blogs/2014-09-05-hutils-explore-your-structured-data-logs/
60
+ # For Ruby parser see:
61
+ # https://github.com/cyberdelia/logfmt-ruby
62
+ def self.summary_logger(device = 'output.txt')
63
+ require 'time'
64
+ logger = Logger.new(device)
65
+ logger.level = Logger::INFO
66
+ logger.formatter = proc { |severity, datetime, progname, msg|
67
+ msg = "'#{msg}'"
68
+ "level=#{severity} time=#{datetime.utc.iso8601(6)} pid=#{Process.pid} progname=#{progname} msg=#{msg}#{$INPUT_RECORD_SEPARATOR}"
69
+ }
70
+ logger
71
+ end
72
+
73
+ def self.stdout_logger
74
+ logger = Logger.new(STDOUT)
75
+ logger.level = Logger::INFO
76
+ logger.formatter = proc { |_, _, _, msg| "#{msg}#{$INPUT_RECORD_SEPARATOR}" }
77
+ logger
78
+ end
79
+
80
+ def initialize(options)
81
+ @writer = ENV['SUMMARIZE'] ? self.class.summary_logger : self.class.stdout_logger
82
+ @repeat_count = options[:repeat_count]
83
+ @pattern = options[:pattern]
84
+ @commit_hash = options.fetch(:commit_hash) { `git rev-parse --short HEAD`.chomp }
85
+ @base = options.fetch(:base) { ESCAPED_BASE }
86
+ @env = Array(options[:env]).join(' ')
87
+ @rubyopt = options[:rubyopt] # TODO: rename
88
+ end
89
+
90
+ def run
91
+ files.each do |path|
92
+ next if !@pattern.empty? && /#{@pattern.join('|')}/ !~ File.basename(path)
93
+ run_single(Shellwords.shellescape(path))
94
+ end
95
+ end
96
+
97
+ private
98
+
99
+ def files
100
+ Dir[File.join(base, 'bm_*')]
101
+ end
102
+
103
+ def run_single(path)
104
+ script = "RAILS_ENV=production #{@env} ruby #{@rubyopt} #{path}"
105
+ environment = `ruby -v`.chomp.strip[/\d+\.\d+\.\d+\w+/]
106
+
107
+ runs_output = measure(script)
108
+ if runs_output.empty?
109
+ results = { error: :no_results }
110
+ return
111
+ end
112
+
113
+ results = {}
114
+ results['commit_hash'] = commit_hash
115
+ results['version'] = runs_output.first['version']
116
+ results['rails_version'] = runs_output.first['rails_version']
117
+ results['benchmark_run[environment]'] = environment
118
+ results['runs'] = []
119
+
120
+ runs_output.each do |output|
121
+ results['runs'] << {
122
+ 'benchmark_type[category]' => output['label'],
123
+ 'benchmark_run[result][iterations_per_second]' => output['iterations_per_second'].round(3),
124
+ 'benchmark_run[result][total_allocated_objects_per_iteration]' => output['total_allocated_objects_per_iteration']
125
+ }
126
+ end
127
+ ensure
128
+ results && report(results)
129
+ end
130
+
131
+ def report(results)
132
+ @writer.info { 'Benchmark results:' }
133
+ @writer.info { JSON.pretty_generate(results) }
134
+ end
135
+
136
+ def summarize(result)
137
+ puts "#{result['label']} #{result['iterations_per_second']}/ips; #{result['total_allocated_objects_per_iteration']} objects"
138
+ end
139
+
140
+ # FIXME: ` provides the full output but it'll return failed output as well.
141
+ def measure(script)
142
+ results = Hash.new { |h, k| h[k] = [] }
143
+
144
+ @repeat_count.times do
145
+ output = sh(script)
146
+ output.each_line do |line|
147
+ next if line.nil?
148
+ begin
149
+ result = JSON.parse(line)
150
+ rescue JSON::ParserError
151
+ result = { error: line } # rubocop:disable Lint/UselessAssignment
152
+ else
153
+ summarize(result)
154
+ results[result['label']] << result
155
+ end
156
+ end
157
+ end
158
+
159
+ results.map do |_, bm_runs|
160
+ bm_runs.sort_by do |run|
161
+ run['iterations_per_second']
162
+ end.last
163
+ end
164
+ end
165
+
166
+ def sh(cmd)
167
+ `#{cmd}`
168
+ end
169
+ end
170
+
171
+ BenchmarkDriver.parse_argv_and_run if $PROGRAM_NAME == __FILE__