instructions 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/.gitignore +9 -0
  2. data/Gemfile +7 -0
  3. data/Gemfile.lock +26 -0
  4. data/LICENSE.txt +20 -0
  5. data/README.textile +141 -0
  6. data/Rakefile +40 -0
  7. data/VERSION +1 -0
  8. data/extras/hint_errors.en.yml +27 -0
  9. data/init.rb +0 -0
  10. data/install.rb +1 -0
  11. data/instructions.gemspec +19 -0
  12. data/instructions.gemspec.generated +89 -0
  13. data/lib/core_files.rb +4 -0
  14. data/lib/instructions.rb +6 -0
  15. data/lib/instructions/abstract.rb +21 -0
  16. data/lib/instructions/attribute_state.rb +25 -0
  17. data/lib/instructions/configuration.rb +49 -0
  18. data/lib/instructions/core_ext/action_view_ext.rb +9 -0
  19. data/lib/instructions/error_message.rb +9 -0
  20. data/lib/instructions/error_message_tag.rb +21 -0
  21. data/lib/instructions/field_error.rb +33 -0
  22. data/lib/instructions/field_instructions.rb +6 -0
  23. data/lib/instructions/form_helper.rb +65 -0
  24. data/lib/instructions/formatters.rb +10 -0
  25. data/lib/instructions/instructions_tag.rb +15 -0
  26. data/lib/instructions/tag.rb +71 -0
  27. data/lib/instructions/valid_instructions_tag.rb +9 -0
  28. data/rails/init.rb +0 -0
  29. data/spec/attribute_state_spec.rb +62 -0
  30. data/spec/configuration_spec.rb +74 -0
  31. data/spec/error_message_spec.rb +42 -0
  32. data/spec/error_message_tag_spec.rb +30 -0
  33. data/spec/fake_model.rb +17 -0
  34. data/spec/field_error_spec.rb +85 -0
  35. data/spec/form_helper_spec.rb +123 -0
  36. data/spec/formatters_spec.rb +76 -0
  37. data/spec/spec_helper.rb +7 -0
  38. data/spec/support/field_instructions_namespace.rb +2 -0
  39. data/spec/support/html_safe_string.rb +7 -0
  40. data/spec/support/rspec_patches.rb +22 -0
  41. data/spec/tag_spec.rb +136 -0
  42. data/test/instructions_test.rb +8 -0
  43. data/test/test_helper.rb +3 -0
  44. data/uninstall.rb +1 -0
  45. metadata +110 -0
@@ -0,0 +1,9 @@
1
+ .DS_Store
2
+ .rvmrc
3
+ .rspec
4
+ coverage
5
+ rdoc
6
+ doc
7
+ .yardoc
8
+ .bundle
9
+ pkg
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source "http://rubygems.org"
2
+
3
+ group :development do
4
+ gem "rspec", "~> 2.1.0"
5
+ gem "bundler", "~> 1.0.0"
6
+ gem "jeweler", "~> 1.5.2"
7
+ end
@@ -0,0 +1,26 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ diff-lcs (1.1.2)
5
+ git (1.2.5)
6
+ jeweler (1.5.2)
7
+ bundler (~> 1.0.0)
8
+ git (>= 1.2.5)
9
+ rake
10
+ rake (0.8.7)
11
+ rspec (2.1.0)
12
+ rspec-core (~> 2.1.0)
13
+ rspec-expectations (~> 2.1.0)
14
+ rspec-mocks (~> 2.1.0)
15
+ rspec-core (2.1.0)
16
+ rspec-expectations (2.1.0)
17
+ diff-lcs (~> 1.1.2)
18
+ rspec-mocks (2.1.0)
19
+
20
+ PLATFORMS
21
+ ruby
22
+
23
+ DEPENDENCIES
24
+ bundler (~> 1.0.0)
25
+ jeweler (~> 1.5.2)
26
+ rspec (~> 2.1.0)
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Scott Bellware
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.
@@ -0,0 +1,141 @@
1
+ h1. Instructions Plugin for Rails 3
2
+
3
+ h2. Introduction
4
+
5
+ Instructions is a form builder plugin for Ruby on Rails 3 that displays model errors and hints for form fields next to those fields.
6
+
7
+ The plugin renders instructions for fields when the fields don't have errors, and renders model errors for fields when they do have errors.
8
+
9
+ It wraps labels for fields with errors in a div that has a unique HTML ID and class names that provide easy control over styling, as well as selecting elements for web testing frameworks like Selenium and context-scraping tools.
10
+
11
+ A field with many error messages is represented as a comma-separated list. For Example: @Name can't be blank, is not a number@
12
+
13
+ The rendering of the field and the hints and error messages can be piped through procs for further formatting. These procs can be configured globally, as well as specified directly in markup on a field-by-field basis. Global configuration is done with a Rails initializer.
14
+
15
+ h2. How it Plugs in to Rails
16
+
17
+ During initialization, the plugin replaces the @ActionView::Base.field_error_proc@ with the plugin's code (found in @field_error.rb@) and mixes in the @instructions@ helper into the existing FormHelper. See @lib/instructions/core_ext/action_view.ext@ for details.
18
+
19
+ h2. Installation
20
+
21
+ The Instructions plugin is a Ruby Gem hosted at rubygems.org.
22
+
23
+ Install the gem using:
24
+
25
+ @gem install instructions@
26
+
27
+ h3. Configurable Formatting of Field Names, Error Messages, and Instructions
28
+
29
+ Field names and instructions and error message formatting can be controlled and customized using configurable interceptors.
30
+
31
+ There are three contexts where the plugin can produce different results. In all three cases, these results are configurable using Proc objects that can provided using a Rails initializer:
32
+
33
+ |*Symbol*|*Conditions*|
34
+ |:new|The initial rendering of a new action when a field's hint is rendered|
35
+ |:valid|The rendering for an attribute that has no validation errors after a post|
36
+ |:invalid|The rendering for an attribute that does have validation errors after a post|
37
+
38
+ In each case, the attribute name formatting and the instructions formatting can be intercepted and replaced.
39
+
40
+ The following code is an example of a Rails initializer (in the @config/initializers@ directory of your rails app).
41
+
42
+ NOTE: The filename of the initializer is @instructions.rb@, but the filename isn't significant. As long as the initializer is in the initializer directory.
43
+
44
+ bc.. upcase_proc = Proc.new { |text| text.upcase }
45
+ downcase_proc = Proc.new { |text| text.downcase }
46
+ empty_proc = Proc.new { "" }
47
+
48
+ Instructions.configure do |config|
49
+ config.format_attribute_name :new, &downcase_proc
50
+ config.format_instructions :new, &downcase_proc
51
+
52
+ config.format_attribute_name :valid, &downcase_proc
53
+ config.format_instructions :valid, &empty_proc
54
+
55
+ config.format_attribute_name :invalid, &downcase_proc
56
+ config.format_instructions :invalid, &downcase_proc
57
+
58
+ config.message_joiner { |messages| messages.join ', ' }
59
+ end
60
+
61
+ p. Note also that you can configure a proc that controls how a list of messages are joined.
62
+
63
+ p. The configuration code above:
64
+ * Converts the attribute name and the instructions to lower case during the initial get of an input form for a "new" action
65
+ * Converts the attribute name to lower case when rendering the instructions for a field for an attribute that has a value and has no validation errors. The instructions, in this case, are not rendered at all as they are replaced with an empty string.
66
+ * Converts the attribute name and error message to lower case when rendering the instructions for a field for an attribute that has a value and has validation errors.
67
+ * Joins all error messages using a comma followed by a space.
68
+
69
+ h3. A Note on Attribute Name Formatting
70
+
71
+ Attribute names are humanized using @model.class.human_attribute_name(attribute_name)@ before being passed to the tag helpers.
72
+
73
+ h2. Example
74
+
75
+ The example demonstrates the use of Field Instructions with a label and a textbox. The example model is a @Person@ and the model attribute is @Name@.
76
+
77
+ The name attribute has a @validates_presence_of@ constraint.
78
+
79
+ The ERB code for the label, text box, and instructions helper is:
80
+
81
+ bc.. <%= form.label(:name, "Name") %>
82
+ <%= form.text_field(:name) %>
83
+ <%= form.instructions(:name, "can't be blank") %>
84
+
85
+ p. The HTML rendering for this is:
86
+
87
+ bc.. <label for="project_name">Name</label>
88
+ <input id="project_name" name="project[name]" size="30" type="text" />
89
+ <div id="project_name_instructions" data-for="project_name" class="instructions">name can't be blank</div>
90
+
91
+ p. When the project_name input is posted with a blank value, the instructions div is replaced with an error message div that contains any model errors for the project.name attribute:
92
+
93
+ @<div id="project_name_error_message" data-for="project_name" class="instructions invalid">name can't be blank</div>@
94
+
95
+ h3. Example App
96
+
97
+ See "https://github.com/sbellware/instructions-example":https://github.com/sbellware/instructions-example for a (very) basic example of a Rails 3 app with Instructions.
98
+
99
+ h2. Using Symbols and i18n Errors to Render Hints
100
+
101
+ The instructions helper can be invoked with a symbol (eg: @:blank@) rather than passing instructions as text. This will use the error messages defined in Rails' i18n as hints. In order to use this, the default error message strings have to be changed to allow them to be used both as hints and as error messages.
102
+
103
+ See @extras/hint_errors.en.yml@ for examples. This file can be copied into your application's @config/locals@ if you prefer to use this technique.
104
+
105
+ h3. Some Examples
106
+
107
+ Render the i18n @:blank@ error message as a hint:
108
+
109
+ <%= form.instructions(:name, :blank) %>
110
+
111
+ Render the i18n @:blank@ and @:too_short@ error messages as a hint:
112
+
113
+ <%= form.instructions(:name, [:blank, {:too_short => 2}]) %>
114
+
115
+ Note that in the above example, the minimum length of the field must be specified since the @:too_short@ string requires an interpolation argument to be provided. The definition of the @:too_short@ string is @hint_errors.en.yml@ is:
116
+
117
+ @too_long: "can't be more than %{count} characters"@
118
+
119
+ h2. Rails Defaults Changed by the Plugin
120
+
121
+ The _default_ rails rendering of the HTML when the form is posted with a blank value in the name textbox is:
122
+
123
+ bc.. <div class="field_with_errors"><label for="project_name">Name</label></div>
124
+ <div class="field_with_errors"><input id="project_name" name="project[name]" size="30" type="text" value="" /></div>
125
+
126
+ p. Using the Instructions plugin, the rendering of the HTML when the form is posted with a blank value in the name textbox is:
127
+
128
+ bc.. <div class="field_with_errors_label"><label for="project_name">Name</label></div>
129
+ <div class="field_with_errors"><input id="project_name" name="thing[name]" size="30" type="text" value=""></div>
130
+ <div id="project_name_error_message" data-for="project_name" class="instructions invalid">name can't be blank</div>
131
+
132
+ h2. CSS Classes and HTML Markup
133
+
134
+ |*Name*|*Attribute Type*|*Notes*|
135
+ |instructions|Class|Applied to the div that contains the instructions|
136
+ |valid|Class|Applied to the div that contains the instructions for fields with valid data after the form is posted. This class is always rendered together with the "instructions" class. The order of precedence of the class renderings is @instructions@ first, followed by @valid@ second, allowing styling for valid instructions to override the general instructions styling. The HTML rendering for the class attribute for the span is: @class="instructions valid"@|
137
+ |invalid|Class|Applied to the div that contains error messages after the form is posted|
138
+ |{model}_{attribute}_instructions|Id|A unique ID applied to the div that contains the instructions for the field, where @{model}@ is the name of the ActiveModel object and @{attribute}@ is the name of the attribute that has the error. Example: @project_name_instructions@|
139
+ |{model}_{attribute}_error_message|Id|A unique ID applied to the div that contains the error messages for the field, where @{model}@ is the name of the ActiveModel object and @{attribute}@ is the name of the attribute that has the error. Example: @project_name_error_message@|
140
+ |field_with_errors|Class|Applied to a div that wraps the HTML element that represents the model field that has errors.|
141
+ |field_with_errors_label|Class|Applied to a div that wraps any HTML label for the field with errors.|
@@ -0,0 +1,40 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'rake'
11
+
12
+ require 'jeweler'
13
+ Jeweler::Tasks.new
14
+ task 'gemspec:generate' do
15
+ puts "NOTE: The gemspec is not generated by Jeweler. It's maintained in instructions.gemspec."
16
+ end
17
+ Jeweler::RubygemsDotOrgTasks.new
18
+
19
+ require 'rspec/core'
20
+ require 'rspec/core/rake_task'
21
+ RSpec::Core::RakeTask.new(:spec) do |spec|
22
+ spec.pattern = FileList['spec/**/*_spec.rb']
23
+ end
24
+
25
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
26
+ spec.pattern = 'spec/**/*_spec.rb'
27
+ spec.rcov = false
28
+ end
29
+
30
+ task :default => :spec
31
+
32
+ require 'rake/rdoctask'
33
+ Rake::RDocTask.new do |rdoc|
34
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
35
+
36
+ rdoc.rdoc_dir = 'rdoc'
37
+ rdoc.title = "instructions #{version}"
38
+ rdoc.rdoc_files.include('README*')
39
+ rdoc.rdoc_files.include('lib/**/*.rb')
40
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,27 @@
1
+ en:
2
+ errors:
3
+ # The default format to use in full error messages.
4
+ format: "%{attribute} %{message}"
5
+
6
+ # The values :model, :attribute and :value are always available for interpolation
7
+ # The value :count is available when applicable. Can be used for pluralization.
8
+ messages:
9
+ inclusion: "must be included in the list"
10
+ exclusion: "must not be reserved"
11
+ invalid: "can't be invalid"
12
+ confirmation: "must match the confirmation"
13
+ accepted: "must be accepted"
14
+ empty: "can't be empty"
15
+ blank: "can't be blank"
16
+ too_long: "can't be more than %{count} characters"
17
+ too_short: "can't be less than %{count} characters"
18
+ wrong_length: "must be %{count} characters"
19
+ not_a_number: "must be a number"
20
+ not_an_integer: "must be an integer"
21
+ greater_than: "must be greater than %{count}"
22
+ greater_than_or_equal_to: "must be greater than or equal to %{count}"
23
+ equal_to: "must be equal to %{count}"
24
+ less_than: "must be less than %{count}"
25
+ less_than_or_equal_to: "must be less than or equal to %{count}"
26
+ odd: "must be odd"
27
+ even: "must be even"
data/init.rb ADDED
File without changes
@@ -0,0 +1 @@
1
+ # Install hook code here
@@ -0,0 +1,19 @@
1
+ Gem::Specification.new do |s|
2
+ s.rubygems_version = %q{1.3.7}
3
+
4
+ s.name = %q{instructions}
5
+ s.description = %q{Rails 3 plugin that renders error messages and input hints next to their fields with semantic markup}
6
+ s.summary = %q{Semantic Error Message and Hint Markup for Rails 3 Form Fields}
7
+ s.version = "0.1.0"
8
+ s.date = %q{2011-01-24}
9
+ s.authors = ["Scott Bellware"]
10
+ s.email = %q{opensource@ampgt.com}
11
+ s.homepage = %q{http://github.com/sbellware/instructions}
12
+ s.licenses = ["MIT"]
13
+
14
+ s.files = `git ls-files`.split("\n")
15
+ s.test_files = `git ls-files -- {spec}/*`.split("\n")
16
+
17
+ s.require_path = "lib"
18
+ end
19
+
@@ -0,0 +1,89 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{instructions}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Scott Bellware"]
12
+ s.date = %q{2011-01-24}
13
+ s.description = %q{Rails 3 plugin that renders error messages and input hints next to their fields with semantic markup}
14
+ s.email = %q{opensource@ampgt.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.textile"
18
+ ]
19
+ s.files = [
20
+ ".gitignore",
21
+ "Gemfile",
22
+ "Gemfile.lock",
23
+ "LICENSE.txt",
24
+ "README.textile",
25
+ "Rakefile",
26
+ "VERSION",
27
+ "extras/hint_errors.en.yml",
28
+ "init.rb",
29
+ "install.rb",
30
+ "instructions.gemspec",
31
+ "lib/core_files.rb",
32
+ "lib/instructions.rb",
33
+ "lib/instructions/abstract.rb",
34
+ "lib/instructions/attribute_state.rb",
35
+ "lib/instructions/configuration.rb",
36
+ "lib/instructions/core_ext/action_view_ext.rb",
37
+ "lib/instructions/error_message.rb",
38
+ "lib/instructions/error_message_tag.rb",
39
+ "lib/instructions/field_error.rb",
40
+ "lib/instructions/field_instructions.rb",
41
+ "lib/instructions/form_helper.rb",
42
+ "lib/instructions/formatters.rb",
43
+ "lib/instructions/instructions_tag.rb",
44
+ "lib/instructions/tag.rb",
45
+ "lib/instructions/valid_instructions_tag.rb",
46
+ "rails/init.rb",
47
+ "spec/attribute_state_spec.rb",
48
+ "spec/configuration_spec.rb",
49
+ "spec/error_message_spec.rb",
50
+ "spec/error_message_tag_spec.rb",
51
+ "spec/fake_model.rb",
52
+ "spec/field_error_spec.rb",
53
+ "spec/form_helper_spec.rb",
54
+ "spec/formatters_spec.rb",
55
+ "spec/spec_helper.rb",
56
+ "spec/support/field_instructions_namespace.rb",
57
+ "spec/support/html_safe_string.rb",
58
+ "spec/support/rspec_patches.rb",
59
+ "spec/tag_spec.rb",
60
+ "test/instructions_test.rb",
61
+ "test/test_helper.rb",
62
+ "uninstall.rb"
63
+ ]
64
+ s.homepage = %q{http://github.com/sbellware/instructions}
65
+ s.licenses = ["MIT"]
66
+ s.require_paths = ["lib"]
67
+ s.rubygems_version = %q{1.3.7}
68
+ s.summary = %q{Semantic Error Message and Hint Markup for Rails 3 Form Fields}
69
+
70
+ if s.respond_to? :specification_version then
71
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
72
+ s.specification_version = 3
73
+
74
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
75
+ s.add_development_dependency(%q<rspec>, ["~> 2.1.0"])
76
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
77
+ s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
78
+ else
79
+ s.add_dependency(%q<rspec>, ["~> 2.1.0"])
80
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
81
+ s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
82
+ end
83
+ else
84
+ s.add_dependency(%q<rspec>, ["~> 2.1.0"])
85
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
86
+ s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
87
+ end
88
+ end
89
+
@@ -0,0 +1,4 @@
1
+ core_files = Dir[File.join(File.expand_path(File.dirname(__FILE__)), 'instructions', '*.rb')]
2
+ core_files.each do |file|
3
+ require file.gsub(/.rb/, '')
4
+ end
@@ -0,0 +1,6 @@
1
+ require File.join(File.expand_path(File.dirname(__FILE__)), 'core_files')
2
+
3
+ core_ext_files = Dir[File.join(File.expand_path(File.dirname(__FILE__)), '..', 'lib', 'instructions', 'core_ext', '*.rb')]
4
+ core_ext_files.each do |file|
5
+ require file.gsub(/.rb/, '')
6
+ end
@@ -0,0 +1,21 @@
1
+ # see: http://stackoverflow.com/questions/512466/how-to-implement-an-abstract-class-in-ruby
2
+
3
+ module Instructions
4
+ module Abstract
5
+ def self.included(base)
6
+ base.extend(ClassMethods)
7
+ end
8
+
9
+ module ClassMethods
10
+ def abstract_method(*args)
11
+ args.each do |name|
12
+ class_eval(<<-END, __FILE__, __LINE__)
13
+ def #{name}(*args)
14
+ raise NotImplementedError.new("#{self}\##{name} is abstract.")
15
+ end
16
+ END
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,25 @@
1
+ module Instructions
2
+ module AttributeState
3
+ def determine(method)
4
+ return :new if initial_get_of_new_form? method
5
+ return :valid if valid_attribute? method
6
+ return :invalid if not valid_attribute? method
7
+ end
8
+
9
+ def initial_get_of_new_form?(method)
10
+ @object.new_record? and blank_valid_attribute? method
11
+ end
12
+
13
+ def blank_valid_attribute?(method)
14
+ valid_attribute? method and blank_attribute? method
15
+ end
16
+
17
+ def valid_attribute?(method)
18
+ @object.errors[method].empty?
19
+ end
20
+
21
+ def blank_attribute?(method)
22
+ @object.send(method).nil?
23
+ end
24
+ end
25
+ end