instructions 0.1.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.
- data/.gitignore +9 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +26 -0
- data/LICENSE.txt +20 -0
- data/README.textile +141 -0
- data/Rakefile +40 -0
- data/VERSION +1 -0
- data/extras/hint_errors.en.yml +27 -0
- data/init.rb +0 -0
- data/install.rb +1 -0
- data/instructions.gemspec +19 -0
- data/instructions.gemspec.generated +89 -0
- data/lib/core_files.rb +4 -0
- data/lib/instructions.rb +6 -0
- data/lib/instructions/abstract.rb +21 -0
- data/lib/instructions/attribute_state.rb +25 -0
- data/lib/instructions/configuration.rb +49 -0
- data/lib/instructions/core_ext/action_view_ext.rb +9 -0
- data/lib/instructions/error_message.rb +9 -0
- data/lib/instructions/error_message_tag.rb +21 -0
- data/lib/instructions/field_error.rb +33 -0
- data/lib/instructions/field_instructions.rb +6 -0
- data/lib/instructions/form_helper.rb +65 -0
- data/lib/instructions/formatters.rb +10 -0
- data/lib/instructions/instructions_tag.rb +15 -0
- data/lib/instructions/tag.rb +71 -0
- data/lib/instructions/valid_instructions_tag.rb +9 -0
- data/rails/init.rb +0 -0
- data/spec/attribute_state_spec.rb +62 -0
- data/spec/configuration_spec.rb +74 -0
- data/spec/error_message_spec.rb +42 -0
- data/spec/error_message_tag_spec.rb +30 -0
- data/spec/fake_model.rb +17 -0
- data/spec/field_error_spec.rb +85 -0
- data/spec/form_helper_spec.rb +123 -0
- data/spec/formatters_spec.rb +76 -0
- data/spec/spec_helper.rb +7 -0
- data/spec/support/field_instructions_namespace.rb +2 -0
- data/spec/support/html_safe_string.rb +7 -0
- data/spec/support/rspec_patches.rb +22 -0
- data/spec/tag_spec.rb +136 -0
- data/test/instructions_test.rb +8 -0
- data/test/test_helper.rb +3 -0
- data/uninstall.rb +1 -0
- metadata +110 -0
@@ -0,0 +1,49 @@
|
|
1
|
+
module Instructions
|
2
|
+
class Configuration
|
3
|
+
attr_reader :attribute_name_formatters
|
4
|
+
attr_reader :instructions_formatters
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@attribute_name_formatters = {}
|
8
|
+
@instructions_formatters = {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.default_message_joiner
|
12
|
+
Proc.new { |messages| messages.join ', ' }
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.instance
|
16
|
+
@@instance ||= new
|
17
|
+
end
|
18
|
+
|
19
|
+
def format_attribute_name(attribute_state, &block)
|
20
|
+
@attribute_name_formatters[attribute_state] = block
|
21
|
+
end
|
22
|
+
|
23
|
+
def format_instructions(attribute_state, &block)
|
24
|
+
@instructions_formatters[attribute_state] = block
|
25
|
+
end
|
26
|
+
|
27
|
+
def formatters(attribute_state)
|
28
|
+
{:attribute_name => @attribute_name_formatters[attribute_state],
|
29
|
+
:instructions => @instructions_formatters[attribute_state]}
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.formatters(attribute_state)
|
33
|
+
instance.formatters attribute_state
|
34
|
+
end
|
35
|
+
|
36
|
+
def message_joiner(&block)
|
37
|
+
if block_given?
|
38
|
+
@message_joiner = block
|
39
|
+
else
|
40
|
+
@message_joiner ||= self.class.default_message_joiner
|
41
|
+
end
|
42
|
+
@message_joiner
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.message_joiner
|
46
|
+
instance.message_joiner
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require File.join(File.expand_path(File.dirname(__FILE__)), 'tag')
|
2
|
+
|
3
|
+
module Instructions
|
4
|
+
class ErrorMessageTag
|
5
|
+
include Tag
|
6
|
+
extend ErrorMessage
|
7
|
+
|
8
|
+
def self.build(method, model, model_name, formatters={}, *options)
|
9
|
+
error_message = compose_error_message(method, model)
|
10
|
+
new method, model, model_name, error_message, formatters, *options
|
11
|
+
end
|
12
|
+
|
13
|
+
def descriptor
|
14
|
+
"error_message"
|
15
|
+
end
|
16
|
+
|
17
|
+
def css_class
|
18
|
+
"instructions invalid"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Instructions
|
2
|
+
class FieldError
|
3
|
+
def self.field_error_proc
|
4
|
+
Proc.new do |html_tag, error_data|
|
5
|
+
render_field_with_error(html_tag, error_data).html_safe
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.render_field_with_error(html_tag, error_data)
|
10
|
+
if html_tag =~ /^<label/
|
11
|
+
return wrap_label_tag_with_field_with_errors_label_div(html_tag)
|
12
|
+
end
|
13
|
+
|
14
|
+
model = error_data.object
|
15
|
+
|
16
|
+
model_name = error_data.object_name
|
17
|
+
attribute_name = error_data.method_name
|
18
|
+
human_attribute_name = model.class.human_attribute_name(attribute_name)
|
19
|
+
attribute_errors = model.errors[attribute_name.to_sym]
|
20
|
+
|
21
|
+
field_with_errors_div = wrap_html_tag_with_field_with_errors_div(html_tag)
|
22
|
+
"#{field_with_errors_div}"
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.wrap_html_tag_with_field_with_errors_div(html_tag)
|
26
|
+
"<div class=\"field_with_errors\">#{html_tag}</div>"
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.wrap_label_tag_with_field_with_errors_label_div(div_tag)
|
30
|
+
"<div class=\"field_with_errors_label\">#{div_tag}</div>"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require File.join(File.expand_path(File.dirname(__FILE__)), 'attribute_state')
|
2
|
+
require File.join(File.expand_path(File.dirname(__FILE__)), 'formatters')
|
3
|
+
|
4
|
+
module Instructions
|
5
|
+
module FormHelper
|
6
|
+
include AttributeState
|
7
|
+
include Formatters
|
8
|
+
|
9
|
+
def instructions(method, message=nil, options={})
|
10
|
+
text = message_text method, message
|
11
|
+
renderer(method, text, options).render
|
12
|
+
end
|
13
|
+
|
14
|
+
def message_text(method, message)
|
15
|
+
return message if message.is_a? String
|
16
|
+
return active_model_message(method, message) if [Symbol, Hash].include? message.class
|
17
|
+
return active_model_messages(method, message) if message.is_a? Array
|
18
|
+
end
|
19
|
+
|
20
|
+
def active_model_messages(method, message)
|
21
|
+
messages = []
|
22
|
+
generator = generator(@object)
|
23
|
+
message.each do |m|
|
24
|
+
messages << active_model_message(method, m, generator)
|
25
|
+
end
|
26
|
+
Configuration.message_joiner.call messages
|
27
|
+
end
|
28
|
+
|
29
|
+
def active_model_message(method, message, generator=nil)
|
30
|
+
generator ||= generator(@object)
|
31
|
+
if message.is_a? Hash
|
32
|
+
return generator.generate_message(method, message.keys[0], :count => message.values[0])
|
33
|
+
else
|
34
|
+
return generator.generate_message(method, message)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def generator(object)
|
39
|
+
ActiveModel::Errors.new object
|
40
|
+
end
|
41
|
+
|
42
|
+
def test
|
43
|
+
def method_missing(method_name, *args)
|
44
|
+
"<div class=\"method_missing\" style=\"font-weight: bold; color: red;\">No helper found to respond to \"#{method_name}\"</div>"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def renderer(method, text, options={})
|
49
|
+
attribute_state = determine method
|
50
|
+
formatters = merge attribute_state, options
|
51
|
+
|
52
|
+
if attribute_state == :new
|
53
|
+
return InstructionsTag.new method, @object, @object_name, text, formatters
|
54
|
+
end
|
55
|
+
|
56
|
+
if attribute_state == :valid
|
57
|
+
return ValidInstructionsTag.new method, @object, @object_name, text, formatters
|
58
|
+
end
|
59
|
+
|
60
|
+
if attribute_state == :invalid
|
61
|
+
return ErrorMessageTag.build method, @object, @object_name, formatters
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module Instructions
|
2
|
+
module Formatters
|
3
|
+
def merge(attribute_state, options)
|
4
|
+
configured_formatters = Configuration.formatters(attribute_state)
|
5
|
+
tag_formatters = (options[:formatters] || {}) [attribute_state] || {}
|
6
|
+
|
7
|
+
configured_formatters.merge tag_formatters
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require File.join(File.expand_path(File.dirname(__FILE__)), 'abstract')
|
2
|
+
|
3
|
+
module Instructions
|
4
|
+
module Tag
|
5
|
+
include Abstract
|
6
|
+
abstract_method :descriptor
|
7
|
+
abstract_method :css_class
|
8
|
+
|
9
|
+
attr_reader :formatters
|
10
|
+
|
11
|
+
def initialize(method, model, model_name, instructions, formatters={})
|
12
|
+
@method = method
|
13
|
+
@model = model
|
14
|
+
@instructions = instructions
|
15
|
+
@model_name = model_name
|
16
|
+
@formatters = formatters
|
17
|
+
end
|
18
|
+
|
19
|
+
def render
|
20
|
+
"#{open_tag}#{instructions}#{close_tag}".html_safe
|
21
|
+
end
|
22
|
+
|
23
|
+
def instructions
|
24
|
+
return "#{formatted_attribute_name}" if @instructions.nil? or @instructions.empty?
|
25
|
+
"#{formatted_attribute_name} #{formatted_instructions}".strip
|
26
|
+
end
|
27
|
+
|
28
|
+
def formatted_attribute_name
|
29
|
+
return attribute_name_formatter.call(attribute_name) if attribute_name_formatter
|
30
|
+
attribute_name
|
31
|
+
end
|
32
|
+
|
33
|
+
def attribute_name
|
34
|
+
@model.class.human_attribute_name(@method)
|
35
|
+
end
|
36
|
+
|
37
|
+
def attribute_name_formatter
|
38
|
+
@formatters[:attribute_name]
|
39
|
+
end
|
40
|
+
|
41
|
+
def formatted_instructions
|
42
|
+
return instructions_formatter.call(@instructions) if instructions_formatter
|
43
|
+
@instructions
|
44
|
+
end
|
45
|
+
|
46
|
+
def instructions_formatter
|
47
|
+
@formatters[:instructions]
|
48
|
+
end
|
49
|
+
|
50
|
+
def identifier
|
51
|
+
"#{field_identifier}_#{descriptor}"
|
52
|
+
end
|
53
|
+
|
54
|
+
def field_identifier
|
55
|
+
"#{@model_name}_#{@method}"
|
56
|
+
end
|
57
|
+
|
58
|
+
def formatted_css_class
|
59
|
+
return "#{css_class} empty" if instructions.empty?
|
60
|
+
"#{css_class}"
|
61
|
+
end
|
62
|
+
|
63
|
+
def open_tag
|
64
|
+
"<div id=\"#{identifier}\" data-for=\"#{field_identifier}\" class=\"#{formatted_css_class}\">"
|
65
|
+
end
|
66
|
+
|
67
|
+
def close_tag
|
68
|
+
"</div>"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
data/rails/init.rb
ADDED
File without changes
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe AttributeState do
|
4
|
+
before do
|
5
|
+
@model = Object.new
|
6
|
+
|
7
|
+
@model_errors = []
|
8
|
+
@model.stub :errors => @model_errors
|
9
|
+
|
10
|
+
@attribute_state_object = Object.new.extend AttributeState
|
11
|
+
model = @model
|
12
|
+
@attribute_state_object.instance_eval { @object = model }
|
13
|
+
|
14
|
+
setup_model_errors
|
15
|
+
setup_model_state
|
16
|
+
|
17
|
+
@state = @attribute_state_object.determine :some_attribute
|
18
|
+
end
|
19
|
+
|
20
|
+
context "unsaved model" do
|
21
|
+
def setup_model_state
|
22
|
+
@model.stub :new_record? => true
|
23
|
+
end
|
24
|
+
|
25
|
+
context "attribute without a value for an unsaved model" do
|
26
|
+
def setup_model_errors
|
27
|
+
@model.stub :some_attribute => nil
|
28
|
+
@model_errors.stub :[] => []
|
29
|
+
end
|
30
|
+
|
31
|
+
observe "is: new" do
|
32
|
+
@state.should == :new
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context "saved model" do
|
38
|
+
def setup_model_state
|
39
|
+
@model.stub :new_record? => false
|
40
|
+
end
|
41
|
+
|
42
|
+
context "attribute does not have errors" do
|
43
|
+
def setup_model_errors
|
44
|
+
@model_errors.stub :[] => []
|
45
|
+
end
|
46
|
+
|
47
|
+
observe "is: valid" do
|
48
|
+
@state.should == :valid
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context "attribute has errors" do
|
53
|
+
def setup_model_errors
|
54
|
+
@model_errors.stub :[] => ["some error message"]
|
55
|
+
end
|
56
|
+
|
57
|
+
observe "is: invalid" do
|
58
|
+
@state.should == :invalid
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
context Instructions, "configuring" do
|
4
|
+
before do
|
5
|
+
Configuration.stub :instance => @configuration
|
6
|
+
|
7
|
+
@config_block_arg = Object.new
|
8
|
+
Instructions.configure do |config|
|
9
|
+
@config_block_arg = config
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
observe "invokes the configuration block" do
|
14
|
+
@config_block_arg.should == @configuration
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe Configuration do
|
19
|
+
before do
|
20
|
+
@proc = Proc.new {}
|
21
|
+
end
|
22
|
+
|
23
|
+
context "configuring an attribute name formatter" do
|
24
|
+
before do
|
25
|
+
subject.format_attribute_name :some_attribute_state, &@proc
|
26
|
+
end
|
27
|
+
|
28
|
+
observe "adds attribute name formatter block to the configured formatters" do
|
29
|
+
subject.attribute_name_formatters[:some_attribute_state].should == @proc
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context "configuring an instruction formatter" do
|
34
|
+
before do
|
35
|
+
subject.format_instructions :some_attribute_state, &@proc
|
36
|
+
end
|
37
|
+
|
38
|
+
observe "adds attribute name formatter block to the configured formatters" do
|
39
|
+
subject.instructions_formatters[:some_attribute_state].should == @proc
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context "getting formatters for a specific kind" do
|
44
|
+
before do
|
45
|
+
subject.format_attribute_name :some_attribute_state, &@proc
|
46
|
+
subject.format_instructions :some_attribute_state, &@proc
|
47
|
+
end
|
48
|
+
|
49
|
+
observe "gets the attribute name formatter registered for the kind" do
|
50
|
+
subject.formatters(:attribute_name).should_not be_nil
|
51
|
+
end
|
52
|
+
|
53
|
+
observe "gets the instructions formatter registered for the kind" do
|
54
|
+
subject.formatters(:instructions).should_not be_nil
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context "short hand for retrieving formatters (using class methods)" do
|
59
|
+
before do
|
60
|
+
Configuration.stub :instance => subject
|
61
|
+
subject.stub :formatters => nil
|
62
|
+
end
|
63
|
+
|
64
|
+
because { Configuration.formatters :some_attribute_state }
|
65
|
+
|
66
|
+
observe "uses the configuration singleton" do
|
67
|
+
Configuration.should_have_received :instance
|
68
|
+
end
|
69
|
+
|
70
|
+
observe "uses gets the formatters from the configuration singleton" do
|
71
|
+
subject.should_have_received :formatters
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|