drape 1.0.0.beta1
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.
- checksums.yaml +7 -0
- data/.codeclimate.yml +27 -0
- data/.gitignore +18 -0
- data/.rspec +2 -0
- data/.rubocop.yml +1159 -0
- data/.travis.yml +14 -0
- data/.yardopts +1 -0
- data/CHANGELOG.md +230 -0
- data/CONTRIBUTING.md +20 -0
- data/Gemfile +16 -0
- data/Guardfile +26 -0
- data/LICENSE +7 -0
- data/README.md +592 -0
- data/Rakefile +76 -0
- data/drape.gemspec +31 -0
- data/gemfiles/5.0.gemfile +8 -0
- data/lib/drape.rb +63 -0
- data/lib/drape/automatic_delegation.rb +55 -0
- data/lib/drape/collection_decorator.rb +102 -0
- data/lib/drape/decoratable.rb +93 -0
- data/lib/drape/decoratable/equality.rb +26 -0
- data/lib/drape/decorated_association.rb +33 -0
- data/lib/drape/decorates_assigned.rb +44 -0
- data/lib/drape/decorator.rb +282 -0
- data/lib/drape/delegation.rb +13 -0
- data/lib/drape/factory.rb +91 -0
- data/lib/drape/finders.rb +36 -0
- data/lib/drape/helper_proxy.rb +43 -0
- data/lib/drape/helper_support.rb +5 -0
- data/lib/drape/lazy_helpers.rb +13 -0
- data/lib/drape/railtie.rb +69 -0
- data/lib/drape/tasks/test.rake +9 -0
- data/lib/drape/test/devise_helper.rb +30 -0
- data/lib/drape/test/minitest_integration.rb +6 -0
- data/lib/drape/test/rspec_integration.rb +20 -0
- data/lib/drape/test_case.rb +42 -0
- data/lib/drape/undecorate.rb +9 -0
- data/lib/drape/version.rb +3 -0
- data/lib/drape/view_context.rb +104 -0
- data/lib/drape/view_context/build_strategy.rb +46 -0
- data/lib/drape/view_helpers.rb +34 -0
- data/lib/generators/controller_override.rb +17 -0
- data/lib/generators/mini_test/decorator_generator.rb +20 -0
- data/lib/generators/mini_test/templates/decorator_spec.rb +4 -0
- data/lib/generators/mini_test/templates/decorator_test.rb +4 -0
- data/lib/generators/rails/decorator_generator.rb +36 -0
- data/lib/generators/rails/templates/decorator.rb +19 -0
- data/lib/generators/rspec/decorator_generator.rb +9 -0
- data/lib/generators/rspec/templates/decorator_spec.rb +4 -0
- data/lib/generators/test_unit/decorator_generator.rb +9 -0
- data/lib/generators/test_unit/templates/decorator_test.rb +4 -0
- data/spec/draper/collection_decorator_spec.rb +305 -0
- data/spec/draper/decoratable/equality_spec.rb +10 -0
- data/spec/draper/decoratable_spec.rb +203 -0
- data/spec/draper/decorated_association_spec.rb +85 -0
- data/spec/draper/decorates_assigned_spec.rb +70 -0
- data/spec/draper/decorator_spec.rb +828 -0
- data/spec/draper/factory_spec.rb +250 -0
- data/spec/draper/finders_spec.rb +165 -0
- data/spec/draper/helper_proxy_spec.rb +61 -0
- data/spec/draper/lazy_helpers_spec.rb +21 -0
- data/spec/draper/undecorate_spec.rb +19 -0
- data/spec/draper/view_context/build_strategy_spec.rb +116 -0
- data/spec/draper/view_context_spec.rb +160 -0
- data/spec/draper/view_helpers_spec.rb +8 -0
- data/spec/dummy/.gitignore +21 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/assets/config/manifest.js +3 -0
- data/spec/dummy/app/assets/images/.keep +0 -0
- data/spec/dummy/app/assets/javascripts/application.js +16 -0
- data/spec/dummy/app/assets/javascripts/cable.js +13 -0
- data/spec/dummy/app/assets/javascripts/channels/.keep +0 -0
- data/spec/dummy/app/assets/stylesheets/application.css +15 -0
- data/spec/dummy/app/channels/application_cable/channel.rb +4 -0
- data/spec/dummy/app/channels/application_cable/connection.rb +5 -0
- data/spec/dummy/app/controllers/application_controller.rb +5 -0
- data/spec/dummy/app/controllers/concerns/.keep +0 -0
- data/spec/dummy/app/controllers/posts_controller.rb +21 -0
- data/spec/dummy/app/decorators/mongoid_post_decorator.rb +4 -0
- data/spec/dummy/app/decorators/post_decorator.rb +59 -0
- data/spec/dummy/app/helpers/application_helper.rb +5 -0
- data/spec/dummy/app/jobs/application_job.rb +2 -0
- data/spec/dummy/app/mailers/application_mailer.rb +4 -0
- data/spec/dummy/app/mailers/post_mailer.rb +18 -0
- data/spec/dummy/app/models/admin.rb +5 -0
- data/spec/dummy/app/models/application_record.rb +3 -0
- data/spec/dummy/app/models/concerns/.keep +0 -0
- data/spec/dummy/app/models/mongoid_post.rb +5 -0
- data/spec/dummy/app/models/post.rb +3 -0
- data/spec/dummy/app/models/user.rb +5 -0
- data/spec/dummy/app/views/layouts/application.html.erb +9 -0
- data/spec/dummy/app/views/layouts/mailer.html.erb +13 -0
- data/spec/dummy/app/views/layouts/mailer.text.erb +1 -0
- data/spec/dummy/app/views/post_mailer/decorated_email.html.erb +31 -0
- data/spec/dummy/app/views/posts/_post.html.erb +40 -0
- data/spec/dummy/app/views/posts/show.html.erb +1 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +9 -0
- data/spec/dummy/bin/rake +9 -0
- data/spec/dummy/bin/setup +34 -0
- data/spec/dummy/bin/spring +15 -0
- data/spec/dummy/bin/update +29 -0
- data/spec/dummy/config.ru +5 -0
- data/spec/dummy/config/application.rb +21 -0
- data/spec/dummy/config/boot.rb +2 -0
- data/spec/dummy/config/cable.yml +10 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +51 -0
- data/spec/dummy/config/environments/production.rb +91 -0
- data/spec/dummy/config/environments/test.rb +42 -0
- data/spec/dummy/config/initializers/active_record_belongs_to_required_by_default.rb +6 -0
- data/spec/dummy/config/initializers/application_controller_renderer.rb +6 -0
- data/spec/dummy/config/initializers/assets.rb +11 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/callback_terminator.rb +6 -0
- data/spec/dummy/config/initializers/cookies_serializer.rb +5 -0
- data/spec/dummy/config/initializers/devise.rb +3 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/mime_types.rb +4 -0
- data/spec/dummy/config/initializers/per_form_csrf_tokens.rb +4 -0
- data/spec/dummy/config/initializers/request_forgery_protection.rb +4 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/ssl_options.rb +4 -0
- data/spec/dummy/config/initializers/to_time_preserves_timezone.rb +10 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/mongoid.yml +16 -0
- data/spec/dummy/config/puma.rb +47 -0
- data/spec/dummy/config/routes.rb +8 -0
- data/spec/dummy/config/secrets.yml +22 -0
- data/spec/dummy/config/spring.rb +6 -0
- data/spec/dummy/db/migrate/20121019115657_create_posts.rb +7 -0
- data/spec/dummy/db/schema.rb +19 -0
- data/spec/dummy/db/seeds.rb +2 -0
- data/spec/dummy/lib/assets/.keep +0 -0
- data/spec/dummy/lib/tasks/.keep +0 -0
- data/spec/dummy/lib/tasks/test.rake +16 -0
- data/spec/dummy/log/.keep +0 -0
- data/spec/dummy/public/404.html +67 -0
- data/spec/dummy/public/422.html +67 -0
- data/spec/dummy/public/500.html +66 -0
- data/spec/dummy/public/apple-touch-icon-precomposed.png +0 -0
- data/spec/dummy/public/apple-touch-icon.png +0 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/public/robots.txt +5 -0
- data/spec/dummy/spec/decorators/active_model_serializers_spec.rb +12 -0
- data/spec/dummy/spec/decorators/devise_spec.rb +64 -0
- data/spec/dummy/spec/decorators/helpers_spec.rb +21 -0
- data/spec/dummy/spec/decorators/post_decorator_spec.rb +66 -0
- data/spec/dummy/spec/decorators/spec_type_spec.rb +7 -0
- data/spec/dummy/spec/decorators/view_context_spec.rb +22 -0
- data/spec/dummy/spec/mailers/post_mailer_spec.rb +33 -0
- data/spec/dummy/spec/models/mongoid_post_spec.rb +8 -0
- data/spec/dummy/spec/models/post_spec.rb +6 -0
- data/spec/dummy/spec/shared_examples/decoratable.rb +26 -0
- data/spec/dummy/spec/spec_helper.rb +8 -0
- data/spec/dummy/test/controllers/.keep +0 -0
- data/spec/dummy/test/fixtures/.keep +0 -0
- data/spec/dummy/test/fixtures/files/.keep +0 -0
- data/spec/dummy/test/helpers/.keep +0 -0
- data/spec/dummy/test/integration/.keep +0 -0
- data/spec/dummy/test/mailers/.keep +0 -0
- data/spec/dummy/test/models/.keep +0 -0
- data/spec/dummy/test/test_helper.rb +10 -0
- data/spec/dummy/vendor/assets/javascripts/.keep +0 -0
- data/spec/dummy/vendor/assets/stylesheets/.keep +0 -0
- data/spec/generators/controller/controller_generator_spec.rb +24 -0
- data/spec/generators/decorator/decorator_generator_spec.rb +94 -0
- data/spec/integration/integration_spec.rb +68 -0
- data/spec/performance/active_record.rb +4 -0
- data/spec/performance/benchmark.rb +57 -0
- data/spec/performance/decorators.rb +41 -0
- data/spec/performance/models.rb +20 -0
- data/spec/spec_helper.rb +41 -0
- data/spec/support/dummy_app.rb +88 -0
- data/spec/support/matchers/have_text.rb +50 -0
- data/spec/support/shared_examples/decoratable_equality.rb +40 -0
- data/spec/support/shared_examples/view_helpers.rb +39 -0
- metadata +497 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
shared_examples_for 'a decoratable model' do
|
|
2
|
+
describe '.decorate' do
|
|
3
|
+
it 'applies a collection decorator to a scope' do
|
|
4
|
+
described_class.create
|
|
5
|
+
decorated = described_class.limit(1).decorate
|
|
6
|
+
|
|
7
|
+
expect(decorated.size).to eq(1)
|
|
8
|
+
expect(decorated).to be_decorated
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
describe '#==' do
|
|
13
|
+
it "is true for other instances' decorators" do
|
|
14
|
+
if defined?(Mongoid) && Mongoid::VERSION.to_f < 3.1 && described_class < Mongoid::Document
|
|
15
|
+
pending 'Mongoid < 3.1 overrides `#==`'
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
described_class.create
|
|
19
|
+
one = described_class.first
|
|
20
|
+
other = described_class.first
|
|
21
|
+
|
|
22
|
+
expect(one).not_to be(other)
|
|
23
|
+
expect(one == other.decorate).to be_truthy
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
ENV['RAILS_ENV'] ||= 'test'
|
|
2
|
+
require File.expand_path('../../config/environment', __FILE__)
|
|
3
|
+
require 'rails/test_help'
|
|
4
|
+
|
|
5
|
+
class ActiveSupport::TestCase
|
|
6
|
+
# Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
|
|
7
|
+
fixtures :all
|
|
8
|
+
|
|
9
|
+
# Add more helper methods to be used by all tests here...
|
|
10
|
+
end
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'rails'
|
|
3
|
+
|
|
4
|
+
# TODO: Fix this file
|
|
5
|
+
# require 'ammeter/init'
|
|
6
|
+
# require 'generators/controller_override'
|
|
7
|
+
# require 'generators/rails/decorator_generator'
|
|
8
|
+
|
|
9
|
+
# describe Rails::Generators::ControllerGenerator do
|
|
10
|
+
# destination File.expand_path('../tmp', __FILE__)
|
|
11
|
+
#
|
|
12
|
+
# before { prepare_destination }
|
|
13
|
+
# after(:all) { FileUtils.rm_rf destination_root }
|
|
14
|
+
#
|
|
15
|
+
# describe 'the generated decorator' do
|
|
16
|
+
# subject { file('app/decorators/your_model_decorator.rb') }
|
|
17
|
+
#
|
|
18
|
+
# describe 'naming' do
|
|
19
|
+
# before { run_generator %w(YourModels) }
|
|
20
|
+
#
|
|
21
|
+
# it { should contain 'class YourModelDecorator' }
|
|
22
|
+
# end
|
|
23
|
+
# end
|
|
24
|
+
# end
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'rails'
|
|
3
|
+
|
|
4
|
+
# TODO: Fix this file
|
|
5
|
+
# require 'ammeter/init'
|
|
6
|
+
# require 'generators/rails/decorator_generator'
|
|
7
|
+
#
|
|
8
|
+
# describe Rails::Generators::DecoratorGenerator do
|
|
9
|
+
# destination File.expand_path("../tmp", __FILE__)
|
|
10
|
+
#
|
|
11
|
+
# before { prepare_destination }
|
|
12
|
+
# after(:all) { FileUtils.rm_rf destination_root }
|
|
13
|
+
#
|
|
14
|
+
# describe "the generated decorator" do
|
|
15
|
+
# subject { file("app/decorators/your_model_decorator.rb") }
|
|
16
|
+
#
|
|
17
|
+
# describe "naming" do
|
|
18
|
+
# before { run_generator %w(YourModel) }
|
|
19
|
+
#
|
|
20
|
+
# it { should contain "class YourModelDecorator" }
|
|
21
|
+
# end
|
|
22
|
+
#
|
|
23
|
+
# describe "namespacing" do
|
|
24
|
+
# subject { file("app/decorators/namespace/your_model_decorator.rb") }
|
|
25
|
+
# before { run_generator %w(Namespace::YourModel) }
|
|
26
|
+
#
|
|
27
|
+
# it { should contain "class Namespace::YourModelDecorator" }
|
|
28
|
+
# end
|
|
29
|
+
#
|
|
30
|
+
# describe "inheritance" do
|
|
31
|
+
# context "by default" do
|
|
32
|
+
# before { run_generator %w(YourModel) }
|
|
33
|
+
#
|
|
34
|
+
# it { should contain "class YourModelDecorator < Drape::Decorator" }
|
|
35
|
+
# end
|
|
36
|
+
#
|
|
37
|
+
# context "with the --parent option" do
|
|
38
|
+
# before { run_generator %w(YourModel --parent=FooDecorator) }
|
|
39
|
+
#
|
|
40
|
+
# it { should contain "class YourModelDecorator < FooDecorator" }
|
|
41
|
+
# end
|
|
42
|
+
#
|
|
43
|
+
# context "with an ApplicationDecorator" do
|
|
44
|
+
# before do
|
|
45
|
+
# allow_any_instance_of(Object).to receive(:require).with("application_decorator").and_return(
|
|
46
|
+
# stub_const "ApplicationDecorator", Class.new
|
|
47
|
+
# )
|
|
48
|
+
# end
|
|
49
|
+
#
|
|
50
|
+
# before { run_generator %w(YourModel) }
|
|
51
|
+
#
|
|
52
|
+
# it { should contain "class YourModelDecorator < ApplicationDecorator" }
|
|
53
|
+
# end
|
|
54
|
+
# end
|
|
55
|
+
# end
|
|
56
|
+
#
|
|
57
|
+
# context "with -t=rspec" do
|
|
58
|
+
# describe "the generated spec" do
|
|
59
|
+
# subject { file("spec/decorators/your_model_decorator_spec.rb") }
|
|
60
|
+
#
|
|
61
|
+
# describe "naming" do
|
|
62
|
+
# before { run_generator %w(YourModel -t=rspec) }
|
|
63
|
+
#
|
|
64
|
+
# it { should contain "describe YourModelDecorator" }
|
|
65
|
+
# end
|
|
66
|
+
#
|
|
67
|
+
# describe "namespacing" do
|
|
68
|
+
# subject { file("spec/decorators/namespace/your_model_decorator_spec.rb") }
|
|
69
|
+
# before { run_generator %w(Namespace::YourModel -t=rspec) }
|
|
70
|
+
#
|
|
71
|
+
# it { should contain "describe Namespace::YourModelDecorator" }
|
|
72
|
+
# end
|
|
73
|
+
# end
|
|
74
|
+
# end
|
|
75
|
+
#
|
|
76
|
+
# context "with -t=test_unit" do
|
|
77
|
+
# describe "the generated test" do
|
|
78
|
+
# subject { file("test/decorators/your_model_decorator_test.rb") }
|
|
79
|
+
#
|
|
80
|
+
# describe "naming" do
|
|
81
|
+
# before { run_generator %w(YourModel -t=test_unit) }
|
|
82
|
+
#
|
|
83
|
+
# it { should contain "class YourModelDecoratorTest < Drape::TestCase" }
|
|
84
|
+
# end
|
|
85
|
+
#
|
|
86
|
+
# describe "namespacing" do
|
|
87
|
+
# subject { file("test/decorators/namespace/your_model_decorator_test.rb") }
|
|
88
|
+
# before { run_generator %w(Namespace::YourModel -t=test_unit) }
|
|
89
|
+
#
|
|
90
|
+
# it { should contain "class Namespace::YourModelDecoratorTest < Drape::TestCase" }
|
|
91
|
+
# end
|
|
92
|
+
# end
|
|
93
|
+
# end
|
|
94
|
+
# end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'support/dummy_app'
|
|
3
|
+
require 'support/matchers/have_text'
|
|
4
|
+
|
|
5
|
+
app = DummyApp.new(ENV['RAILS_ENV'])
|
|
6
|
+
spec_types = {
|
|
7
|
+
view: ['/posts/1', 'PostsController'],
|
|
8
|
+
mailer: ['/posts/1/mail', 'PostMailer']
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
app.start_server do
|
|
12
|
+
spec_types.each do |type, (path, controller)|
|
|
13
|
+
page = app.get(path)
|
|
14
|
+
|
|
15
|
+
describe "in a #{type}" do
|
|
16
|
+
it 'runs in the correct environment' do
|
|
17
|
+
expect(page).to have_text(app.environment).in('#environment')
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it 'uses the correct view context controller' do
|
|
21
|
+
expect(page).to have_text(controller).in('#controller')
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it 'can use built-in helpers' do
|
|
25
|
+
expect(page).to have_text('Once upon a...').in('#truncated')
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it 'can use built-in private helpers' do
|
|
29
|
+
# Nokogiri unescapes text!
|
|
30
|
+
expect(page).to have_text('<script>danger</script>').in('#html_escaped')
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it 'can use user-defined helpers from app/helpers' do
|
|
34
|
+
expect(page).to have_text('Hello, world!').in('#hello_world')
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it 'can use user-defined helpers from the controller' do
|
|
38
|
+
expect(page).to have_text('Goodnight, moon!').in('#goodnight_moon')
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
if type != :mailer
|
|
42
|
+
it 'can be passed to path helpers' do
|
|
43
|
+
expect(page).to have_text('/posts/1').in('#path_with_decorator')
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it 'can use path helpers with a model' do
|
|
47
|
+
expect(page).to have_text('/posts/1').in('#path_with_model')
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it 'can use path helpers with an id' do
|
|
51
|
+
expect(page).to have_text('/posts/1').in('#path_with_id')
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it 'can be passed to url helpers' do
|
|
56
|
+
expect(page).to have_text('http://test.host/posts/1').in('#url_with_decorator')
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it 'can use url helpers with a model' do
|
|
60
|
+
expect(page).to have_text('http://test.host/posts/1').in('#url_with_model')
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it 'can use url helpers with an id' do
|
|
64
|
+
expect(page).to have_text('http://test.host/posts/1').in('#url_with_id')
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
|
|
3
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
|
|
4
|
+
require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
|
|
5
|
+
|
|
6
|
+
Bundler.require(:default) if defined?(Bundler)
|
|
7
|
+
|
|
8
|
+
require 'benchmark'
|
|
9
|
+
require 'drape'
|
|
10
|
+
require './performance/models'
|
|
11
|
+
require './performance/decorators'
|
|
12
|
+
|
|
13
|
+
Benchmark.bm do |bm|
|
|
14
|
+
puts "\n[ Exclusivelly using #method_missing for model delegation ]"
|
|
15
|
+
[1_000, 10_000, 100_000].each do |i|
|
|
16
|
+
puts "\n[ #{i} ]"
|
|
17
|
+
bm.report('#new ') do
|
|
18
|
+
i.times do
|
|
19
|
+
ProductDecorator.decorate(Product.new)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
bm.report('#hello_world ') do
|
|
24
|
+
i.times do
|
|
25
|
+
ProductDecorator.decorate(Product.new).hello_world
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
bm.report('#sample_class_method ') do
|
|
30
|
+
i.times do
|
|
31
|
+
ProductDecorator.decorate(Product.new).class.sample_class_method
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
puts "\n[ Defining methods on method_missing first hit ]"
|
|
37
|
+
[1_000, 10_000, 100_000].each do |i|
|
|
38
|
+
puts "\n[ #{i} ]"
|
|
39
|
+
bm.report('#new ') do
|
|
40
|
+
i.times do
|
|
41
|
+
FastProductDecorator.decorate(FastProduct.new)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
bm.report('#hello_world ') do
|
|
46
|
+
i.times do
|
|
47
|
+
FastProductDecorator.decorate(FastProduct.new).hello_world
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
bm.report('#sample_class_method ') do
|
|
52
|
+
i.times do
|
|
53
|
+
FastProductDecorator.decorate(FastProduct.new).class.sample_class_method
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
require './performance/models'
|
|
2
|
+
class ProductDecorator < Drape::Decorator
|
|
3
|
+
def awesome_title
|
|
4
|
+
'Awesome Title'
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
# Original #method_missing
|
|
8
|
+
def method_missing(method, *args, &block)
|
|
9
|
+
if allow?(method)
|
|
10
|
+
begin
|
|
11
|
+
model.send(method, *args, &block)
|
|
12
|
+
rescue NoMethodError
|
|
13
|
+
super
|
|
14
|
+
end
|
|
15
|
+
else
|
|
16
|
+
super
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
class FastProductDecorator < Drape::Decorator
|
|
22
|
+
def awesome_title
|
|
23
|
+
'Awesome Title'
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Modified #method_missing
|
|
27
|
+
def method_missing(method, *args, &block)
|
|
28
|
+
if allow?(method)
|
|
29
|
+
begin
|
|
30
|
+
self.class.send :define_method, method do |*inner_args, &inner_block|
|
|
31
|
+
model.send(method, *inner_args, &inner_block)
|
|
32
|
+
end
|
|
33
|
+
send(method, *args, &block)
|
|
34
|
+
rescue NoMethodError
|
|
35
|
+
super
|
|
36
|
+
end
|
|
37
|
+
else
|
|
38
|
+
super
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
require './performance/active_record'
|
|
2
|
+
class Product < ActiveRecord::Base
|
|
3
|
+
def self.sample_class_method
|
|
4
|
+
'sample class method'
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def hello_world
|
|
8
|
+
'Hello, World'
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
class FastProduct < ActiveRecord::Base
|
|
13
|
+
def self.sample_class_method
|
|
14
|
+
'sample class method'
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def hello_world
|
|
18
|
+
'Hello, World'
|
|
19
|
+
end
|
|
20
|
+
end
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
require 'bundler/setup'
|
|
2
|
+
require 'drape'
|
|
3
|
+
require 'rails/version'
|
|
4
|
+
require 'action_controller'
|
|
5
|
+
require 'action_controller/test_case'
|
|
6
|
+
|
|
7
|
+
RSpec.configure do |config|
|
|
8
|
+
config.expect_with(:rspec) { |c| c.syntax = :expect }
|
|
9
|
+
config.order = :random
|
|
10
|
+
config.mock_with :rspec do |mocks|
|
|
11
|
+
mocks.yield_receiver_to_any_instance_implementation_blocks = true
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
class Model; include Drape::Decoratable; end
|
|
16
|
+
|
|
17
|
+
class Product < Model; end
|
|
18
|
+
class SpecialProduct < Product; end
|
|
19
|
+
class Other < Model; end
|
|
20
|
+
class ProductDecorator < Drape::Decorator; end
|
|
21
|
+
class ProductsDecorator < Drape::CollectionDecorator; end
|
|
22
|
+
|
|
23
|
+
class ProductPresenter < Drape::Decorator; end
|
|
24
|
+
|
|
25
|
+
class OtherDecorator < Drape::Decorator; end
|
|
26
|
+
|
|
27
|
+
module Namespaced
|
|
28
|
+
class Product < Model; end
|
|
29
|
+
class ProductDecorator < Drape::Decorator; end
|
|
30
|
+
|
|
31
|
+
class OtherDecorator < Drape::Decorator; end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# After each example, revert changes made to the class
|
|
35
|
+
def protect_class(klass)
|
|
36
|
+
before { stub_const klass.name, Class.new(klass) }
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def protect_module(mod)
|
|
40
|
+
before { stub_const mod.name, mod.dup }
|
|
41
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
require 'socket'
|
|
2
|
+
require 'net/http'
|
|
3
|
+
|
|
4
|
+
# Adapted from code by Jon Leighton
|
|
5
|
+
# https://github.com/jonleighton/focused_controller/blob/ec7ccf1/test/acceptance/app_test.rb
|
|
6
|
+
|
|
7
|
+
class DummyApp
|
|
8
|
+
def initialize(environment)
|
|
9
|
+
unless ['development', 'production'].include?(environment.to_s)
|
|
10
|
+
raise ArgumentError, 'Environment must be development or production'
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
@environment = environment
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
attr_reader :environment
|
|
17
|
+
|
|
18
|
+
def url
|
|
19
|
+
"http://#{localhost}:#{port}"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def get(path)
|
|
23
|
+
Net::HTTP.get(URI(url + path))
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def within_app(&block)
|
|
27
|
+
Dir.chdir(root, &block)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def start_server
|
|
31
|
+
within_app do
|
|
32
|
+
IO.popen("bundle exec rails s -e #{@environment} -p #{port} 2>&1") do |out|
|
|
33
|
+
start = Time.now
|
|
34
|
+
started = false
|
|
35
|
+
output = ''
|
|
36
|
+
timeout = 60.0
|
|
37
|
+
|
|
38
|
+
while !started && !out.eof? && Time.now - start <= timeout
|
|
39
|
+
output << read_output(out)
|
|
40
|
+
sleep 0.1
|
|
41
|
+
|
|
42
|
+
begin
|
|
43
|
+
TCPSocket.new(localhost, port)
|
|
44
|
+
rescue Errno::ECONNREFUSED
|
|
45
|
+
next
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
started = true
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
raise "Server failed to start:\n#{output}" unless started
|
|
52
|
+
|
|
53
|
+
yield
|
|
54
|
+
|
|
55
|
+
Process.kill('KILL', out.pid)
|
|
56
|
+
File.delete('tmp/pids/server.pid')
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
private
|
|
62
|
+
|
|
63
|
+
def root
|
|
64
|
+
File.expand_path('../../dummy', __FILE__)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def localhost
|
|
68
|
+
'127.0.0.1'
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def port
|
|
72
|
+
@port ||= begin
|
|
73
|
+
server = TCPServer.new(localhost, 0)
|
|
74
|
+
server.addr[1]
|
|
75
|
+
ensure
|
|
76
|
+
server.close if server
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def read_output(stream)
|
|
81
|
+
read = IO.select([stream], [], [stream], 0.1)
|
|
82
|
+
output = ''
|
|
83
|
+
loop { output << stream.read_nonblock(1024) } if read
|
|
84
|
+
output
|
|
85
|
+
rescue Errno::EAGAIN, Errno::EWOULDBLOCK, EOFError
|
|
86
|
+
output
|
|
87
|
+
end
|
|
88
|
+
end
|