adornable 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b5866ce4d639cd7201a6467800e5ac85fa9e6b823c57496e8ac670059b5d7f5a
4
- data.tar.gz: '0418007247c26928a448fd069495b41cdd722d8902f7894185c7589ac8586d2e'
3
+ metadata.gz: 4bea3ddf067bcb43aee2c2c9cd380297f6f741dc358572d65903a20fd1a0e1ec
4
+ data.tar.gz: a12b2559a2a647ff58c36f072d4ad0bf71180f3382cffd69d2f43e4fd2d3a040
5
5
  SHA512:
6
- metadata.gz: be5afe7b6f0eaadd690d4c1e62112a71d1e2f9d26481cec53bf4358a99394809d1d8f2b6a71bf464316320776753bb687bc65e71b96378aabe378da0f2065463
7
- data.tar.gz: 250c536c81d0ad0b5c7388af95623e1fc563b0fc41522902542a1e7e2ff9ca0f3f25f6e39378cc1d780ce048e4f184ab4d8a75c2a2f5d1c4ca3621e1084b645d
6
+ metadata.gz: 65d7e3170d90c2388a4e2a9610a6a2d2cfeb872153bb883791bf975d7f18eae4841ce3550010cfb0f17419b9bec7beeecd0d0553d9b2c46d35e6546ff0a23377
7
+ data.tar.gz: f3e79890b4d421628bbbf21680d676a83446915b34a067eb1d25fe45806ed4e78f8729be5a15f4308d1f50cfb51911c7235e05762841dd0ca264df997f3b5538
data/.gitignore CHANGED
@@ -1,11 +1,70 @@
1
- /.bundle/
2
- /.yardoc
3
- /_yardoc/
4
- /coverage/
5
- /doc/
1
+ # generic stuff
2
+ .env
3
+ *.gem
4
+ *.rbc
5
+ log/*.log
6
+ /.config
7
+ /InstalledFiles
6
8
  /pkg/
7
- /spec/reports/
8
9
  /tmp/
9
10
 
10
- # rspec failure tracking
11
+ # testy stuff
12
+ .rspec
11
13
  .rspec_status
14
+ *.orig
15
+ /coverage/
16
+ /coverage/
17
+ /db/*.sqlite3
18
+ /db/*.sqlite3-[0-9]*
19
+ /db/*.sqlite3-journal
20
+ /public/system
21
+ /spec/examples.txt
22
+ /spec/reports/
23
+ /spec/tmp
24
+ /test/tmp/
25
+ /test/version_tmp/
26
+ capybara-*.html
27
+ pickle-email-*.html
28
+ rerun.txt
29
+ test/dummy/db/*.sqlite3
30
+ test/dummy/db/*.sqlite3-journal
31
+ test/dummy/log/*.log
32
+ test/dummy/node_modules/
33
+ test/dummy/storage/
34
+ test/dummy/tmp/
35
+ test/dummy/yarn-error.log
36
+
37
+ # debuggy stuff
38
+ .byebug_history
39
+
40
+ ## doccy stuff
41
+ /.yardoc/
42
+ /_yardoc/
43
+ /doc/
44
+ /rdoc/
45
+
46
+ ## bundly stuff
47
+ /.bundle/
48
+ /vendor/bundle
49
+ /lib/bundler/man/
50
+
51
+ # "for a library or gem, you might want to ignore these files since the code is
52
+ # intended to run in multiple environments"
53
+ Gemfile.lock
54
+ .ruby-version
55
+ .ruby-gemset
56
+
57
+ # rvmmy stuff
58
+ .rvmrc
59
+
60
+ # editory stuff
61
+ .idea
62
+ .vscode
63
+ *.rdb
64
+
65
+ # systemy stuff
66
+ *.swm
67
+ *.swn
68
+ *.swo
69
+ *.swp
70
+ *.DS_Store
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- adornable (1.0.0)
4
+ adornable (1.0.1)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021 Keegan Leitz
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md CHANGED
@@ -1,35 +1,361 @@
1
1
  # Adornable
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/adornable`. To experiment with that code, run `bin/console` for an interactive prompt.
4
-
5
- TODO: Delete this and the text above, and describe your gem
3
+ Adornable provides method decorators in Ruby... 'nuff said.
6
4
 
7
5
  ## Installation
8
6
 
9
- Add this line to your application's Gemfile:
7
+ ### Locally (to your application)
8
+
9
+ Add the gem to your application's `Gemfile`:
10
10
 
11
11
  ```ruby
12
12
  gem 'adornable'
13
13
  ```
14
14
 
15
- And then execute:
15
+ ...and then run:
16
+
17
+ ```bash
18
+ bundle install
19
+ ```
20
+
21
+ ### Globally (to your system)
16
22
 
17
- $ bundle
23
+ Alternatively, install it globally:
18
24
 
19
- Or install it yourself as:
25
+ ```bash
26
+ gem install adornable
27
+ ```
20
28
 
21
- $ gem install adornable
29
+ ...but why would you do that?
22
30
 
23
31
  ## Usage
24
32
 
25
- TODO: Write usage instructions here
33
+ ### The basics
34
+
35
+ Think of a decorator as if it's just a wrapper function. You want something to happen before, around, or after a method is called, in a reusable (but dynamic) way? Maybe you want to print to a log whenever a certain method is called, or memoize its result so that additional calls don't have to re-execute the body of the method. You've tried this:
36
+
37
+ ```rb
38
+ class RandomValueGenerator
39
+ def value
40
+ # logging the method call
41
+ puts "Calling method `RandomValueGenerator#value` with no arguments"
42
+ # memoizing the result
43
+ @value ||= rand
44
+ end
45
+
46
+ def values(max)
47
+ # logging the method call
48
+ puts "Calling method `RandomValueGenerator#values` with arguments `[#{max}]`"
49
+ # memoizing the result
50
+ @values ||= {}
51
+ @values[max] ||= (1..max).map { rand }
52
+ end
53
+ end
54
+
55
+ random_value_generator = RandomValueGenerator.new
56
+
57
+ values1 = random_value_generator.values(1000)
58
+ # Calling method `RandomValueGenerator#values` with arguments `[1000]`
59
+ #=> [0.7044444114998132, 0.401953296596267, 0.3023797513191562, ...]
60
+
61
+ values1 = random_value_generator.values(1000)
62
+ # Calling method `RandomValueGenerator#values` with arguments `[1000]`
63
+ #=> [0.7044444114998132, 0.401953296596267, 0.3023797513191562, ...]
64
+
65
+ values3 = random_value_generator.values(5000)
66
+ # Calling method `RandomValueGenerator#values` with arguments `[5000]`
67
+ #=> [0.9916088057511011, 0.04466750434972333, 0.6073659341272127]
68
+
69
+ value1 = random_value_generator.value
70
+ # Calling method `RandomValueGenerator#value` with no arguments
71
+ #=> 0.4196007135344746
72
+
73
+ value2 = random_value_generator.value
74
+ # Calling method `RandomValueGenerator#value` with no arguments
75
+ #=> 0.4196007135344746
76
+ ```
77
+
78
+ ...but you have a million more methods to write, and if you refactor, you'll have to screw around with a whole metric butt-load of method definitions across your app.
79
+
80
+ How about this instead?
81
+
82
+ ```rb
83
+ class RandomValueGenerator
84
+ extend Adornable
85
+
86
+ decorate :log
87
+ decorate :memoize
88
+ def value
89
+ rand
90
+ end
91
+
92
+ decorate :log
93
+ decorate :memoize_for_arguments
94
+ def values(max)
95
+ (1..max).map { rand }
96
+ end
97
+ end
98
+
99
+ random_value_generator = RandomValueGenerator.new
100
+
101
+ values1 = random_value_generator.values(1000)
102
+ # Calling method `RandomValueGenerator#values` with arguments `[1000]`
103
+ #=> [0.7044444114998132, 0.401953296596267, 0.3023797513191562, ...]
104
+
105
+ values1 = random_value_generator.values(1000)
106
+ # Calling method `RandomValueGenerator#values` with arguments `[1000]`
107
+ #=> [0.7044444114998132, 0.401953296596267, 0.3023797513191562, ...]
108
+
109
+ values3 = random_value_generator.values(5000)
110
+ # Calling method `RandomValueGenerator#values` with arguments `[5000]`
111
+ #=> [0.9916088057511011, 0.04466750434972333, 0.6073659341272127]
112
+
113
+ value1 = random_value_generator.value
114
+ # Calling method `RandomValueGenerator#value` with no arguments
115
+ #=> 0.4196007135344746
116
+
117
+ value2 = random_value_generator.value
118
+ # Calling method `RandomValueGenerator#value` with no arguments
119
+ #=> 0.4196007135344746
120
+ ```
121
+
122
+ Nice, right?
123
+
124
+ > **Note:** in the case of multiple decorators decorating a method, each is executed from top to bottom.
125
+
126
+ ### Adding decorator functionality
127
+
128
+ Add the `::decorate` macro to your classes by `extend`-ing `Adornable`:
129
+
130
+ ```rb
131
+ class Foo
132
+ extend Adornable
133
+
134
+ # ...
135
+ end
136
+ ```
137
+
138
+ ### Decorating methods
139
+
140
+ Use the `decorate` macro to decorate methods.
141
+
142
+ #### Using built-in decorators
143
+
144
+ There are a few built-in decorators:
145
+
146
+ ```rb
147
+ class Foo
148
+ extend Adornable
149
+
150
+ decorate :log
151
+ def some_method
152
+ # ...
153
+ end
154
+
155
+ decorate :memoize
156
+ def some_other_method
157
+ # ...
158
+ end
159
+
160
+ decorate :memoize_for_arguments
161
+ def yet_another_method(some_arg, some_other_arg = true, key_word_arg:, key_word_arg_with_default: 123)
162
+ # ...
163
+ end
164
+
165
+ decorate :log
166
+ decorate :memoize_for_arguments
167
+ def oh_boy_another_method(some_arg, some_other_arg = true, key_word_arg:, key_word_arg_with_default: 123)
168
+ # ...
169
+ end
170
+
171
+ decorate :log
172
+ def self.yeah_it_works_on_class_methods_too
173
+ # ...
174
+ end
175
+ end
176
+ ```
177
+
178
+ - `decorate :log` logs the method name and any passed arguments to the console
179
+ - `decorate :memoize` caches the result of the first call and returns that initial result (and does not execute the method again) for any additional calls
180
+ - `decorate :memoize_for_arguments` acts like `decorate :memoize` but it namespaces that cache by the arguments passed, so it will re-compute (and cache the result) only if the arguments change... if the arguments are the same as any previous time the method was called, it will return the cached result instead
181
+
182
+ > **Note:** in the case of multiple decorators decorating a method, each is executed from top to bottom.
183
+
184
+ #### Using custom decorators explicitly
185
+
186
+ You can reference any decorator method you write, like so:
187
+
188
+ ```rb
189
+ class FooDecorators
190
+ # Note: this is a class method
191
+ def self.blast_it(method_receiver, method_name, arguments)
192
+ puts "Blasting it!"
193
+ value = yield
194
+ "#{value}!"
195
+ end
196
+
197
+ # Note: this is an instance method
198
+ def wait_for_it(method_receiver, method_name, arguments)
199
+ puts "Waiting..."
200
+ value = yield
201
+ "#{value}..."
202
+ end
203
+ end
204
+
205
+ class Foo
206
+ extend Adornable
207
+
208
+ # Note: `from: FooDecorators` references a class (and will look for the
209
+ # `::blast_it` method on that class)
210
+ decorate :blast_it, from: FooDecorators
211
+ def some_method
212
+ "haha I'm a method"
213
+ end
214
+
215
+ # Note: `from: FooDecorators.new` references an instance (and will look for
216
+ # the `#wait_for_it` method on that instance)
217
+ decorate :wait_for_it, from: FooDecorators.new
218
+ def other_method
219
+ "haha I'm another method"
220
+ end
221
+
222
+ decorate :log
223
+ def yet_another_method(foo, bar:)
224
+ "haha I'm yet another method"
225
+ end
226
+ end
227
+
228
+ foo = Foo.new
229
+
230
+ foo.some_method
231
+ #=> "haha I'm a method!" # Note the exclamation mark
232
+
233
+ foo.other_method
234
+ #=> "haha I'm another method..." # Note the ellipsis
235
+
236
+ foo.yet_another_method(123, bloop: "bleep")
237
+ # Calling method `Foo#yet_another_method` with arguments `[123, {:bloop=>"bleep"}]`
238
+ #=> "haha I'm yet another method"
239
+ ```
240
+
241
+ Use the `from:` option to specify what should receive the decorator method. Keep in mind that the decorator method will be called on the thing specified by `from:`... so, if you provide a class, it better be a class method, and if you supply an instance, it better be an instance method.
242
+
243
+ Every decorator method must take the following arguments:
244
+
245
+ - `method_receiver`: the actual object that the [decorated] method is being called on (an object/class); e.g., `Foo` or an instance of `Foo`
246
+ - `method_name`: the name of the [decorated] method being called on `method_receiver` (a symbol); e.g., `:some_method` or `:other_method`
247
+ - `arguments`: an array of arguments passed to the [decorated] method, including keyword arguments; e.g., if `:yet_another_method` was called like `Foo.new.yet_another_method(123, bar: true)` then `arguments` would be `[123, {:bar=>true}]`
248
+
249
+ > **Note:** Every decorator method _should_ also probably `yield` at some point in the method body. I say _"should"_ because, technically, you don't have to, but if you don't then the original method will never be called. That's a valid use-case, but 99% of the time you're gonna want to `yield`.
250
+ >
251
+ > **Note:** the return value of your decorator **will replace the return value of the decorated method,** so _also_ you should probably return whatever value `yield` returned. Again, it is a valid use case to return something _else,_ but 99% of the time you probably want to return the value returned by the wrapped method.
252
+
253
+ Contrived example of when you might want to muck around with the return value:
254
+
255
+ ```rb
256
+ class FooDecorators
257
+ def self.coerce_to_int(method_receiver, method_name, arguments)
258
+ value = yield
259
+ new_value = value.strip.to_i
260
+ puts "New value: #{value.inspect} (class: #{value.class})"
261
+ new_value
262
+ end
263
+ end
264
+
265
+ class Foo
266
+ extend Adornable
267
+
268
+ decorate :coerce_to_int, from: FooDecorators
269
+ def get_number_from_user
270
+ print "Enter a number: "
271
+ value = gets
272
+ puts "Value: #{value.inspect} (class: #{value.class})"
273
+ value
274
+ end
275
+ end
276
+
277
+ foo = Foo.new
278
+
279
+ foo.get_number_from_user
280
+ # Enter a number
281
+ # > 123
282
+ # Value: "123" (class: String)
283
+ # New value: 123 (class: Integer)
284
+ #=> 123
285
+ ```
286
+
287
+ #### Using custom decorators implicitly
288
+
289
+ You can also register decorator receivers so that you don't have to reference them with the `from:` option:
290
+
291
+ ```rb
292
+ class FooDecorators
293
+ # Note: this is a class method
294
+ def self.blast_it(method_receiver, method_name, arguments)
295
+ puts "Blasting it!"
296
+ value = yield
297
+ "#{value}!"
298
+ end
299
+ end
300
+
301
+ class MoreFooDecorators
302
+ # Note: this is a class method
303
+ def self.wait_for_it(method_receiver, method_name, arguments)
304
+ puts "Waiting for it..."
305
+ value = yield
306
+ "#{value}..."
307
+ end
308
+ end
309
+
310
+ class Foo
311
+ extend Adornable
312
+
313
+ add_decorators_from FooDecorators
314
+ add_decorators_from MoreFooDecorators
315
+
316
+ decorate :blast_it
317
+ decorate :wait_for_it
318
+ def some_method
319
+ "haha I'm a method"
320
+ end
321
+ end
322
+
323
+ foo = Foo.new
324
+
325
+ foo.some_method
326
+ # Blasting it!
327
+ # Waiting for it...
328
+ #=> "haha I'm a method!..."
329
+ ```
330
+
331
+ > **Note:** In the case of duplicate decorator methods, later receivers registered with `::add_decorators_from` will override any duplicate decorators from earlier registered receivers.
332
+ >
333
+ > **Note:** in the case of multiple decorators decorating a method, each is executed from top to bottom; i.e., the top wraps the next, which wraps the next, and so on, until the method itself is wrapped.
26
334
 
27
335
  ## Development
28
336
 
29
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
337
+ ### Install dependencies
338
+
339
+ ```bash
340
+ bin/setup
341
+ ```
342
+
343
+ ### Run testss
344
+
345
+ ```bash
346
+ rake spec
347
+ ```
30
348
 
31
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
349
+ ### Create release
350
+
351
+ ```
352
+ rake release
353
+ ```
32
354
 
33
355
  ## Contributing
34
356
 
35
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/adornable.
357
+ Bug reports and pull requests for this project are welcome at its [GitHub page](https://github.com/kjleitz/adornable). If you choose to contribute, please be nice so I don't have to run out of bubblegum, etc.
358
+
359
+ ## License
360
+
361
+ This project is open source, under the terms of the [MIT license.](https://github.com/kjleitz/adornable/blob/master/LICENSE)
@@ -1,3 +1,3 @@
1
1
  module Adornable
2
- VERSION = "1.0.0"
2
+ VERSION = "1.0.1"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: adornable
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Keegan Leitz
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-02-12 00:00:00.000000000 Z
11
+ date: 2021-02-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -64,6 +64,7 @@ files:
64
64
  - ".travis.yml"
65
65
  - Gemfile
66
66
  - Gemfile.lock
67
+ - LICENSE
67
68
  - README.md
68
69
  - Rakefile
69
70
  - adornable.gemspec