volt 0.8.24 → 0.8.26.beta1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4baa719966706c9e9196f42e061abb6e95e7d54c
4
- data.tar.gz: 2b9fcde56cc4cd12ac1503dd464f9444a1e9a866
3
+ metadata.gz: d71fdd9ba18f67e9349026c51395e553bb34143e
4
+ data.tar.gz: 00ac6d296fb6a2e32d7d8326740e378ea5178545
5
5
  SHA512:
6
- metadata.gz: a18bd3212878144d412b33647d98570760c49d2fffcd598083e452dd6c9a3696f8df716e01f4328aff654edb2eef24664c28af7c91151944d37ef64abb146f4c
7
- data.tar.gz: 48f4412a6d4064edf20d9006e539ec74230d49d71b27749a42230c13092ffaba650f3625b5078145be5afa6bef13bc0007af620cab7f11cd3e112bcc5348bf4a
6
+ metadata.gz: 774f656f5a4c365d9f37106a0204efcff36b2b72871d7f4b3bd5334b75f16ec3195b82cdecc5f23781efd76f896d130ae205a5e3f0691bd01888778cc567f12a
7
+ data.tar.gz: c50312a4099bd67fcc60684fca2353b0929c0878cce00e61bef809257ee3613e14796369ea47f18755d24aea59d648da28de32a4974c21a6b70b064079c80cba
data/CHANGELOG.md CHANGED
@@ -1,9 +1,17 @@
1
1
  # Change Log
2
2
 
3
- ## 0.8.34 - 2014-12-01
3
+ ## 0.8.25 - ...
4
+ ### Added
5
+ - Added email validator
6
+ - each_with_index is now supported in views and the ```index``` value is no longer provided by default.
7
+
8
+
9
+ ## 0.8.24 - 2014-12-05
10
+ ### Added
4
11
  - Fix bug with validation inheritance
12
+ - Fixed issue with controller loading.
5
13
 
6
- ## 0.8.33 - 2014-11-30
14
+ ## 0.8.23 - 2014-11-30
7
15
  ### Added
8
16
  - Added url_for and url_with to controllers. (See docs under Controllers)
9
17
 
data/Rakefile CHANGED
@@ -1,8 +1,7 @@
1
1
  require 'bundler'
2
2
  require 'bundler/gem_tasks'
3
- require 'rubocop/rake_task'
4
3
  Bundler.require(:development)
5
-
4
+ require 'rubocop/rake_task'
6
5
  require 'opal'
7
6
 
8
7
  # Add our opal/ directory to the load path
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.8.24
1
+ 0.8.26.beta1
@@ -12,6 +12,7 @@ module Volt
12
12
  end
13
13
 
14
14
  validate login_field, unique: true, length: 8
15
+ validate :email, email: true
15
16
 
16
17
  if RUBY_PLATFORM == 'opal'
17
18
  # Don't validate on the server
data/lib/volt/cli.rb CHANGED
@@ -32,7 +32,8 @@ module Volt
32
32
  end
33
33
 
34
34
  desc 'server', 'run the server on the project in the current directory'
35
- method_option :port, type: :string, aliases: '-p', banner: 'specify which port the server should run on'
35
+ method_option :port, type: :string, aliases: '-p', banner: 'the port the server should run on'
36
+ method_option :bind, type: :string, aliases: '-b', banner: 'the ip the server should bind to'
36
37
 
37
38
  def server
38
39
  if RUBY_PLATFORM == 'java'
@@ -61,9 +62,8 @@ module Volt
61
62
  ENV['SERVER'] = 'true'
62
63
  args = ['start', '--threaded', '--max-persistent-conns', '300']
63
64
  args += ['--max-conns', '400'] unless Gem.win_platform?
64
- if options[:port]
65
- args += ['-p', options[:port].to_s]
66
- end
65
+ args += ['-p', options[:port].to_s] if options[:port]
66
+ args += ['-b', options[:bind].to_s] if options[:bind]
67
67
 
68
68
  Thin::Runner.new(args).run!
69
69
  end
@@ -57,6 +57,8 @@ module Volt
57
57
 
58
58
  # Make sure it gets wrapped
59
59
  def <<(model)
60
+ load_data
61
+
60
62
  if model.is_a?(Model)
61
63
  # Set the new path
62
64
  model.options = @options.merge(path: @options[:path] + [:[]])
@@ -121,6 +123,9 @@ module Volt
121
123
  end
122
124
 
123
125
  def inspect
126
+ # Just load the data on the server making it easier to work with
127
+ load_data if Volt.server?
128
+
124
129
  if @persistor && @persistor.is_a?(Persistors::ArrayStore) && state == :not_loaded
125
130
  # Show a special message letting users know it is not loaded yet.
126
131
  "#<#{self.class}:not loaded, access with [] or size to load>"
@@ -0,0 +1,37 @@
1
+ # Provides a method to setup a field on a model.
2
+ module FieldHelpers
3
+ class InvalidFieldClass < RuntimeError ; end
4
+
5
+ module ClassMethods
6
+ # field lets you declare your fields instead of using the underscore syntax.
7
+ # An optional class restriction can be passed in.
8
+ def field(name, klass=nil)
9
+ if klass && ![String, Numeric].include?(klass)
10
+ raise FieldHelpers::InvalidFieldClass, "valid field types is currently limited to String or Numeric"
11
+ end
12
+
13
+ define_method(name) do
14
+ read_attribute(name)
15
+ end
16
+
17
+ define_method(:"#{name}=") do |val|
18
+ # Check if the value assigned matches the class restriction
19
+ if klass
20
+ # Cast to the right type
21
+ if klass == String
22
+ val = val.to_s
23
+ elsif klass == Numeric
24
+ val = val.to_f
25
+ end
26
+ end
27
+
28
+ assign_attribute(name, val)
29
+ end
30
+ end
31
+ end
32
+
33
+ def self.included(base)
34
+ base.send :extend, ClassMethods
35
+ end
36
+
37
+ end
@@ -5,6 +5,7 @@ require 'volt/models/model_hash_behaviour'
5
5
  require 'volt/models/validations'
6
6
  require 'volt/models/model_state'
7
7
  require 'volt/models/buffer'
8
+ require 'volt/models/field_helpers'
8
9
  require 'volt/reactive/reactive_hash'
9
10
 
10
11
  module Volt
@@ -18,6 +19,7 @@ module Volt
18
19
  include Validations
19
20
  include ModelState
20
21
  include Buffer
22
+ include FieldHelpers
21
23
 
22
24
  attr_reader :attributes
23
25
  attr_reader :parent, :path, :persistor, :options
@@ -1,8 +1,10 @@
1
1
  # require 'volt/models/validations/errors'
2
+ require 'volt/models/validators/email_validator'
2
3
  require 'volt/models/validators/length_validator'
4
+ require 'volt/models/validators/numericality_validator'
5
+ require 'volt/models/validators/phone_number_validator'
3
6
  require 'volt/models/validators/presence_validator'
4
7
  require 'volt/models/validators/unique_validator'
5
- require 'volt/models/validators/numericality_validator'
6
8
 
7
9
  module Volt
8
10
  # Include in any class to get validation logic
@@ -0,0 +1,40 @@
1
+ module Volt
2
+ class EmailValidator
3
+ DEFAULT_REGEX = /^([\w\.%\+\-]+)@([\w\-]+\.)+([\w]{2,})$/i
4
+ ERROR_MESSAGE = 'must be an email address'
5
+
6
+ def self.validate(model, old_model, field_name, options)
7
+ new(model, field_name, options).errors
8
+ end
9
+
10
+ def initialize(model, field_name, options)
11
+ @value = model.read_attribute field_name
12
+
13
+ case options
14
+ when Hash, true, false
15
+ configure options
16
+ else
17
+ fail 'arguments can only be a Boolean or a Hash'
18
+ end
19
+ end
20
+
21
+ def valid?
22
+ return false unless @value.is_a? String
23
+
24
+ !!@value.match(@custom_regex || DEFAULT_REGEX)
25
+ end
26
+
27
+ def errors
28
+ valid? ? {} : { email: [ @custom_message || ERROR_MESSAGE ] }
29
+ end
30
+
31
+ private
32
+
33
+ def configure(options)
34
+ return unless options.is_a? Hash
35
+
36
+ @custom_message = options.fetch(:error_message) { nil }
37
+ @custom_regex = options.fetch(:with) { nil }
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,40 @@
1
+ module Volt
2
+ class PhoneNumberValidator
3
+ DEFAULT_REGEX = /^(\+?\d{1,2}[\.\-\ ]?\d{3}|\(\d{3}\)|\d{3})[\.\-\ ]?\d{3,4}[\.\-\ ]?\d{4}$/
4
+ ERROR_MESSAGE = 'must be a phone number with area or country code'
5
+
6
+ def self.validate(model, old_model, field_name, options)
7
+ new(model, field_name, options).errors
8
+ end
9
+
10
+ def initialize(model, field_name, options)
11
+ @value = model.read_attribute field_name
12
+
13
+ case options
14
+ when Hash, true, false
15
+ configure options
16
+ else
17
+ fail 'arguments can only be a Boolean or a Hash'
18
+ end
19
+ end
20
+
21
+ def valid?
22
+ return false unless @value.is_a? String
23
+
24
+ !!@value.match(@custom_regex || DEFAULT_REGEX)
25
+ end
26
+
27
+ def errors
28
+ valid? ? {} : { phone_number: [ @custom_message || ERROR_MESSAGE ] }
29
+ end
30
+
31
+ private
32
+
33
+ def configure(options)
34
+ return unless options.is_a? Hash
35
+
36
+ @custom_message = options.fetch(:error_message) { nil }
37
+ @custom_regex = options.fetch(:with) { nil }
38
+ end
39
+ end
40
+ end
@@ -2,10 +2,11 @@ require 'volt/page/bindings/base_binding'
2
2
 
3
3
  module Volt
4
4
  class EachBinding < BaseBinding
5
- def initialize(page, target, context, binding_name, getter, variable_name, template_name)
5
+ def initialize(page, target, context, binding_name, getter, variable_name, index_name, template_name)
6
6
  super(page, target, context, binding_name)
7
7
 
8
8
  @item_name = variable_name
9
+ @index_name = index_name
9
10
  @template_name = template_name
10
11
 
11
12
  @templates = []
@@ -28,7 +29,6 @@ module Volt
28
29
  # Since we're checking things like size, we don't want this to be re-triggered on a
29
30
  # size change, so we run without tracking.
30
31
  Computation.run_without_tracking do
31
- # puts "RELOAD:-------------- #{value.inspect}"
32
32
  # Adjust to the new size
33
33
  values = current_values(value)
34
34
  @value = values
@@ -56,9 +56,8 @@ module Volt
56
56
 
57
57
  def item_removed(position)
58
58
  # Remove dependency
59
- @templates[position].context.locals[:index_dependency].remove
59
+ @templates[position].context.locals[:_index_dependency].remove
60
60
 
61
- # puts "REMOVE AT: #{position.inspect} - #{@templates[position].inspect} - #{@templates.inspect}"
62
61
  @templates[position].remove_anchors
63
62
  @templates[position].remove
64
63
  @templates.delete_at(position)
@@ -84,17 +83,21 @@ module Volt
84
83
  item_context.locals[@item_name.to_sym] = proc { @value[item_context.locals[:_index_value]]}
85
84
 
86
85
  position_dependency = Dependency.new
87
- item_context.locals[:index_dependency] = position_dependency
86
+ item_context.locals[:_index_dependency] = position_dependency
88
87
 
89
88
  # Get and set index
90
- item_context.locals[:index=] = proc do |val|
89
+ item_context.locals[:_index=] = proc do |val|
91
90
  position_dependency.changed!
92
91
  item_context.locals[:_index_value] = val
93
92
  end
94
93
 
95
- item_context.locals[:index] = proc do
96
- position_dependency.depend
97
- item_context.locals[:_index_value]
94
+ # If the user provides an each_with_index, we can assign the lookup for the index
95
+ # variable here.
96
+ if @index_name
97
+ item_context.locals[@index_name.to_sym] = proc do
98
+ position_dependency.depend
99
+ item_context.locals[:_index_value]
100
+ end
98
101
  end
99
102
 
100
103
  item_template = TemplateRenderer.new(@page, @target, item_context, binding_name, @template_name)
@@ -109,7 +112,7 @@ module Volt
109
112
  size = @templates.size
110
113
  if size > 0
111
114
  start_index.upto(size - 1) do |index|
112
- @templates[index].context.locals[:index=].call(index)
115
+ @templates[index].context.locals[:_index=].call(index)
113
116
  end
114
117
  end
115
118
  end
@@ -1,6 +1,6 @@
1
1
  module Volt
2
2
  # The tasks class provides an interface to call tasks on
3
- # the backend server.
3
+ # the backend server. This class is setup as page.task (as a singleton)
4
4
  class Tasks
5
5
  def initialize(page)
6
6
  @page = page
@@ -21,7 +21,6 @@ module Volt
21
21
  @promises[promise_id] = promise
22
22
 
23
23
  # TODO: Timeout on these callbacks
24
-
25
24
  @page.channel.send_message([promise_id, class_name, method_name, meta_data, *args])
26
25
 
27
26
  promise
@@ -1,12 +1,15 @@
1
1
  module Volt
2
2
  class EachScope < ViewScope
3
- def initialize(handler, path, content)
3
+ def initialize(handler, path, content, with_index)
4
4
  super(handler, path)
5
- # @content, @variable_name = content.strip.split(/ as /)
6
5
 
7
- @content, @variable_name = content.split(/.each\s+do\s+\|/)
8
-
9
- @variable_name = @variable_name.gsub(/\|/, '')
6
+ if with_index
7
+ @content, @variable_name = content.split(/.each_with_index\s+do\s+\|/)
8
+ @variable_name, @index_name = @variable_name.gsub(/\|/, '').split(/\s*,\s*/)
9
+ else
10
+ @content, @variable_name = content.split(/.each\s+do\s+\|/)
11
+ @variable_name = @variable_name.gsub(/\|/, '')
12
+ end
10
13
  end
11
14
 
12
15
  def close_scope
@@ -17,7 +20,7 @@ module Volt
17
20
  super
18
21
 
19
22
  @handler.html << "<!-- $#{binding_number} --><!-- $/#{binding_number} -->"
20
- @handler.scope.last.save_binding(binding_number, "lambda { |__p, __t, __c, __id| Volt::EachBinding.new(__p, __t, __c, __id, Proc.new { #{@content} }, #{@variable_name.inspect}, #{@path.inspect}) }")
23
+ @handler.scope.last.save_binding(binding_number, "lambda { |__p, __t, __c, __id| Volt::EachBinding.new(__p, __t, __c, __id, Proc.new { #{@content} }, #{@variable_name.inspect}, #{@index_name.inspect}, #{@path.inspect}) }")
21
24
  end
22
25
  end
23
26
  end
@@ -42,7 +42,9 @@ module Volt
42
42
  add_template(args)
43
43
  else
44
44
  if content =~ /.each\s+do\s+\|/
45
- add_each(content)
45
+ add_each(content, false)
46
+ elsif content =~ /.each_with_index\s+do\s+\|/
47
+ add_each(content, true)
46
48
  else
47
49
  add_content_binding(content)
48
50
  end
@@ -76,8 +78,8 @@ module Volt
76
78
  fail '#else can only be added inside of an if block'
77
79
  end
78
80
 
79
- def add_each(content)
80
- @handler.scope << EachScope.new(@handler, @path + "/__each#{@binding_number}", content)
81
+ def add_each(content, with_index)
82
+ @handler.scope << EachScope.new(@handler, @path + "/__each#{@binding_number}", content, with_index)
81
83
  end
82
84
 
83
85
  def add_template(content)
@@ -7,8 +7,9 @@
7
7
  <div id="count">{{ completed }} of {{ _todos.size }}</div>
8
8
 
9
9
  <table id="todos-table" class="table">
10
- {{ _todos.each do |todo| }}
10
+ {{ _todos.each_with_index do |todo, idx| }}
11
11
  <tr>
12
+ <td>{{ idx+1 }}.</td>
12
13
  <td><input type="checkbox" checked="{{ todo._completed }}"></td>
13
14
  <td class="name {{ if todo._completed }}complete{{ end }}">{{todo._name}}</td>
14
15
  <td><button e-click="remove_todo(todo)">X</button></td>
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+ require 'volt/models'
3
+
4
+ class ExampleModelWithField < Volt::Model
5
+ field :name
6
+ field :value, Numeric
7
+ end
8
+
9
+ describe "field helpers" do
10
+ it 'should allow a user to setup a field that can be written to and read' do
11
+ model = ExampleModelWithField.new
12
+
13
+ expect(model.name).to eq(nil)
14
+ model.name = 'jimmy'
15
+ expect(model.name).to eq('jimmy')
16
+
17
+ expect(model.value).to eq(nil)
18
+ model.value = '20.5'
19
+
20
+ # Should be cast to float
21
+ expect(model.value).to eq(20.5)
22
+ end
23
+
24
+ it 'should raise an error when an invalid cast type is provided' do
25
+ expect do
26
+ ExampleModelWithField.field :awesome, Array
27
+ end.to raise_error(FieldHelpers::InvalidFieldClass)
28
+ end
29
+ end
@@ -1,19 +1,23 @@
1
- require 'volt/models'
1
+ require 'spec_helper'
2
2
 
3
3
  class TestModel < Volt::Model
4
- validate :name, length: 4
4
+ validate :count, numericality: { min: 5, max: 10 }
5
5
  validate :description, length: { message: 'needs to be longer', length: 50 }
6
+ validate :email, email: true
7
+ validate :name, length: 4
8
+ validate :phone_number, phone_number: true
6
9
  validate :username, presence: true
7
- validate :count, numericality: { min: 5, max: 10 }
8
10
  end
9
11
 
10
12
  describe Volt::Model do
11
13
  it 'should validate the name' do
12
14
  expect(TestModel.new.errors).to eq(
13
- name: ['must be at least 4 characters'],
15
+ count: ['must be a number'],
14
16
  description: ['needs to be longer'],
15
- username: ['must be specified'],
16
- count: ['must be a number']
17
+ email: ['must be an email address'],
18
+ name: ['must be at least 4 characters'],
19
+ phone_number: ['must be a phone number with area or country code'],
20
+ username: ['must be specified']
17
21
  )
18
22
  end
19
23
 
@@ -36,7 +40,9 @@ describe Volt::Model do
36
40
 
37
41
  model.save!
38
42
 
39
- expect(model.marked_errors.keys).to eq([:name, :description, :username, :count])
43
+ expect(model.marked_errors.keys).to eq(
44
+ [:count, :description, :email, :name, :phone_number, :username]
45
+ )
40
46
  end
41
47
 
42
48
  describe 'length' do
@@ -81,4 +87,31 @@ describe Volt::Model do
81
87
  end
82
88
  end
83
89
 
90
+ describe 'email' do
91
+ it 'should validate email' do
92
+ model = TestModel.new
93
+
94
+ expect(model.marked_errors).to eq({})
95
+
96
+ model.mark_field!(:email)
97
+
98
+ expect(model.marked_errors).to eq(
99
+ email: ['must be an email address']
100
+ )
101
+ end
102
+ end
103
+
104
+ describe 'phone_number' do
105
+ it 'should validate phone number' do
106
+ model = TestModel.new
107
+
108
+ expect(model.marked_errors).to eq({})
109
+
110
+ model.mark_field!(:phone_number)
111
+
112
+ expect(model.marked_errors).to eq(
113
+ phone_number: ['must be a phone number with area or country code']
114
+ )
115
+ end
116
+ end
84
117
  end
@@ -0,0 +1,120 @@
1
+ require 'spec_helper'
2
+
3
+ describe Volt::EmailValidator do
4
+ subject { Volt::EmailValidator.new(*params) }
5
+ let(:params) { [ model, field_name, options ] }
6
+
7
+ let(:model) { Volt::Model.new email: email }
8
+ let(:field_name) { :email }
9
+ let(:options) { true }
10
+
11
+ let(:valid_email) { 'test@example.com' }
12
+ let(:invalid_email) { 'test@example-com' }
13
+ let(:email) { valid_email }
14
+
15
+ describe '.validate' do
16
+ let(:result) { described_class.validate(*params.dup.insert(1, nil)) }
17
+
18
+ before do
19
+ allow(described_class).to receive(:new).and_return subject
20
+ allow(subject).to receive(:errors).and_call_original
21
+
22
+ result
23
+ end
24
+
25
+ it 'initializes an email validator with the provided arguments' do
26
+ expect(described_class).to have_received(:new).with(*params)
27
+ end
28
+
29
+ it 'calls errors on the email validator' do
30
+ expect(subject).to have_received :errors
31
+ end
32
+
33
+ it 'returns the result of calling errors on the validator' do
34
+ expect(subject.errors).to eq result
35
+ end
36
+ end
37
+
38
+ describe '#valid?' do
39
+ context 'when using the default regex' do
40
+ let(:options) { true }
41
+
42
+ context 'when the email is valid' do
43
+ let(:email) { valid_email }
44
+
45
+ specify { expect(subject.valid?).to eq true }
46
+ end
47
+
48
+ context 'when the email is missing a TLD' do
49
+ let(:email) { 'test@example' }
50
+
51
+ specify { expect(subject.valid?).to eq false }
52
+ end
53
+
54
+ context 'when the email TLD is only one character' do
55
+ let(:email) { 'test@example.c' }
56
+
57
+ specify { expect(subject.valid?).to eq false }
58
+ end
59
+
60
+ context 'when the email is missing an username' do
61
+ let(:email) { '@example.com' }
62
+
63
+ specify { expect(subject.valid?).to eq false }
64
+ end
65
+
66
+ context 'when the email is missing the @ symbol' do
67
+ let(:email) { 'test.example.com' }
68
+
69
+ specify { expect(subject.valid?).to eq false }
70
+ end
71
+ end
72
+
73
+ context 'when using a custom regex' do
74
+ let(:options) { { with: /.+\@.+/ } }
75
+
76
+ context 'and the email qualifies' do
77
+ let(:email) { 'test@example' }
78
+
79
+ specify { expect(subject.valid?).to eq true }
80
+ end
81
+
82
+ context 'and the email does not qualify' do
83
+ let(:email) { 'test$example' }
84
+
85
+ specify { expect(subject.valid?).to eq false }
86
+ end
87
+ end
88
+ end
89
+
90
+ describe '#errors' do
91
+ context 'when the model has a valid email' do
92
+ let(:email) { valid_email }
93
+
94
+ it 'returns an empty error hash' do
95
+ expect(subject.errors).to eq({})
96
+ end
97
+ end
98
+
99
+ context 'when the model has an invalid email' do
100
+ let(:email) { invalid_email }
101
+
102
+ it 'returns an array of errors for email' do
103
+ expect(subject.errors).to eq(email: ['must be an email address'])
104
+ end
105
+ end
106
+
107
+ context 'when provided a custom error message' do
108
+ let(:options) { { error_message: custom_message } }
109
+ let(:custom_message) { 'this is a custom message' }
110
+
111
+ context 'and the email is invalid' do
112
+ let(:email) { invalid_email }
113
+
114
+ it 'returns errors with the custom message' do
115
+ expect(subject.errors).to eq(email: [ custom_message ])
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,146 @@
1
+ require 'spec_helper'
2
+
3
+ describe Volt::PhoneNumberValidator do
4
+ subject { Volt::PhoneNumberValidator.new(*params) }
5
+ let(:params) { [ model, field_name, options ] }
6
+
7
+ let(:model) { Volt::Model.new phone_number: phone_number }
8
+ let(:field_name) { :phone_number }
9
+ let(:options) { true }
10
+
11
+ let(:valid_us_number) { '(123)-123-1234' }
12
+ let(:valid_intl_number) { '+12 123 123 1234' }
13
+ let(:invalid_number) { '1234-123-123456' }
14
+ let(:phone_number) { valid_us_number }
15
+
16
+ describe '.validate' do
17
+ let(:result) { described_class.validate(*params.dup.insert(1, nil)) }
18
+
19
+ before do
20
+ allow(described_class).to receive(:new).and_return subject
21
+ allow(subject).to receive(:errors).and_call_original
22
+
23
+ result
24
+ end
25
+
26
+ it 'initializes a phone number validator with the provided arguments' do
27
+ expect(described_class).to have_received(:new).with(*params)
28
+ end
29
+
30
+ it 'calls errors on the phone number validator' do
31
+ expect(subject).to have_received :errors
32
+ end
33
+
34
+ it 'returns the result of calling errors on the validator' do
35
+ expect(subject.errors).to eq result
36
+ end
37
+ end
38
+
39
+ describe '#valid?' do
40
+ context 'when using the default regex' do
41
+ let(:options) { true }
42
+
43
+ context 'when the phone number is a valid US number' do
44
+ let(:phone_number) { valid_us_number }
45
+
46
+ specify { expect(subject.valid?).to eq true }
47
+ end
48
+
49
+ context 'when the phone number is a valid international number' do
50
+ let(:phone_number) { valid_intl_number }
51
+
52
+ specify { expect(subject.valid?).to eq true }
53
+ end
54
+
55
+ context 'when the phone number uses dashes' do
56
+ let(:phone_number) { '123-123-1234' }
57
+
58
+ specify { expect(subject.valid?).to eq true }
59
+ end
60
+
61
+ context 'when the phone number uses periods' do
62
+ let(:phone_number) { '123.123.1234' }
63
+
64
+ specify { expect(subject.valid?).to eq true }
65
+ end
66
+
67
+ context 'when the phone number uses spaces' do
68
+ let(:phone_number) { '123 123 1234' }
69
+
70
+ specify { expect(subject.valid?).to eq true }
71
+ end
72
+
73
+ context 'when the phone number uses parentheses and a space' do
74
+ let(:phone_number) { '(123) 123.1234' }
75
+
76
+ specify { expect(subject.valid?).to eq true }
77
+ end
78
+
79
+ context 'when an international number uses a plus' do
80
+ let(:phone_number) { '+12 123 123 1234' }
81
+
82
+ specify { expect(subject.valid?).to eq true }
83
+ end
84
+
85
+ context 'when an international number does not use a plus' do
86
+ let(:phone_number) { '12 123 123 1234' }
87
+
88
+ specify { expect(subject.valid?).to eq true }
89
+ end
90
+
91
+ context 'when an international number is from the UK' do
92
+ let(:phone_number) { '+12 123 1234 1234' }
93
+
94
+ specify { expect(subject.valid?).to eq true }
95
+ end
96
+ end
97
+
98
+ context 'when using a custom regex' do
99
+ let(:options) { { with: /\d{10}/ } }
100
+
101
+ context 'and the phone number qualifies' do
102
+ let(:phone_number) { '1231231234' }
103
+
104
+ specify { expect(subject.valid?).to eq true }
105
+ end
106
+
107
+ context 'and the phone number does not qualify' do
108
+ let(:phone_number) { '123-123-1234' }
109
+
110
+ specify { expect(subject.valid?).to eq false }
111
+ end
112
+ end
113
+ end
114
+
115
+ describe '#errors' do
116
+ context 'when the model has a valid phone number' do
117
+ let(:phone_number) { valid_us_number }
118
+
119
+ it 'returns an empty error hash' do
120
+ expect(subject.errors).to eq({})
121
+ end
122
+ end
123
+
124
+ context 'when the model has an invalid phone number' do
125
+ let(:phone_number) { invalid_number }
126
+
127
+ it 'returns an array of errors for phone number' do
128
+ expect(subject.errors).to eq(
129
+ phone_number: ['must be a phone number with area or country code'])
130
+ end
131
+ end
132
+
133
+ context 'when provided a custom error message' do
134
+ let(:options) { { error_message: custom_message } }
135
+ let(:custom_message) { 'this is a custom message' }
136
+
137
+ context 'and the phone number is invalid' do
138
+ let(:phone_number) { invalid_number }
139
+
140
+ it 'returns errors with the custom message' do
141
+ expect(subject.errors).to eq(phone_number: [ custom_message ])
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end
@@ -117,7 +117,7 @@ describe Volt::ViewParser do
117
117
  'html' => " <div class=\"main\">\n <!-- $0 --><!-- $/0 -->\n </div>\n",
118
118
  'bindings' => {
119
119
  0 => [
120
- "lambda { |__p, __t, __c, __id| Volt::EachBinding.new(__p, __t, __c, __id, Proc.new { _items }, \"item\", \"main/main/main/body/__each0/__template/0\") }"
120
+ "lambda { |__p, __t, __c, __id| Volt::EachBinding.new(__p, __t, __c, __id, Proc.new { _items }, \"item\", nil, \"main/main/main/body/__each0/__template/0\") }"
121
121
  ]
122
122
  }
123
123
  })
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: volt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.24
4
+ version: 0.8.26.beta1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryan Stout
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-12-05 00:00:00.000000000 Z
11
+ date: 2014-12-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -453,9 +453,6 @@ files:
453
453
  - app/volt/views/notices/index.html
454
454
  - bin/volt
455
455
  - docs/FAQ.md
456
- - docs/GETTING_STARTED.md
457
- - docs/JAVASCRIPT_COMPONENTS.md
458
- - docs/WHY.md
459
456
  - docs/volt-logo.jpg
460
457
  - lib/volt.rb
461
458
  - lib/volt/assets/test.rb
@@ -490,6 +487,7 @@ files:
490
487
  - lib/volt/models/array_model.rb
491
488
  - lib/volt/models/buffer.rb
492
489
  - lib/volt/models/cursor.rb
490
+ - lib/volt/models/field_helpers.rb
493
491
  - lib/volt/models/model.rb
494
492
  - lib/volt/models/model_hash_behaviour.rb
495
493
  - lib/volt/models/model_helpers.rb
@@ -510,8 +508,10 @@ files:
510
508
  - lib/volt/models/persistors/store_state.rb
511
509
  - lib/volt/models/url.rb
512
510
  - lib/volt/models/validations.rb
511
+ - lib/volt/models/validators/email_validator.rb
513
512
  - lib/volt/models/validators/length_validator.rb
514
513
  - lib/volt/models/validators/numericality_validator.rb
514
+ - lib/volt/models/validators/phone_number_validator.rb
515
515
  - lib/volt/models/validators/presence_validator.rb
516
516
  - lib/volt/models/validators/unique_validator.rb
517
517
  - lib/volt/page/bindings/attribute_binding.rb
@@ -625,10 +625,13 @@ files:
625
625
  - spec/integration/templates_spec.rb
626
626
  - spec/integration/url_spec.rb
627
627
  - spec/integration/user_spec.rb
628
+ - spec/models/field_helpers_spec.rb
628
629
  - spec/models/model_spec.rb
629
630
  - spec/models/persistors/params_spec.rb
630
631
  - spec/models/persistors/store_spec.rb
631
632
  - spec/models/validations_spec.rb
633
+ - spec/models/validators/email_validator_spec.rb
634
+ - spec/models/validators/phone_number_validator_spec.rb
632
635
  - spec/page/bindings/content_binding_spec.rb
633
636
  - spec/page/bindings/template_binding_spec.rb
634
637
  - spec/page/sub_context_spec.rb
@@ -721,9 +724,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
721
724
  version: '0'
722
725
  required_rubygems_version: !ruby/object:Gem::Requirement
723
726
  requirements:
724
- - - ">="
727
+ - - ">"
725
728
  - !ruby/object:Gem::Version
726
- version: '0'
729
+ version: 1.3.1
727
730
  requirements: []
728
731
  rubyforge_project:
729
732
  rubygems_version: 2.2.2
@@ -772,10 +775,13 @@ test_files:
772
775
  - spec/integration/templates_spec.rb
773
776
  - spec/integration/url_spec.rb
774
777
  - spec/integration/user_spec.rb
778
+ - spec/models/field_helpers_spec.rb
775
779
  - spec/models/model_spec.rb
776
780
  - spec/models/persistors/params_spec.rb
777
781
  - spec/models/persistors/store_spec.rb
778
782
  - spec/models/validations_spec.rb
783
+ - spec/models/validators/email_validator_spec.rb
784
+ - spec/models/validators/phone_number_validator_spec.rb
779
785
  - spec/page/bindings/content_binding_spec.rb
780
786
  - spec/page/bindings/template_binding_spec.rb
781
787
  - spec/page/sub_context_spec.rb
@@ -1,28 +0,0 @@
1
- # Getting Started
2
-
3
- Volt relies on a few concepts to take make web development faster and easier. The first of these is reactive programming. Data on the front and back end is stored in models. Instead of manually updating a page when the data changes, the page is coded using a templating language which automatically updates when the data changes.
4
-
5
- ## Bindings and Models
6
-
7
- This automaic updating is done via bindings and models. In Volt app's all data is stored in a model. From your html, you can bind things like attributes and text to a value in a model.
8
-
9
- ### Name Example
10
-
11
- ```html
12
- <label>Name:</label>
13
- <input type="text" value="{page._name}" />
14
- <p>Hello {page._name}</p>
15
- ```
16
-
17
- In the example above, our model is called page (more about page later). Any time a user changes the value of the field, page._name will be updated to the fields value. When page._name is changed, the fields value changes. Also when ```page._name``` changes, the page will show the text "Hello ..." where ... is the value of page._name. These "two-way bindings" help us eliminiate a lot of code by keeping all of our application state in our models. Data displayed in a view is always computed live from the data in the models.
18
-
19
- ### Meal Cost Splitter Example
20
-
21
- ```html
22
- <label>Cost:</label><input type="text" value="{page._cost}" /><br />
23
- <label>People:</label><input type="text" value="{page._people}" /><br />
24
- <p>Cost Per Person: {page._cost.to_f / page._people.to_f}</p>
25
- ```
26
- In this example, a user can enter a cost and a number of people. When either changes, the Cost Per Person will update.
27
-
28
- ###
@@ -1,2 +0,0 @@
1
- # Integrating JavaScript
2
-
data/docs/WHY.md DELETED
@@ -1,39 +0,0 @@
1
- # Why Volt?
2
-
3
- Volt is a new web framework. You use Ruby for both your client and server code. Volt helps you break your code into reusable components. It handles managing all assets and dependencies for you. Volt automatically updates your pages for you when your model data changes and sync's that data to the database for you. By providing reusable structure and handling common tasks, Volt lets you build web app's really fast!
4
-
5
- # Features
6
-
7
- ## Components
8
-
9
- Volt projects are broken into components. Components are easy to create and simple to reuse. They are easily shared and only require one line of code to insert into your project. Volt provides many common components out of the box.
10
-
11
- ## Reactive
12
-
13
- Data in volt is reactive by default. Changes to the data is automatically updated in the DOM.
14
-
15
-
16
- ## Data Syncing
17
-
18
- A lot of modern web development is moving data between the front-end to the back-end. Volt eliminates all of that work. Model's on the front-end automatically sync to the back-end, and vice versa. Validations are run on both sides for security. Models on the front-end are automatically updated whenever they are changed anywhere else (another browser, a background task, etc..)
19
-
20
-
21
- # Why Volt is Awesome
22
-
23
- - only the relevant DOM is updated. There is no match and patch algorithm to update from strings like other frameworks, all associations are tracked through our reactive core, so we know exactly what needs to be updated without the need to generate any extra HTML. This has a few advantages, namely that things like input fields are retained, so any properties (focus, tab position, etc...) are also retained.
24
-
25
-
26
- # Why Ruby
27
-
28
- Isomorphic type system with javascript
29
-
30
- In web development today, JavaScript gets to be the default language by virtue of being in the browser. JavaScript is a very good language, but it has a lot of warts. (See http://wtfjs.com/ for some great examples) Some of these can introduce bugs, others are just difficult to deal with. JavaScript was rushed to market quickly and standardized very quickly. Ruby was used by a small community for years while most of the kinks were worked out. Ruby also has some great concepts such as [uniform access](http://en.wikipedia.org/wiki/Uniform_access_principle), [mixin's](http://en.wikipedia.org/wiki/Mixin), [duck typing](http://en.wikipedia.org/wiki/Duck_typing), and [blocks](http://yehudakatz.com/2012/01/10/javascript-needs-blocks/) to name a few. While many of these features can be implemented in JavaScript in userland, few are standardardized and the solutions are seldom eloquent.
31
-
32
- [5,10,1].sort()
33
- // [1, 10, 5]
34
-
35
- Uniform access and duck typing provides us with the ability to make reactive objects that have the exact same interface as a normal object. This is a big win, nothing new to learn to do reactive programming. They can also be used interchangably with regular objects.
36
-
37
- # Why Opal
38
-
39
- Opal is really an increadiable project, and the core team has done a great job. Ruby and JavaScript are similar in a lot of ways. This lets Opal compile to JavaScript that is very readable. This also means that Opal's performance is great. You'll find that in most cases Ruby code runs with no performance penality compared to the eqivilent JavaScript code.