checkboxes 0.0.1
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 +7 -0
- data/.infinity_test +3 -0
- data/.rspec +3 -0
- data/Gemfile +3 -0
- data/README.textile +58 -0
- data/Rakefile +10 -0
- data/checkboxes.gemspec +27 -0
- data/lib/checkboxes.rb +13 -0
- data/lib/checkboxes/core.rb +48 -0
- data/lib/checkboxes/helpers.rb +25 -0
- data/lib/checkboxes/methods.rb +43 -0
- data/lib/checkboxes/version.rb +3 -0
- data/spec/checkboxes/helpers_spec.rb +34 -0
- data/spec/checkboxes/integration_spec.rb +35 -0
- data/spec/checkboxes/methods_spec.rb +49 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/assets/javascripts/welcome.js +2 -0
- data/spec/dummy/app/assets/stylesheets/welcome.css +4 -0
- data/spec/dummy/app/controllers/application_controller.rb +3 -0
- data/spec/dummy/app/controllers/users_controller.rb +41 -0
- data/spec/dummy/app/controllers/welcome_controller.rb +5 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/helpers/welcome_helper.rb +2 -0
- data/spec/dummy/app/models/group.rb +7 -0
- data/spec/dummy/app/models/user.rb +5 -0
- data/spec/dummy/app/models/user_group.rb +4 -0
- data/spec/dummy/app/views/layouts/application.html.erb +12 -0
- data/spec/dummy/app/views/users/_form.html.erb +10 -0
- data/spec/dummy/app/views/users/edit.html.erb +6 -0
- data/spec/dummy/app/views/users/index.html.erb +13 -0
- data/spec/dummy/app/views/users/new.html.erb +3 -0
- data/spec/dummy/app/views/users/show.html.erb +20 -0
- data/spec/dummy/app/views/welcome/index.html.erb +3 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +15 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/database.yml +22 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +26 -0
- data/spec/dummy/config/environments/production.rb +49 -0
- data/spec/dummy/config/environments/test.rb +35 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/inflections.rb +10 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/routes.rb +4 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +26 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/public/javascripts/application.js +2 -0
- data/spec/dummy/public/javascripts/controls.js +965 -0
- data/spec/dummy/public/javascripts/dragdrop.js +974 -0
- data/spec/dummy/public/javascripts/effects.js +1123 -0
- data/spec/dummy/public/javascripts/prototype.js +6001 -0
- data/spec/dummy/public/javascripts/rails.js +191 -0
- data/spec/dummy/public/stylesheets/.gitkeep +0 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/factories.rb +7 -0
- data/spec/schema.rb +16 -0
- data/spec/spec_helper.rb +17 -0
- metadata +176 -0
data/.gitignore
ADDED
data/.infinity_test
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/README.textile
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
h2. Introduction
|
2
|
+
|
3
|
+
This gem will help you to handle _has_many :through_ associations when you need to add/remove these ones with checkboxes. If you think about this type of situation you do the same steps for each association you have to handle with checkboxes. I exctracted this gem from a project where I had to handle about ten associations with checkboxes.
|
4
|
+
|
5
|
+
h2. How to use it
|
6
|
+
|
7
|
+
The best way to describe throughcheckboxes is to show some code:
|
8
|
+
|
9
|
+
In your model:
|
10
|
+
|
11
|
+
<pre>
|
12
|
+
class User < ActiveRecord::Base
|
13
|
+
has_many :groups,:through=>:user_groups
|
14
|
+
has_many :user_groups
|
15
|
+
# add your associations
|
16
|
+
checkboxes_for :groups
|
17
|
+
end
|
18
|
+
|
19
|
+
class Group < ActiveRecord::Base
|
20
|
+
attr_accessible :name
|
21
|
+
|
22
|
+
def to_s
|
23
|
+
name
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class UserGroup < ActiveRecord::Base
|
28
|
+
belongs_to :user
|
29
|
+
belongs_to :group
|
30
|
+
end
|
31
|
+
</pre>
|
32
|
+
|
33
|
+
In your view:
|
34
|
+
|
35
|
+
<pre>
|
36
|
+
<%= form_for @user do |f| %>
|
37
|
+
<%= f.error_messages %>
|
38
|
+
<%= f.checkboxes_for :groups %>
|
39
|
+
</p>
|
40
|
+
<p><%= f.submit %></p>
|
41
|
+
<% end %>
|
42
|
+
</pre>
|
43
|
+
|
44
|
+
and that's all. Now, when you submit your form you get the has_many :through association updated.
|
45
|
+
|
46
|
+
h2. Roadmap
|
47
|
+
|
48
|
+
* improve documentation
|
49
|
+
* write options for helper
|
50
|
+
* write more helpers
|
51
|
+
|
52
|
+
h2. Copyright
|
53
|
+
|
54
|
+
This program is free software. It comes without any warranty,
|
55
|
+
to the extent permitted by applicable law. You can redistribute
|
56
|
+
it and/or modify it under the terms of the Do What The Fuck You
|
57
|
+
Want To Public License, Version 2, as published by Sam Hocevar.
|
58
|
+
See http://sam.zoy.org/wtfpl/COPYING for more details.
|
data/Rakefile
ADDED
data/checkboxes.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "checkboxes/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "checkboxes"
|
7
|
+
s.version = Checkboxes::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["lucapette"]
|
10
|
+
s.email = ["lucapette@gmail.com"]
|
11
|
+
s.homepage = "https://github.com/lucapette/checkboxes"
|
12
|
+
s.summary = %q{This gem will handle has_many :through association with checkboxes}
|
13
|
+
s.description = %q{This gem will help you to handle has_many :through associations when you need to add/remove these ones with checkboxes.}
|
14
|
+
|
15
|
+
s.rubyforge_project = "checkboxes"
|
16
|
+
s.add_development_dependency 'sqlite3'
|
17
|
+
s.add_dependency "rails", ">= 3.0.0"
|
18
|
+
s.add_development_dependency "rspec-rails", ">= 2.5.0"
|
19
|
+
s.add_development_dependency "capybara"
|
20
|
+
s.add_development_dependency "factory_girl"
|
21
|
+
s.add_development_dependency "rspec_tag_matchers"
|
22
|
+
|
23
|
+
s.files = `git ls-files`.split("\n")
|
24
|
+
s.test_files = `git ls-files -- {spec}/*`.split("\n")
|
25
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
26
|
+
s.require_paths = ["lib"]
|
27
|
+
end
|
data/lib/checkboxes.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
require 'action_view'
|
3
|
+
require 'checkboxes/core'
|
4
|
+
require 'checkboxes/methods'
|
5
|
+
require 'checkboxes/helpers'
|
6
|
+
|
7
|
+
if defined?(ActiveRecord::Base)
|
8
|
+
ActiveRecord::Base.send :include, Checkboxes::Methods
|
9
|
+
end
|
10
|
+
|
11
|
+
if defined?(ActionView::Helpers::FormBuilder)
|
12
|
+
ActionView::Helpers::FormBuilder.send :include,Checkboxes::Helpers::FormBuilderExtension
|
13
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Checkboxes
|
2
|
+
|
3
|
+
module Core # This module contains utility methods for both extensions and helpers.
|
4
|
+
|
5
|
+
class CheckboxesNameError < StandardError; end
|
6
|
+
|
7
|
+
extend self
|
8
|
+
|
9
|
+
# Checks the validity of the relations
|
10
|
+
# on a given class.
|
11
|
+
# A relation will be valid:
|
12
|
+
# * It exists
|
13
|
+
# * It's an _has_many
|
14
|
+
# * It uses the _through_ option
|
15
|
+
# @todo makes error raised more specific
|
16
|
+
# @raise CheckboxesNameError Generic Error
|
17
|
+
# @return [boolean] It returns _true_, it raises an exception if the validation fails.
|
18
|
+
def validate(klass, *relations)
|
19
|
+
for relation in relations
|
20
|
+
unless (klass.reflections[relation].present? &&
|
21
|
+
klass.reflections[relation].macro == :has_many &&
|
22
|
+
klass.reflections[relation].options[:through].present?)
|
23
|
+
raise CheckboxesNameError, "#{relation} isn't an has_many :through for model #{klass}, check it out please."
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns the class of a relation of
|
29
|
+
# of a given class.
|
30
|
+
# It cares about the _source_ option of the
|
31
|
+
# _has_many_.
|
32
|
+
# @return [class]
|
33
|
+
def relation_klass(klass, relation)
|
34
|
+
source=klass.reflections[relation].options[:source]
|
35
|
+
relation_klass = source.present? ? source.to_s : relation.to_s
|
36
|
+
relation_klass.classify.constantize
|
37
|
+
end
|
38
|
+
|
39
|
+
# Returns a pattern name for the attr_accessor used for handling checkboxes.
|
40
|
+
# @return [String]
|
41
|
+
def field_name(relation)
|
42
|
+
"__#{relation}_ids"
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Checkboxes
|
2
|
+
|
3
|
+
module Helpers
|
4
|
+
|
5
|
+
module FormBuilderExtension
|
6
|
+
|
7
|
+
def checkboxes_for(relation)
|
8
|
+
|
9
|
+
field_name=Checkboxes::Core.field_name(relation)
|
10
|
+
|
11
|
+
output = ""
|
12
|
+
output << "#{self.label(relation)}<br />"
|
13
|
+
for record in Checkboxes::Core.relation_klass(self.object.class, relation).all
|
14
|
+
output << @template.check_box_tag("#{@object_name}[#{field_name}][]",record.id,self.object.send(relation).include?(record),:id=>record)
|
15
|
+
output << "#{record}<br />"
|
16
|
+
end
|
17
|
+
output << @template.hidden_field_tag("#{@object_name}[#{field_name}][]")
|
18
|
+
output.html_safe
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Checkboxes
|
2
|
+
|
3
|
+
# This module contains all the methods that _concern_ with
|
4
|
+
# ActiveRecord::Base.
|
5
|
+
module Methods
|
6
|
+
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
|
11
|
+
# Use it in your model for the relations you need to
|
12
|
+
# handle with checkboxes.
|
13
|
+
# @return [nil]
|
14
|
+
# @raise CheckboxesNameError If any relation is not valid.
|
15
|
+
def checkboxes_for(*relations)
|
16
|
+
|
17
|
+
klass = self
|
18
|
+
|
19
|
+
Checkboxes::Core.validate(klass, *relations)
|
20
|
+
|
21
|
+
relations.each do |relation|
|
22
|
+
|
23
|
+
field_name=Checkboxes::Core.field_name(relation)
|
24
|
+
|
25
|
+
attr_accessible field_name.to_sym
|
26
|
+
|
27
|
+
define_method "#{field_name}=" do |ids|
|
28
|
+
send("#{relation}=",ids.select { |id| id.present? }.map {|id| Checkboxes::Core.relation_klass(klass, relation).find(id)})
|
29
|
+
end
|
30
|
+
|
31
|
+
define_method field_name do
|
32
|
+
send("#{relation}").map {|e| e.id}
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "helper method" do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
@user = Factory(:user)
|
7
|
+
@template = ActionView::Base.new
|
8
|
+
@builder = ActionView::Helpers::FormBuilder.new(:user, @user, @template, {}, proc {})
|
9
|
+
end
|
10
|
+
|
11
|
+
context "with a bad relation" do
|
12
|
+
it "raises an exception" do
|
13
|
+
lambda { @builder.checkboxes_for :groupes }.should raise_error
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context "with a good relation" do
|
18
|
+
|
19
|
+
before(:each) do
|
20
|
+
@user.groups << Factory(:group)
|
21
|
+
@output_buffer = @builder.checkboxes_for :groups
|
22
|
+
end
|
23
|
+
|
24
|
+
it "contains a label for the given relation" do
|
25
|
+
@output_buffer.should have_tag("label",/#{:groups.to_s.camelize}/)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "contains a checked checkbox for each relation" do
|
29
|
+
@output_buffer.should have_tag("input[@checked]", :count=>@user.groups.count)
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
feature "adding association using checkboxes" do
|
4
|
+
before(:all) do
|
5
|
+
class User < ActiveRecord::Base
|
6
|
+
checkboxes_for :groups
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should add the checked association" do
|
11
|
+
@user = Factory.create(:user)
|
12
|
+
@groups=FactoryGirl.create_list(:group, 2)
|
13
|
+
@g1 = @groups.first
|
14
|
+
|
15
|
+
visit edit_user_path(@user)
|
16
|
+
check(@g1.to_s)
|
17
|
+
click_on('Update User')
|
18
|
+
|
19
|
+
page.should have_content(@g1.to_s)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should remove the unchecked association" do
|
23
|
+
@user = Factory.create(:user)
|
24
|
+
@g1 = Factory.create(:group)
|
25
|
+
|
26
|
+
@user.groups << @g1
|
27
|
+
|
28
|
+
visit edit_user_path(@user)
|
29
|
+
uncheck(@g1.to_s)
|
30
|
+
|
31
|
+
click_on('Update User')
|
32
|
+
|
33
|
+
page.should have_no_content(@g1.to_s)
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "checkboxes_for" do
|
4
|
+
|
5
|
+
context "with a bad relation" do
|
6
|
+
it "raises an exception" do
|
7
|
+
lambda {
|
8
|
+
class User
|
9
|
+
checkboxes_for :gropues
|
10
|
+
end
|
11
|
+
}.should raise_error(Checkboxes::Core::CheckboxesNameError)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
context "with a good association" do
|
16
|
+
|
17
|
+
before(:each) do
|
18
|
+
class User
|
19
|
+
checkboxes_for :groups
|
20
|
+
end
|
21
|
+
@user = Factory(:user)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "adds instance getter" do
|
25
|
+
@user.should respond_to Checkboxes::Core::field_name(:groups)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "adds instance setter" do
|
29
|
+
@user.should respond_to "#{Checkboxes::Core::field_name(:groups)}=".to_sym
|
30
|
+
end
|
31
|
+
|
32
|
+
it "saves relations through added method" do
|
33
|
+
g1 = Factory(:group)
|
34
|
+
g2 = Factory(:group)
|
35
|
+
@user.send("#{Checkboxes::Core::field_name(:groups)}=",[g1.id, g2.id])
|
36
|
+
@user.should have(2).groups
|
37
|
+
end
|
38
|
+
|
39
|
+
it "delete relations through added method" do
|
40
|
+
g1 = Factory(:group)
|
41
|
+
g2 = Factory(:group)
|
42
|
+
@user.send("#{Checkboxes::Core::field_name(:groups)}=",[g1.id, g2.id])
|
43
|
+
@user.send("#{Checkboxes::Core::field_name(:groups)}=",[g1.id])
|
44
|
+
@user.groups.should_not include g2
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
data/spec/dummy/Rakefile
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
# Add your own tasks in files placed in lib/tasks ending in .rake,
|
2
|
+
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
|
3
|
+
|
4
|
+
require File.expand_path('../config/application', __FILE__)
|
5
|
+
require 'rake'
|
6
|
+
|
7
|
+
Dummy::Application.load_tasks
|
@@ -0,0 +1,41 @@
|
|
1
|
+
class UsersController < ApplicationController
|
2
|
+
def index
|
3
|
+
@users = User.all
|
4
|
+
end
|
5
|
+
|
6
|
+
def show
|
7
|
+
@user = User.find(params[:id])
|
8
|
+
end
|
9
|
+
|
10
|
+
def new
|
11
|
+
@user = User.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def create
|
15
|
+
@user = User.new(params[:user])
|
16
|
+
if @user.save
|
17
|
+
redirect_to @user, :notice => "Successfully created user."
|
18
|
+
else
|
19
|
+
render :action => 'new'
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def edit
|
24
|
+
@user = User.find(params[:id])
|
25
|
+
end
|
26
|
+
|
27
|
+
def update
|
28
|
+
@user = User.find(params[:id])
|
29
|
+
if @user.update_attributes(params[:user])
|
30
|
+
redirect_to @user, :notice => "Successfully updated user."
|
31
|
+
else
|
32
|
+
render :action => 'edit'
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def destroy
|
37
|
+
@user = User.find(params[:id])
|
38
|
+
@user.destroy
|
39
|
+
redirect_to users_url, :notice => "Successfully destroyed user."
|
40
|
+
end
|
41
|
+
end
|