ruby_motion_query 0.6.1 → 0.7.0

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
  SHA1:
3
- metadata.gz: 6a97dab886591d15adcbca5f06dae750e7938c12
4
- data.tar.gz: 44496f23c8aba6bc4b0c9d1d686c932bfca5d9b7
3
+ metadata.gz: 6f9a3624a7543f1c6a828948bb3d2da590c8ad78
4
+ data.tar.gz: f377f2d6321fbe5a18b2e1fa4a8fedfff7023f63
5
5
  SHA512:
6
- metadata.gz: deb2ae07fb598b80d15827148d833d44ff1b2dbe16af222acb14194b8e1702bd69c7bcb5f4a1a59c8d13ae045898cfe0e2524bce8cdafa7c7ecbcf25a702e8e4
7
- data.tar.gz: 64bc7ca900954291b974dd027558c88b5cbf451b7f08be87b2121c9e427daa12d3ab5e85ba9c28f609fb6026bed0a2a8e0d6fd01191ceb7759612164b4fcb0e3
6
+ metadata.gz: 25be56991e401ba84649de80eccf69a45f7df4691507a7c1c5e8f1d2bb783bac5a8625608c0d757282bd8fe27bde29fe3afcf1d5b22eedba7b037f684c69a4a2
7
+ data.tar.gz: 9ccb416fa1ea67824729649c1b2e884682ec8b04feece7aec42f3dc2b705de0f2d3880a25d3a301ebc9dfd47fa6d86b20bc64df11c426ca975d6b5b313661fc7
@@ -73,6 +73,16 @@ module RubyMotionQuery
73
73
  end
74
74
  end
75
75
 
76
+ # Allow users to set data with equals
77
+ #
78
+ # @return [RMQ]
79
+ #
80
+ # @example
81
+ # rmq(my_view).data = 'some data'
82
+ def data=(new_data)
83
+ data(new_data)
84
+ end
85
+
76
86
  # @return [RMQ]
77
87
  def send(method, args = nil)
78
88
  selected.each do |view|
@@ -5,21 +5,33 @@ module RubyMotionQuery
5
5
  #
6
6
  # @return [RMQ]
7
7
  def animate(opts = {}, &block)
8
-
8
+
9
9
  animations_callback = (block || opts[:animations] || opts[:changes])
10
+ before_callback = opts[:before]
10
11
  after_callback = (opts[:completion] || opts[:after])
11
12
  return self unless animations_callback
12
13
 
13
-
14
14
  working_selected = self.selected
15
15
  self_rmq = self
16
16
 
17
17
  working_selected.each do |view|
18
18
  view_rmq = self_rmq.wrap(view)
19
19
 
20
+ return_var = nil
21
+
22
+ if before_callback
23
+ return_var = before_callback.call(view_rmq)
24
+ end
25
+
20
26
  animations_lambda = -> do
21
- # TODO, check arity and allow no params
22
- animations_callback.call(view_rmq)
27
+ case animations_callback.arity
28
+ when 0
29
+ animations_callback.call
30
+ when 1
31
+ animations_callback.call(view_rmq)
32
+ when 2
33
+ animations_callback.call(view_rmq, return_var)
34
+ end
23
35
  end
24
36
 
25
37
  after_lambda = if after_callback
@@ -146,7 +158,7 @@ module RubyMotionQuery
146
158
  opts = {
147
159
  duration: 0.5,
148
160
  animations: ->(cq) {
149
- cq.style do |st|
161
+ cq.style do |st|
150
162
  st.opacity = 1.0
151
163
  st.scale = 0.8
152
164
  end
@@ -168,14 +180,14 @@ module RubyMotionQuery
168
180
  duration: 0.4 + (rand(8) / 10),
169
181
  options: UIViewAnimationOptionCurveEaseIn|UIViewAnimationOptionBeginFromCurrentState,
170
182
  animations: ->(cq) {
171
- cq.style do |st|
183
+ cq.style do |st|
172
184
  st.top = @rmq.device.height + st.height
173
- st.rotation = 180 + rand(50)
185
+ st.rotation = 180 + rand(50)
174
186
  end
175
187
  },
176
188
  completion: ->(did_finish, q) {
177
189
  if did_finish
178
- q.style do |st|
190
+ q.style do |st|
179
191
  st.rotation = 0
180
192
  end
181
193
 
@@ -196,6 +208,74 @@ module RubyMotionQuery
196
208
  self.fade_out(duration: 0.2, after: lambda {|did_finish, rmq| rmq.animations.fade_in(duration: 0.2)})
197
209
  end
198
210
 
211
+ # @return [RMQ]
212
+ def slide_in(opts = {})
213
+ from_direction = opts[:from_direction] || :right
214
+
215
+ opts = {
216
+ duration: 0.5,
217
+ options: UIViewAnimationOptionCurveEaseIn,
218
+ before: ->(bq) {
219
+ start_frame = bq.get.frame
220
+
221
+ case from_direction
222
+ when :right
223
+ bq.move(l: rmq.device.width)
224
+ when :left
225
+ bq.move(l: -rmq.device.width)
226
+ when :top
227
+ bq.move(t: -rmq.device.height)
228
+ else :bottom
229
+ bq.move(t: rmq.device.height)
230
+ end
231
+ start_frame
232
+ },
233
+ animations: ->(aq, return_var) {
234
+ aq.frame = return_var
235
+ }
236
+ }.merge(opts)
237
+
238
+ @rmq.animate(opts)
239
+ end
240
+
241
+ # @return [RMQ]
242
+ def slide_out(opts = {})
243
+ remove_view = opts[:remove_view]
244
+ to_direction = opts[:to_direction] || :left
245
+
246
+ opts = {
247
+ duration: 0.5,
248
+ options: UIViewAnimationOptionCurveEaseIn,
249
+ before: ->(bq) {
250
+ start_frame = bq.get.frame
251
+ start_frame
252
+ },
253
+ animations: ->(aq, return_var) {
254
+ case to_direction
255
+ when :right
256
+ aq.move(l: rmq.device.screen_width)
257
+ when :left
258
+ aq.move(l: -rmq.device.screen_width)
259
+ when :top
260
+ aq.move(t: -rmq.device.screen_height)
261
+ else :bottom
262
+ aq.move(t: rmq.device.screen_height)
263
+ end
264
+ },
265
+ completion: ->(did_finish, q) {
266
+ if did_finish
267
+ if remove_view
268
+ q.remove
269
+ else
270
+ q.hide
271
+ end
272
+ end
273
+ }
274
+ }.merge(opts)
275
+
276
+ @rmq.animate(opts)
277
+ end
278
+
199
279
  # @return [RMQ]
200
280
  def start_spinner(style = UIActivityIndicatorViewStyleGray)
201
281
  spinner = Animations.window_spinner(style)
@@ -210,7 +290,7 @@ module RubyMotionQuery
210
290
  @rmq.create_rmq_in_context(spinner)
211
291
  end
212
292
 
213
- protected
293
+ protected
214
294
 
215
295
  def self.window_spinner(style = UIActivityIndicatorViewStyleGray)
216
296
  @_window_spinner ||= begin
@@ -12,7 +12,12 @@ module RubyMotionQuery
12
12
  tags.keys
13
13
  end
14
14
 
15
- # *Do not* use this, use {RMQ#tag} instead:
15
+ def validation_errors; @_validation_errors ||= {}; end
16
+ def validation_errors=(value); @_validation_errors = value; end
17
+ def validations; @_validations ||= []; end
18
+ def validations=(value); @_validations = value; end
19
+
20
+ # *Do not* use this, use {RMQ#tag} instead:
16
21
  # @example
17
22
  # rmq(my_view).tag(:foo)
18
23
  def tag(*tag_or_tags)
@@ -25,7 +30,7 @@ module RubyMotionQuery
25
30
  end
26
31
  elsif tag_or_tags.is_a?(Hash)
27
32
  tag_or_tags.each do |tag_name, tag_value|
28
- tags[tag_name] = tag_value
33
+ tags[tag_name] = tag_value
29
34
  end
30
35
  elsif tag_or_tags.is_a?(Symbol)
31
36
  tags[tag_or_tags] = 1
@@ -13,7 +13,7 @@ module RubyMotionQuery
13
13
  end
14
14
 
15
15
  # @return [Debug]
16
- def debug
16
+ def debug
17
17
  Debug
18
18
  end
19
19
  end
@@ -48,7 +48,7 @@ module RubyMotionQuery
48
48
  Deep log - #{label}
49
49
  At: #{Time.now.to_s}
50
50
 
51
- Callers:
51
+ Callers:
52
52
  #{callers.join("\n - ")}
53
53
 
54
54
  Objects:
@@ -61,7 +61,7 @@ module RubyMotionQuery
61
61
  label
62
62
  end
63
63
 
64
- # Warning, this is very slow to output log, checking truthy however is
64
+ # Warning, this is very slow to output log, checking truthy however is
65
65
  # basically as performant as an if statement
66
66
  #
67
67
  # @example
@@ -3,7 +3,9 @@ module RubyMotionQuery
3
3
  attr_accessor :block, :recognizer, :event, :sdk_event_or_recognizer, :gesture, :sender
4
4
 
5
5
  def initialize(sender, event, block)
6
- if @sdk_event_or_recognizer = VIEW_GESTURES[event]
6
+ if CONTROL_EVENTS[event] == ValidationEvent
7
+ return ValidationEvent.new(block)
8
+ elsif @sdk_event_or_recognizer = VIEW_GESTURES[event]
7
9
  @gesture = true
8
10
  elsif sender.is_a?(UIControl)
9
11
  @gesture = false
@@ -127,7 +129,9 @@ module RubyMotionQuery
127
129
 
128
130
  application: UIControlEventApplicationReserved,
129
131
  system: UIControlEventSystemReserved,
130
- all: UIControlEventAllEvents
132
+ all: UIControlEventAllEvents,
133
+ valid: ValidationEvent,
134
+ invalid: ValidationEvent
131
135
  }
132
136
 
133
137
  VIEW_GESTURES = {
@@ -39,6 +39,9 @@ module RubyMotionQuery
39
39
  # :application
40
40
  # :system
41
41
  # :all
42
+ #
43
+ # :valid
44
+ # :invalid
42
45
 
43
46
  # Gestures for UIView
44
47
  # :tap
@@ -53,17 +56,17 @@ module RubyMotionQuery
53
56
  # :long_press
54
57
 
55
58
  # Options for gestures
56
- # :cancels_touches_in_view
57
- # :delegate
58
- # :taps_required
59
- # :fingers_required
60
- # :maximum_number_of_touches
61
- # :minimum_number_of_touches
62
- # :allowable_movement
63
- # :minimum_press_duration
64
- # :direction
65
- # :rotation
66
- # :scale
59
+ # :cancels_touches_in_view
60
+ # :delegate
61
+ # :taps_required
62
+ # :fingers_required
63
+ # :maximum_number_of_touches
64
+ # :minimum_number_of_touches
65
+ # :allowable_movement
66
+ # :minimum_press_duration
67
+ # :direction
68
+ # :rotation
69
+ # :scale
67
70
  #
68
71
  # @example
69
72
  # rmq.append(UIButton).on(:touch) do |sender|
@@ -86,7 +89,7 @@ module RubyMotionQuery
86
89
  # end
87
90
  def on(event, args = {}, &block)
88
91
  selected.each do |view|
89
- events(view).on(view, event, args, &block)
92
+ events(view).on(view, event, args, &block)
90
93
  end
91
94
 
92
95
  self
@@ -130,8 +133,8 @@ module RubyMotionQuery
130
133
  def on(view, event, args = {}, &block)
131
134
  raise "[RMQ Error] Event already exists on this object: #{event}. Remove first, using .off" if @event_set[event]
132
135
 
133
- if rmqe = RubyMotionQuery::Event.new(view, event, block)
134
- rmqe.set_options(args)
136
+ if rmqe = event_instance(view, event, block)
137
+ rmqe.set_options(args) if rmqe.respond_to? :set_options
135
138
 
136
139
  @event_set[event] = rmqe
137
140
  end
@@ -143,14 +146,23 @@ module RubyMotionQuery
143
146
  events.flatten!
144
147
  events = @event_set.keys if events.length == 0
145
148
 
146
- events.each do |event|
149
+ events.each do |event|
147
150
  if rm_event = @event_set.delete(event)
148
- rm_event.remove
151
+ rm_event.remove
149
152
  end
150
153
  end
151
154
 
152
155
  self
153
156
  end
154
157
 
158
+ private
159
+
160
+ def event_instance(view, event, block)
161
+ if [:valid, :invalid].include? event
162
+ RubyMotionQuery::ValidationEvent.new(view, event, block)
163
+ else
164
+ RubyMotionQuery::Event.new(view, event, block)
165
+ end
166
+ end
155
167
  end
156
168
  end
@@ -104,13 +104,13 @@ module RubyMotionQuery
104
104
  NAV_BAR_BOTTOM = 64
105
105
 
106
106
  DEFAULT = {
107
- num_columns: 10,
107
+ num_columns: 12,
108
108
  column_gutter: 10,
109
- num_rows: 16,
109
+ num_rows: 18,
110
110
  row_gutter: 10,
111
111
  content_left_margin: 10,
112
112
  content_right_margin: 10,
113
- content_top_margin: 70,
113
+ content_top_margin: 74,
114
114
  content_bottom_margin: 10
115
115
  }
116
116
 
@@ -381,6 +381,12 @@ module RubyMotionQuery
381
381
  def accessibility_label=(value)
382
382
  @view.accessibilityLabel = value
383
383
  end
384
+
385
+ def validation_errors=(values)
386
+ # set custom validation messages on rules
387
+ @view.rmq_data.validation_errors = values
388
+ end
389
+
384
390
  end
385
391
  end
386
392
  end
@@ -50,7 +50,7 @@ module RubyMotionQuery
50
50
  end
51
51
 
52
52
  if created
53
- new_view.rmq_did_create(self.wrap(new_view))
53
+ new_view.rmq_did_create(self.wrap(new_view))
54
54
  new_view.rmq_created
55
55
  end
56
56
  new_view.rmq_build
@@ -65,16 +65,16 @@ module RubyMotionQuery
65
65
  end
66
66
  alias :insert :add_subview
67
67
 
68
- # Performs a create, then appends view to the end of the subview array of the
68
+ # Performs a create, then appends view to the end of the subview array of the
69
69
  # views you have selected (or the rootview if you have nothing selected).
70
70
  #
71
71
  # When you build, create, or append a view, the method rmq_build is called
72
72
  # inside the view. If you are creating a your own subclass of a UIView, then
73
- # that is a good place to do your initialization. Your view is created, then
73
+ # that is a good place to do your initialization. Your view is created, then
74
74
  # appended, then rmq_build is called, then the style is applied (if it exists)
75
75
  #
76
- # @example
77
- # # Creating a new view instance then append it. Passing in the class
76
+ # @example
77
+ # # Creating a new view instance then append it. Passing in the class
78
78
  # # to create
79
79
  # rmq.append(UIButton, :my_button_style)
80
80
  # @title = rmq.append(ULabel, :title).get
@@ -82,7 +82,7 @@ module RubyMotionQuery
82
82
  # # You can also pass in an existing view
83
83
  # my_view = UIView.alloc.initWithFrame([[0,0],[10,10]])
84
84
  # rmq.append(my_view, :my_style)
85
- #
85
+ #
86
86
  # # Stylename is optional
87
87
  # rmq.append(UIImageView)
88
88
  #
@@ -121,21 +121,21 @@ module RubyMotionQuery
121
121
  end
122
122
  alias :prepend! :unshift!
123
123
 
124
- # Creates a view then returns an rmq with that view in it. It does not add that
124
+ # Creates a view then returns an rmq with that view in it. It does not add that
125
125
  # view to the view tree (append does this). This is useful for stuff like creating
126
- # table cells. You can use the rmq_did_create method, just like you do when you
126
+ # table cells. You can use the rmq_did_create method, just like you do when you
127
127
  # append a subview
128
128
  #
129
129
  # @return [RMQ] wrapping the view that was just create
130
130
  #
131
131
  # @example
132
132
  # def tableView(table_view, cellForRowAtIndexPath: index_path)
133
- # cell = tableView.dequeueReusableCellWithIdentifier(CELL_IDENTIFIER) || begin
133
+ # cell = tableView.dequeueReusableCellWithIdentifier(CELL_IDENTIFIER) || begin
134
134
  # rmq.create(StoreCell, :store_cell, reuse_identifier: CELL_IDENTIFIER)
135
135
  # end
136
136
  # end
137
137
  #
138
- # class StoreCell < UITableViewCell
138
+ # class StoreCell < UITableViewCell
139
139
  # def rmq_did_create(self_in_rmq)
140
140
  # self_in_rmq.append(UILabel, :title_label)
141
141
  # end
@@ -160,7 +160,7 @@ module RubyMotionQuery
160
160
  # in collectionview cells for example
161
161
  #
162
162
  # @example
163
- # # In your collectionview
163
+ # # In your collectionview
164
164
  # rmq.build(cell) unless cell.reused
165
165
  #
166
166
  # # Then in your cell
@@ -174,6 +174,15 @@ module RubyMotionQuery
174
174
  add_subview view, opts
175
175
  end
176
176
 
177
+ # Same as build, but instantly returns the view, without having to use .get
178
+ #
179
+ # @example
180
+ # @my_cell = rmq.build! cell
181
+ def build!(view, style = nil, opts = {})
182
+ build(view, style, opts).get
183
+ end
184
+
185
+
177
186
  protected
178
187
 
179
188
  def create_view(klass, opts)
@@ -1,5 +1,6 @@
1
1
  module RubyMotionQuery
2
2
  class RMQ
3
+
3
4
  # @return [Validation]
4
5
  def self.validation
5
6
  Validation
@@ -9,9 +10,105 @@ module RubyMotionQuery
9
10
  def validation
10
11
  Validation
11
12
  end
12
- end
13
+
14
+ # @return [RMQ]
15
+ def validates(rule, options={})
16
+ selected.each do |view|
17
+ view.rmq_data.validations << Validation.new(rule, options)
18
+ end
19
+ self
20
+ end
21
+
22
+ # @return [RMQ]
23
+ def clear_validations!
24
+ selected.each do |view|
25
+ view.rmq_data.validations = []
26
+ end
27
+ self
28
+ end
29
+
30
+ # This method validates all the selected and is responsible
31
+ # for calling invalid/valid events
32
+ #
33
+ # @return [Boolean] false if any validations fail
34
+ def valid?
35
+ result = true
36
+
37
+ selected.each do |view|
38
+ view.rmq_data.validations.each do |validation|
39
+
40
+ has_events = view.rmq_data.events
41
+
42
+ if validation.valid?(rmq(view).data)
43
+ if has_events && view.rmq_data.events.has_event?(:valid)
44
+ view.rmq_data.events[:valid].fire!
45
+ end
46
+ else
47
+ if has_events && view.rmq_data.events.has_event?(:invalid)
48
+ view.rmq_data.events[:invalid].fire!
49
+ end
50
+ result = false
51
+ end
52
+ end
53
+ end
54
+ return result
55
+ end
56
+
57
+ # @return [Array] of error messages for failed validations
58
+ def validation_errors
59
+ errors = []
60
+ selected.each do |view|
61
+ view.rmq_data.validations.each do |validation|
62
+ unless validation.valid_status
63
+ default_error = "Validation Error - input requires valid #{validation.rule_name.to_s}."
64
+ rule_message = view.rmq_data.validation_errors[validation.rule_name] || default_error
65
+ errors.push(rule_message)
66
+ end
67
+ end
68
+ end
69
+ return errors
70
+ end
71
+
72
+ # @return [Array] of views where validations have failed
73
+ def invalid
74
+ invalid = []
75
+ selected.each do |view|
76
+ view.rmq_data.validations.each do |validation|
77
+ invalid.push(view) unless validation.valid_status
78
+ end
79
+ end
80
+ return invalid
81
+ end
82
+
83
+ # @return [Array] of views where validations have failed
84
+ def valid
85
+ invalid = []
86
+ selected.each do |view|
87
+ view.rmq_data.validations.each do |validation|
88
+ invalid.push(view) if validation.valid_status
89
+ end
90
+ end
91
+ return invalid
92
+ end
93
+
94
+ end # End RMQ
13
95
 
14
96
  class Validation
97
+ attr_reader :valid_status, :rule_name
98
+
99
+ def initialize(rule, options={})
100
+ @rule = @@validation_methods[rule]
101
+ raise "RMQ validation error: :#{rule} is not one of the supported validation methods." unless @rule
102
+ @rule_name = rule
103
+ @options = options
104
+ @valid_status = true
105
+ end
106
+
107
+ def valid?(data, options={})
108
+ @options = options.merge(@options)
109
+ @valid_status = @rule.call(data, @options)
110
+ end
111
+
15
112
  class << self
16
113
  # Validation Regex from jQuery validation -> https://github.com/jzaefferer/jquery-validation/blob/master/src/core.js#L1094-L1200
17
114
  EMAIL = Regexp.new('^[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$')
@@ -26,18 +123,52 @@ module RubyMotionQuery
26
123
  USZIP = Regexp.new('^\d{5}(-\d{4})?$')
27
124
  # 7 or 10 digit number, delimiters are spaces, dashes, or periods
28
125
  USPHONE = Regexp.new('^(?:(?:\+?1\s*(?:[.-]\s*)?)?(?:(\s*([2-9]1[02-9]|[2-9][02-8]1|[2-9][02-8][02-9]‌​)\s*)|([2-9]1[02-9]|[2-9][02-8]1|[2-9][02-8][02-9]))\s*(?:[.-]\s*)?)?([2-9]1[02-‌​9]|[2-9][02-9]1|[2-9][02-9]{2})\s*(?:[.-]\s*)?([0-9]{4})$')
29
-
126
+ # Strong password (at least [8 chars, 1 upper, 1 lower, 1 number])
127
+ STRONGPW = Regexp.new('^(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9]).{8,}$')
128
+ # Has at least 1 uppercase letter
129
+ HASUPPER = Regexp.new('^(?=.*[A-Z]).+$')
130
+ # Has at least 1 lowercase letter
131
+ HASLOWER = Regexp.new('^(?=.*[a-z]).+$')
132
+ # Has some kind of value not just whitespace (doesn't require data to be stripped)
133
+ PRESENCE = Regexp.new('\S+')
30
134
 
31
135
  @@validation_methods = {
32
- :email => lambda { |value| Validation.regex_match?(value, EMAIL)},
33
- :url => lambda { |value| Validation.regex_match?(value, URL)},
34
- :dateiso => lambda { |value| Validation.regex_match?(value, DATEISO)},
35
- :number => lambda { |value| Validation.regex_match?(value, NUMBER)},
36
- :digits => lambda { |value| Validation.regex_match?(value, DIGITS)},
37
- :ipv4 => lambda { |value| Validation.regex_match?(value, IPV4)},
38
- :time => lambda { |value| Validation.regex_match?(value, TIME)},
39
- :uszip => lambda { |value| Validation.regex_match?(value, USZIP)},
40
- :usphone => lambda { |value| Validation.regex_match?(value, USPHONE)}
136
+ :email => lambda { |value, opts| Validation.regex_match?(value, EMAIL)},
137
+ :url => lambda { |value, opts| Validation.regex_match?(value, URL)},
138
+ :dateiso => lambda { |value, opts| Validation.regex_match?(value, DATEISO)},
139
+ :number => lambda { |value, opts| Validation.regex_match?(value, NUMBER)},
140
+ :digits => lambda { |value, opts| Validation.regex_match?(value, DIGITS)},
141
+ :ipv4 => lambda { |value, opts| Validation.regex_match?(value, IPV4)},
142
+ :time => lambda { |value, opts| Validation.regex_match?(value, TIME)},
143
+ :uszip => lambda { |value, opts| Validation.regex_match?(value, USZIP)},
144
+ :usphone => lambda { |value, opts| Validation.regex_match?(value, USPHONE)},
145
+ :strong_password => lambda { |value, opts| Validation.regex_match?(value, STRONGPW)},
146
+ :has_upper => lambda { |value, opts| Validation.regex_match?(value, HASUPPER)},
147
+ :has_lower => lambda { |value, opts| Validation.regex_match?(value, HASLOWER)},
148
+ :presence => lambda { |value, opts| Validation.regex_match?(value, PRESENCE)},
149
+ :length => lambda { |value, opts|
150
+ opts = {
151
+ exact_length: nil,
152
+ max_length: Float::INFINITY,
153
+ min_length: 0,
154
+ strip: false
155
+ }.merge(opts)
156
+
157
+ # Range magic 8..16
158
+ if opts[:exact_length].is_a? Range
159
+ opts[:min_length] = opts[:exact_length].begin
160
+ opts[:max_length] = opts[:exact_length].end
161
+ opts[:exact_length] = nil
162
+ end
163
+
164
+ # allowing option to strip input before assessing length
165
+ value.strip! if opts[:strip]
166
+
167
+ # check length validation
168
+ v = if opts[:exact_length] then (value.length == opts[:exact_length]) else true end
169
+ v = v && value.length <= opts[:max_length]
170
+ v = v && value.length >= opts[:min_length]
171
+ }
41
172
  }
42
173
 
43
174
  # Add tags
@@ -46,23 +177,21 @@ module RubyMotionQuery
46
177
  # rmq.validation.valid?(53.8, :number)
47
178
  # rmq.validation.valid?(54, :digits)
48
179
  # rmq.validation.valid?('https://www.tacoland.com', :url)
49
- # rmq.validation.valid?('2014-03-02'), :dateiso)
180
+ # rmq.validation.valid?('2014-03-02', :dateiso)
181
+ # rmq.validation.valid?('', :email, allow_blank: true)
50
182
  #
51
183
  # @return [Boolean]
52
- def valid?(value, *rule_or_rules)
53
- #shortcircuit if debugging
184
+ def valid?(value, rule, options={})
185
+ # shortcircuit if debugging
54
186
  return true if RubyMotionQuery::RMQ.debugging?
55
- rule_or_rules.each do |rule|
56
- # only supported validations
57
- raise "RMQ validation error: :#{rule} is not one of the supported validation methods." unless @@validation_methods.include?(rule)
58
- #return false if validation_failed
59
- return false unless @@validation_methods[rule].call(value)
60
- end
61
- true
187
+ # shortcircuit for optionals
188
+ return true if (options[:allow_blank] && (value.nil? || value.empty?))
189
+
190
+ Validation.new(rule).valid?(value, options)
62
191
  end
63
192
 
64
193
  def regex_match?(value, regex)
65
- (value.to_s =~ regex) == 0
194
+ (value.to_s =~ regex) != nil
66
195
  end
67
196
 
68
197
  end
@@ -0,0 +1,24 @@
1
+ module RubyMotionQuery
2
+ class ValidationEvent
3
+ def initialize(sender, event, block)
4
+ @sender = sender
5
+ @event = event
6
+ @block = block
7
+ end
8
+
9
+ def fire!
10
+
11
+ if @block
12
+ case @block.arity
13
+ when 2
14
+ @block.call(@sender, self)
15
+ when 1
16
+ @block.call(@sender)
17
+ else
18
+ @block.call
19
+ end
20
+ end
21
+ end
22
+
23
+ end
24
+ end
@@ -1,5 +1,5 @@
1
1
  module RubyMotionQuery
2
- VERSION = "0.6.1"
2
+ VERSION = "0.7.0"
3
3
 
4
4
  class RMQ
5
5
  def version
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby_motion_query
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.1
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Todd Werth
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-07-02 00:00:00.000000000 Z
12
+ date: 2014-08-12 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bacon
@@ -100,6 +100,7 @@ files:
100
100
  - motion/ruby_motion_query/traverse.rb
101
101
  - motion/ruby_motion_query/utils.rb
102
102
  - motion/ruby_motion_query/validation.rb
103
+ - motion/ruby_motion_query/validation_event.rb
103
104
  - motion/ruby_motion_query/version.rb
104
105
  - templates/collection_view_controller/app/controllers/name_controller.rb
105
106
  - templates/collection_view_controller/app/stylesheets/name_cell_stylesheet.rb