mystique 0.9.1 → 1.0.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a2ab3b70d916e56b8cce430379cf148e049ad2681931f5ed0f81eaa089f2fc86
4
- data.tar.gz: 9785cc05e6ef78b7228ff97da5b11d303f6ff2053ac7a124ee69f6b15e021516
3
+ metadata.gz: 02f8184f7dc42d547bf1a953219e7fc4fbb441a6bc435370a48bd971b218cd8d
4
+ data.tar.gz: 3e379ef4b394972fc13598a585bcfa8a182e5b1ea62fb33719fba79a37f2caf1
5
5
  SHA512:
6
- metadata.gz: c85c86ccea8b6a7178a3dc18a17e0cdad8663be3d0861e3ae6043cd1e8ec1e5479a327ca672bb60785f42dbab313b8fe475daaa776dda06186a63e3015aebd26
7
- data.tar.gz: 6e30f16fb9fcf8867a8a285c5c54bbdc4f865ea4ef0eea014a3927cff130f4cbde4d016b92f6e424291db02ddf64df88365d4ac4eda7f79ef16cd3bbb7b361fe
6
+ metadata.gz: c41407e8ae208303733524ff0688d3cb8fe6ff743f2460fd310f547ec06d2a01fa30fd39ce1186053c12f99effbc06e370f3ba697b9ffb52c1a067b402506c18
7
+ data.tar.gz: ea4e30ff77ded641e3e97915940566a5d72b5e7ea4430c67503bf7ae08157e880a9c3c5905b4e2501839cc827925a8bbe6dd7bdb0b3bfd790ff82579cd2f1a26
data/README.md CHANGED
@@ -4,7 +4,7 @@ Mystique is a gem that implements the presenter pattern. It allows you to augmen
4
4
 
5
5
  ## How to present
6
6
 
7
- Mystique ships with the `.present` method, which wraps the target object in a presenter and yield the presenter if a block is given, or returns it. If there default presenter is not available, the original object gets yielded/returned.
7
+ Mystique ships with the `.present` method, which wraps the target object in a presenter and yield the presenter if a block is given, and returns it. If there default presenter is not available, the original object gets yielded/returned.
8
8
 
9
9
  ```ruby
10
10
  Item = Struct.new(:name, :price)
@@ -26,10 +26,18 @@ The default presenter is inferred from the target object's class name. So, for t
26
26
 
27
27
  If `ItemPresenter` is not defined, you'll get back your original item.
28
28
 
29
+ ```ruby
30
+ Other = Class.new
31
+
32
+ other_presenter = Mystique.present(Other.new)
33
+
34
+ other_presenter.class
35
+ # => Other
36
+ ```
29
37
 
30
38
  ### Context
31
39
 
32
- The context is the object that, conveniently, provides the context in which the target object will be rendered.
40
+ The context is the object that, conveniently, provides the context in which the original object will be rendered.
33
41
 
34
42
  Currently, the context defaults to a null context, which accepts any message sent and does nothing.
35
43
 
@@ -38,19 +46,34 @@ You can set the context in 3 ways:
38
46
  #### Using the `.context` method when defining your presenter:
39
47
 
40
48
  ```ruby
41
- class UserPresenter < Mystique::Presenter
42
- context MyHelpers
49
+ module UrlHelpers
50
+ def self.root_path
51
+ "/"
52
+ end
53
+ end
43
54
 
44
- # ...
55
+ Web = Class.new
56
+
57
+ class WebPresenter < Mystique::Presenter
58
+ context UrlHelpers
59
+
60
+ def root
61
+ h.root_path
62
+ end
45
63
  end
64
+
65
+ web_presenter = Mystique.present(Web.new)
66
+
67
+ web_presenter.root
68
+ # => "/"
46
69
  ```
47
70
 
48
- This will set the `MyHelpers` module as the context for any instance of `UserPresenter`
71
+ This will set the `UrlHelpers` module as the context for any instance of `WebPresenter`
49
72
 
50
73
  #### Passing it to the present method
51
74
 
52
75
  ```ruby
53
- user_presenter = Mystique.present(some_user, context: MyHelpers)
76
+ user_presenter = Mystique.present(some_web_instance, context: UrlHelpers)
54
77
  ```
55
78
 
56
79
  Which will set `MyHelpers` as the context just for `user_presenter`
@@ -64,9 +87,11 @@ but if you pass a new context to a specific instance, it will use that one.
64
87
 
65
88
  ## Formatting
66
89
 
67
- Mystique provides a `format` method that allows you to define defaults for some response types.
90
+ Mystique provides an `apply_format` method that allows you to define defaults for some response types.
91
+
92
+ In every case, `apply_format` will accept a value or a block to return, which will yield the found value and the context.
68
93
 
69
- In every case, `format` will accept a value or a block to return, which will yield the found value and the context.
94
+ In order to apply that format to a method, you must specify which method by calling `format`. If you forget to do this, Mystique will just retrieve the value from the original object and return that.
70
95
 
71
96
  ### Specific values
72
97
 
@@ -75,11 +100,14 @@ This is a great way to return a default String when you get a nil back (but it's
75
100
  ```ruby
76
101
  Item = Struct.new(:name, :price)
77
102
  class ItemPresenter < Mystique::Presenter
78
- format nil, "N/A"
103
+ format :price
104
+
105
+ apply_format nil, "N/A"
79
106
  end
80
107
 
81
- item_presenter.price
82
- # => "N/A"
108
+ Mystique.present(Item.new("Headphones")) do |item_presenter|
109
+ item_presenter.price # => "N/A"
110
+ end
83
111
  ```
84
112
 
85
113
  ### Classes
@@ -87,11 +115,21 @@ item_presenter.price
87
115
  You can pass a class name to the format method, and if the returned value is an instance of that class, it will return the specified value/block
88
116
 
89
117
  ```ruby
118
+ module Helpers
119
+ def self.number_to_currency(number)
120
+ "$ %0.2f" % number
121
+ end
122
+ end
123
+
124
+ Item = Struct.new(:name, :price)
125
+
90
126
  class ItemPresenter < Mystique::Presenter
91
127
  context Helpers
92
- format Float do |value, context|
128
+ apply_format Float do |value, context|
93
129
  context.number_to_currency(value)
94
130
  end
131
+
132
+ format :price
95
133
  end
96
134
 
97
135
  item_presenter = Mystique.present(Item.new("Rubik's Cube", 5.3))
@@ -111,10 +149,14 @@ module Helpers
111
149
  end
112
150
  end
113
151
 
152
+ User = Struct.new(:name, :email)
153
+
114
154
  class UserPresenter < Mystique::Presenter
115
155
  context Helpers
116
156
 
117
- format /\w+@\w+\.\w+/ do |email, context|
157
+ format :email
158
+
159
+ apply_format /\w+@\w+\.\w+/ do |email, context|
118
160
  context.link_to(email, "mailto://#{email}")
119
161
  end
120
162
  end
@@ -130,16 +172,23 @@ user_presenter.email
130
172
  You can also set multiple matchers by using the `.format_multiple` method:
131
173
 
132
174
  ```ruby
133
- Mystique.present(Item.new("Wine", 50, 42.3)) do |presenter|
134
- presenter.class
135
- # => ItemPresenter
175
+ require "time"
136
176
 
137
- presenter.price
138
- # => "$ 50.00"
177
+ User = Struct.new(:last_log_in)
139
178
 
140
- presenter.list_price
141
- # => "$ 42.30"
179
+ class UserPresenter < Mystique::Presenter
180
+ format :last_log_in
181
+
182
+ format_multiple Date, Time do |value|
183
+ value.to_date.strftime("%-d %b %Y")
184
+ end
142
185
  end
186
+
187
+ Mystique.present(User.new(Time.now)).last_log_in
188
+ # => "29 Jul 2020"
189
+
190
+ Mystique.present(User.new(Date.today)).last_log_in
191
+ # => "29 Jul 2020"
143
192
  ```
144
193
 
145
194
  ## Installation
@@ -1,38 +1,38 @@
1
1
  require "mystique/version"
2
2
 
3
3
  require "callable"
4
- require "string_plus"
5
4
 
6
5
  require "mystique/undefined"
6
+ require "mystique/presenter_class"
7
7
  require "mystique/presenter"
8
8
 
9
9
  module Mystique
10
- module_function
11
10
 
12
- def present(object, with: nil, context: nil, &block)
11
+ def self.present(object, with: nil, context: nil, &block)
13
12
  begin
14
- presenter_class = presenter_class_for(object, with)
13
+ presenter_class = PresenterClass.new(object, with).to_class
15
14
  presenter_class.for(object, context, &block)
16
15
  rescue NameError
17
16
  return object
18
17
  end
19
18
  end
20
19
 
21
- def present_collection(collection, context: nil, &block)
22
- return to_enum(:present_collection, collection, context: context, &block) unless block_given?
20
+ def self.present_collection(collection, context: nil, with: nil, &block)
21
+ return to_enum(:present_collection, collection, context: context, with: with, &block) unless block_given?
23
22
 
24
23
  collection.each do |element|
25
24
  case block.arity
26
25
  when 2
27
- block.call( present(element, context: context), element )
26
+ block.call( present(element, context: context, with: with), element )
28
27
  else
29
- block.call(present(element, context: context))
28
+ block.call(present(element, context: context, with: with))
30
29
  end
31
30
  end
32
31
  end
33
32
 
34
- def presenter_class_for(object, with)
35
- with || StringPlus.constantize("#{object.class}Presenter")
33
+ private
34
+
35
+ def self.presenter_class_for(object, with)
36
+ with || Object.send(:const_get, "#{object.class}Presenter")
36
37
  end
37
- private :presenter_class_for
38
38
  end
@@ -10,9 +10,9 @@ module Mystique
10
10
  end
11
11
 
12
12
  def self.for(object, context=nil)
13
- self.new(object, context).tap do |presenter|
13
+ new(object, context).tap { |presenter|
14
14
  yield presenter if block_given?
15
- end
15
+ }
16
16
  end
17
17
 
18
18
  def context
@@ -21,29 +21,31 @@ module Mystique
21
21
  alias :ctx :context
22
22
  alias :h :context
23
23
 
24
- def target
24
+ def o
25
25
  @__object__
26
26
  end
27
27
 
28
28
  def inspect
29
- "<#{self.class}(#{target.inspect}) context: #{context.inspect}>"
29
+ "<#{self.class}(#{o.inspect}) context: #{context.inspect}>"
30
30
  end
31
31
 
32
32
  private
33
33
 
34
34
  def method_missing(method, *args, &block)
35
- return target.send(method, *args, &block) if method.to_s.start_with?("to_")
36
-
37
- value = target.send(method, *args, &block)
38
-
39
- case
40
- when formatted_method?(method)
41
- format( value )
42
- when presented_method?(method)
43
- Mystique.present(value, context: context)
44
- else
45
- value
46
- end
35
+ return o.send(method, *args, &block) if method.to_s.start_with?("to_")
36
+
37
+ o.send(method, *args, &block).yield_self { |value|
38
+ case
39
+ when formatted_method?(method)
40
+ format( value )
41
+ when presented_method?(method)
42
+ Mystique.present(value, context: context)
43
+ when presented_collection?(method)
44
+ Mystique.present_collection(value, context: context, &block)
45
+ else
46
+ value
47
+ end
48
+ }
47
49
  end
48
50
 
49
51
  def formatted_method?(method)
@@ -54,16 +56,22 @@ module Mystique
54
56
  __presented_methods__.include?(method)
55
57
  end
56
58
 
59
+ def presented_collection?(method)
60
+ __presented_collections__.include?(method)
61
+ end
62
+
57
63
  def format(value)
58
- result = if __formats__.keys.include?(value)
64
+ result = case
65
+ when __formats__.keys.include?(value)
59
66
  __formats__[value]
60
- elsif __regex_formats__.any? { |regex, _| value =~ regex}
67
+ when __regex_formats__.any? { |regex, _| value =~ regex}
61
68
  __regex_formats__.select { |regex, _| value =~ regex}.first.last
62
- elsif __class_formats__.any? { |klass, _| value.is_a?(klass)}
69
+ when __class_formats__.any? { |klass, _| value.is_a?(klass)}
63
70
  __class_formats__.select { |klass, _| value.is_a?(klass)}.first.last
64
71
  else
65
72
  value
66
73
  end
74
+
67
75
  Mystique.present(Callable(result).call(value, context))
68
76
  end
69
77
 
@@ -88,6 +96,12 @@ module Mystique
88
96
  end
89
97
  end
90
98
 
99
+ def self.present_collection(matcher)
100
+ if matcher.is_a?(Symbol)
101
+ __presented_collections__ << matcher
102
+ end
103
+ end
104
+
91
105
  def self.format_and_present(matcher)
92
106
  format_method(method)
93
107
  present_method(method)
@@ -97,6 +111,10 @@ module Mystique
97
111
  @__presented_methods__ ||= []
98
112
  end
99
113
 
114
+ def self.__presented_collections__
115
+ @__presented_collections__ ||= []
116
+ end
117
+
100
118
  def self.__formatted_methods__
101
119
  @__formatted_methods__ ||= []
102
120
  end
@@ -105,6 +123,10 @@ module Mystique
105
123
  self.class.__presented_methods__
106
124
  end
107
125
 
126
+ def __presented_collections__
127
+ self.class.__presented_collections__
128
+ end
129
+
108
130
  def __formatted_methods__
109
131
  self.class.__formatted_methods__
110
132
  end
@@ -0,0 +1,36 @@
1
+ module Mystique
2
+ class PresenterClass
3
+ def initialize(object, with=nil)
4
+ @with = with
5
+ @object = object
6
+ end
7
+
8
+ def to_class
9
+ with || Object.send(:const_get, class_name)
10
+ end
11
+
12
+ def class_name
13
+ return with.to_s if with
14
+
15
+ "#{base_class_name(object)}Presenter"
16
+ end
17
+
18
+ private
19
+
20
+ attr_reader :with
21
+ attr_reader :object
22
+
23
+ def base_class_name(for_object)
24
+ case for_object
25
+ when Symbol, String
26
+ for_object.to_s.split(/_/).map(&:capitalize).join
27
+ when Array
28
+ for_object.map { |current_object|
29
+ base_class_name(current_object)
30
+ }.join("::")
31
+ else
32
+ for_object.class.to_s
33
+ end
34
+ end
35
+ end
36
+ end
@@ -1,3 +1,3 @@
1
1
  module Mystique
2
- VERSION = "0.9.1"
2
+ VERSION = "1.0.1"
3
3
  end
@@ -19,10 +19,9 @@ Gem::Specification.new do |spec|
19
19
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
20
  spec.require_paths = ["lib"]
21
21
 
22
- spec.add_development_dependency "bundler", "~> 1.8"
23
- spec.add_development_dependency "rake", "~> 10.0"
22
+ spec.add_development_dependency "bundler", "~> 2.1.4"
23
+ spec.add_development_dependency "rake", ">= 12.3.3"
24
24
  spec.add_development_dependency "rspec"
25
25
  spec.add_development_dependency "pry-byebug"
26
26
  spec.add_runtime_dependency "callable"
27
- spec.add_runtime_dependency "string_plus"
28
27
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mystique
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.1
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Federico Iachetti
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-05-10 00:00:00.000000000 Z
11
+ date: 2020-07-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -16,28 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.8'
19
+ version: 2.1.4
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '1.8'
26
+ version: 2.1.4
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: '10.0'
33
+ version: 12.3.3
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - "~>"
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: '10.0'
40
+ version: 12.3.3
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rspec
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -80,20 +80,6 @@ dependencies:
80
80
  - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
- - !ruby/object:Gem::Dependency
84
- name: string_plus
85
- requirement: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - ">="
88
- - !ruby/object:Gem::Version
89
- version: '0'
90
- type: :runtime
91
- prerelease: false
92
- version_requirements: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - ">="
95
- - !ruby/object:Gem::Version
96
- version: '0'
97
83
  description: Put a presenter in front of your objects.
98
84
  email:
99
85
  - iachetti.federico@gmail.com
@@ -113,6 +99,7 @@ files:
113
99
  - lib/mystique.rb
114
100
  - lib/mystique/null_context.rb
115
101
  - lib/mystique/presenter.rb
102
+ - lib/mystique/presenter_class.rb
116
103
  - lib/mystique/presenters.rb
117
104
  - lib/mystique/presenters/hash_presenter.rb
118
105
  - lib/mystique/undefined.rb
@@ -122,7 +109,7 @@ homepage: https://github.com/iachettifederico/mystique
122
109
  licenses:
123
110
  - MIT
124
111
  metadata: {}
125
- post_install_message:
112
+ post_install_message:
126
113
  rdoc_options: []
127
114
  require_paths:
128
115
  - lib
@@ -137,8 +124,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
137
124
  - !ruby/object:Gem::Version
138
125
  version: '0'
139
126
  requirements: []
140
- rubygems_version: 3.0.1
141
- signing_key:
127
+ rubygems_version: 3.0.8
128
+ signing_key:
142
129
  specification_version: 4
143
130
  summary: Ruby presenter gem.
144
131
  test_files: []