comply 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 722d89b836f206876be44dad520abe01f66f83d7
4
+ data.tar.gz: 1d4f4a587d745cf3defa6c0c28404dc894d92a00
5
+ SHA512:
6
+ metadata.gz: 242e7876ee58be0be396eba9a9c7f10f8e1a9083dfcefa0e4c309a659cf4441418b00a04de08f4bc9e19ab74568f38a9b6e059b421e84100cce216d81ea1706a
7
+ data.tar.gz: e22103b29fd7020d029cae89c4dcac823b3052ff2229c1308499286f172adf69fa492f0cc397c3ec083e189321c7cb64b84fe10576c1c6d7d744566f8de13cb7
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2014 YOURNAME
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,162 @@
1
+ # Comply
2
+ Server-side ActiveRecord validations, all up in your client-side HTML.
3
+
4
+ ## Intention and Purpose
5
+ Rendering the same page over and over due to validations is a slow and unpleasant user experience. The logical alternative is, of course, validating the form on the page, before it is submitted.
6
+
7
+ Though it's better, doing that still requires duplicating your validations on the client and server. Any time you change a validation in one place, it must be changed in the other as well. In addition, some validations require hitting the server and database, for example validating the uniqueness of an email address.
8
+
9
+ The solution offered here is combine the server-side validations with inline error messages on the form. This is done by creating an API for validating your objects. When triggered, the form is serialized and sent to the API endpoint, where it is instantiated as your ActiveRecord model, and validations are run. The API returns the instance's error messages, which the form uses to determine if the described object is valid.
10
+
11
+ ## Basic Usage
12
+ Include Comply in your gemfile:
13
+ ```ruby
14
+ gem 'comply'
15
+ ```
16
+ If you are using Rails 3, you need to include `strong_parameters`
17
+ ```ruby
18
+ gem 'comply'
19
+ gem 'strong_parameters'
20
+ ```
21
+ Mount the engine in your `routes.rb`:
22
+ ```ruby
23
+ mount Comply::Engine => '/comply'
24
+ ```
25
+ Require the javascript files & dependencies in the form's corresponding javascript file (requires Asset Pipeline):
26
+ ```js
27
+ //= require jquery
28
+ //= require comply
29
+ ```
30
+
31
+ In your form:
32
+ Add the `data-validate-model` tag to your form tag with the name of the model you intend to validate:
33
+ ```erb
34
+ <%= form_for @movie, html: { data: { validate_model: 'movie' } } do |f| %>
35
+ ```
36
+ On the input tags you want validated, add the `data-validate` tag:
37
+ ```erb
38
+ <div class="field">
39
+ <%= f.label :title %><br>
40
+ <%= f.text_field :title, data: { validate: true } %>
41
+ </div>
42
+ <div class="field">
43
+ <%= f.label :description %><br>
44
+ <%= f.text_area :description, data: { validate: true } %>
45
+ </div>
46
+ ```
47
+
48
+ Of course, this all assumes you have ActiveRecord validations set up in your model:
49
+ ```ruby
50
+ class Movie < ActiveRecord::Base
51
+ validates :title, presence: true, uniqueness: true
52
+ validates :desciption, presence: true
53
+ end
54
+ ```
55
+
56
+ ## Customizations
57
+ You can control the behavior of your validatable forms and inputs in a variety of ways.
58
+
59
+ ### Validation route
60
+ You can specify the route to be used for validation on the form by setting the `data-validate-route` attribute.
61
+ ```erb
62
+ <%= form_for @movie, html: { data: { validate_model: 'movie', validate_route: custom_validation_path } } do |f| %>
63
+ ```
64
+
65
+ ### Success messages
66
+ Want success messages to add some personality to your forms? Add the message as the `data-validate-success` attribute.
67
+ ```erb
68
+ <%= f.text_field :title, data: { validate: true, validate_success: "Sweet title yo!" } %>
69
+ ```
70
+
71
+ ### Event triggers
72
+ You can change the jQuery event which triggers the input validation with the `data-validate-event` attribute.
73
+ ```erb
74
+ <%= f.text_field :description, data: { validate: true, validate_event: 'click' } %>
75
+ ```
76
+ Note: the default event is `input keyup`.
77
+
78
+ ### Timeouts
79
+ You can delay validation by setting the `data-validate-timeout` attribute. This is great for things like checking a string's format so that it won't validate until the user has had a chance to finish typing.
80
+ ```erb
81
+ <%= f.text_field :description, data: { validate: true, validate_timeout: 1000 } %>
82
+ ```
83
+ Note: the amount is in milliseconds, and the default amount is 500 milliseconds.
84
+
85
+ ### Validation dependencies
86
+ Sometimes you have two fields that you want to validate at the same time. You can ensure they will be serialized and validated together using the `data-validate-with` attribute, which takes the jQuery selector of the dependent object. This can be useful for things like confirmation fields.
87
+ ```erb
88
+ <%= f.label :password %>
89
+ <%= f.password_field :password, class: "span6" %>
90
+
91
+ <%= f.label :password_confirmation %>
92
+ <%= f.password_field :password_confirmation, data: { validate: true, validate_with: '#user_password' } %>
93
+ ```
94
+
95
+ ### Multiparameter attributes
96
+ Want to validate multiple fields as one like a multiparameter attribute? Add the `data-multiparam` attribute to your inputs. This is great for multivalue fields like dates.
97
+ ```erb
98
+ <%= f.date_select :release_date, {}, data: { validate: true, multiparam: 'release_date' } %>
99
+ ```
100
+
101
+ #### Forcing validations on multiparameters
102
+ Normally, a multiparameter input won't validate until all of its fields have been completed. If you want to override that, you can set the `data-validate-force` attribute. This is good if you don't want to represent your multiparameter fields as one unit.
103
+ ```erb
104
+ <%= f.date_select :release_date, { order: [:month, :day] }, data: { validate: true, multiparam: 'release_date' } %>
105
+ <%= f.text_field :'release_date(1i)', id: 'release_date_1i', data: { validate: true, multiparam: 'release_date', validate_force: true } %>
106
+ ```
107
+
108
+ ## Customizing the ValidationMessage class
109
+ If you don't want to use the default `Comply.ValidationMessage`, which is responsible for putting the validation message tag on the page and handling the display of messages, great news: you can overwrite it!
110
+
111
+ After you've included Comply in the Asset Pipeline, feel free to extend it! For example (in `foo.js.coffee`):
112
+ ```coffeescript
113
+ class Comply.ValidationMessage extends Comply.BaseValidationMessage
114
+ constructor: (@$el) ->
115
+ super
116
+ console.log 'We can build him. We have the technology.'
117
+
118
+ successMessage: (message = '') ->
119
+ super
120
+ console.log 'All systems go.'
121
+
122
+ errorMessage: (message = '') ->
123
+ super
124
+ console.log 'Houston, we have a problem.'
125
+
126
+ resetMessage: (message) ->
127
+ super
128
+ console.log 'Mulligan!'
129
+ ```
130
+
131
+ ## Mounting the engine elsewhere
132
+ If you would like to mount the engine under a different namespace, all you need to do is add the engine path to the javascript object:
133
+
134
+ ```ruby
135
+ # routes.rb
136
+ mount Comply::Engine => '/joanie_loves_chachi'
137
+ ```
138
+ ```javascript
139
+ //= require comply
140
+
141
+ Comply.enginePath = 'joanie_loves_chachi';
142
+ ```
143
+
144
+ ## Customizing Validation Behavior
145
+ You can override the validation behavior by inheriting from `Comply::ValidationsController`. This can be useful for cases like forms which update specific attributes of an instance.
146
+
147
+ For example, if you have a "change email" form, and you want to validate the email's uniqueness against the current user, you can override the `validation_instance` method on `Comply::ValidationsController`, set corresponding routes, and add the `data-validate-route` attribute to your form.
148
+ ```ruby
149
+ # my_validations_controller.rb
150
+ class MyValidationsController < Comply::ValidationsController
151
+ def validation_instance
152
+ current_user.attributes = @fields
153
+ current_user
154
+ end
155
+ end
156
+
157
+ # routes.rb
158
+ match 'validations' => 'my_validations#show', only: :show, as: :my_validation
159
+ ```
160
+ ```erb
161
+ <%= form_for @movie, html: { data: { validate_model: 'movie', validate_route: my_validation_path } } do |f| %>
162
+ ```
data/Rakefile ADDED
@@ -0,0 +1,34 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'Comply'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.rdoc')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
18
+ load 'rails/tasks/engine.rake'
19
+
20
+
21
+
22
+ Bundler::GemHelper.install_tasks
23
+
24
+ require 'rake/testtask'
25
+
26
+ Rake::TestTask.new(:test) do |t|
27
+ t.libs << 'lib'
28
+ t.libs << 'test'
29
+ t.pattern = 'test/**/*_test.rb'
30
+ t.verbose = false
31
+ end
32
+
33
+
34
+ task default: :test
@@ -0,0 +1,21 @@
1
+ class Comply.BaseValidationMessage
2
+ constructor: (@$el) ->
3
+ if multiparam = @$el.data('multiparam')
4
+ selector = "[data-multiparam=#{multiparam}].validation-msg"
5
+ @$el.parent().append("<div data-multiparam='#{multiparam}' class='validation-msg'></div>") unless $(selector).length
6
+ @$messageField = $(selector)
7
+ else
8
+ @$el.after("<div class='validation-msg'></div>")
9
+ @$messageField = @$el.siblings('.validation-msg')
10
+
11
+ successMessage: (message = '') ->
12
+ @resetMessage message
13
+ @$messageField.addClass 'success'
14
+
15
+ errorMessage: (message = '') ->
16
+ @resetMessage message
17
+ @$messageField.addClass 'error'
18
+
19
+ resetMessage: (message) ->
20
+ @$messageField.html message
21
+ @$messageField.removeClass 'error success'
@@ -0,0 +1,3 @@
1
+ window.Comply = {
2
+ enginePath: 'comply',
3
+ }
@@ -0,0 +1,4 @@
1
+ #= require comply/config
2
+ #= require_tree .
3
+
4
+ $ -> $('[data-validate-model]').each -> new Comply.ValidatableForm $(this)
@@ -0,0 +1,38 @@
1
+ class Comply.ValidatableForm
2
+ constructor: (@$form) ->
3
+ @inputs = @_inputs()
4
+ @model = @$form.data('validate-model')
5
+ @$button = @$form.find('[type=submit]')
6
+ @validationRoute = @$form.data('validate-route') or "/#{Comply.enginePath}/validations"
7
+ @$button.click @validateInputs
8
+ @$button.attr('disabled', false).removeClass('disabled')
9
+
10
+ validateInputs: (event) =>
11
+ event.preventDefault()
12
+ @validate inputs: @inputs, submit: true
13
+
14
+ validate: (options) ->
15
+ $.get @validationRoute, @_params(), (response) =>
16
+ return @$form.submit() if options.submit and @_allSuccess(response)
17
+ @_setMessages(options.inputs, response)
18
+
19
+ # private
20
+
21
+ _setMessages: (inputs, response) =>
22
+ for input in inputs
23
+ status = @_isSuccess(input, response)
24
+ input.setMessage status, @_responseValue(input, response, status)
25
+
26
+ _allSuccess: (response) -> not (err for field, err of response.error when err.length).length
27
+
28
+ _inputs: ->
29
+ for input in @$form.find('[data-validate][name]')
30
+ new Comply.ValidatableInput $(input), this
31
+
32
+ _isSuccess: (input, response) ->
33
+ if !!@_responseValue(input, response) then 'error' else 'success'
34
+
35
+ _params: -> "#{$.param(model: @model)}&#{@$form.serialize()}"
36
+
37
+ _responseValue: (input, response, status = 'error') ->
38
+ if response[status]? then response[status][input.attrName]
@@ -0,0 +1,41 @@
1
+ class Comply.ValidatableInput
2
+ constructor: (@$el, @form) ->
3
+ parts = @$el.attr('name').replace(/\]/g, '').split('[')
4
+ @model = parts[0]
5
+ @attrName = parts[parts.length - 1].split('(')[0]
6
+ @event = @$el.data('validate-event') or 'input keyup'
7
+ @timeoutLength = @$el.data('validate-timeout') or 500
8
+ @successMessage = @$el.data('validate-success')
9
+ @dependency = @_dependency()
10
+ @message = new Comply.ValidationMessage @$el
11
+ @$el.bind @event, @validate
12
+
13
+ validate: =>
14
+ if @_validatable()
15
+ clearTimeout @timeout
16
+ @timeout = setTimeout @_submitValidations, @timeoutLength
17
+
18
+ setMessage: (status, message) ->
19
+ message = @successMessage if status is 'success'
20
+ @message["#{status}Message"] message
21
+
22
+ #private
23
+
24
+ _dependency: ->
25
+ new Comply.ValidatableInput($("#{@$el.data('validate-with')}[name]"), @form) if @$el.data('validate-with')?
26
+
27
+ _forceValidate: -> @forceValidate or= @$el.data('validate-force')
28
+
29
+ _multiparam: -> @multiparam or= @$el.data('multiparam')
30
+
31
+ _multiparamFields: ->
32
+ field for field in $("[data-multiparam=#{@_multiparam()}]") when field isnt @$el[0]
33
+
34
+ _$multiparamFields: ->
35
+ @$multiparamFields or= $(field) for field in @_multiparamFields()
36
+
37
+ _submitValidations: => @form.validate inputs: (el for el in [this, @dependency] when el)
38
+
39
+ _validatable: ->
40
+ return true if @_forceValidate() or not @_multiparam()
41
+ !($el for $el in @_$multiparamFields() when !$el.val().length).length
@@ -0,0 +1 @@
1
+ class Comply.ValidationMessage extends Comply.BaseValidationMessage
@@ -0,0 +1,5 @@
1
+ module Comply
2
+ class ApplicationController < ActionController::Base
3
+ include ::SslRequirement
4
+ end
5
+ end
@@ -0,0 +1,33 @@
1
+ module Comply
2
+ class ValidationsController < Comply::ApplicationController
3
+ ssl_allowed :show
4
+
5
+ before_filter :require_model, :require_fields
6
+
7
+ def show
8
+ @instance = validation_instance
9
+ @instance.valid?
10
+ render json: { error: @instance.errors }
11
+ end
12
+
13
+ private
14
+
15
+ def validation_instance
16
+ @model.new(post_params)
17
+ end
18
+
19
+ def post_params
20
+ params.require(params[:model].to_sym).permit!
21
+ end
22
+
23
+ def require_fields
24
+ @fields = params[params[:model].downcase] if params[params[:model].downcase].present?
25
+ render json: { error: 'Form fields not found' }, status: 500 unless @fields
26
+ end
27
+
28
+ def require_model
29
+ @model = params[:model].classify.constantize if params[:model].present?
30
+ render json: { error: 'Model not found' }, status: 500 unless @model
31
+ end
32
+ end
33
+ end
data/config/routes.rb ADDED
@@ -0,0 +1,3 @@
1
+ Comply::Engine.routes.draw do
2
+ match '/validations' => 'validations#show', via: :get
3
+ end
@@ -0,0 +1,5 @@
1
+ module Comply
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace Comply
4
+ end
5
+ end
@@ -0,0 +1,3 @@
1
+ module Comply
2
+ VERSION = '1.0.0'
3
+ end
data/lib/comply.rb ADDED
@@ -0,0 +1,5 @@
1
+ require 'comply/engine'
2
+ require 'ssl_requirement'
3
+
4
+ module Comply
5
+ end
metadata ADDED
@@ -0,0 +1,159 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: comply
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - '@jacobaweiss'
8
+ - '@andyjbas'
9
+ - '@azach'
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2014-06-13 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: coffee-rails
17
+ requirement: !ruby/object:Gem::Requirement
18
+ requirements:
19
+ - - '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - '>='
27
+ - !ruby/object:Gem::Version
28
+ version: '0'
29
+ - !ruby/object:Gem::Dependency
30
+ name: bartt-ssl_requirement
31
+ requirement: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - ~>
34
+ - !ruby/object:Gem::Version
35
+ version: 1.4.2
36
+ type: :runtime
37
+ prerelease: false
38
+ version_requirements: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ~>
41
+ - !ruby/object:Gem::Version
42
+ version: 1.4.2
43
+ - !ruby/object:Gem::Dependency
44
+ name: rails
45
+ requirement: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - '>='
48
+ - !ruby/object:Gem::Version
49
+ version: '0'
50
+ type: :runtime
51
+ prerelease: false
52
+ version_requirements: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - '>='
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ - !ruby/object:Gem::Dependency
58
+ name: jasmine-rails
59
+ requirement: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - '>='
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ type: :development
65
+ prerelease: false
66
+ version_requirements: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - '>='
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ - !ruby/object:Gem::Dependency
72
+ name: pry
73
+ requirement: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ type: :development
79
+ prerelease: false
80
+ version_requirements: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - '>='
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
85
+ - !ruby/object:Gem::Dependency
86
+ name: rspec-rails
87
+ requirement: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ~>
90
+ - !ruby/object:Gem::Version
91
+ version: '2.14'
92
+ type: :development
93
+ prerelease: false
94
+ version_requirements: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ~>
97
+ - !ruby/object:Gem::Version
98
+ version: '2.14'
99
+ - !ruby/object:Gem::Dependency
100
+ name: sqlite3
101
+ requirement: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - '>='
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ type: :development
107
+ prerelease: false
108
+ version_requirements: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - '>='
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
113
+ description: Validate your ActiveRecord models on the client, showing their error
114
+ and success messages by providing a validation controller.
115
+ email:
116
+ - jack@lumoslabs.com
117
+ executables: []
118
+ extensions: []
119
+ extra_rdoc_files: []
120
+ files:
121
+ - app/assets/javascripts/comply/base_validation_message.js.coffee
122
+ - app/assets/javascripts/comply/config.js.coffee
123
+ - app/assets/javascripts/comply/index.js.coffee
124
+ - app/assets/javascripts/comply/validatable_form.js.coffee
125
+ - app/assets/javascripts/comply/validatable_input.js.coffee
126
+ - app/assets/javascripts/comply/validation_message.js.coffee
127
+ - app/controllers/comply/application_controller.rb
128
+ - app/controllers/comply/validations_controller.rb
129
+ - config/routes.rb
130
+ - lib/comply/engine.rb
131
+ - lib/comply/version.rb
132
+ - lib/comply.rb
133
+ - MIT-LICENSE
134
+ - Rakefile
135
+ - README.md
136
+ homepage: http://www.github.com/lumoslabs/comply
137
+ licenses: []
138
+ metadata: {}
139
+ post_install_message:
140
+ rdoc_options: []
141
+ require_paths:
142
+ - lib
143
+ required_ruby_version: !ruby/object:Gem::Requirement
144
+ requirements:
145
+ - - '>='
146
+ - !ruby/object:Gem::Version
147
+ version: '0'
148
+ required_rubygems_version: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - '>='
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ requirements: []
154
+ rubyforge_project:
155
+ rubygems_version: 2.1.11
156
+ signing_key:
157
+ specification_version: 4
158
+ summary: Inline validation of your ActiveRecord models via the AJAX internets
159
+ test_files: []