mousevc 0.0.2.pre.alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +136 -0
- data/Rakefile +8 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/examples/example.rb +65 -0
- data/lib/mousevc/app.rb +135 -0
- data/lib/mousevc/controller.rb +56 -0
- data/lib/mousevc/error.rb +17 -0
- data/lib/mousevc/input.rb +188 -0
- data/lib/mousevc/model.rb +35 -0
- data/lib/mousevc/persistence.rb +52 -0
- data/lib/mousevc/router.rb +77 -0
- data/lib/mousevc/validation.rb +353 -0
- data/lib/mousevc/version.rb +7 -0
- data/lib/mousevc/view.rb +66 -0
- data/lib/mousevc.rb +49 -0
- data/mousevc.gemspec +33 -0
- metadata +111 -0
@@ -0,0 +1,35 @@
|
|
1
|
+
require_relative 'validation.rb'
|
2
|
+
|
3
|
+
module Mousevc
|
4
|
+
|
5
|
+
##
|
6
|
+
# The base model class for a Mousevc application.
|
7
|
+
# Provides access to basic functionality for validating input before modifying data
|
8
|
+
# via it's +@validation+ attribute, an instance of the +Mousevc::Validation+ class.
|
9
|
+
|
10
|
+
class Model
|
11
|
+
|
12
|
+
##
|
13
|
+
# @!attribute [r]
|
14
|
+
# @return [Mousevc::Validation] a reference to the model's validation instance
|
15
|
+
|
16
|
+
attr_reader :validation
|
17
|
+
|
18
|
+
##
|
19
|
+
# Creates a new +Mousevc::Model+ instance
|
20
|
+
#
|
21
|
+
# @param options [Hash] optionally accepts the following keys:
|
22
|
+
# - :validation => [Mousevc::Router] an instance of the +Mousevc::Validation+ class
|
23
|
+
|
24
|
+
def initialize(options={})
|
25
|
+
@validation = options[:validation] ? options[:validation] : Validation.new
|
26
|
+
clear
|
27
|
+
end
|
28
|
+
|
29
|
+
##
|
30
|
+
# Overridable empty method for clearing data in the model subclass
|
31
|
+
|
32
|
+
def clear
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Mousevc
|
2
|
+
|
3
|
+
##
|
4
|
+
# The +Persistance+ class enables storage and retrieval of models.
|
5
|
+
# While it is intended for storing models it can store
|
6
|
+
# any value as internally it stores the value in a Ruby Hash.
|
7
|
+
#
|
8
|
+
# @note The +Persistance+ class only stores data during the application life cycle. Once the application quits the data may still exist in memory if not explicitly cleared, however it should not be relied upon to be their for subsequent executions.
|
9
|
+
|
10
|
+
class Persistence
|
11
|
+
|
12
|
+
##
|
13
|
+
# Internal data storage
|
14
|
+
|
15
|
+
@@models = {}
|
16
|
+
|
17
|
+
##
|
18
|
+
# Allows clearing of all or some of the models currently stored.
|
19
|
+
# Clears all data and models stored if no keys are provided
|
20
|
+
#
|
21
|
+
# @param args [Symbol] a list of keys to clear from the currently stored models
|
22
|
+
|
23
|
+
def self.clear(*args)
|
24
|
+
if args.empty?
|
25
|
+
@@models.clear
|
26
|
+
else
|
27
|
+
args.each {|arg| @@models.delete(arg)}
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
##
|
32
|
+
# Get a stored model by key
|
33
|
+
#
|
34
|
+
# @param key [Symbol] the key under which the model is stored
|
35
|
+
# @return [Mousevc::Model, Any] the stored model or value
|
36
|
+
|
37
|
+
def self.get(key)
|
38
|
+
@@models[key]
|
39
|
+
end
|
40
|
+
|
41
|
+
##
|
42
|
+
# Set the value of a storage key
|
43
|
+
# @raise [Mousevc::Error] if the key already exists
|
44
|
+
#
|
45
|
+
# @param key [Symbol] the key under which the model is stored
|
46
|
+
|
47
|
+
def self.set(key, value)
|
48
|
+
raise Error.new("Cannot persist, a value already exists at: #{key}") unless @@models[key].nil?
|
49
|
+
@@models.store(key, value)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require_relative 'controller.rb'
|
2
|
+
require_relative 'model.rb'
|
3
|
+
require_relative 'view.rb'
|
4
|
+
|
5
|
+
module Mousevc
|
6
|
+
|
7
|
+
##
|
8
|
+
# Router routes requests to the controller method. Instantiated by the +Mousevc::App+ class.
|
9
|
+
|
10
|
+
class Router
|
11
|
+
|
12
|
+
##
|
13
|
+
# @!attribute controller
|
14
|
+
#
|
15
|
+
# @note Set this variable to the string name of the controller class to be instantiated upon the next application loop
|
16
|
+
#
|
17
|
+
# @return [String] the instance of the current controller
|
18
|
+
|
19
|
+
attr_accessor :controller
|
20
|
+
|
21
|
+
# @!attribute model
|
22
|
+
#
|
23
|
+
# @note Set this variable to the string name of the model class to be instantiated upon the next application loop
|
24
|
+
# @note Can be used to retrieve a model from the +Mousevc::Persistence+ class when set to a symbol.
|
25
|
+
#
|
26
|
+
# @return [String, Symbol] the instance of the current model.
|
27
|
+
|
28
|
+
attr_accessor :model
|
29
|
+
|
30
|
+
# @!attribute action
|
31
|
+
#
|
32
|
+
# @note Set this variable to the symbol name of the method to call on the next controller
|
33
|
+
#
|
34
|
+
# @return [Symbol] the action sent to the controller method
|
35
|
+
|
36
|
+
attr_accessor :action
|
37
|
+
|
38
|
+
##
|
39
|
+
# Creates a new +Mousevc::Router+ instance
|
40
|
+
#
|
41
|
+
# @param options [Hash] expects the following keys:
|
42
|
+
# - :controller => [String] name of default controller class
|
43
|
+
# - :model => [String] name of default model class
|
44
|
+
# - :action => [Symbol] method to call on default controller
|
45
|
+
# - :views => [String] relative path to views directory
|
46
|
+
|
47
|
+
def initialize(options={})
|
48
|
+
@controller = options[:controller] ? options[:controller] : 'Controller'
|
49
|
+
@model = options[:model] ? options[:model] : 'Model'
|
50
|
+
@action = options[:action] ? options[:action] : :hello_mousevc
|
51
|
+
@views = options[:views]
|
52
|
+
end
|
53
|
+
|
54
|
+
##
|
55
|
+
# Routes by:
|
56
|
+
#
|
57
|
+
# 1. creating an instance of the current controller in the +@controller+ attribute
|
58
|
+
# 1. creating an instance of the current model in the +@model+ attribute
|
59
|
+
# 1. creating a new or finding the desired model
|
60
|
+
# 1. passing that controller that model instance, a view instance, and an instance of +self+
|
61
|
+
# 1. sending the controller the current action in +@action+
|
62
|
+
|
63
|
+
def route
|
64
|
+
model = nil
|
65
|
+
model = Persistence.get(@model) if @model.is_a?(Symbol)
|
66
|
+
model = Persistence.get(@controller.to_sym) unless Persistence.get(@controller.to_sym).nil?
|
67
|
+
model = Mousevc.factory(@model).new unless model
|
68
|
+
view = View.new(:dir => @views)
|
69
|
+
controller = Mousevc.factory(@controller).new(
|
70
|
+
:view => view,
|
71
|
+
:model => model,
|
72
|
+
:router => self
|
73
|
+
)
|
74
|
+
controller.send(@action)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,353 @@
|
|
1
|
+
module Mousevc
|
2
|
+
|
3
|
+
##
|
4
|
+
# Extend this class to perform validations on user input in your model.
|
5
|
+
# Use the +@error+ attribute to pass notices to the +Mousevc::Input+ class.
|
6
|
+
# From there the notice can be easily output in your views.
|
7
|
+
#
|
8
|
+
# Each predicate method in this class sets the appropriate error message ready
|
9
|
+
# to be output to the user.
|
10
|
+
|
11
|
+
class Validation
|
12
|
+
|
13
|
+
##
|
14
|
+
# @!attribute error
|
15
|
+
#
|
16
|
+
# @return [String] the error message generated during validation
|
17
|
+
|
18
|
+
attr_accessor :error
|
19
|
+
|
20
|
+
##
|
21
|
+
# Returns +true+ if the string containers only uppercase or lowercase letters
|
22
|
+
#
|
23
|
+
# @param value [String] the value
|
24
|
+
# @return [Boolean]
|
25
|
+
|
26
|
+
def alpha?(value)
|
27
|
+
is_alpha = coerce_bool (value =~ /^[a-zA-Z]+$/)
|
28
|
+
unless is_alpha
|
29
|
+
@error = "Error, expected only alphabetical characters, got: #{value}"
|
30
|
+
end
|
31
|
+
is_alpha
|
32
|
+
end
|
33
|
+
|
34
|
+
##
|
35
|
+
# Returns +true+ if the value is equal to the other
|
36
|
+
#
|
37
|
+
# @param value [Any] the value
|
38
|
+
# @param other [Any] the other value
|
39
|
+
# @return [Boolean]
|
40
|
+
|
41
|
+
def matches?(value, other)
|
42
|
+
is_match = coerce_bool (value == other)
|
43
|
+
unless is_match
|
44
|
+
@error = "Error, expected value to match: #{other}, got: #{value}"
|
45
|
+
end
|
46
|
+
is_match
|
47
|
+
end
|
48
|
+
|
49
|
+
##
|
50
|
+
# Returns +true+ if the value is not equal to the other
|
51
|
+
#
|
52
|
+
# @param value [Any] the value
|
53
|
+
# @param other [Any] the other value
|
54
|
+
# @return [Boolean]
|
55
|
+
|
56
|
+
def differs?(value, other)
|
57
|
+
is_different = coerce_bool (value != other)
|
58
|
+
unless is_different
|
59
|
+
@error = "Error, expected value to differ from: #{other}, got: #{value}"
|
60
|
+
end
|
61
|
+
is_different
|
62
|
+
end
|
63
|
+
|
64
|
+
##
|
65
|
+
# Returns +true+ if the value has at least the specified length
|
66
|
+
#
|
67
|
+
# @param value [String] the value
|
68
|
+
# @param length [Integer] the length
|
69
|
+
# @return [Boolean]
|
70
|
+
|
71
|
+
def min_length?(value, length)
|
72
|
+
has_min_length = coerce_bool (value.length >= length)
|
73
|
+
unless has_min_length
|
74
|
+
@error = "Error, expected value to have at least #{length} characters, got: #{value.length}"
|
75
|
+
end
|
76
|
+
has_min_length
|
77
|
+
end
|
78
|
+
|
79
|
+
##
|
80
|
+
# Returns +true+ if the value has at most the specified length
|
81
|
+
#
|
82
|
+
# @param value [String] the value
|
83
|
+
# @param length [Integer] the length
|
84
|
+
# @return [Boolean]
|
85
|
+
|
86
|
+
def max_length?(value, length)
|
87
|
+
has_max_length = coerce_bool (value.length <= length)
|
88
|
+
unless has_max_length
|
89
|
+
@error = "Error, expected value to have at most #{length} characters, got: #{value.length}"
|
90
|
+
end
|
91
|
+
has_max_length
|
92
|
+
end
|
93
|
+
|
94
|
+
##
|
95
|
+
# Returns +true+ if the value has exactly the specified length
|
96
|
+
#
|
97
|
+
# @param value [String] the value
|
98
|
+
# @param length [Integer] the length
|
99
|
+
# @return [Boolean]
|
100
|
+
|
101
|
+
def exact_length?(value, length)
|
102
|
+
has_exact_length = coerce_bool (value.length == length)
|
103
|
+
unless has_exact_length
|
104
|
+
@error = "Error, expected value to have exactly #{length} characters, got: #{value.length}"
|
105
|
+
end
|
106
|
+
has_exact_length
|
107
|
+
end
|
108
|
+
|
109
|
+
##
|
110
|
+
# Returns +true+ if the value is greater than the other given value
|
111
|
+
# @note This method does note covert strings to numbers
|
112
|
+
#
|
113
|
+
# @param value [Mixed] the value
|
114
|
+
# @param other [Mixed] the other value
|
115
|
+
# @return [Boolean]
|
116
|
+
|
117
|
+
def greater_than?(value, other)
|
118
|
+
is_greater = coerce_bool (value > other)
|
119
|
+
unless is_greater
|
120
|
+
@error = "Error, expected value to be greater than #{other}, got: #{value}"
|
121
|
+
end
|
122
|
+
is_greater
|
123
|
+
end
|
124
|
+
|
125
|
+
##
|
126
|
+
# Returns +true+ if the value is greater than or equal to the other given value
|
127
|
+
# @note This method does note covert strings to numbers
|
128
|
+
#
|
129
|
+
# @param value [Mixed] the value
|
130
|
+
# @param other [Mixed] the other value
|
131
|
+
# @return [Boolean]
|
132
|
+
|
133
|
+
def greater_than_equal_to?(value, other)
|
134
|
+
is_greater_eq = coerce_bool (value >= other)
|
135
|
+
unless is_greater_eq
|
136
|
+
@error = "Error, expected value to be greater than or equal to #{other}, got: #{value}"
|
137
|
+
end
|
138
|
+
is_greater_eq
|
139
|
+
end
|
140
|
+
|
141
|
+
##
|
142
|
+
# Returns +true+ if the value is less than the other given value
|
143
|
+
# @note This method does note covert strings to numbers
|
144
|
+
#
|
145
|
+
# @param value [Mixed] the value
|
146
|
+
# @param other [Mixed] the other value
|
147
|
+
# @return [Boolean]
|
148
|
+
|
149
|
+
def less_than?(value, other)
|
150
|
+
is_less = coerce_bool (value < other)
|
151
|
+
unless is_less
|
152
|
+
@error = "Error, expected value to be less than #{other}, got: #{value}"
|
153
|
+
end
|
154
|
+
is_less
|
155
|
+
end
|
156
|
+
|
157
|
+
##
|
158
|
+
# Returns +true+ if the value is less than or equal to the other given value
|
159
|
+
# @note This method does note covert strings to numbers
|
160
|
+
#
|
161
|
+
# @param value [Mixed] the value
|
162
|
+
# @param other [Mixed] the other value
|
163
|
+
# @return [Boolean]
|
164
|
+
|
165
|
+
def less_than_equal_to?(value, other)
|
166
|
+
is_less_eq = coerce_bool (value <= other)
|
167
|
+
unless is_less_eq
|
168
|
+
@error = "Error, expected value to be less than or equal to #{other}, got: #{value}"
|
169
|
+
end
|
170
|
+
is_less_eq
|
171
|
+
end
|
172
|
+
|
173
|
+
##
|
174
|
+
# Returns +true+ if the value is an integer or a decimal, includes positive and negative
|
175
|
+
#
|
176
|
+
# @param value [String] the value
|
177
|
+
# @return [Boolean]
|
178
|
+
|
179
|
+
def numeric?(value)
|
180
|
+
is_numeric = (integer?(value) || decimal?(value))
|
181
|
+
unless is_numeric
|
182
|
+
@error = "Error, expected value to be numeric, got: #{value}"
|
183
|
+
end
|
184
|
+
is_numeric
|
185
|
+
end
|
186
|
+
|
187
|
+
##
|
188
|
+
# Returns +true+ if the value is an integer, includes positive and negative
|
189
|
+
#
|
190
|
+
# @param value [String] the value
|
191
|
+
# @return [Boolean]
|
192
|
+
|
193
|
+
def integer?(value)
|
194
|
+
is_integer = coerce_bool (value =~ /^[-+]?\d+$/)
|
195
|
+
unless is_integer
|
196
|
+
@error = "Error, expected value to be an integer, got: #{value}"
|
197
|
+
end
|
198
|
+
is_integer
|
199
|
+
end
|
200
|
+
|
201
|
+
##
|
202
|
+
# Returns +true+ if the value is an decimal, includes positive and negative
|
203
|
+
#
|
204
|
+
# @param value [String] the value
|
205
|
+
# @return [Boolean]
|
206
|
+
|
207
|
+
def decimal?(value)
|
208
|
+
is_decimal = coerce_bool (value =~ /[+-]?[\d_]+\.[\d_]+(e[+-]?[\d_]+)?\b|[+-]?[\d_]+e[+-]?[\d_]+\b/i)
|
209
|
+
unless is_decimal
|
210
|
+
@error = "Error, expected value to be a decimal, got: #{value}"
|
211
|
+
end
|
212
|
+
is_decimal
|
213
|
+
end
|
214
|
+
|
215
|
+
##
|
216
|
+
# Returns +true+ if the value is a positive integer
|
217
|
+
#
|
218
|
+
# @param value [String] the value
|
219
|
+
# @return [Boolean]
|
220
|
+
|
221
|
+
def natural?(value)
|
222
|
+
is_natural = coerce_bool (value =~ /^\d+$/)
|
223
|
+
unless is_natural
|
224
|
+
@error = "Error, expected value to be a natural number, got: #{value}"
|
225
|
+
end
|
226
|
+
is_natural
|
227
|
+
end
|
228
|
+
|
229
|
+
##
|
230
|
+
# Returns +true+ if the value is a positive integer greater than zero
|
231
|
+
#
|
232
|
+
# @param value [String] the value
|
233
|
+
# @return [Boolean]
|
234
|
+
|
235
|
+
def natural_no_zero?(value)
|
236
|
+
is_natural_no_zero = coerce_bool (value =~ /^[1-9]+$/)
|
237
|
+
unless is_natural_no_zero
|
238
|
+
@error = "Error, expected value to be a natural number above 0, got: #{value}"
|
239
|
+
end
|
240
|
+
is_natural_no_zero
|
241
|
+
end
|
242
|
+
|
243
|
+
##
|
244
|
+
# Returns +true+ if the value is a valid url and accounts for many edge cases
|
245
|
+
#
|
246
|
+
# @param value [String] the value
|
247
|
+
# @return [Boolean]
|
248
|
+
|
249
|
+
def url?(value)
|
250
|
+
is_url = coerce_bool (value =~ /^(?:(?:https?|ftp):\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-{1})*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-{1})*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,}))\.?)(?::\d{2,5})?(?:[\/?#]\S*)?$/i)
|
251
|
+
unless is_url
|
252
|
+
@error = "Error, expected value to be a url, got: #{value}"
|
253
|
+
end
|
254
|
+
is_url
|
255
|
+
end
|
256
|
+
|
257
|
+
##
|
258
|
+
# Returns +true+ if the value is a valid email address
|
259
|
+
#
|
260
|
+
# @note This method is more a validation that the value is probably an email. The best way to confirm an email is to require the user to enter it twice, compare them, and ultimately send them an email!
|
261
|
+
#
|
262
|
+
# @param value [String] the value
|
263
|
+
# @return [Boolean]
|
264
|
+
|
265
|
+
def email?(value)
|
266
|
+
is_email = coerce_bool (value =~ /.+@.+/)
|
267
|
+
unless is_email
|
268
|
+
@error = "Error, expected value to be an email address, got: #{value}"
|
269
|
+
end
|
270
|
+
is_email
|
271
|
+
end
|
272
|
+
|
273
|
+
##
|
274
|
+
# Returns +true+ if the value is a valid IP address i.e. between 0.0.0.0 and 255.255.255.255
|
275
|
+
#
|
276
|
+
# @param value [String] the value
|
277
|
+
# @return [Boolean]
|
278
|
+
|
279
|
+
def ip?(value)
|
280
|
+
is_ip = coerce_bool (value =~ /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/)
|
281
|
+
unless is_ip
|
282
|
+
@error = "Error, expected value to be an IP address, got: #{value}"
|
283
|
+
end
|
284
|
+
is_ip
|
285
|
+
end
|
286
|
+
|
287
|
+
##
|
288
|
+
# Returns +true+ if the value contains only letters, numbers
|
289
|
+
# and has a length between the min and max of the given range
|
290
|
+
#
|
291
|
+
# @param value [String] the value
|
292
|
+
# @param range [Range] the min and max length of the password, defaults to +8..15+
|
293
|
+
# @return [Boolean]
|
294
|
+
|
295
|
+
def password?(value, range=8..15)
|
296
|
+
is_password = coerce_bool (value =~ /^(?!.*["'])(?=.*\d)(?=.*[a-z])\S{#{range.min},#{range.max}}$/)
|
297
|
+
unless is_password
|
298
|
+
@error = "Error, expected value to contain only lowercase letters, numbers, and have length between #{range.min} and #{range.max}, got: #{value}"
|
299
|
+
end
|
300
|
+
is_password
|
301
|
+
end
|
302
|
+
|
303
|
+
##
|
304
|
+
# Returns +true+ if the value contains only letters, numbers and has a length,
|
305
|
+
# has a length between the min and max of the given range,
|
306
|
+
# and contains at least one uppercase letter
|
307
|
+
#
|
308
|
+
# @param value [String] the value
|
309
|
+
# @param range [Range] the min and max length of the password, defaults to +8..15+
|
310
|
+
# @return [Boolean]
|
311
|
+
|
312
|
+
def password_caps?(value, range=8..15)
|
313
|
+
is_password_caps = coerce_bool (value =~ /^(?!.*["'])(?=.*\d)(?=.*[A-Z])(?=.*[a-z])\S{#{range.min},#{range.max}}$/)
|
314
|
+
unless is_password_caps
|
315
|
+
@error = "Error, expected value to contain lowercase letters, numbers, have length between #{range.min} and #{range.max} and at least one uppercase letter, got: #{value}"
|
316
|
+
end
|
317
|
+
is_password_caps
|
318
|
+
end
|
319
|
+
|
320
|
+
##
|
321
|
+
# Returns +true+ if the value contains only letters, numbers and has a length,
|
322
|
+
# has a length between the min and max of the given range,
|
323
|
+
# contains at least one uppercase letter,
|
324
|
+
# and contains at least one symbol
|
325
|
+
#
|
326
|
+
# @note Excludes single and double quotes.
|
327
|
+
#
|
328
|
+
# @param value [String] the value
|
329
|
+
# @param range [Range] the min and max length of the password, defaults to +8..15+
|
330
|
+
# @return [Boolean]
|
331
|
+
|
332
|
+
def password_symbols?(value, range=8..15)
|
333
|
+
is_password_symbols = coerce_bool (value =~ /^(?!.*["'])(?=.*[~!@#$%^&*()_+\-\\|{}<>\[\]:;?\/])(?=.*\d)(?=.*[A-Z])(?=.*[a-z])\S{#{range.min},#{range.max}}$/)
|
334
|
+
unless is_password_symbols
|
335
|
+
@error = @error = "Error, expected value to contain lowercase letters, numbers, have length between #{range.min} and #{range.max}, at least one uppercase letter and at least one symbol, got: #{value}"
|
336
|
+
end
|
337
|
+
is_password_symbols
|
338
|
+
end
|
339
|
+
|
340
|
+
protected
|
341
|
+
|
342
|
+
##
|
343
|
+
# A method used to guarantee a value is converted explicitly into either +true+ or +false+
|
344
|
+
#
|
345
|
+
# @param value [Any] the original value
|
346
|
+
# @return [Boolean]
|
347
|
+
|
348
|
+
def coerce_bool(value)
|
349
|
+
!!value
|
350
|
+
end
|
351
|
+
|
352
|
+
end
|
353
|
+
end
|
data/lib/mousevc/view.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'erb'
|
2
|
+
|
3
|
+
module Mousevc
|
4
|
+
|
5
|
+
##
|
6
|
+
# The view rendering class of Mousevc.
|
7
|
+
# @note Currently only supports ERB templates and a file naming convention of: +[VIEW_NAME].txt.erb+
|
8
|
+
|
9
|
+
class View
|
10
|
+
|
11
|
+
##
|
12
|
+
# @!attribute dir
|
13
|
+
#
|
14
|
+
# @return [String] the path to the views directory
|
15
|
+
|
16
|
+
attr_reader :dir
|
17
|
+
|
18
|
+
##
|
19
|
+
# Create a new +Mousevc::View+ instance
|
20
|
+
|
21
|
+
def initialize(options={})
|
22
|
+
@dir = options[:dir]
|
23
|
+
end
|
24
|
+
|
25
|
+
##
|
26
|
+
# Renders a view, passing it the given data. In the ERB template the hash variables will be available as instance variables e.g. +@view_variable+
|
27
|
+
#
|
28
|
+
# @note If the string passed to the +view+ parameter is an existing file it will be used as the ERB template. Otherwise the string will be parsed as ERB.
|
29
|
+
#
|
30
|
+
# @note Optionally the view output can be suppressed via setting output to +false+. The view will be returned as a string allowing later output e.g. +render('view', {:data => data}, false)+
|
31
|
+
#
|
32
|
+
# @note In the event that you want to suppress output and not provide any data you may substitute +data+ for +output+ in the parameter list e.g. +render('view', false)+
|
33
|
+
#
|
34
|
+
# @param view [String] the name of the view with +.txt.erb+ omitted
|
35
|
+
# @param args [Array] accepts 2 additional parameters.
|
36
|
+
# - +data+ [Hash] the data to pass to the view (optionally omit)
|
37
|
+
# - +output+ [Boolean] false if output is to be suppressed
|
38
|
+
#
|
39
|
+
# @return [String] the rendered view as a string
|
40
|
+
|
41
|
+
def render(view, *args)
|
42
|
+
data = args[0].is_a?(Hash) ? args[0] : {}
|
43
|
+
output = true
|
44
|
+
output = false if args[0] == false || args[1] == false
|
45
|
+
path = "#{Dir.pwd}/#{@dir}/#{view}.txt.erb"
|
46
|
+
view = File.file?(path) ? File.read(path) : view
|
47
|
+
to_ivars(data)
|
48
|
+
result = ERB.new(view).result(binding)
|
49
|
+
puts result if output
|
50
|
+
result
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
##
|
56
|
+
# Take the given data hash and converts the key/value pairs into instance variables.
|
57
|
+
#
|
58
|
+
# @param data [Hash] the data to convert into instance variables.
|
59
|
+
|
60
|
+
def to_ivars(data)
|
61
|
+
data.to_h.each do |key, value|
|
62
|
+
instance_variable_set("@#{key.to_s}", value)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
data/lib/mousevc.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
require_relative 'mousevc/version.rb'
|
2
|
+
require_relative 'mousevc/app.rb'
|
3
|
+
|
4
|
+
##
|
5
|
+
# Mousevc is the top level module and namespace for the Mousevc framework.
|
6
|
+
|
7
|
+
module Mousevc
|
8
|
+
|
9
|
+
##
|
10
|
+
# @todo Add link to documentation
|
11
|
+
#
|
12
|
+
# @return [String] some pretty ASCII art
|
13
|
+
|
14
|
+
def self.art
|
15
|
+
lines = [
|
16
|
+
"",
|
17
|
+
"(`) (`)",
|
18
|
+
"=('o')=",
|
19
|
+
" m m ",
|
20
|
+
"",
|
21
|
+
"MousevC",
|
22
|
+
"V L",
|
23
|
+
"C I",
|
24
|
+
"",
|
25
|
+
"by",
|
26
|
+
"Bideo Wego",
|
27
|
+
"http://bideowego.com/mousevc",
|
28
|
+
"",
|
29
|
+
"Documentation",
|
30
|
+
"http://www.rubydoc.info/gems/mousevc"
|
31
|
+
]
|
32
|
+
width = lines.max.length
|
33
|
+
width = width.even? ? width + 1 : width
|
34
|
+
lines.map do |s|
|
35
|
+
s.center(width)
|
36
|
+
end.join("\n")
|
37
|
+
end
|
38
|
+
|
39
|
+
##
|
40
|
+
# Generates a Mousevc class constant ready for instantiation
|
41
|
+
#
|
42
|
+
# @return [Constant] the class constant
|
43
|
+
|
44
|
+
def self.factory(class_name)
|
45
|
+
"Mousevc::#{class_name}".split('::').inject(Object) do |object, string|
|
46
|
+
object.const_get(string)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/mousevc.gemspec
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'mousevc/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "mousevc"
|
8
|
+
spec.version = Mousevc::VERSION
|
9
|
+
spec.authors = ["Chris Scavello"]
|
10
|
+
spec.email = ["bideowego@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{A tiny mouse sized MVC framework to jump start command line apps.}
|
13
|
+
#spec.description = %q{TODO: Write a longer description or delete this line.}
|
14
|
+
spec.homepage = "http://bideowego.com/mousevc"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
# Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
|
18
|
+
# delete this section to allow pushing this gem to any host.
|
19
|
+
if spec.respond_to?(:metadata)
|
20
|
+
spec.metadata['allowed_push_host'] = "https://rubygems.org"
|
21
|
+
else
|
22
|
+
raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
|
23
|
+
end
|
24
|
+
|
25
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
26
|
+
spec.bindir = "exe"
|
27
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
28
|
+
spec.require_paths = ["lib"]
|
29
|
+
|
30
|
+
spec.add_development_dependency "bundler", "~> 1.10"
|
31
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
32
|
+
spec.add_development_dependency "rspec"
|
33
|
+
end
|