tutor 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.rspec +2 -0
- data/.travis.yml +11 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +233 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/tutor.rb +8 -0
- data/lib/tutor/attributes.rb +68 -0
- data/lib/tutor/attributes/attribute.rb +74 -0
- data/lib/tutor/attributes/block.rb +9 -0
- data/lib/tutor/attributes/blocks.rb +5 -0
- data/lib/tutor/attributes/blocks/alias.rb +5 -0
- data/lib/tutor/attributes/blocks/alias/reader.rb +12 -0
- data/lib/tutor/attributes/blocks/alias/writer.rb +12 -0
- data/lib/tutor/attributes/blocks/instance.rb +5 -0
- data/lib/tutor/attributes/blocks/instance/reader.rb +14 -0
- data/lib/tutor/attributes/blocks/instance/writer.rb +14 -0
- data/lib/tutor/attributes/default.rb +17 -0
- data/lib/tutor/attributes/method.rb +35 -0
- data/lib/tutor/attributes/type.rb +16 -0
- data/lib/tutor/version.rb +3 -0
- data/tutor.gemspec +27 -0
- metadata +140 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 5c666a631a2d3b8b6c899f2bafada1124fdc5394
|
4
|
+
data.tar.gz: 337a2e8693d0c06538ff146fc68730ce696f51ca
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0c62dd8c2bc8480df46b71492ba5e30eb79ae009d3e8a12ddb10b37f6c46f6e02f4052f3876dae5019ffdd8138789278e4c439eb30445a362fbef7d5963f5583
|
7
|
+
data.tar.gz: 4ded16e07f09e1ccfaa0089782e0982bb835544a56ba873c4ea526124f4f513280b58df8902e004b3f19cdbb6e1e33f619c831319a9304d0fbe4dda759e196d2
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 David Elner
|
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
|
13
|
+
all 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
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,233 @@
|
|
1
|
+
Tutor
|
2
|
+
==========
|
3
|
+
|
4
|
+
[![Build Status](https://travis-ci.org/delner/tutor.svg?branch=master)](https://travis-ci.org/delner/tutor) ![Gem Version](https://badge.fury.io/rb/tutor.svg)
|
5
|
+
###### *For Ruby 2+*
|
6
|
+
|
7
|
+
*Supplemental teachings for your Ruby classes.*
|
8
|
+
|
9
|
+
### Table of Contents
|
10
|
+
|
11
|
+
1. [Introduction](#introduction)
|
12
|
+
2. [Installation](#installation)
|
13
|
+
3. [Features](#features)
|
14
|
+
1. [Attributes](#attributes)
|
15
|
+
1. [Type-checking](#type-checking)
|
16
|
+
2. [Defaults](#defaults)
|
17
|
+
3. [Aliasing](#aliasing)
|
18
|
+
4. [Custom getter & setter](#custom-getter-and-setter)
|
19
|
+
5. [Inheritance](#inheritance)
|
20
|
+
4. [Testing](#testing)
|
21
|
+
5. [Development](#development)
|
22
|
+
6. [Contributing](#contributing)
|
23
|
+
4. [License](#license)
|
24
|
+
|
25
|
+
### <a name="introduction"></a>Introduction
|
26
|
+
|
27
|
+
**Tutor** adds some useful patterns and idioms to mixin to your classes.
|
28
|
+
|
29
|
+
e.g. Attributes with type and defaults
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
class Vertex; end
|
33
|
+
class Polygon
|
34
|
+
include Tutor::Attributes
|
35
|
+
|
36
|
+
attribute(:sides, type: Integer, default: 3)
|
37
|
+
attribute(:vertices, default: lambda { |polygon| Array.new(polygon.sides) { Vertex.new } })
|
38
|
+
end
|
39
|
+
|
40
|
+
p = Polygon.new
|
41
|
+
p.intialize_attributes
|
42
|
+
p.sides # => 3
|
43
|
+
p.vertices # => [#<Vertex>, #<Vertex>, #<Vertex>]
|
44
|
+
```
|
45
|
+
|
46
|
+
###<a name="installation"></a> Installation
|
47
|
+
|
48
|
+
##### If you're not using Bundler...
|
49
|
+
|
50
|
+
Install the gem via:
|
51
|
+
|
52
|
+
```
|
53
|
+
gem install tutor
|
54
|
+
```
|
55
|
+
|
56
|
+
Then require it into your application with:
|
57
|
+
|
58
|
+
```
|
59
|
+
require 'tutor'
|
60
|
+
```
|
61
|
+
|
62
|
+
##### If you're using Bundler...
|
63
|
+
|
64
|
+
Add the gem to your Gemfile:
|
65
|
+
|
66
|
+
```
|
67
|
+
gem 'tutor'
|
68
|
+
```
|
69
|
+
|
70
|
+
And then `bundle install` to install the gem and its dependencies.
|
71
|
+
|
72
|
+
###<a name="features"></a> Features
|
73
|
+
|
74
|
+
####<a name="attributes"></a> Attributes
|
75
|
+
|
76
|
+
Enable attributes on a class by including `Tutor::Attributes` module into the class/module you want to add attributes to.
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
class Polygon
|
80
|
+
include Tutor::Attributes
|
81
|
+
end
|
82
|
+
```
|
83
|
+
|
84
|
+
Then add an `attribute` to add a `name` and `name=` method to the class.
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
class Polygon
|
88
|
+
include Tutor::Attributes
|
89
|
+
|
90
|
+
attribute(:sides)
|
91
|
+
end
|
92
|
+
|
93
|
+
Polygon.method_defined?(:sides) # => true
|
94
|
+
Polygon.method_defined?(:sides=) # => true
|
95
|
+
```
|
96
|
+
|
97
|
+
Of course, if this is all you need, then you might be better served just using `attr_accessor`. However, `attribute` gives you access to a few additional options.
|
98
|
+
|
99
|
+
#####<a name="type-checking"></a> Type-checking
|
100
|
+
|
101
|
+
```ruby
|
102
|
+
attribute(:sides, type: Integer)
|
103
|
+
```
|
104
|
+
|
105
|
+
Add type-checking by adding the `type` option, and the class you want to type check against. Any value that is of that type, or inherits from it, will be permitted. Any other value will raise an `ArgumentError`.
|
106
|
+
|
107
|
+
By default, `nil` passes the type check. However, if you want to disallow `nil` values, you can set the `nullable: false` option.
|
108
|
+
|
109
|
+
```ruby
|
110
|
+
attribute(:sides, type: Integer, nullable: false)
|
111
|
+
```
|
112
|
+
|
113
|
+
#####<a name="defaults"></a> Defaults
|
114
|
+
|
115
|
+
```ruby
|
116
|
+
attribute(:vertices, default: 3)
|
117
|
+
```
|
118
|
+
|
119
|
+
Adding the `default` option sets a default value for attribute, when `initialize_attributes` is called. To automatically set these defaults, add the function call to your `initialize` function.
|
120
|
+
|
121
|
+
```ruby
|
122
|
+
def initialize
|
123
|
+
initialize_attributes
|
124
|
+
end
|
125
|
+
```
|
126
|
+
|
127
|
+
You can also pass it a `Hash` of attributes, which will override any default values.
|
128
|
+
|
129
|
+
```ruby
|
130
|
+
def initialize(custom_attributes = {})
|
131
|
+
initialize_attributes(custom_attributes)
|
132
|
+
end
|
133
|
+
```
|
134
|
+
|
135
|
+
You can also set the default to a `Proc` or `lambda`. This is useful for non-static values, like class objects, where you want unique default objects per instance of your class.
|
136
|
+
|
137
|
+
```ruby
|
138
|
+
# NOTE: The following two definitions are NOT equivalent.
|
139
|
+
|
140
|
+
# Sets default to the same object for all instances.
|
141
|
+
attribute(:vertices, default: Vertex.new)
|
142
|
+
|
143
|
+
# Sets default to the different object for each instance.
|
144
|
+
attribute(:vertices, default: -> { Vertex.new })
|
145
|
+
```
|
146
|
+
|
147
|
+
These blocks optionally can be defined to accept an argument. In which case, the object being initialized will be passed in. Any explicitly provided attribute values, or previously set defaults will be accessible.
|
148
|
+
|
149
|
+
```ruby
|
150
|
+
attribute :vertices,
|
151
|
+
default: lambda { |object| object.sides.times { Vertex.new } }
|
152
|
+
```
|
153
|
+
|
154
|
+
#####<a name="aliasing"></a> Aliasing
|
155
|
+
|
156
|
+
```ruby
|
157
|
+
attribute(:nodes)
|
158
|
+
attribute(:vertices, alias: :nodes)
|
159
|
+
```
|
160
|
+
|
161
|
+
Alias any other attribute with the `alias` option, and the name of the method.
|
162
|
+
|
163
|
+
##### <a name="custom-getter-and-setter"></a>Custom getter & setter
|
164
|
+
|
165
|
+
```ruby
|
166
|
+
attribute :vertices,
|
167
|
+
get: lambda { |object| object.nodes },
|
168
|
+
set: lambda { |object, value| object.nodes = value }
|
169
|
+
```
|
170
|
+
|
171
|
+
You can add custom get or set behavior for the attribute, by passing a `Proc` into the `get` or `set` options.
|
172
|
+
|
173
|
+
```ruby
|
174
|
+
attribute :vertices,
|
175
|
+
get: lambda { |object| object.nodes },
|
176
|
+
set: false
|
177
|
+
```
|
178
|
+
|
179
|
+
Passing a `false` or `nil` value for either `get` or `set` will skip that method declaration.
|
180
|
+
|
181
|
+
#####<a name="inheritance"></a> Inheritance
|
182
|
+
|
183
|
+
```ruby
|
184
|
+
class Angle; end
|
185
|
+
|
186
|
+
class Polygon
|
187
|
+
include Tutor::Attributes
|
188
|
+
attribute(:sides, type: Integer)
|
189
|
+
end
|
190
|
+
|
191
|
+
class Triangle < Polygon
|
192
|
+
include Tutor::Attributes
|
193
|
+
attribute(:angles, default: lambda { |o| o.sides.times { Angle.new } })
|
194
|
+
end
|
195
|
+
|
196
|
+
Triangle.new.initialize_attributes(sides: 3)
|
197
|
+
# => #<Triangle @sides=3, @angles=[ #<Angle>, #<Angle>, #<Angle>]>
|
198
|
+
```
|
199
|
+
|
200
|
+
Attributes can be initialized and accessed from inheriting classes. If an attribute name in a subclass conflicts with an already existing attribute or method, it will raise a `NameError`. You can, however, override the parent by passing the `override` option.
|
201
|
+
|
202
|
+
```ruby
|
203
|
+
class Angle; end
|
204
|
+
|
205
|
+
class Polygon
|
206
|
+
include Tutor::Attributes
|
207
|
+
attribute(:sides, type: Integer, default: 3)
|
208
|
+
end
|
209
|
+
|
210
|
+
class Square < Polygon
|
211
|
+
include Tutor::Attributes
|
212
|
+
attribute(:sides, type: Integer, default: 4, override: true)
|
213
|
+
end
|
214
|
+
|
215
|
+
Square.new.initialize_attributes
|
216
|
+
# => #<Square @sides=4>
|
217
|
+
```
|
218
|
+
|
219
|
+
### <a name="testing"></a>Testing
|
220
|
+
|
221
|
+
*Description pending.*
|
222
|
+
|
223
|
+
### <a name="development"></a>Development
|
224
|
+
|
225
|
+
Install dependencies using `bundle install`. Run tests using `bundle exec rspec`.
|
226
|
+
|
227
|
+
### <a name="contributing"></a>Contributing
|
228
|
+
|
229
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/delner/tutor.
|
230
|
+
|
231
|
+
### <a name="license"></a>License
|
232
|
+
|
233
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "tutor"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/lib/tutor.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
module Tutor::Attributes
|
2
|
+
def self.included(base)
|
3
|
+
base.send(:extend, ClassMethods)
|
4
|
+
base.send(:include, InstanceMethods)
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def attributes
|
9
|
+
base_attributes = self.superclass.respond_to?(:attributes) ? self.superclass.attributes : []
|
10
|
+
(__attributes + base_attributes).uniq(&:name)
|
11
|
+
end
|
12
|
+
|
13
|
+
def has_attribute?(name)
|
14
|
+
attributes.any? { |attribute| attribute.name == name }
|
15
|
+
end
|
16
|
+
|
17
|
+
protected
|
18
|
+
|
19
|
+
def attribute(name, options = {})
|
20
|
+
attribute = Tutor::Attributes::Attribute.new(name, options)
|
21
|
+
attribute.tap do |a|
|
22
|
+
__attributes << a
|
23
|
+
a.add_attribute_methods(self)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def __attributes
|
30
|
+
@attributes ||= []
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
module InstanceMethods
|
35
|
+
def initialize_attributes(attributes = {})
|
36
|
+
self.tap do |object|
|
37
|
+
# Set any explicitly passed attribute values first
|
38
|
+
set_attributes(attributes)
|
39
|
+
# Then defaults last, so they can use any explicitly provided values
|
40
|
+
default_attributes = object.class.attributes.select { |attribute| !attributes.has_key?(attribute.name) }
|
41
|
+
default_attributes.each do |attribute|
|
42
|
+
# Do them individually so one default can use another
|
43
|
+
set_attribute(attribute.name, attribute.default_value_for(object))
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def set_attributes(attributes = {})
|
49
|
+
self.tap do |object|
|
50
|
+
attributes.each do |name, value|
|
51
|
+
set_attribute(name, value)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def set_attribute(name, value)
|
57
|
+
raise NameError.new("Unknown attribute!", name) unless self.class.has_attribute?(name)
|
58
|
+
self.send("#{name.to_s}=".to_sym, value)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
require 'tutor/attributes/block'
|
64
|
+
require 'tutor/attributes/blocks'
|
65
|
+
require 'tutor/attributes/method'
|
66
|
+
require 'tutor/attributes/default'
|
67
|
+
require 'tutor/attributes/type'
|
68
|
+
require 'tutor/attributes/attribute'
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module Tutor::Attributes
|
2
|
+
class Attribute
|
3
|
+
include Tutor::Attributes::Default
|
4
|
+
include Tutor::Attributes::Type
|
5
|
+
|
6
|
+
attr_accessor \
|
7
|
+
:name,
|
8
|
+
:get,
|
9
|
+
:set,
|
10
|
+
:alias,
|
11
|
+
:reader_method,
|
12
|
+
:writer_method,
|
13
|
+
:override
|
14
|
+
|
15
|
+
def initialize(name, options = {})
|
16
|
+
self.name = name.to_sym
|
17
|
+
self.type = options[:type]
|
18
|
+
self.nullable = options.has_key?(:nullable) ? options[:nullable] : true
|
19
|
+
self.default = options[:default]
|
20
|
+
self.override = options.has_key?(:override) ? options[:override] : false
|
21
|
+
self.alias = options[:alias]
|
22
|
+
self.get = options.has_key?(:get) ? options[:get] && Tutor::Attributes::Block.new(&options[:get]) : get_default
|
23
|
+
self.set = options.has_key?(:set) ? options[:set] && Tutor::Attributes::Block.new(&options[:set]) : set_default
|
24
|
+
end
|
25
|
+
|
26
|
+
def add_attribute_methods(klass)
|
27
|
+
[
|
28
|
+
add_reader_method(klass),
|
29
|
+
add_writer_method(klass)
|
30
|
+
].compact
|
31
|
+
end
|
32
|
+
|
33
|
+
def add_reader_method(klass)
|
34
|
+
return if !self.get
|
35
|
+
self.reader_method = add_method(
|
36
|
+
klass,
|
37
|
+
self.name,
|
38
|
+
body: self.get
|
39
|
+
)
|
40
|
+
end
|
41
|
+
|
42
|
+
def add_writer_method(klass)
|
43
|
+
return if !self.set
|
44
|
+
self.writer_method = add_method(
|
45
|
+
klass,
|
46
|
+
"#{self.name}=".to_sym,
|
47
|
+
pre_execute: lambda { |object, value| self.check_value_type!(value) },
|
48
|
+
body: self.set
|
49
|
+
)
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def add_method(klass, name, options = {}, &block)
|
55
|
+
Tutor::Attributes::Method.new(name, options, &block).tap { |m| m.define_on(klass, override: self.override) }
|
56
|
+
end
|
57
|
+
|
58
|
+
def get_default
|
59
|
+
if self.alias
|
60
|
+
Tutor::Attributes::Blocks::Alias::Reader.new(self.alias)
|
61
|
+
else
|
62
|
+
Tutor::Attributes::Blocks::Instance::Reader.new(self.name)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def set_default
|
67
|
+
if self.alias
|
68
|
+
Tutor::Attributes::Blocks::Alias::Writer.new(self.alias)
|
69
|
+
else
|
70
|
+
Tutor::Attributes::Blocks::Instance::Writer.new(self.name)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Tutor::Attributes::Blocks::Alias
|
2
|
+
class Reader < Tutor::Attributes::Block
|
3
|
+
attr_reader :aliased_name, :block
|
4
|
+
|
5
|
+
def initialize(aliased_name)
|
6
|
+
self.tap do |block|
|
7
|
+
@aliased_name = aliased_name
|
8
|
+
super() { |object| object.send(block.aliased_name) }
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Tutor::Attributes::Blocks::Alias
|
2
|
+
class Writer < Tutor::Attributes::Block
|
3
|
+
attr_reader :aliased_name, :block
|
4
|
+
|
5
|
+
def initialize(aliased_name)
|
6
|
+
self.tap do |block|
|
7
|
+
@aliased_name = aliased_name
|
8
|
+
super() { |object, value| object.send(block.aliased_name, value) }
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Tutor::Attributes::Blocks::Instance
|
2
|
+
class Reader < Tutor::Attributes::Block
|
3
|
+
attr_reader :name, :block
|
4
|
+
|
5
|
+
def initialize(name, &block)
|
6
|
+
self.tap do |block|
|
7
|
+
@name = name
|
8
|
+
super() do |object|
|
9
|
+
object.instance_variable_get("@#{block.name.to_s}".to_sym)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Tutor::Attributes::Blocks::Instance
|
2
|
+
class Writer < Tutor::Attributes::Block
|
3
|
+
attr_reader :name, :block
|
4
|
+
|
5
|
+
def initialize(name)
|
6
|
+
self.tap do |block|
|
7
|
+
@name = name
|
8
|
+
super() do |object, value|
|
9
|
+
object.instance_variable_set("@#{block.name.to_s}".to_sym, value)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Tutor::Attributes
|
2
|
+
module Default
|
3
|
+
attr_accessor :default
|
4
|
+
|
5
|
+
def default_value_for(object)
|
6
|
+
if self.default.class <= Proc
|
7
|
+
if self.default.lambda? && self.default.parameters.empty?
|
8
|
+
self.default.call
|
9
|
+
else
|
10
|
+
self.default.call(object)
|
11
|
+
end
|
12
|
+
else
|
13
|
+
self.default
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Tutor::Attributes
|
2
|
+
class Method
|
3
|
+
attr_accessor \
|
4
|
+
:name,
|
5
|
+
:body,
|
6
|
+
:pre_execute,
|
7
|
+
:post_execute
|
8
|
+
|
9
|
+
def initialize(name, options = {}, &block)
|
10
|
+
self.name = name.to_sym
|
11
|
+
self.body = block ? Tutor::Attributes::Block.new(&block) : options[:body]
|
12
|
+
self.pre_execute = options[:pre_execute]
|
13
|
+
self.post_execute = options[:post_execute]
|
14
|
+
end
|
15
|
+
|
16
|
+
def define_on(klass, override: false)
|
17
|
+
if !override && klass.method_defined?(self.name)
|
18
|
+
raise NameError.new("Attribute name conflicts with existing method!", self.name)
|
19
|
+
else
|
20
|
+
klass.send(:define_method, self.name, &method_block)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def method_block
|
25
|
+
method = self
|
26
|
+
Proc.new do |*args|
|
27
|
+
return_value = nil
|
28
|
+
method.pre_execute.call(self, *args) unless method.pre_execute.nil?
|
29
|
+
return_value = method.body.block.call(self, *args) unless method.body.nil?
|
30
|
+
method.post_execute.call(self, *args) unless method.post_execute.nil?
|
31
|
+
return_value
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Tutor::Attributes
|
2
|
+
module Type
|
3
|
+
attr_accessor \
|
4
|
+
:type,
|
5
|
+
:nullable
|
6
|
+
|
7
|
+
def valid_value_type?(value)
|
8
|
+
self.type.nil? || (self.nullable && value.nil?) || !(value.class <= self.type).nil?
|
9
|
+
end
|
10
|
+
|
11
|
+
def check_value_type!(value)
|
12
|
+
raise ArgumentError.new("Invalid value type assigned to attribute!") unless self.valid_value_type?(value)
|
13
|
+
true
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/tutor.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'tutor/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "tutor"
|
8
|
+
spec.version = Tutor::VERSION
|
9
|
+
spec.authors = ["David Elner"]
|
10
|
+
spec.email = ["david@davidelner.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{Supplemental teachings for your Ruby classes.}
|
13
|
+
spec.description = %q{Supplemental teachings for your Ruby classes. Adds common patterns and idioms to decorate your classes with.}
|
14
|
+
spec.homepage = "http://github.com/delner/tutor"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
|
+
spec.bindir = "exe"
|
19
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
spec.add_development_dependency "bundler", "~> 1.11"
|
23
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
24
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
25
|
+
spec.add_development_dependency "rspec-collection_matchers", "~> 1.0"
|
26
|
+
spec.add_development_dependency "pry"
|
27
|
+
end
|
metadata
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tutor
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- David Elner
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-05-21 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.11'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.11'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec-collection_matchers
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: pry
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description: Supplemental teachings for your Ruby classes. Adds common patterns and
|
84
|
+
idioms to decorate your classes with.
|
85
|
+
email:
|
86
|
+
- david@davidelner.com
|
87
|
+
executables: []
|
88
|
+
extensions: []
|
89
|
+
extra_rdoc_files: []
|
90
|
+
files:
|
91
|
+
- ".gitignore"
|
92
|
+
- ".rspec"
|
93
|
+
- ".travis.yml"
|
94
|
+
- Gemfile
|
95
|
+
- LICENSE.txt
|
96
|
+
- README.md
|
97
|
+
- Rakefile
|
98
|
+
- bin/console
|
99
|
+
- bin/setup
|
100
|
+
- lib/tutor.rb
|
101
|
+
- lib/tutor/attributes.rb
|
102
|
+
- lib/tutor/attributes/attribute.rb
|
103
|
+
- lib/tutor/attributes/block.rb
|
104
|
+
- lib/tutor/attributes/blocks.rb
|
105
|
+
- lib/tutor/attributes/blocks/alias.rb
|
106
|
+
- lib/tutor/attributes/blocks/alias/reader.rb
|
107
|
+
- lib/tutor/attributes/blocks/alias/writer.rb
|
108
|
+
- lib/tutor/attributes/blocks/instance.rb
|
109
|
+
- lib/tutor/attributes/blocks/instance/reader.rb
|
110
|
+
- lib/tutor/attributes/blocks/instance/writer.rb
|
111
|
+
- lib/tutor/attributes/default.rb
|
112
|
+
- lib/tutor/attributes/method.rb
|
113
|
+
- lib/tutor/attributes/type.rb
|
114
|
+
- lib/tutor/version.rb
|
115
|
+
- tutor.gemspec
|
116
|
+
homepage: http://github.com/delner/tutor
|
117
|
+
licenses:
|
118
|
+
- MIT
|
119
|
+
metadata: {}
|
120
|
+
post_install_message:
|
121
|
+
rdoc_options: []
|
122
|
+
require_paths:
|
123
|
+
- lib
|
124
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
125
|
+
requirements:
|
126
|
+
- - ">="
|
127
|
+
- !ruby/object:Gem::Version
|
128
|
+
version: '0'
|
129
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
130
|
+
requirements:
|
131
|
+
- - ">="
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
134
|
+
requirements: []
|
135
|
+
rubyforge_project:
|
136
|
+
rubygems_version: 2.6.4
|
137
|
+
signing_key:
|
138
|
+
specification_version: 4
|
139
|
+
summary: Supplemental teachings for your Ruby classes.
|
140
|
+
test_files: []
|