message_block 1.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.rdoc ADDED
@@ -0,0 +1,9 @@
1
+ == [2010-10-13] 1.0 Major Updates
2
+
3
+ * Finally updated to be a Gem
4
+ * Internal refactoring
5
+ * Converted tests to RSpec
6
+ * New cucumber features
7
+ * Added support for Rails 3.0
8
+
9
+ == 0.1.0 Initial Beta Version
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Ben Hughes
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.rdoc ADDED
@@ -0,0 +1,126 @@
1
+ == Message Block
2
+
3
+ Implements the common view pattern by which a list of messages are shown at the top,
4
+ often a combination of flash messages and ActiveRecord validation issues on one or more models.
5
+ This allows for a nice, stylized block of messages at the top of the page with icons
6
+ indicating what type of message it is (error, confirmation, warning, etc.)
7
+
8
+ This view helper acts as a replacement for error_messages_for by taking error messages
9
+ from your models and combing them with flash messages (multiple types such as error, confirm, etc.)
10
+ and outputting them to your view. This plugin comes with an example stylesheet and images.
11
+
12
+ == Installation
13
+
14
+ Include the gem using bundler in your Gemfile:
15
+
16
+ gem "message_block"
17
+
18
+ Then run the rake task to install the static files:
19
+
20
+ rake message_block:install
21
+
22
+ == Usage
23
+
24
+ Once you install this, you should now have a set of images at public/images/message_block and
25
+ a basic stylesheet installed at public/stylesheets/message_block.css. First you'll want to either
26
+ reference this in your layout or copy the declarations to your main layout. Then you can use
27
+ the helper <tt><%= message_block %></tt> as described below:
28
+
29
+ The first argument specifies a hash options:
30
+
31
+ * <tt>:on</tt> - specifies one or many model names for which to check error messages.
32
+ * <tt>:model_error_type</tt> - specifies the message type to use for validation errors; defaults to 'error'
33
+ * <tt>:flash_types</tt> - specifies the keys to check in the flash hash. Messages will be grouped in ul
34
+ lists according to this type. Defaults to: %w(back confirm error info warn)
35
+ * <tt>:html</tt> - Specifies HTML options for the containing div
36
+ * <tt>:id</tt> - Specifies ID of the containing div; defaults to 'message_block'
37
+ * <tt>:class</tt> - Specifies class name of the containing div; defaults to nothing.
38
+ * <tt>:container</tt> - specifies which block-level element to contain the errors (defaults to :div).
39
+
40
+ === Example
41
+ Imagine you have a form for entering a user and a comment:
42
+
43
+ <%= message_block :on => [:user, :comment] %>
44
+
45
+ Imagine also you set these flash variables in the controller:
46
+ class CommentsController
47
+ def create
48
+ flash.now[:error] = "Error A"
49
+ flash.now[:confirm] = "Confirmation A" # Note you can use different types
50
+ flash.now[:warn] = ["Warn A", "Warn B"] # Can set to an array for multiple messages
51
+ end
52
+ end
53
+
54
+ And let's say that you want to show these messages but also show the validation issues
55
+ given that both user and comment fail ActiveRecord validation:
56
+
57
+ <div id="message_block" class="message_block">
58
+ <ul class="error">
59
+ <li>Error A</li>
60
+ <li>User first name is required.</li>
61
+ <li>Comment contents is required.</li>
62
+ </ul>
63
+ <ul class="confirm">
64
+ <li>Confirmation A</li>
65
+ </ul>
66
+ <ul class="warn">
67
+ <li>Warn A</li>
68
+ <li>Warn B</li>
69
+ </ul>
70
+ </div>
71
+
72
+ === Ajax & JavaScript Integration
73
+
74
+ Sometimes you'll want to use the message block pattern within JavaScript. Wouldn't it be nice to just populate
75
+ the message_block DOM tree based on a JavaScript object not unlike Rails flash?
76
+
77
+ Included in the plugin is a Prototype JS implementation to make this easier. Note that this file is *not* automatically
78
+ copied when the plugin is installed. Currently only a Prototype version is available (jQuery coming soon!)
79
+
80
+ assets/javascripts/message_block_prototype.js
81
+
82
+ Example Usage:
83
+
84
+ <div id="something">
85
+ <%= message_block :on => :job %>
86
+ </div>
87
+
88
+ <script type="text/javascript">
89
+ document.observe('dom:loaded', function() {
90
+ message_block = new MessageBlock('message_block'); // Default ID is message_block, being explicit
91
+
92
+ // Updates with two errors and one confirmation
93
+ message_block.update({
94
+ error: ['Error One', 'Error Two'],
95
+ confirm: ['Confirmation Message']
96
+ });
97
+
98
+ // Clears the message block
99
+ message_block.clear();
100
+
101
+ // Same as above...
102
+ message_block.update({});
103
+ });
104
+ </script>
105
+
106
+ This could be useful if you're interacting with stuff via Ajax, for example:
107
+
108
+ # In Controller
109
+ wants.json do
110
+ render :status => :unprocessable_entity, :json => { 'error' => @job.errors.full_messages }
111
+ end
112
+
113
+ # JavaScript Ajax
114
+ new Ajax.Request("/jobs", {
115
+ method: 'get',
116
+
117
+ onFailure: function(transport) {
118
+ this.message_block.update(transport.responseJSON);
119
+ },
120
+
121
+ onSuccess: function(transport) {
122
+ this.message_block.clear();
123
+
124
+ // Do something...
125
+ }
126
+ });
data/Rakefile ADDED
@@ -0,0 +1,23 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rake/rdoctask'
4
+ require 'spec/rake/spectask'
5
+
6
+ desc "Run specs"
7
+ Spec::Rake::SpecTask.new do |t|
8
+ t.spec_files = Rake::FileList["spec/**/*_spec.rb"]
9
+ t.spec_opts = ["-c"]
10
+ end
11
+
12
+ task :default => :spec
13
+
14
+ desc "Generate documentation for the plugin."
15
+ Rake::RDocTask.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = "rdoc"
17
+ rdoc.title = "message_block"
18
+ rdoc.options << "--line-numbers" << "--inline-source"
19
+ rdoc.rdoc_files.include('README')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+ Dir["#{File.dirname(__FILE__)}/lib/tasks/*.rake"].sort.each { |ext| load ext }
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require "message_block"
@@ -0,0 +1,54 @@
1
+ module MessageBlock
2
+ module Helpers
3
+
4
+ def message_block(options = {})
5
+ options.assert_valid_keys(:on, :model_error_type, :flash_types, :html, :id, :class, :container)
6
+
7
+ options[:model_error_type] ||= :error
8
+ options[:flash_types] ||= [:notice, :back, :confirm, :error, :info, :warn].sort_by(&:to_s)
9
+ options[:on] ||= controller.controller_name.split('/').last.gsub(/\_controller$/, '').singularize.to_sym
10
+ options[:html] ||= {:id => "message_block", :class => "message_block"}
11
+ options[:html][:id] = options[:id] if options[:id]
12
+ options[:html][:class] = options[:class] if options[:class]
13
+ options[:container] = :div if options[:container].nil?
14
+
15
+ flash_messages = {}
16
+
17
+ options[:flash_types].each do |type|
18
+ entries = flash[type.to_sym]
19
+ next if entries.nil?
20
+ entries = [entries] unless entries.is_a?(Array)
21
+
22
+ flash_messages[type.to_sym] ||= []
23
+ flash_messages[type.to_sym] += entries
24
+ end
25
+
26
+ options[:on] = [options[:on]] unless options[:on].is_a?(Array)
27
+
28
+ options[:on] = [options[:on]] unless options[:on].is_a?(Array)
29
+ model_objects = options[:on].map do |model_object|
30
+ if model_object.instance_of?(String) or model_object.instance_of?(Symbol)
31
+ instance_variable_get("@#{model_object}")
32
+ else
33
+ model_object
34
+ end
35
+ end.select {|m| !m.nil? }
36
+
37
+ model_errors = model_objects.inject([]) {|b, m| b += m.errors.full_messages }
38
+
39
+ flash_messages[options[:model_error_type].to_sym] ||= []
40
+ flash_messages[options[:model_error_type].to_sym] += model_errors
41
+
42
+ contents = flash_messages.keys.sort_by(&:to_s).select {|type| !flash_messages[type.to_sym].empty? }.map do |type|
43
+ "<ul class=\"#{type}\">" + flash_messages[type.to_sym].map {|message| "<li>#{message}</li>" }.join + "</ul>"
44
+ end.join
45
+
46
+ if options[:container]
47
+ content_tag(options[:container], contents, options[:html], false)
48
+ else
49
+ contents
50
+ end
51
+ end
52
+
53
+ end
54
+ end
@@ -0,0 +1,21 @@
1
+ module MessageBlock
2
+ if defined?(Rails::Railtie)
3
+ class Railtie < Rails::Railtie
4
+ initializer "message_block.insert_helpers" do
5
+ ActiveSupport.on_load(:action_view) do
6
+ MessageBlock::Railtie.insert
7
+ end
8
+ end
9
+
10
+ rake_tasks do
11
+ load "tasks/message_block.rake"
12
+ end
13
+ end
14
+ end
15
+
16
+ class Railtie
17
+ def self.insert
18
+ ActionView::Base.send(:include, MessageBlock::Helpers)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,4 @@
1
+ require "rubygems"
2
+ require "active_support"
3
+ require "message_block/helpers"
4
+ require "message_block/railtie"
@@ -0,0 +1,36 @@
1
+ namespace :message_block do
2
+ desc "Installs static files for the message_block gem"
3
+ task :install do
4
+ puts "Copying assets..."
5
+
6
+ images_source = File.join(File.dirname(__FILE__), "..", "..", "assets", "images")
7
+ stylesheets_source = File.join(File.dirname(__FILE__), "..", "..", "assets", "stylesheets", "message_block.css")
8
+ javascripts_source = File.join(File.dirname(__FILE__), "..", "..", "assets", "javascripts", "message_block.js")
9
+
10
+ images_target = Rails.root.join("public", "images", "message_block")
11
+ stylesheets_target = Rails.root.join("public", "stylesheets", "message_block.css")
12
+ javascripts_target = Rails.root.join("public", "javascripts", "message_block.js")
13
+
14
+ unless File.exists?(images_target)
15
+ FileUtils.cp_r(images_source, images_target)
16
+ puts " Copied Images"
17
+ else
18
+ puts " Images appear to already be installed, not copying."
19
+ end
20
+
21
+ unless File.exists?(stylesheets_target)
22
+ FileUtils.cp(stylesheets_source, stylesheets_target)
23
+ puts " Copied Stylesheet"
24
+ else
25
+ puts " Stylesheet appears to already be installed, not copying"
26
+ end
27
+
28
+ unless File.exists?(javascripts_target)
29
+ FileUtils.cp(javascripts_source, javascripts_target)
30
+ puts " Copied Javascript"
31
+ else
32
+ puts " Javascript appears to already be installed, not copying"
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,124 @@
1
+ require "spec_helper"
2
+
3
+ describe MessageBlock::Helpers do
4
+ include ActionView::Helpers::TagHelper
5
+ include MessageBlock::Helpers
6
+
7
+ before do
8
+ setup_post
9
+ setup_user
10
+ end
11
+
12
+ it "should not accept invalid options" do
13
+ lambda { message_block :invalid => "option" }.should raise_error(ArgumentError)
14
+ end
15
+
16
+ it "should accept valid options" do
17
+ lambda {
18
+ message_block :on => :post,
19
+ :model_error_type => "fail",
20
+ :flash_types => %w(error warn),
21
+ :html => {:style => 'height: 10em'},
22
+ :id => "block",
23
+ :class => "messages"
24
+ }.should_not raise_error
25
+ end
26
+
27
+ it "should show nothing with no errors" do
28
+ message_block.should == %(<div class="message_block" id="message_block"></div>)
29
+ end
30
+
31
+ it "should automatically find post errorts with posts controller" do
32
+ @controller = posts_controller
33
+ output = message_block
34
+ output.should == %(<div class="message_block" id="message_block"><ul class="error"><li>Author name can't be empty</li></ul></div>)
35
+ end
36
+
37
+ it "should give no error for post" do
38
+ output = message_block(:on => :post)
39
+ output.should == %(<div class="message_block" id="message_block"><ul class="error"><li>Author name can't be empty</li></ul></div>)
40
+ end
41
+
42
+ it "should give error for user" do
43
+ output = message_block(:on => :user)
44
+ output.should == %(<div class="message_block" id="message_block"><ul class="error"><li>User email can't be empty</li></ul></div>)
45
+ end
46
+
47
+ it "should give errors for both post and user" do
48
+ output = message_block(:on => [:post, :user])
49
+ output.should == %(<div class="message_block" id="message_block"><ul class="error"><li>Author name can't be empty</li><li>User email can't be empty</li></ul></div>)
50
+ end
51
+
52
+ it "should give errors for both post and user in the correct order" do
53
+ output = message_block(:on => [:user, :post])
54
+ output.should == %(<div class="message_block" id="message_block"><ul class="error"><li>User email can't be empty</li><li>Author name can't be empty</li></ul></div>)
55
+ end
56
+
57
+ it "should give error for user given direct instance variable" do
58
+ output = message_block(:on => @user)
59
+ output.should == %(<div class="message_block" id="message_block"><ul class="error"><li>User email can't be empty</li></ul></div>)
60
+ end
61
+
62
+ it "should respect model error type" do
63
+ output = message_block(:on => :user, :model_error_type => "fail")
64
+ output.should == %(<div class="message_block" id="message_block"><ul class="fail"><li>User email can't be empty</li></ul></div>)
65
+ end
66
+
67
+ it "should be able to specify id for containing div" do
68
+ output = message_block(:id => "messages")
69
+ output.should == %(<div class="message_block" id="messages"></div>)
70
+ end
71
+
72
+ it "should be able to specify class for containing div" do
73
+ output = message_block(:class => "messages")
74
+ output.should == %(<div class="messages" id="message_block"></div>)
75
+ end
76
+
77
+ it "should be able to specify html options for containing div" do
78
+ output = message_block(:html => {:id => "block", :class => "messages"})
79
+ output.should == %(<div class="messages" id="block"></div>)
80
+ end
81
+
82
+ it "should be able to specify container option as false" do
83
+ output = message_block(:on => :post, :container => false)
84
+ output.should == %(<ul class="error"><li>Author name can't be empty</li></ul>)
85
+ end
86
+
87
+ it "should be able to specify container option" do
88
+ output = message_block(:on => :post, :container => :fieldset)
89
+ output.should == %(<fieldset class="message_block" id="message_block"><ul class="error"><li>Author name can't be empty</li></ul></fieldset>)
90
+ end
91
+
92
+ it "should be able to see flash error string" do
93
+ flash[:error] = "Error A"
94
+ output = message_block
95
+ output.should == %(<div class="message_block" id="message_block"><ul class="error"><li>Error A</li></ul></div>)
96
+ end
97
+
98
+ it "should be able to see flash error array" do
99
+ flash[:error] = ["Error A", "Error B"]
100
+ output = message_block
101
+ output.should == %(<div class="message_block" id="message_block"><ul class="error"><li>Error A</li><li>Error B</li></ul></div>)
102
+ end
103
+
104
+ it "should be able to see default flash types" do
105
+ default_types = [:notice, :back, :confirm, :error, :info, :warn].sort_by(&:to_s)
106
+ default_types.each do |type|
107
+ flash[type] = type.to_s
108
+ end
109
+
110
+ expected_contents = default_types.map do |type|
111
+ content_tag(:ul, content_tag(:li, type.to_s), :class => type.to_s)
112
+ end.join
113
+
114
+ output = message_block
115
+ output.should == %(<div class="message_block" id="message_block">#{expected_contents}</div>)
116
+ end
117
+
118
+ it "should be able to see flash error alongside model error" do
119
+ flash[:error] = "Error A"
120
+ output = message_block(:on => :post)
121
+ output.should == %(<div class="message_block" id="message_block"><ul class="error"><li>Error A</li><li>Author name can't be empty</li></ul></div>)
122
+ end
123
+
124
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1,5 @@
1
+ --colour
2
+ --format nested
3
+ --loadby mtime
4
+ --reverse
5
+ --backtrace
@@ -0,0 +1,74 @@
1
+ require "rubygems"
2
+ require "spec"
3
+ require "active_support"
4
+ require "action_view"
5
+ require "message_block"
6
+
7
+ require File.dirname(__FILE__) + '/../init'
8
+
9
+ Spec::Runner.configure do |config|
10
+ config.mock_with :mocha
11
+ end
12
+
13
+ # Borrowed model stubs from Rails active_record_helper_test.rb
14
+ # TODO: Re-implement using mocha
15
+ Post = Struct.new("Post", :title, :author_name)
16
+ User = Struct.new("User", :email)
17
+
18
+ def setup_post
19
+ @post = Post.new
20
+ def @post.errors
21
+ Class.new do
22
+ def on(field)
23
+ case field.to_s
24
+ when "author_name"
25
+ "can't be empty"
26
+ when "body"
27
+ true
28
+ else
29
+ false
30
+ end
31
+ end
32
+ def empty?() false end
33
+ def count() 1 end
34
+ def full_messages() [ "Author name can't be empty" ] end
35
+ end.new
36
+ end
37
+
38
+ @post.title = "Hello World"
39
+ @post.author_name = ""
40
+ end
41
+
42
+ def setup_user
43
+ @user = User.new
44
+ def @user.errors
45
+ Class.new {
46
+ def on(field) field == "email" end
47
+ def empty?() false end
48
+ def count() 1 end
49
+ def full_messages() [ "User email can't be empty" ] end
50
+ }.new
51
+ end
52
+
53
+ @user.email = ""
54
+ end
55
+
56
+ def controller
57
+ @controller ||= Class.new {
58
+ def controller_name
59
+ "widgets_controller"
60
+ end
61
+ }.new
62
+ end
63
+
64
+ def posts_controller
65
+ Class.new {
66
+ def controller_name
67
+ "posts_controller"
68
+ end
69
+ }.new
70
+ end
71
+
72
+ def flash
73
+ @flash ||= {}
74
+ end
metadata ADDED
@@ -0,0 +1,110 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: message_block
3
+ version: !ruby/object:Gem::Version
4
+ hash: 15
5
+ prerelease: false
6
+ segments:
7
+ - 1
8
+ - 0
9
+ version: "1.0"
10
+ platform: ruby
11
+ authors:
12
+ - Ben Hughes
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-10-14 00:00:00 +08:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: activesupport
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 5
29
+ segments:
30
+ - 2
31
+ - 2
32
+ - 1
33
+ version: 2.2.1
34
+ type: :runtime
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ name: actionpack
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ hash: 5
45
+ segments:
46
+ - 2
47
+ - 2
48
+ - 1
49
+ version: 2.2.1
50
+ type: :runtime
51
+ version_requirements: *id002
52
+ description: Implements the common view pattern by which a list of messages are shown at the top, often a combination of flash messages and ActiveRecord validation issues on one or more models.
53
+ email: ben@railsgarden.com
54
+ executables: []
55
+
56
+ extensions: []
57
+
58
+ extra_rdoc_files: []
59
+
60
+ files:
61
+ - lib/message_block/helpers.rb
62
+ - lib/message_block/railtie.rb
63
+ - lib/message_block.rb
64
+ - lib/tasks/message_block.rake
65
+ - spec/message_block/helpers_spec.rb
66
+ - spec/spec.opts
67
+ - spec/spec_helper.rb
68
+ - CHANGELOG.rdoc
69
+ - LICENSE
70
+ - Rakefile
71
+ - README.rdoc
72
+ - init.rb
73
+ has_rdoc: true
74
+ homepage: http://github.com/rubiety/message_block
75
+ licenses: []
76
+
77
+ post_install_message:
78
+ rdoc_options: []
79
+
80
+ require_paths:
81
+ - lib
82
+ required_ruby_version: !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ hash: 3
88
+ segments:
89
+ - 0
90
+ version: "0"
91
+ required_rubygems_version: !ruby/object:Gem::Requirement
92
+ none: false
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ hash: 19
97
+ segments:
98
+ - 1
99
+ - 3
100
+ - 4
101
+ version: 1.3.4
102
+ requirements: []
103
+
104
+ rubyforge_project: message_block
105
+ rubygems_version: 1.3.7
106
+ signing_key:
107
+ specification_version: 3
108
+ summary: Flash message and error_messages_for handling with a common interface.
109
+ test_files: []
110
+