gift_wrap 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 37ded86a1243c84591148c1c278126e00eca4c05
4
+ data.tar.gz: cc8f73fc149fdc246b96b43fa597d250562d429e
5
+ SHA512:
6
+ metadata.gz: a1b13e46b2b4c1eae2aa13e6b5d3c386ae4c2efd211ca6b015c3c7e86edebd9e96ab8ba069e0dd51c9edf594ae7c6a7b5b9547ef0b11662b439af1300e65df46
7
+ data.tar.gz: 782ab93cb6d43ffdd15faa770ee183a06f94d56a05a0442c2df21adfce77dca24256ef8d4fbe8152a197f138ca40d9c8e3b27719618b3958cbf244bc318b2469
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,56 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ gift_wrap (0.2.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ activemodel (4.2.6)
10
+ activesupport (= 4.2.6)
11
+ builder (~> 3.1)
12
+ activerecord (4.2.6)
13
+ activemodel (= 4.2.6)
14
+ activesupport (= 4.2.6)
15
+ arel (~> 6.0)
16
+ activesupport (4.2.6)
17
+ i18n (~> 0.7)
18
+ json (~> 1.7, >= 1.7.7)
19
+ minitest (~> 5.1)
20
+ thread_safe (~> 0.3, >= 0.3.4)
21
+ tzinfo (~> 1.1)
22
+ ansi (1.5.0)
23
+ arel (6.0.3)
24
+ builder (3.2.2)
25
+ i18n (0.7.0)
26
+ json (1.8.3)
27
+ minitest (5.9.0)
28
+ minitest-reporters (1.1.9)
29
+ ansi
30
+ builder
31
+ minitest (>= 5.0)
32
+ ruby-progressbar
33
+ rake (10.5.0)
34
+ ruby-progressbar (1.8.1)
35
+ sqlite3 (1.3.11)
36
+ thread_safe (0.3.5)
37
+ turn-again-reporter (1.1.0)
38
+ minitest-reporters (~> 1.0, >= 1.0.8)
39
+ tzinfo (1.2.2)
40
+ thread_safe (~> 0.1)
41
+
42
+ PLATFORMS
43
+ ruby
44
+
45
+ DEPENDENCIES
46
+ activemodel (~> 4.0)
47
+ activerecord (~> 4.0, >= 4.0.0)
48
+ bundler (~> 1.7)
49
+ gift_wrap!
50
+ minitest-reporters (~> 1.1)
51
+ rake (~> 10.0)
52
+ sqlite3 (~> 1.3, >= 1.3.0)
53
+ turn-again-reporter (~> 1.1, >= 1.1.0)
54
+
55
+ BUNDLED WITH
56
+ 1.12.5
data/LICENSE.md ADDED
@@ -0,0 +1,27 @@
1
+ Copyright (c) 2016, Paul Kwiatkowski
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+
7
+ * Redistributions of source code must retain the above copyright notice, this
8
+ list of conditions and the following disclaimer.
9
+
10
+ * Redistributions in binary form must reproduce the above copyright notice,
11
+ this list of conditions and the following disclaimer in the documentation
12
+ and/or other materials provided with the distribution.
13
+
14
+ * Neither the name of adalog nor the names of its
15
+ contributors may be used to endorse or promote products derived from
16
+ this software without specific prior written permission.
17
+
18
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README.md ADDED
@@ -0,0 +1 @@
1
+ # GiftWrap
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs << "test"
6
+ t.test_files = Dir[File.join(File.dirname(__FILE__), 'test/**/*test.rb')]
7
+ end
data/lib/gift_wrap.rb ADDED
@@ -0,0 +1,16 @@
1
+ module GiftWrap
2
+
3
+ def self.config
4
+ @config ||= GiftWrap::Configuration.new
5
+ end
6
+
7
+
8
+ def self.configure
9
+ yield(config)
10
+ end
11
+
12
+ end
13
+
14
+ Dir[File.join(File.dirname(__FILE__), "gift_wrap", "*.rb")].each do |rb_file|
15
+ require rb_file
16
+ end
@@ -0,0 +1,54 @@
1
+ module GiftWrap
2
+ module ActiveRecordPresenter
3
+
4
+ def self.included(base)
5
+ base.send(:include, ::GiftWrap::Presenter)
6
+ base.extend(::GiftWrap::ActiveRecordPresenter::ClassMethods)
7
+ end
8
+
9
+
10
+ module ClassMethods
11
+
12
+ ##
13
+ # Sets delegate methods via ::unwrap_for en masse for all columns.
14
+ # The :attribute keyword argument can specify which columns are attributes
15
+ # in one of three ways from different values:
16
+ # - true: all columns will be considered attributes
17
+ # - false: no columns will be considered attributes
18
+ # - A hash with the key :only, whose value specifies which columns to consider as
19
+ # attributes, either singular or as an array of column names.
20
+ # - A hash with the key :except, whose value specifies which columns to consider as
21
+ # attributes, either singular or as an array of column names.
22
+ def unwrap_columns_for(active_record_model, attribute: true, **options)
23
+ columns = active_record_model.columns.map { |col| col.name.to_sym }
24
+ if true == attribute || false == attribute
25
+ unwrap_for(*columns, attribute: attribute, **options)
26
+ elsif Hash === attribute
27
+ as_attributes, not_attributes = partition_columns_for_attributes(columns, attribute)
28
+ unwrap_for(*as_attributes, attribute: true, **options)
29
+ unwrap_for(*not_attributes, attribute: false, **options)
30
+ else
31
+ unwrap_for(*columns, attribute: attribute, **options)
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ def partition_columns_for_attributes(columns, attribute_options)
38
+ partitioned =
39
+ if attribute_options.key?(:only)
40
+ accepted_columns = [attribute_options.fetch(:only)].flatten
41
+ columns.partition { |col| accepted_columns.include?(col) }
42
+ elsif attribute_options.key?(:except)
43
+ rejected_columns = [attribute_options.fetch(:except)].flatten
44
+ columns.partition { |col| !rejected_columns.include?(col) }
45
+ else
46
+ [columns, []]
47
+ end
48
+ return *partitioned
49
+ end
50
+
51
+ end
52
+
53
+ end
54
+ end
@@ -0,0 +1,15 @@
1
+ module GiftWrap
2
+ class Configuration
3
+
4
+ attr_accessor :use_serializers
5
+
6
+ def initialize
7
+ @use_serializers = !!defined?(ActiveModel)
8
+ end
9
+
10
+ def use_serializers?
11
+ @use_serializers
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,126 @@
1
+ module GiftWrap
2
+ module Presenter
3
+
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ if GiftWrap.config.use_serializers? && defined? ActiveModel::Serializers::JSON
7
+ base.send(:include, ActiveModel::Serializers::JSON)
8
+ end
9
+ end
10
+
11
+ ##
12
+ # Current options:
13
+ # - associations: a hash mapping association names to the presenter class
14
+ # to use when wrapping the association. Used to override the :with setting of
15
+ # a call to ::wrap_association at an instance level.
16
+ def initialize(wrapped_object, **options)
17
+ @wrapped_object = wrapped_object
18
+ @wrapped_association_presenters = options.fetch(:associations, {})
19
+ end
20
+
21
+ ##
22
+ # Used in methods defined by ::wrap_association to determine the presenter class that
23
+ # is used for a particular association name. First checks any instance-specific options
24
+ # for the association name, and falls back to those defined by the :with option passed
25
+ # to any ::wrap_association call for said association_name.
26
+ def wrapped_association_presenter(association_name)
27
+ if @wrapped_association_presenters.none?
28
+ self.class.wrapped_association_defaults.fetch(association_name) do |name|
29
+ raise NoMethodError.new("No association registered as '#{name}'.")
30
+ end
31
+ else
32
+ @wrapped_association_presenters.fetch(
33
+ association_name,
34
+ self.class.wrapped_association_defaults.fetch(association_name) do |name|
35
+ raise NoMethodError.new("No association registered as '#{name}'.")
36
+ end)
37
+ end
38
+ end
39
+
40
+ ##
41
+ # For use by ActiveModel::Serializers::JSON in building a default set of values
42
+ # when calling #as_json or #to_json
43
+ def attributes
44
+ self.class.attributes.each.with_object({}) do |msg, attr_hash|
45
+ attr_hash[msg.to_s] = self.send(msg)
46
+ end
47
+ end
48
+
49
+
50
+ module ClassMethods
51
+
52
+ ##
53
+ # Contains the of messages (which are used as hash keys) to send to self and collect
54
+ # when building an attributes hash.
55
+ def attributes
56
+ @attributes ||= Set.new
57
+ end
58
+
59
+ ##
60
+ # Contains the list of methods which will be delegated to the wrapped object rather than
61
+ # defined on presenter class itself.
62
+ def unwrapped_methods
63
+ @unwrapped_methods ||= Set.new
64
+ end
65
+
66
+ ##
67
+ # Contains the default settings for building any associations. These may be overridden
68
+ # on a per-intance basis.
69
+ def wrapped_association_defaults
70
+ @wrapped_association_defaults ||= {}
71
+ end
72
+
73
+ ##
74
+ # Defines a private method name by which the wrapped object may be referenced internally.
75
+ def wrapped_as(reference)
76
+ define_method(reference) do
77
+ @wrapped_object
78
+ end
79
+ send(:private, reference)
80
+ end
81
+
82
+ ##
83
+ # Declares one or more messages (method names) to be attributes.
84
+ def attribute(*names)
85
+ names.flatten.each do |name|
86
+ attributes << name
87
+ end
88
+ end
89
+
90
+ ##
91
+ # Declares that one or more received messages (method calls) should be delegated directly
92
+ # to the wrapped object, and which may be optionally declared as attributes.
93
+ #
94
+ def unwrap_for(*names, attribute: false, **options)
95
+ names = names.flatten
96
+ names.each do |name|
97
+ unwrapped_methods << name
98
+ attributes << name if attribute
99
+ end
100
+ delegate(*names, to: :@wrapped_object)
101
+ end
102
+
103
+ ##
104
+ # Declares that the result of a delegated method call should be wrapped in another
105
+ # presenter, as defined by the :with keyword argument.
106
+ # This results in a method by the name of the first parameter by default, but may be
107
+ # customized with the :as keyword argument.
108
+ # Associations whose method produces an enumerable (ideally an Array) will have each
109
+ # item wrapped in the presenter and collected in an Array which is then returned.
110
+ def wrap_association(association, with: , as: association, **options)
111
+ wrapped_association_defaults[as] = with
112
+ define_method(as) do
113
+ presenter_class = wrapped_association_presenter(as)
114
+ associated = @wrapped_object.send(association)
115
+ if associated.respond_to?(:each)
116
+ associated.map { |assoc| presenter_class.new(assoc, **options) }
117
+ else
118
+ presenter_class.new(associated, **options)
119
+ end
120
+ end
121
+ end
122
+
123
+ end
124
+
125
+ end
126
+ end
@@ -0,0 +1,3 @@
1
+ module GiftWrap
2
+ VERSION = '0.2.0'
3
+ end
@@ -0,0 +1,14 @@
1
+ ActiveRecord::Schema.define do
2
+ self.verbose = false
3
+
4
+ create_table :users, :force => true do |t|
5
+ t.string :email
6
+ t.string :first_name
7
+ t.string :last_name
8
+ t.string :encrypted_password
9
+ t.integer :sign_in_count
10
+
11
+ t.timestamps null: false
12
+ end
13
+
14
+ end
@@ -0,0 +1,18 @@
1
+ ##
2
+ # An object to associate with a map, for testing associations & wrapping thereof.
3
+ class Legend
4
+
5
+ def initialize(colored_regions, colored_lines)
6
+ @colored_regions = colored_regions
7
+ @colored_lines = colored_lines
8
+ end
9
+
10
+ def region_meaning(color)
11
+ @colored_regions[color]
12
+ end
13
+
14
+ def line_meaning(color)
15
+ @colored_lines[color]
16
+ end
17
+
18
+ end
@@ -0,0 +1,26 @@
1
+ ##
2
+ # Everyone loves maps.
3
+ class Map
4
+
5
+ attr_reader :type, :center, :units, :legend
6
+ attr_accessor :notes
7
+
8
+ def initialize(type, center, units, legend = :asshole_mapmaker_forgot_legend)
9
+ @type = type
10
+ @center = center
11
+ @units = units
12
+ @notes = ""
13
+ @legend = legend
14
+ end
15
+
16
+ def shows_roads?
17
+ maps_with_roads.include?(type)
18
+ end
19
+
20
+ private
21
+
22
+ def maps_with_roads
23
+ ['road', 'traffic', 'political']
24
+ end
25
+
26
+ end
@@ -0,0 +1,7 @@
1
+ class User < ActiveRecord::Base
2
+
3
+ def initials
4
+ "#{first_name[0]}#{last_name[0]}"
5
+ end
6
+
7
+ end
@@ -0,0 +1,37 @@
1
+ module UserRecords
2
+
3
+ def self.create!
4
+ users_attributes.each do |attrs|
5
+ User.create(attrs)
6
+ end
7
+ end
8
+
9
+
10
+ def self.temporal_reference_point
11
+ @temporal_reference_point ||= Time.now
12
+ end
13
+
14
+
15
+ def self.users_attributes
16
+ [ { id: 1,
17
+ email: "paulwall@example.com",
18
+ first_name: "Paul",
19
+ last_name: "Wall",
20
+ encrypted_password: "$2a$10$DbRvyFxovWAly4ZCDtcJ6uVhbMGya2iGiLCURhSwM1ZGUyXpM5UiW",
21
+ sign_in_count: 2,
22
+ created_at: temporal_reference_point,
23
+ updated_at: temporal_reference_point,
24
+ },
25
+ { id: 2,
26
+ email: "gendoarrighetti@example.com",
27
+ first_name: "Gendo",
28
+ last_name: "Arrighetti",
29
+ encrypted_password: "$2a$10$DbRvyFxovWAly4ZCDtcJ6uVhbMGya2iGiLCURhSwM1ZGUyXpM5UiW",
30
+ sign_in_count: 0,
31
+ created_at: temporal_reference_point,
32
+ updated_at: temporal_reference_point,
33
+ },
34
+ ]
35
+ end
36
+
37
+ end
@@ -0,0 +1,70 @@
1
+ require 'bundler/setup'
2
+ require 'minitest/autorun'
3
+ require 'minitest/reporters'
4
+ require 'minitest/reporters/turn_again_reporter'
5
+ Minitest::Reporters.use!(Minitest::Reporters::TurnAgainReporter.new)
6
+
7
+ ##
8
+ # The following active_support and active_model files are the specific minimum
9
+ # dependencies for using ActiveModel::Serializers::JSON
10
+ require 'active_support/json'
11
+ require 'active_support/concern'
12
+ require 'active_support/core_ext/class/attribute'
13
+ require 'active_model/naming'
14
+ require 'active_model/serialization'
15
+ require 'active_model/serializers/json'
16
+
17
+ ##
18
+ # And of course, require ourselves.
19
+ require 'gift_wrap'
20
+
21
+ ##
22
+ # Blatantly stolen from ActiveSupport::Testing::Declarative
23
+ # Allows for test files such as
24
+ # test "verify something" do
25
+ # ...
26
+ # end
27
+ # which become methods named test_verify_something, leaving a visual difference
28
+ # between tests themselves and any helper methods declared in the usual
29
+ # manner of `def some_helper_method`.
30
+ module DeclarativeTests
31
+ def test(name, &block)
32
+ test_name = "test_#{name.gsub(/\s+/,'_')}".to_sym
33
+ defined = instance_method(test_name) rescue false
34
+ raise "#{test_name} is already defined in #{self}" if defined
35
+ if block_given?
36
+ define_method(test_name, &block)
37
+ else
38
+ define_method(test_name) do
39
+ flunk "No implementation provided for #{name}"
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ class Minitest::Test
46
+ extend DeclarativeTests
47
+ end
48
+
49
+ ##
50
+ # ActiveRecord setup for testing use of GiftWrap::ActiveRecordPresenter
51
+ #
52
+ # TODO (possibly):
53
+ # Have some sort of method for switching on/off inclusion and execution
54
+ # of active_record in tests. The goal would be to ensure that no tests
55
+ # unrelated to ActiveRecord accidentally come to rely on its presence, or on
56
+ # the presence of the friends it pulls in (ActiveModel, ActiveSupport).
57
+ #
58
+ # Some ordering of some of these steps is important:
59
+ # 1. Set up in-memory sqlite3 connection.
60
+ # 2. Require the model class.
61
+ # 3. Load the database schema. Doing this before loading the model broke things?
62
+ # 4. Load a file that creates some user rows and run said creation method.
63
+ require 'active_record'
64
+ require 'sqlite3'
65
+
66
+ ActiveRecord::Base.establish_connection adapter: "sqlite3", database: ":memory:"
67
+ require File.join(File.dirname(__FILE__), "domain", "user.rb")
68
+ require File.join(File.dirname(__FILE__), "database", "schema.rb")
69
+ require File.join(File.dirname(__FILE__), "fixtures", "user_records.rb")
70
+ UserRecords.create!
@@ -0,0 +1,115 @@
1
+ require 'test_helper'
2
+
3
+ class ActiveRecordPresenterTest < Minitest::Test
4
+
5
+ ## Test Implementation Classes #################################################
6
+
7
+
8
+ class SimpleUserPresenter
9
+ include GiftWrap::ActiveRecordPresenter
10
+
11
+ unwrap_columns_for ::User
12
+
13
+ unwrap_for :initials
14
+
15
+ def email_with_display_name
16
+ "#{first_name} #{last_name} <#{email}>"
17
+ end
18
+
19
+ end
20
+
21
+
22
+ class NoAttributeUserPresenter
23
+ include GiftWrap::ActiveRecordPresenter
24
+ unwrap_columns_for ::User, attribute: false
25
+ end
26
+
27
+
28
+ class UserPresenterUsingOnly
29
+ include GiftWrap::ActiveRecordPresenter
30
+ unwrap_columns_for ::User, attribute: { only: [:first_name, :email, :last_name] }
31
+ end
32
+
33
+
34
+ class UserPresenterUsingExcept
35
+ include GiftWrap::ActiveRecordPresenter
36
+ unwrap_columns_for ::User, attribute: { except: [:first_name, :email, :last_name] }
37
+ end
38
+
39
+
40
+ ## Setup Helpers #########################################################
41
+
42
+
43
+ def sample_user
44
+ User.find(1)
45
+ end
46
+
47
+
48
+ ## Test Cases ##################################################################
49
+
50
+
51
+ test "can unwrap columns" do
52
+ paulwall = sample_user
53
+ presenter = SimpleUserPresenter.new(paulwall)
54
+ assert(presenter.respond_to?(:email))
55
+ assert(presenter.respond_to?(:first_name))
56
+ assert(presenter.respond_to?(:encrypted_password))
57
+ assert_equal(paulwall.email, presenter.email)
58
+ end
59
+
60
+
61
+ test "can respond to additional instance methods" do
62
+ paulwall = sample_user
63
+ presenter = SimpleUserPresenter.new(paulwall)
64
+ assert_equal("Paul Wall <paulwall@example.com>", presenter.email_with_display_name)
65
+ end
66
+
67
+
68
+ test "can wrap methods as expected of a presenter" do
69
+ paulwall = sample_user
70
+ presenter = SimpleUserPresenter.new(paulwall)
71
+ assert_equal("PW", paulwall.initials)
72
+ assert_equal("PW", presenter.initials)
73
+ end
74
+
75
+
76
+ test "columns are set as presenter attributes by default" do
77
+ paulwall = sample_user
78
+ presenter = SimpleUserPresenter.new(paulwall)
79
+ assert_includes(presenter.attributes.keys, 'email')
80
+ assert_includes(presenter.attributes.keys, 'created_at')
81
+ assert_includes(presenter.attributes.keys, 'updated_at')
82
+ assert_includes(presenter.attributes.keys, 'first_name')
83
+ assert_includes(presenter.attributes.keys, 'last_name')
84
+ assert_includes(presenter.attributes.keys, 'sign_in_count' )
85
+ end
86
+
87
+
88
+ test "unwrap_columns_for can be asked to set no columns as attributes" do
89
+ presenter = NoAttributeUserPresenter.new(sample_user)
90
+ assert_equal({}, presenter.attributes)
91
+ end
92
+
93
+
94
+ test "unwrap_columns_for can specify attributes with :only" do
95
+ presenter = UserPresenterUsingOnly.new(sample_user)
96
+ assert_includes(presenter.attributes.keys, 'email')
97
+ assert_includes(presenter.attributes.keys, 'first_name')
98
+ assert_includes(presenter.attributes.keys, 'last_name')
99
+ refute_includes(presenter.attributes.keys, 'created_at')
100
+ refute_includes(presenter.attributes.keys, 'updated_at')
101
+ refute_includes(presenter.attributes.keys, 'sign_in_count')
102
+ end
103
+
104
+
105
+ test "unwrap_columns_for can specify attributes with :except" do
106
+ presenter = UserPresenterUsingExcept.new(sample_user)
107
+ assert_includes(presenter.attributes.keys, 'created_at')
108
+ assert_includes(presenter.attributes.keys, 'updated_at')
109
+ assert_includes(presenter.attributes.keys, 'sign_in_count')
110
+ refute_includes(presenter.attributes.keys, 'email')
111
+ refute_includes(presenter.attributes.keys, 'first_name')
112
+ refute_includes(presenter.attributes.keys, 'last_name')
113
+ end
114
+
115
+ end
@@ -0,0 +1,43 @@
1
+ require 'test_helper'
2
+ require 'domain/map'
3
+ require 'domain/legend'
4
+
5
+ class ConfigurationTest < Minitest::Test
6
+
7
+ ## Setup Helpers #################################################
8
+
9
+ ##
10
+ # Since we're reconfiguring module-wide settings, we can't rely on a class defined
11
+ # at load time to pick up on changes to the configuration during successive test cases.
12
+ def build_presenter_class
13
+ Class.new do
14
+ include GiftWrap::Presenter
15
+ unwrap_for :type
16
+ unwrap_for :units, attribute: true
17
+ end
18
+ end
19
+
20
+
21
+ ## Test Cases ##################################################################
22
+
23
+
24
+ test "presenters include serializers when use_serializers is true" do
25
+ GiftWrap.configure do |config|
26
+ config.use_serializers = true
27
+ end
28
+ map = Map.new("physical", ["here", "there"], "mi")
29
+ presenter = build_presenter_class.new(map)
30
+ assert_kind_of(ActiveModel::Serializers::JSON, presenter)
31
+ end
32
+
33
+
34
+ test "presenters do not include serializers when use_serializers is false" do
35
+ GiftWrap.configure do |config|
36
+ config.use_serializers = false
37
+ end
38
+ map = Map.new("physical", ["here", "there"], "mi")
39
+ presenter = build_presenter_class.new(map)
40
+ refute_kind_of(ActiveModel::Serializers::JSON, presenter)
41
+ end
42
+
43
+ end
@@ -0,0 +1,259 @@
1
+ require 'test_helper'
2
+ require 'domain/map'
3
+ require 'domain/legend'
4
+
5
+ class PresenterTest < Minitest::Test
6
+
7
+ ## Test Implementation Classes #################################################
8
+
9
+
10
+ ##
11
+ # A presenter which adds a few new methods and attributes to a given Map.
12
+ class SimpleMapPresenter
13
+ include GiftWrap::Presenter
14
+
15
+ attribute :metric?
16
+
17
+ unwrap_for :type
18
+ unwrap_for :units, attribute: true
19
+
20
+ def metric?
21
+ metric_map_units.include?(units)
22
+ end
23
+
24
+ def contains_region?(region_name)
25
+ false # Implementation not important
26
+ end
27
+
28
+ private
29
+
30
+ def metric_map_units
31
+ ['m', 'km']
32
+ end
33
+
34
+ end
35
+
36
+ ##
37
+ # Gives an explicit name to the wrapped object for reference
38
+ # internally to the class, and has a method which uses that reference
39
+ # for use in proving that an internal reference is functioning.
40
+ class ExplicitReferencePresenter
41
+ include GiftWrap::Presenter
42
+
43
+ wrapped_as :explicit_reference
44
+
45
+ def uses_explicit_reference(msg_name)
46
+ explicit_reference.send(msg_name)
47
+ end
48
+
49
+ end
50
+
51
+ ##
52
+ # Presenter intended to work with a Legend.
53
+ class LegendPresenter
54
+ include GiftWrap::Presenter
55
+
56
+ unwrap_for :line_meaning
57
+
58
+ attribute :red_lines
59
+
60
+ def red_lines
61
+ line_meaning(:red)
62
+ end
63
+
64
+ def yellow_lines
65
+ line_meaning(:yellow)
66
+ end
67
+
68
+ def green_lines
69
+ line_meaning(:green)
70
+ end
71
+
72
+ end
73
+
74
+ ##
75
+ # Map Presenter which wraps its :legend association, for use in testing associations
76
+ # being wrapped in a presenter of their own.
77
+ class LegendaryMapPresenter
78
+ include GiftWrap::Presenter
79
+
80
+ unwrap_for :type, :units
81
+
82
+ wrap_association :legend, with: LegendPresenter
83
+
84
+ def metric?
85
+ metric_map_units.include?(units)
86
+ end
87
+
88
+ private
89
+
90
+ def metric_map_units
91
+ ['m', 'km']
92
+ end
93
+
94
+ end
95
+
96
+ ##
97
+ # Another presenter for legends as an alternative to test if overriding association
98
+ # presenters on a per-instance basis functions.
99
+ class MisleadingLegendPresenter
100
+ include GiftWrap::Presenter
101
+
102
+ unwrap_for :line_meaning
103
+
104
+ def red_lines
105
+ "no congestion"
106
+ end
107
+
108
+ def yellow_lines
109
+ "no congestion"
110
+ end
111
+
112
+ def green_lines
113
+ "no congestion"
114
+ end
115
+
116
+ end
117
+
118
+ ##
119
+ # Map Presenter which wraps its :legend association with the name :foobar
120
+ class FoobarLegendMapPresenter
121
+ include GiftWrap::Presenter
122
+
123
+ unwrap_for :type, :units
124
+
125
+ wrap_association :legend, with: LegendPresenter, as: :foobar
126
+
127
+ def metric?
128
+ metric_map_units.include?(units)
129
+ end
130
+
131
+ private
132
+
133
+ def metric_map_units
134
+ ['m', 'km']
135
+ end
136
+
137
+ end
138
+
139
+
140
+ ## Setup Helpers #########################################################
141
+
142
+
143
+ def physical_map
144
+ Map.new("physical", ["here", "there"], "mi")
145
+ end
146
+
147
+
148
+ def map_with_legend
149
+ Map.new("traffic", "downtown", "km", traffic_legend)
150
+ end
151
+
152
+
153
+ def traffic_legend
154
+ Legend.new(
155
+ { beige: "land",
156
+ blue: "water"
157
+ },
158
+ { green: "no congestion",
159
+ yellow: "light congestion",
160
+ red: "heavy congestion",
161
+ black: "impassable"
162
+ })
163
+ end
164
+
165
+
166
+ ## Test Cases ##################################################################
167
+
168
+
169
+ test "unwrapped methods are delegated properly" do
170
+ map = physical_map
171
+ presenter = SimpleMapPresenter.new(map)
172
+ assert_equal(map.type, presenter.type)
173
+ assert_equal(map.units, presenter.units)
174
+ end
175
+
176
+
177
+ test "methods not explicitly unwrapped are not accessible" do
178
+ map = physical_map
179
+ presenter = SimpleMapPresenter.new(map)
180
+ assert(map.respond_to?(:center))
181
+ refute(presenter.respond_to?(:center))
182
+ end
183
+
184
+
185
+ test "attributes can include unwrapped methods" do
186
+ attributes = SimpleMapPresenter.new(physical_map).attributes
187
+ assert_includes(attributes.keys, 'units')
188
+ end
189
+
190
+
191
+ test "attributes do not include unwrapped methods by default" do
192
+ attributes = SimpleMapPresenter.new(physical_map).attributes
193
+ refute_includes(attributes.keys, 'type')
194
+ end
195
+
196
+
197
+ test "attributes hash include explicitly declared attributes" do
198
+ attributes = SimpleMapPresenter.new(physical_map).attributes
199
+ assert_includes(attributes.keys, 'metric?')
200
+ end
201
+
202
+
203
+ test "can reference a wrapped object internally via wrapped_as name" do
204
+ map = physical_map
205
+ presenter = ExplicitReferencePresenter.new(map)
206
+ assert_equal(map.type, presenter.uses_explicit_reference(:type))
207
+ assert_equal(map.units, presenter.uses_explicit_reference(:units))
208
+ assert_raises(NoMethodError) do |variable|
209
+ presenter.explicit_reference
210
+ end
211
+ end
212
+
213
+
214
+ test "associations can be wrapped with their own presenter class" do
215
+ map = map_with_legend
216
+ presenter = LegendaryMapPresenter.new(map)
217
+ assert(map.respond_to?(:legend))
218
+ assert(presenter.respond_to?(:legend))
219
+ assert(LegendPresenter === presenter.legend)
220
+ assert(presenter.legend.respond_to?(:line_meaning))
221
+ assert(presenter.legend.respond_to?(:red_lines))
222
+ refute_includes(presenter.legend.attributes.keys, 'line_meaning')
223
+ assert_includes(presenter.legend.attributes.keys, 'red_lines')
224
+ assert_equal(presenter.legend.green_lines, 'no congestion')
225
+ assert_equal(presenter.legend.red_lines, 'heavy congestion')
226
+ end
227
+
228
+
229
+ test "associations' class can be specified on a per-instance basis" do
230
+ map = map_with_legend
231
+ presenter = LegendaryMapPresenter.new(map, associations: {
232
+ legend: MisleadingLegendPresenter
233
+ })
234
+ assert(map.respond_to?(:legend))
235
+ assert(presenter.respond_to?(:legend))
236
+ assert(MisleadingLegendPresenter === presenter.legend)
237
+ assert(presenter.legend.respond_to?(:line_meaning))
238
+ assert(presenter.legend.respond_to?(:red_lines))
239
+ assert_equal(presenter.legend.green_lines, 'no congestion')
240
+ assert_equal(presenter.legend.red_lines, 'no congestion')
241
+ end
242
+
243
+
244
+ test "associations' name can be something other than assoication name" do
245
+ map = map_with_legend
246
+ presenter = FoobarLegendMapPresenter.new(map)
247
+ assert(map.respond_to?(:legend))
248
+ assert(presenter.respond_to?(:foobar))
249
+ assert(LegendPresenter === presenter.foobar)
250
+ # Most of this mirrors the other association tests for good measure.
251
+ assert(presenter.foobar.respond_to?(:line_meaning))
252
+ assert(presenter.foobar.respond_to?(:red_lines))
253
+ refute_includes(presenter.foobar.attributes.keys, 'line_meaning')
254
+ assert_includes(presenter.foobar.attributes.keys, 'red_lines')
255
+ assert_equal(presenter.foobar.green_lines, 'no congestion')
256
+ assert_equal(presenter.foobar.red_lines, 'heavy congestion')
257
+ end
258
+
259
+ end
metadata ADDED
@@ -0,0 +1,194 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gift_wrap
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Paul Kwiatkowski
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-06-27 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activemodel
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '4.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '4.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.7'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.7'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: minitest-reporters
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.1'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.1'
69
+ - !ruby/object:Gem::Dependency
70
+ name: turn-again-reporter
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.1'
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: 1.1.0
79
+ type: :development
80
+ prerelease: false
81
+ version_requirements: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - "~>"
84
+ - !ruby/object:Gem::Version
85
+ version: '1.1'
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: 1.1.0
89
+ - !ruby/object:Gem::Dependency
90
+ name: activerecord
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '4.0'
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ version: 4.0.0
99
+ type: :development
100
+ prerelease: false
101
+ version_requirements: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - "~>"
104
+ - !ruby/object:Gem::Version
105
+ version: '4.0'
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: 4.0.0
109
+ - !ruby/object:Gem::Dependency
110
+ name: sqlite3
111
+ requirement: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - "~>"
114
+ - !ruby/object:Gem::Version
115
+ version: '1.3'
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: 1.3.0
119
+ type: :development
120
+ prerelease: false
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - "~>"
124
+ - !ruby/object:Gem::Version
125
+ version: '1.3'
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ version: 1.3.0
129
+ description: A simple Ruby presenter library, for those who enjoy a strong separation
130
+ of concerns. You include a module, call some class-level macro-style methods, and
131
+ suddenly you're presenting for a wrapped object. No magic. If your knowledge of
132
+ pattern names comes from the Rails ecosystem, you might have used the popular Draper
133
+ 'decorator' library. Think of this like that one, except the term 'presenter' is
134
+ a better fit.
135
+ email:
136
+ - paul@groupraise.com
137
+ executables: []
138
+ extensions: []
139
+ extra_rdoc_files: []
140
+ files:
141
+ - Gemfile
142
+ - Gemfile.lock
143
+ - LICENSE.md
144
+ - README.md
145
+ - Rakefile
146
+ - lib/gift_wrap.rb
147
+ - lib/gift_wrap/active_record_presenter.rb
148
+ - lib/gift_wrap/configuration.rb
149
+ - lib/gift_wrap/presenter.rb
150
+ - lib/gift_wrap/version.rb
151
+ - test/database/schema.rb
152
+ - test/domain/legend.rb
153
+ - test/domain/map.rb
154
+ - test/domain/user.rb
155
+ - test/fixtures/user_records.rb
156
+ - test/test_helper.rb
157
+ - test/unit/active_record_presenter_test.rb
158
+ - test/unit/configuration_test.rb
159
+ - test/unit/presenter_test.rb
160
+ homepage: https://github.com/swifthand/adalog
161
+ licenses:
162
+ - Revised BSD, see LICENSE.md
163
+ metadata: {}
164
+ post_install_message:
165
+ rdoc_options: []
166
+ require_paths:
167
+ - lib
168
+ required_ruby_version: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - ">="
171
+ - !ruby/object:Gem::Version
172
+ version: '0'
173
+ required_rubygems_version: !ruby/object:Gem::Requirement
174
+ requirements:
175
+ - - ">="
176
+ - !ruby/object:Gem::Version
177
+ version: '0'
178
+ requirements: []
179
+ rubyforge_project:
180
+ rubygems_version: 2.4.8
181
+ signing_key:
182
+ specification_version: 4
183
+ summary: A simple Ruby presenter library, for those who enjoy a strong separation
184
+ of concerns.
185
+ test_files:
186
+ - test/database/schema.rb
187
+ - test/domain/legend.rb
188
+ - test/domain/map.rb
189
+ - test/domain/user.rb
190
+ - test/fixtures/user_records.rb
191
+ - test/test_helper.rb
192
+ - test/unit/active_record_presenter_test.rb
193
+ - test/unit/configuration_test.rb
194
+ - test/unit/presenter_test.rb