wedgeio 0.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 +7 -0
- data/.gitignore +14 -0
- data/Gemfile +9 -0
- data/LICENSE.txt +22 -0
- data/README.md +49 -0
- data/Rakefile +9 -0
- data/lib/roda/plugins/wedge.rb +93 -0
- data/lib/wedge.rb +238 -0
- data/lib/wedge/component.rb +321 -0
- data/lib/wedge/config.rb +128 -0
- data/lib/wedge/dom.rb +139 -0
- data/lib/wedge/events.rb +136 -0
- data/lib/wedge/html.rb +29 -0
- data/lib/wedge/opal.rb +18 -0
- data/lib/wedge/plugins/form.rb +431 -0
- data/lib/wedge/plugins/history.rb +92 -0
- data/lib/wedge/plugins/location.rb +78 -0
- data/lib/wedge/plugins/pjax.rb +65 -0
- data/lib/wedge/plugins/validations.rb +251 -0
- data/lib/wedge/utilis/blank.rb +133 -0
- data/lib/wedge/utilis/element.rb +23 -0
- data/lib/wedge/utilis/hash.rb +77 -0
- data/lib/wedge/utilis/indifferent_hash.rb +209 -0
- data/lib/wedge/utilis/methods.rb +25 -0
- data/lib/wedge/utilis/nokogiri.rb +44 -0
- data/lib/wedge/utilis/titleize.rb +97 -0
- data/lib/wedge/utilis/try.rb +106 -0
- data/lib/wedge/version.rb +3 -0
- data/test.rb +44 -0
- data/test/dummy/app.rb +34 -0
- data/test/dummy/components/bar.rb +14 -0
- data/test/dummy/components/base.rb +5 -0
- data/test/dummy/components/root.rb +42 -0
- data/test/dummy/config.ru +6 -0
- data/test/dummy/forms/bar.rb +5 -0
- data/test/dummy/forms/foo.rb +6 -0
- data/test/test.js +59 -0
- data/test/test_basic_component.rb +34 -0
- data/test/test_browserio.rb +13 -0
- data/test/test_helper.rb +38 -0
- data/wedge.gemspec +32 -0
- metadata +236 -0
@@ -0,0 +1,92 @@
|
|
1
|
+
module Wedge
|
2
|
+
module Plugins
|
3
|
+
class History < Component
|
4
|
+
config.name :history_plugin
|
5
|
+
end
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
if RUBY_ENGINE == 'opal'
|
10
|
+
require 'wedge/plugins/location'
|
11
|
+
|
12
|
+
module Browser
|
13
|
+
# {Window} instances are {Native} objects used to wrap native window instances.
|
14
|
+
#
|
15
|
+
# Generally, you will want to use the top level {::Window} instance, which
|
16
|
+
# wraps `window` from the main page.
|
17
|
+
class Window
|
18
|
+
include Native
|
19
|
+
|
20
|
+
# @!attribute [r] history
|
21
|
+
# @return [History] the history for this window
|
22
|
+
def history
|
23
|
+
History.new(`#@native.history`) if `#@native.history`
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# {History} allows manipulation of the session history.
|
28
|
+
#
|
29
|
+
# @see https://developer.mozilla.org/en-US/docs/Web/API/History
|
30
|
+
class History
|
31
|
+
include Native
|
32
|
+
|
33
|
+
# @!attribute [r] length
|
34
|
+
# @return [Integer] how many items are in the history
|
35
|
+
alias_native :length
|
36
|
+
|
37
|
+
# Go back in the history.
|
38
|
+
#
|
39
|
+
# @param number [Integer] how many items to go back
|
40
|
+
def back(number = 1)
|
41
|
+
`History.go(-number)`
|
42
|
+
end
|
43
|
+
|
44
|
+
# Go forward in the history.
|
45
|
+
#
|
46
|
+
# @param number [Integer] how many items to go forward
|
47
|
+
def forward(number = 1)
|
48
|
+
`History.go(number)`
|
49
|
+
end
|
50
|
+
|
51
|
+
# Push an item in the history.
|
52
|
+
#
|
53
|
+
# @param item [String] the item to push in the history
|
54
|
+
# @param data [Object] additional state to push
|
55
|
+
def push(item, data = nil)
|
56
|
+
data = `null` if data.nil?
|
57
|
+
|
58
|
+
`History.pushState(jQuery.parseJSON(data.$to_json()), null, item)`
|
59
|
+
end
|
60
|
+
|
61
|
+
# Replace the current history item with another.
|
62
|
+
#
|
63
|
+
# @param item [String] the item to replace with
|
64
|
+
# @param data [Object] additional state to replace
|
65
|
+
def replace(item, data = nil)
|
66
|
+
data = `null` if data.nil?
|
67
|
+
|
68
|
+
`History.replaceState(data, null, item)`
|
69
|
+
end
|
70
|
+
|
71
|
+
def get_state
|
72
|
+
Native(`History.getState()`)
|
73
|
+
end
|
74
|
+
|
75
|
+
# @!attribute [r] current
|
76
|
+
# @return [String] the current item
|
77
|
+
def current
|
78
|
+
$window.location.path
|
79
|
+
end
|
80
|
+
|
81
|
+
def change &block
|
82
|
+
%x{
|
83
|
+
History.Adapter.bind(window,'statechange',function(e){
|
84
|
+
var state = History.getState();
|
85
|
+
state = #{Native(`state`)}
|
86
|
+
return #{block.call(`state`)}
|
87
|
+
});
|
88
|
+
}
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module Browser
|
2
|
+
|
3
|
+
# Allows manipulation of a location, usually from {Window} and {DOM::Document}.
|
4
|
+
#
|
5
|
+
# @see https://developer.mozilla.org/en-US/docs/Web/API/Location
|
6
|
+
class Location
|
7
|
+
include Native
|
8
|
+
|
9
|
+
# Change the location.
|
10
|
+
#
|
11
|
+
# @param url [String, #to_s] the URL to go to
|
12
|
+
def assign(url)
|
13
|
+
`#@native.assign(#{url.to_s})`
|
14
|
+
end
|
15
|
+
|
16
|
+
# Replace the current URL.
|
17
|
+
#
|
18
|
+
# @param url [String, #to_s] the URL to go to
|
19
|
+
def replace(url)
|
20
|
+
`#@native.replace(#{url.to_s})`
|
21
|
+
end
|
22
|
+
|
23
|
+
# Reload the page.
|
24
|
+
#
|
25
|
+
# @param force [Boolean] whether to force the reload
|
26
|
+
def reload(force = false)
|
27
|
+
`#@native.reload(force)`
|
28
|
+
end
|
29
|
+
|
30
|
+
# Convert the location to a string.
|
31
|
+
def to_s
|
32
|
+
`#@native.toString()`
|
33
|
+
end
|
34
|
+
|
35
|
+
# @!attribute fragment
|
36
|
+
# @return [String] the hash fragment of the location URI
|
37
|
+
alias_native :fragment, :hash
|
38
|
+
alias_native :fragment=, :hash=
|
39
|
+
|
40
|
+
# @!attribute host
|
41
|
+
# @return [String] the host part of the location URI
|
42
|
+
alias_native :host
|
43
|
+
alias_native :host=
|
44
|
+
|
45
|
+
# @!attribute uri
|
46
|
+
# @return [String] the whole location URI
|
47
|
+
alias_native :uri, :href
|
48
|
+
alias_native :uri=, :href=
|
49
|
+
|
50
|
+
# @!attribute path
|
51
|
+
# @return [String] the path part of the location URI
|
52
|
+
alias_native :path, :pathname
|
53
|
+
alias_native :path=, :pathname=
|
54
|
+
|
55
|
+
# @!attribute port
|
56
|
+
# @return [Integer] the port part of the location URI
|
57
|
+
alias_native :port
|
58
|
+
alias_native :port=
|
59
|
+
|
60
|
+
# @!attribute scheme
|
61
|
+
# @return [String] the scheme part of the location URI
|
62
|
+
alias_native :scheme, :protocol
|
63
|
+
alias_native :scheme=, :protocol=
|
64
|
+
|
65
|
+
# @!attribute query
|
66
|
+
# @return [String] the query part of the location URI
|
67
|
+
alias_native :query, :search
|
68
|
+
alias_native :query=, :search=
|
69
|
+
end
|
70
|
+
|
71
|
+
class Window
|
72
|
+
# @!attribute [r] location
|
73
|
+
# @return [Location] the location for the window
|
74
|
+
def location
|
75
|
+
Location.new(`#@native.location`) if `#@native.location`
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Wedge
|
2
|
+
module Plugins
|
3
|
+
class Pjax < Component
|
4
|
+
config.name :pjax, :pjax_plugin
|
5
|
+
config.requires :history_plugin
|
6
|
+
|
7
|
+
class Nanobar
|
8
|
+
include Native
|
9
|
+
|
10
|
+
alias_native :go
|
11
|
+
alias_native :start
|
12
|
+
alias_native :finish
|
13
|
+
|
14
|
+
def initialize options = {}
|
15
|
+
`var Nanobar=function(){"use strict";var t,i,e,s,h,n,o={width:"100%",height:"4px",zIndex:9999,top:"0"},a={width:0,height:"100%",clear:"both",transition:"height .3s"};return t=function(t,i){var e;for(e in i)t.style[e]=i[e];t.style["float"]="left"},s=function(){var t=this,i=this.width-this.here;.1>i&&i>-.1?(h.call(this,this.here),this.moving=!1,100==this.width&&(this.el.style.height=0,setTimeout(function(){t.cont.el.removeChild(t.el)},300))):(h.call(this,this.width-i/4),setTimeout(function(){t.go()},16))},h=function(t){this.width=t,this.el.style.width=this.width+"%"},n=function(){var t=new i(this);this.bars.unshift(t)},i=function(i){this.el=document.createElement("div"),this.el.style.backgroundColor=i.opts.bg,this.width=0,this.here=0,this.moving=!1,this.cont=i,t(this.el,a),i.el.appendChild(this.el)},i.prototype.go=function(t){t?(this.here=t,this.moving||(this.moving=!0,s.call(this))):this.moving&&s.call(this)},e=function(i){var e,s,h=this.opts=i||{};h.bg=h.bg||"#000",this.bars=[],e=this.el=document.createElement("div"),t(this.el,o),h.id&&(e.id=h.id),h.className&&(e.className=h.className),e.style.position=h.target?"relative":"fixed",h.target?(s=h.target,s.insertBefore(e,h.target.firstChild)):(s=document.getElementsByTagName("body")[0],s.appendChild(e)),s.className="nanobar-custom-parent",n.call(this)},e.prototype.go=function(t){this.bars[0].go(t),100==t&&n.call(this)},e.prototype.start=function(){(function(){var t=this.bars[0],i=function(){setTimeout(function(){var e=t.here+Math.round(10*Math.random());t.here>=99||(e>99&&(e=99),t.go(e),i())},500)};t.go(10),i()}).call(this)},e.prototype.finish=function(){this.go(100)},e}();`
|
16
|
+
super `new Nanobar(options)`
|
17
|
+
end
|
18
|
+
end if client?
|
19
|
+
|
20
|
+
def progress_bar
|
21
|
+
$pjax_progress_bar
|
22
|
+
end
|
23
|
+
|
24
|
+
def get href = false
|
25
|
+
$pjax_progress_bar = Nanobar.new({bg: '#f99f22'}.to_n)
|
26
|
+
progress_bar.start
|
27
|
+
`$(document).trigger('page:click')`
|
28
|
+
$window.history.push href, pjax: true
|
29
|
+
end
|
30
|
+
|
31
|
+
on :click, 'a' do |el, evt|
|
32
|
+
href = el.attr 'href'
|
33
|
+
|
34
|
+
unless href =~ /^#/i || href =~ /^javascript:/i || el.attr('target') == '_blank'
|
35
|
+
evt.prevent_default
|
36
|
+
get href
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
on :history_change do |e|
|
41
|
+
if e.data.pjax
|
42
|
+
progress_bar.start
|
43
|
+
`$(document).trigger('page:request')`
|
44
|
+
HTTP.get(e.url) do |response|
|
45
|
+
res = Native(response.xhr)
|
46
|
+
html = res.responseText
|
47
|
+
# grab and add the body
|
48
|
+
matches = html.match(/<body[^>]*>((.|[\n\r])*)<\/body>/im)
|
49
|
+
dom.find('body').html matches[1]
|
50
|
+
# grab and eval the scripts
|
51
|
+
matches = html.match(/<script>((.|[\n\r])*)<\/script>/im)
|
52
|
+
# `eval(#{matches[0]})`
|
53
|
+
(matches[1] || '').split('</script>').each do |script|
|
54
|
+
# script = script.sub('<script>', '')
|
55
|
+
script = script.strip.sub('</html>', '').sub('<script>', '')
|
56
|
+
`jQuery.globalEval(script);`
|
57
|
+
end
|
58
|
+
progress_bar.finish
|
59
|
+
`$('html, body').animate({ scrollTop: 0 }, 0); $(document).trigger('page:load');`
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,251 @@
|
|
1
|
+
module Wedge
|
2
|
+
module Plugins
|
3
|
+
class Form < Component
|
4
|
+
# Provides a base implementation for extensible validation routines.
|
5
|
+
# {Scrivener::Validations} currently only provides the following assertions:
|
6
|
+
#
|
7
|
+
# * assert
|
8
|
+
# * assert_present
|
9
|
+
# * assert_format
|
10
|
+
# * assert_numeric
|
11
|
+
# * assert_url
|
12
|
+
# * assert_email
|
13
|
+
# * assert_member
|
14
|
+
# * assert_length
|
15
|
+
# * assert_decimal
|
16
|
+
# * assert_equal
|
17
|
+
#
|
18
|
+
# The core tenets that Scrivener::Validations advocates can be summed up in a
|
19
|
+
# few bullet points:
|
20
|
+
#
|
21
|
+
# 1. Validations are much simpler and better done using composition rather
|
22
|
+
# than macros.
|
23
|
+
# 2. Error messages should be kept separate and possibly in the view or
|
24
|
+
# presenter layer.
|
25
|
+
# 3. It should be easy to write your own validation routine.
|
26
|
+
#
|
27
|
+
# Other validations are simply added on a per-model or per-project basis.
|
28
|
+
#
|
29
|
+
# @example
|
30
|
+
#
|
31
|
+
# class Quote
|
32
|
+
# attr_accessor :title
|
33
|
+
# attr_accessor :price
|
34
|
+
# attr_accessor :date
|
35
|
+
#
|
36
|
+
# def validate
|
37
|
+
# assert_present :title
|
38
|
+
# assert_numeric :price
|
39
|
+
# assert_format :date, /\A[\d]{4}-[\d]{1,2}-[\d]{1,2}\z
|
40
|
+
# end
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# s = Quote.new
|
44
|
+
# s.valid?
|
45
|
+
# # => false
|
46
|
+
#
|
47
|
+
# s.errors
|
48
|
+
# # => { :title => [:not_present],
|
49
|
+
# :price => [:not_numeric],
|
50
|
+
# :date => [:format] }
|
51
|
+
#
|
52
|
+
module Validations
|
53
|
+
def server? &block
|
54
|
+
RUBY_ENGINE == 'ruby'
|
55
|
+
end
|
56
|
+
alias :server :server?
|
57
|
+
|
58
|
+
def client?
|
59
|
+
RUBY_ENGINE == 'opal'
|
60
|
+
end
|
61
|
+
alias :client :client?
|
62
|
+
|
63
|
+
def self.server? &block
|
64
|
+
RUBY_ENGINE == 'ruby'
|
65
|
+
end
|
66
|
+
alias :server :server?
|
67
|
+
|
68
|
+
def self.client?
|
69
|
+
RUBY_ENGINE == 'opal'
|
70
|
+
end
|
71
|
+
alias :client :client?
|
72
|
+
|
73
|
+
# Check if the current model state is valid. Each call to {#valid?} will
|
74
|
+
# reset the {#errors} array.
|
75
|
+
#
|
76
|
+
# All validations should be declared in a `validate` method.
|
77
|
+
#
|
78
|
+
# @example
|
79
|
+
#
|
80
|
+
# class Login
|
81
|
+
# attr_accessor :username
|
82
|
+
# attr_accessor :password
|
83
|
+
#
|
84
|
+
# def validate
|
85
|
+
# assert_present :user
|
86
|
+
# assert_present :password
|
87
|
+
# end
|
88
|
+
# end
|
89
|
+
#
|
90
|
+
def valid?
|
91
|
+
errors.clear
|
92
|
+
validate
|
93
|
+
errors.empty?
|
94
|
+
end
|
95
|
+
|
96
|
+
# Base validate implementation. Override this method in subclasses.
|
97
|
+
def validate
|
98
|
+
end
|
99
|
+
|
100
|
+
# Hash of errors for each attribute in this model.
|
101
|
+
def errors
|
102
|
+
@errors ||= Hash.new { |hash, key| hash[key] = [] }
|
103
|
+
end
|
104
|
+
|
105
|
+
protected
|
106
|
+
|
107
|
+
# Allows you to do a validation check against a regular expression.
|
108
|
+
# It's important to note that this internally calls {#assert_present},
|
109
|
+
# therefore you need not structure your regular expression to check
|
110
|
+
# for a non-empty value.
|
111
|
+
#
|
112
|
+
# @param [Symbol] att The attribute you want to verify the format of.
|
113
|
+
# @param [Regexp] format The regular expression with which to compare
|
114
|
+
# the value of att with.
|
115
|
+
# @param [Array<Symbol, Symbol>] error The error that should be returned
|
116
|
+
# when the validation fails.
|
117
|
+
def assert_format(att, format, error = [att, :format])
|
118
|
+
if assert_present(att, error)
|
119
|
+
assert(_attributes.send(att).to_s.match(format), error)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# The most basic and highly useful assertion. Simply checks if the
|
124
|
+
# value of the attribute is empty.
|
125
|
+
#
|
126
|
+
# @param [Symbol] att The attribute you wish to verify the presence of.
|
127
|
+
# @param [Array<Symbol, Symbol>] error The error that should be returned
|
128
|
+
# when the validation fails.
|
129
|
+
def assert_present(att, error = [att, :not_present])
|
130
|
+
if att.is_a? Array
|
131
|
+
att.each { |a| assert_present(a, error = [a, :not_present])}
|
132
|
+
else
|
133
|
+
if klass = _form[att]
|
134
|
+
options = {}
|
135
|
+
options[:key] = _options[:key] if _options.key? :key
|
136
|
+
|
137
|
+
f = klass.new(_attributes.send(att).attributes, options)
|
138
|
+
assert(f.valid?, [att, f.errors])
|
139
|
+
else
|
140
|
+
assert(!_attributes.send(att).to_s.empty?, error)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# Checks if all the characters of an attribute is a digit.
|
146
|
+
#
|
147
|
+
# @param [Symbol] att The attribute you wish to verify the numeric format.
|
148
|
+
# @param [Array<Symbol, Symbol>] error The error that should be returned
|
149
|
+
# when the validation fails.
|
150
|
+
def assert_numeric(att, error = [att, :not_numeric])
|
151
|
+
if assert_present(att, error)
|
152
|
+
if client?
|
153
|
+
assert_format(att, /^\-?\d+$/, error)
|
154
|
+
else
|
155
|
+
assert_format(att, /\A\-?\d+\z/, error)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
if client?
|
161
|
+
URL = /^(http|https):\/\/([a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}|(2 5[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3} |localhost)(:[0-9]{1,5})?(\/.*)?$/i
|
162
|
+
else
|
163
|
+
URL = /\A(http|https):\/\/([a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}|(2 5[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3} |localhost)(:[0-9]{1,5})?(\/.*)?\z/i
|
164
|
+
end
|
165
|
+
|
166
|
+
def assert_url(att, error = [att, :not_url])
|
167
|
+
if assert_present(att, error)
|
168
|
+
assert_format(att, URL, error)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
if client?
|
173
|
+
EMAIL = /^[a-z0-9!\#$%&'*\/=\?^{|}+_-]+(?:\.[a-z0-9!\#$%&'*\/=\?^{|}+_-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i
|
174
|
+
else
|
175
|
+
EMAIL = /\A[a-z0-9!\#$%&'*\/=\?^{|}+_-]+(?:\.[a-z0-9!\#$%&'*\/=\?^{|}+_-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\z/i
|
176
|
+
end
|
177
|
+
|
178
|
+
def assert_email(att, error = [att, :not_email])
|
179
|
+
if assert_present(att, error)
|
180
|
+
assert_format(att, EMAIL, error)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
def assert_member(att, set, err = [att, :not_valid])
|
185
|
+
assert(set.include?(_attributes.send(att)), err)
|
186
|
+
end
|
187
|
+
|
188
|
+
def assert_length(att, range, error = [att, :not_in_range])
|
189
|
+
if assert_present(att, error)
|
190
|
+
val = _attributes.send(att).to_s
|
191
|
+
assert range.include?(val.length), error
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
if client?
|
196
|
+
DECIMAL = /^\-?(\d+)?(\.\d+)?$/
|
197
|
+
else
|
198
|
+
DECIMAL = /\A\-?(\d+)?(\.\d+)?\z/
|
199
|
+
end
|
200
|
+
|
201
|
+
def assert_decimal(att, error = [att, :not_decimal])
|
202
|
+
assert_format att, DECIMAL, error
|
203
|
+
end
|
204
|
+
|
205
|
+
# Check that the attribute has the expected value. It uses === for
|
206
|
+
# comparison, so type checks are possible too. Note that in order
|
207
|
+
# to make the case equality work, the check inverts the order of
|
208
|
+
# the arguments: `assert_equal :foo, Bar` is translated to the
|
209
|
+
# expression `Bar === send(:foo)`.
|
210
|
+
#
|
211
|
+
# @example
|
212
|
+
#
|
213
|
+
# def validate
|
214
|
+
# assert_equal :status, "pending"
|
215
|
+
# assert_equal :quantity, Fixnum
|
216
|
+
# end
|
217
|
+
#
|
218
|
+
# @param [Symbol] att The attribute you wish to verify for equality.
|
219
|
+
# @param [Object] value The value you want to test against.
|
220
|
+
# @param [Array<Symbol, Symbol>] error The error that should be returned
|
221
|
+
# when the validation fails.
|
222
|
+
def assert_equal(att, value, error = [att, :not_equal])
|
223
|
+
assert value === _attributes.send(att), error
|
224
|
+
end
|
225
|
+
|
226
|
+
# The grand daddy of all assertions. If you want to build custom
|
227
|
+
# assertions, or even quick and dirty ones, you can simply use this method.
|
228
|
+
#
|
229
|
+
# @example
|
230
|
+
#
|
231
|
+
# class CreatePost
|
232
|
+
# attr_accessor :slug
|
233
|
+
# attr_accessor :votes
|
234
|
+
#
|
235
|
+
# def validate
|
236
|
+
# assert_slug :slug
|
237
|
+
# assert votes.to_i > 0, [:votes, :not_valid]
|
238
|
+
# end
|
239
|
+
#
|
240
|
+
# protected
|
241
|
+
# def assert_slug(att, error = [att, :not_slug])
|
242
|
+
# assert send(att).to_s =~ /\A[a-z\-0-9]+\z/, error
|
243
|
+
# end
|
244
|
+
# end
|
245
|
+
def assert(value, error)
|
246
|
+
value or errors[error.first].push(error.last) && false
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|