From 411d613686a3c02e6944dad511a3967d345b45ad Mon Sep 17 00:00:00 2001 From: Benjamin Fleischer Date: Tue, 5 Jan 2016 00:48:29 -0600 Subject: [PATCH] Setup dummy test server --- bin/serve_dummy | 39 ++++++++++++++ test/dummy/app.rb | 88 +++++++++++++++++++++++++++++++ test/dummy/config.ru | 3 ++ test/dummy/controllers.rb | 42 +++++++++++++++ test/dummy/fixtures.rb | 106 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 278 insertions(+) create mode 100755 bin/serve_dummy create mode 100644 test/dummy/app.rb create mode 100644 test/dummy/config.ru create mode 100644 test/dummy/controllers.rb create mode 100644 test/dummy/fixtures.rb diff --git a/bin/serve_dummy b/bin/serve_dummy new file mode 100755 index 000000000..960a7126d --- /dev/null +++ b/bin/serve_dummy @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +set -e + +case "$1" in + + start) + config="${CONFIG_RU:-test/dummy/config.ru}" + bundle exec ruby -Ilib -S rackup "$config" --daemonize --pid tmp/dummy_app.pid --warn --server webrick + until [ -f 'tmp/dummy_app.pid' ]; do + sleep 0.1 # give it time to start.. I don't know a better way + done + cat tmp/dummy_app.pid + true + ;; + + stop) + if [ -f 'tmp/dummy_app.pid' ]; then + kill -TERM $(cat tmp/dummy_app.pid) + else + echo 'No pidfile' + false + fi + ;; + + status) + if [ -f 'tmp/dummy_app.pid' ]; then + kill -0 $(cat tmp/dummy_app.pid) + [ "$?" -eq 0 ] + else + echo 'No pidfile' + false + fi + ;; + + *) + echo "Usage: $0 [start|stop|status]" + ;; + +esac diff --git a/test/dummy/app.rb b/test/dummy/app.rb new file mode 100644 index 000000000..42fe4d8e8 --- /dev/null +++ b/test/dummy/app.rb @@ -0,0 +1,88 @@ +# https://github.com/rails-api/active_model_serializers/pull/872 +# approx ref 792fb8a9053f8db3c562dae4f40907a582dd1720 to test against +require 'bundler/setup' + +require 'rails' +require 'active_model' +require 'active_support' +require 'active_support/json' +require 'action_controller' +require 'action_controller/test_case' +require 'action_controller/railtie' +abort "Rails application already defined: #{Rails.application.class}" if Rails.application + +# ref: https://gist.github.com/bf4/8744473 +class DummyApp < Rails::Application + config.action_controller.perform_caching = ENV['CACHE_ON'] != 'off' + config.action_controller.cache_store = :memory_store + + # Set up production configuration + config.eager_load = true + config.cache_classes = true + + config.active_support.test_order = :random + config.secret_token = '1234' + config.secret_key_base = 'abc123' + config.logger = Logger.new(IO::NULL) + + class DummyLogger < ActiveSupport::Logger + def initialize + @file = StringIO.new + super(@file) + end + + def messages + @file.rewind + @file.read + end + end +end + +require 'active_model_serializers' + +# Initialize app before any serializers are defined, for sanity's sake. +# Otherwise, you have to manually set perform caching. +# +# Details: +# +# 1. Upon load, when AMS.config.perform_caching is true, +# serializers inherit the cache store from ActiveModelSerializers.config.cache_store +# 1. If the serializers are loaded before Rails is initialized (`Rails.application.initialize!`), +# these values are nil, and are not applied to the already loaded serializers +# 1. If Rails is initialized before any serializers are loaded, then the configs are set, +# and are used when serializers are loaded +# 1. In either case, `ActiveModelSerializers.config.cache_store`, and +# `ActiveModelSerializers.config.perform_caching` can be set at any time before the serializers +# are loaded, +# e.g. `ActiveModel::Serializer.config.cache_store ||= +# ActiveSupport::Cache.lookup_store(ActionController::Base.cache_store || +# Rails.cache || :memory_store)` +# and `ActiveModelSerializers.config.perform_caching = true` +# 1. If the serializers are loaded before Rails is initialized, then, +# you can set the `_cache` store directly on the serializers. +# `ActiveModel::Serializer._cache ||= +# ActiveSupport::Cache.lookup_store(ActionController::Base.cache_store || +# Rails.cache || :memory_store` +# is sufficient. +# Setting `_cache` to a truthy value will cause the CachedSerializer +# to consider it cached, which will apply to all serializers (bug? :bug: ) +# +# This happens, in part, because the cache store is set for a serializer +# when `cache` is called, and cache is usually called when the serializer is defined. +# +# So, there's now a 'workaround', something to debug, and a starting point. +Rails.application.initialize! + +require_relative 'fixtures' +require_relative 'controllers' + +# Clear cache +ActionController::Base.cache_store.clear + +# Test caching is on +ActiveSupport::Cache::Store.logger = Logger.new(IO::NULL) # seems to be the best way +# the below is used in some rails tests but isn't available/working in all versions, so far as I can tell +# https://github.com/rails/rails/pull/15943 +# ActiveSupport::Notifications.subscribe(/^cache_(.*)\.active_support$/) do |*args| +# p ActiveSupport::Notifications::Event.new(*args) +# end diff --git a/test/dummy/config.ru b/test/dummy/config.ru new file mode 100644 index 000000000..d6f4e7157 --- /dev/null +++ b/test/dummy/config.ru @@ -0,0 +1,3 @@ +require File.expand_path(['..','app'].join(File::SEPARATOR), __FILE__) + +run Rails.application diff --git a/test/dummy/controllers.rb b/test/dummy/controllers.rb new file mode 100644 index 000000000..a2414868c --- /dev/null +++ b/test/dummy/controllers.rb @@ -0,0 +1,42 @@ +class PostController < ActionController::Base + POST = + begin + comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') + author = Author.new(id: 1, name: 'Joao Moura.') + Post.new(id: 1, title: 'New Post', blog: nil, body: 'Body', comments: [comment], author: author) + end + + def render_with_caching_serializer + toggle_cache_status + render json: POST, adapter: :json + end + + def render_with_non_caching_serializer + toggle_cache_status + render json: POST, serializer: CachingPostSerializer, adapter: :json + end + + def render_cache_status + toggle_cache_status + # Uncomment to debug + # STDERR.puts cache_store.class + # STDERR.puts cache_dependencies + render json: {caching: perform_caching}.to_json + end + + private + + def toggle_cache_status + case params[:on] + when 'on'.freeze then self.perform_caching = true + when 'off'.freeze then self.perform_caching = false + else # no-op + end + end +end + +Rails.application.routes.draw do + get '/status(/:on)' => 'post#render_cache_status' + get '/caching(/:on)' => 'post#render_with_caching_serializer' + get '/non_caching(/:on)' => 'post#render_with_non_caching_serializer' +end diff --git a/test/dummy/fixtures.rb b/test/dummy/fixtures.rb new file mode 100644 index 000000000..e507089ed --- /dev/null +++ b/test/dummy/fixtures.rb @@ -0,0 +1,106 @@ +class AuthorSerializer < ActiveModel::Serializer + attributes :id, :name + + has_many :posts, embed: :ids + has_one :bio +end + +class BlogSerializer < ActiveModel::Serializer + attributes :id, :name +end + +class CommentSerializer < ActiveModel::Serializer + attributes :id, :body + + belongs_to :post + belongs_to :author +end + +class PostSerializer < ActiveModel::Serializer + attributes :id, :title, :body + + has_many :comments, serializer: CommentSerializer + belongs_to :blog, serializer: BlogSerializer + belongs_to :author, serializer: AuthorSerializer + + def blog + Blog.new(id: 999, name: 'Custom blog') + end +end + +class CachingAuthorSerializer < AuthorSerializer + cache key: 'writer', skip_digest: true +end + +class CachingCommentSerializer < CommentSerializer + cache expires_in: 1.day, skip_digest: true +end + +class CachingPostSerializer < PostSerializer + cache key: 'post', expires_in: 0.1, skip_digest: true + belongs_to :blog, serializer: BlogSerializer + belongs_to :author, serializer: CachingAuthorSerializer + has_many :comments, serializer: CachingCommentSerializer +end + +# ActiveModelSerializers::Model is a convenient +# serializable class to inherit from when making +# serializable non-activerecord objects. +class BenchmarkModel + include ActiveModel::Model + include ActiveModel::Serializers::JSON + + attr_reader :attributes + + def initialize(attributes = {}) + @attributes = attributes + super + end + + # Defaults to the downcased model name. + def id + attributes.fetch(:id) { self.class.name.downcase } + end + + # Defaults to the downcased model name and updated_at + def cache_key + attributes.fetch(:cache_key) { "#{self.class.name.downcase}/#{id}-#{updated_at.strftime("%Y%m%d%H%M%S%9N")}" } + end + + # Defaults to the time the serializer file was modified. + def updated_at + attributes.fetch(:updated_at) { File.mtime(__FILE__) } + end + + def read_attribute_for_serialization(key) + if key == :id || key == 'id' + attributes.fetch(key) { id } + else + attributes[key] + end + end +end + +class Comment < BenchmarkModel + attr_accessor :id, :body + + def cache_key + "#{self.class.name.downcase}/#{id}" + end +end + +class Author < BenchmarkModel + attr_accessor :id, :name, :posts +end + +class Post < BenchmarkModel + attr_accessor :id, :title, :body, :comments, :blog, :author + + def cache_key + 'benchmarking::post/1-20151215212620000000000' + end +end + +class Blog < BenchmarkModel + attr_accessor :id, :name +end