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.
Files changed (65) hide show
  1. data/.gitignore +7 -0
  2. data/.infinity_test +3 -0
  3. data/.rspec +3 -0
  4. data/Gemfile +3 -0
  5. data/README.textile +58 -0
  6. data/Rakefile +10 -0
  7. data/checkboxes.gemspec +27 -0
  8. data/lib/checkboxes.rb +13 -0
  9. data/lib/checkboxes/core.rb +48 -0
  10. data/lib/checkboxes/helpers.rb +25 -0
  11. data/lib/checkboxes/methods.rb +43 -0
  12. data/lib/checkboxes/version.rb +3 -0
  13. data/spec/checkboxes/helpers_spec.rb +34 -0
  14. data/spec/checkboxes/integration_spec.rb +35 -0
  15. data/spec/checkboxes/methods_spec.rb +49 -0
  16. data/spec/dummy/Rakefile +7 -0
  17. data/spec/dummy/app/assets/javascripts/welcome.js +2 -0
  18. data/spec/dummy/app/assets/stylesheets/welcome.css +4 -0
  19. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  20. data/spec/dummy/app/controllers/users_controller.rb +41 -0
  21. data/spec/dummy/app/controllers/welcome_controller.rb +5 -0
  22. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  23. data/spec/dummy/app/helpers/welcome_helper.rb +2 -0
  24. data/spec/dummy/app/models/group.rb +7 -0
  25. data/spec/dummy/app/models/user.rb +5 -0
  26. data/spec/dummy/app/models/user_group.rb +4 -0
  27. data/spec/dummy/app/views/layouts/application.html.erb +12 -0
  28. data/spec/dummy/app/views/users/_form.html.erb +10 -0
  29. data/spec/dummy/app/views/users/edit.html.erb +6 -0
  30. data/spec/dummy/app/views/users/index.html.erb +13 -0
  31. data/spec/dummy/app/views/users/new.html.erb +3 -0
  32. data/spec/dummy/app/views/users/show.html.erb +20 -0
  33. data/spec/dummy/app/views/welcome/index.html.erb +3 -0
  34. data/spec/dummy/config.ru +4 -0
  35. data/spec/dummy/config/application.rb +15 -0
  36. data/spec/dummy/config/boot.rb +10 -0
  37. data/spec/dummy/config/database.yml +22 -0
  38. data/spec/dummy/config/environment.rb +5 -0
  39. data/spec/dummy/config/environments/development.rb +26 -0
  40. data/spec/dummy/config/environments/production.rb +49 -0
  41. data/spec/dummy/config/environments/test.rb +35 -0
  42. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  43. data/spec/dummy/config/initializers/inflections.rb +10 -0
  44. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  45. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  46. data/spec/dummy/config/initializers/session_store.rb +8 -0
  47. data/spec/dummy/config/locales/en.yml +5 -0
  48. data/spec/dummy/config/routes.rb +4 -0
  49. data/spec/dummy/db/test.sqlite3 +0 -0
  50. data/spec/dummy/public/404.html +26 -0
  51. data/spec/dummy/public/422.html +26 -0
  52. data/spec/dummy/public/500.html +26 -0
  53. data/spec/dummy/public/favicon.ico +0 -0
  54. data/spec/dummy/public/javascripts/application.js +2 -0
  55. data/spec/dummy/public/javascripts/controls.js +965 -0
  56. data/spec/dummy/public/javascripts/dragdrop.js +974 -0
  57. data/spec/dummy/public/javascripts/effects.js +1123 -0
  58. data/spec/dummy/public/javascripts/prototype.js +6001 -0
  59. data/spec/dummy/public/javascripts/rails.js +191 -0
  60. data/spec/dummy/public/stylesheets/.gitkeep +0 -0
  61. data/spec/dummy/script/rails +6 -0
  62. data/spec/factories.rb +7 -0
  63. data/spec/schema.rb +16 -0
  64. data/spec/spec_helper.rb +17 -0
  65. metadata +176 -0
data/.gitignore ADDED
@@ -0,0 +1,7 @@
1
+ pkg/*
2
+ *.gem
3
+ .bundle
4
+ Gemfile.lock
5
+ .yardoc
6
+ doc
7
+ spec/dummy/log/*.log
data/.infinity_test ADDED
@@ -0,0 +1,3 @@
1
+ infinity_test do
2
+ use :rubies => %w(1.8.7@3.x 1.9.2@3.x), :test_framework => :rspec
3
+ end
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ --tty
3
+ --format nested
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
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
@@ -0,0 +1,10 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'rspec/core/rake_task'
5
+
6
+ desc 'Default: run specs'
7
+ task :default => :spec
8
+ RSpec::Core::RakeTask.new do |t|
9
+ t.pattern = "spec/**/*_spec.rb"
10
+ end
@@ -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,3 @@
1
+ module Checkboxes
2
+ VERSION = "0.0.1"
3
+ 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
@@ -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,2 @@
1
+ // Place all the behaviors and hooks related to the matching controller here.
2
+ // All this logic will automatically be available in application.js.
@@ -0,0 +1,4 @@
1
+ /*
2
+ Place all the styles related to the matching controller here.
3
+ They will automatically be included in application.css.
4
+ */
@@ -0,0 +1,3 @@
1
+ class ApplicationController < ActionController::Base
2
+ protect_from_forgery
3
+ end
@@ -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