record_collection 0.3.2

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 (92) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +23 -0
  3. data/.travis.yml +3 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +217 -0
  7. data/Rakefile +16 -0
  8. data/app/assets/images/record_collection/.keep +0 -0
  9. data/app/assets/javascripts/record_collection/application.js.coffee +1 -0
  10. data/app/assets/javascripts/record_collection/multi_select.js.coffee +66 -0
  11. data/app/assets/javascripts/record_collection/optionals.js.coffee +101 -0
  12. data/app/assets/stylesheets/record_collection/application.css +15 -0
  13. data/app/assets/stylesheets/record_collection/multi_select.css.sass +20 -0
  14. data/app/assets/stylesheets/record_collection/optionals.css.sass +48 -0
  15. data/app/controllers/record_collection/application_controller.rb +4 -0
  16. data/app/helpers/record_collection/application_helper.rb +4 -0
  17. data/app/views/layouts/record_collection/application.html.erb +14 -0
  18. data/lib/record_collection.rb +12 -0
  19. data/lib/record_collection/base.rb +98 -0
  20. data/lib/record_collection/engine.rb +5 -0
  21. data/lib/record_collection/name.rb +7 -0
  22. data/lib/record_collection/rails/form_options_helper.rb +48 -0
  23. data/lib/record_collection/rails/routes.rb +21 -0
  24. data/lib/record_collection/version.rb +3 -0
  25. data/record_collection.gemspec +43 -0
  26. data/spec/dummy/README.rdoc +28 -0
  27. data/spec/dummy/Rakefile +6 -0
  28. data/spec/dummy/app/assets/images/.keep +0 -0
  29. data/spec/dummy/app/assets/javascripts/application.js.coffee +10 -0
  30. data/spec/dummy/app/assets/stylesheets/application.css.sass +4 -0
  31. data/spec/dummy/app/assets/stylesheets/components/_forms.css.sass +0 -0
  32. data/spec/dummy/app/assets/stylesheets/components/_structure.css.sass +0 -0
  33. data/spec/dummy/app/assets/stylesheets/scaffolds.scss +69 -0
  34. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  35. data/spec/dummy/app/controllers/concerns/.keep +0 -0
  36. data/spec/dummy/app/controllers/employees_controller.rb +75 -0
  37. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  38. data/spec/dummy/app/mailers/.keep +0 -0
  39. data/spec/dummy/app/models/.keep +0 -0
  40. data/spec/dummy/app/models/concerns/.keep +0 -0
  41. data/spec/dummy/app/models/employee.rb +2 -0
  42. data/spec/dummy/app/models/employee/collection.rb +6 -0
  43. data/spec/dummy/app/models/project.rb +2 -0
  44. data/spec/dummy/app/views/application/_form_errors.html.slim +8 -0
  45. data/spec/dummy/app/views/employees/_form.html.erb +25 -0
  46. data/spec/dummy/app/views/employees/batch_actions.html.slim +13 -0
  47. data/spec/dummy/app/views/employees/edit.html.erb +6 -0
  48. data/spec/dummy/app/views/employees/index.html.slim +25 -0
  49. data/spec/dummy/app/views/employees/new.html.erb +5 -0
  50. data/spec/dummy/app/views/employees/show.html.erb +14 -0
  51. data/spec/dummy/app/views/layouts/application.html.slim +10 -0
  52. data/spec/dummy/bin/bundle +3 -0
  53. data/spec/dummy/bin/rails +4 -0
  54. data/spec/dummy/bin/rake +4 -0
  55. data/spec/dummy/bin/setup +29 -0
  56. data/spec/dummy/config.ru +4 -0
  57. data/spec/dummy/config/application.rb +26 -0
  58. data/spec/dummy/config/boot.rb +5 -0
  59. data/spec/dummy/config/database.yml +25 -0
  60. data/spec/dummy/config/environment.rb +5 -0
  61. data/spec/dummy/config/environments/development.rb +41 -0
  62. data/spec/dummy/config/environments/production.rb +79 -0
  63. data/spec/dummy/config/environments/test.rb +42 -0
  64. data/spec/dummy/config/initializers/assets.rb +11 -0
  65. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  66. data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
  67. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  68. data/spec/dummy/config/initializers/inflections.rb +16 -0
  69. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  70. data/spec/dummy/config/initializers/session_store.rb +3 -0
  71. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  72. data/spec/dummy/config/locales/en.yml +23 -0
  73. data/spec/dummy/config/locales/models.en.yml +5 -0
  74. data/spec/dummy/config/routes.rb +3 -0
  75. data/spec/dummy/config/secrets.yml +22 -0
  76. data/spec/dummy/db/migrate/20150203124634_create_employees.rb +10 -0
  77. data/spec/dummy/db/migrate/20150204103712_add_vegan_to_employees.rb +5 -0
  78. data/spec/dummy/db/migrate/20150204103925_add_admin_to_employees.rb +5 -0
  79. data/spec/dummy/db/migrate/20150204125014_create_projects.rb +11 -0
  80. data/spec/dummy/db/schema.rb +33 -0
  81. data/spec/dummy/lib/assets/.keep +0 -0
  82. data/spec/dummy/log/.keep +0 -0
  83. data/spec/dummy/public/404.html +67 -0
  84. data/spec/dummy/public/422.html +67 -0
  85. data/spec/dummy/public/500.html +66 -0
  86. data/spec/dummy/public/favicon.ico +0 -0
  87. data/spec/features/multi_select_spec.rb +23 -0
  88. data/spec/fixtures/collections.rb +0 -0
  89. data/spec/record_selection/base_spec.rb +113 -0
  90. data/spec/spec_helper.rb +26 -0
  91. data/spec/validations_spec.rb +19 -0
  92. metadata +482 -0
@@ -0,0 +1,15 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any styles
10
+ * defined in the other CSS/SCSS files in this directory. It is generally better to create a new
11
+ * file per style scope.
12
+ *
13
+ *= require_tree .
14
+ *= require_self
15
+ */
@@ -0,0 +1,20 @@
1
+ @import font-awesome
2
+ // Checkboxes
3
+ .selection-toggle-all
4
+ cursor: pointer
5
+ &.unchecked
6
+ @extend .fa, .fa-lg, .fa-square-o
7
+ &.checked
8
+ @extend .fa, .fa-2x, .fa-check-square-o
9
+ color: #449
10
+ .checker
11
+ cursor: pointer
12
+ &.unchecked
13
+ @extend .fa, .fa-lg, .fa-square-o
14
+ color: #999
15
+ &.checked
16
+ @extend .fa, .fa-lg, .fa-check-square-o
17
+ color: #050
18
+ table.with-selection
19
+ td.selection
20
+ width: 46px
@@ -0,0 +1,48 @@
1
+ @import font-awesome
2
+ $alert-color: #f04124
3
+ $checked-color: #43AC6A
4
+ $unchecked-color: #999
5
+ .optional-input-activator-container
6
+ display: inline-block
7
+ .optional-input-activator-toggle
8
+ margin-right: 7px
9
+ &.active
10
+ .optional-input-activator-toggle
11
+ color: #3636BD
12
+ @extend .fa, .fa-2x, .fa-toggle-on
13
+ &.error
14
+ color: $alert-color
15
+ .optional-input-activator-toggle
16
+ color: $alert-color
17
+ &.inactive
18
+ color: #999
19
+ .optional-input-activator-toggle
20
+ @extend .fa, .fa-2x, .fa-toggle-off
21
+ .optional-attribute-container
22
+ .optional-boolean-label
23
+ display: inline-block
24
+ margin-left: 8px
25
+ margin-right: 8px
26
+ &.inactive
27
+ display: none
28
+ &.optional-boolean
29
+ &.active
30
+ .optional-boolean-activator-toggle
31
+ color: #3636BD
32
+ @extend .fa, .fa-2x, .fa-toggle-on
33
+ .optional-boolean-toggle
34
+ &.active
35
+ color: $checked-color
36
+ @extend .fa, .fa-2x, .fa-check-square-o
37
+ &.inactive
38
+ color: $unchecked-color
39
+ @extend .fa, .fa-2x, .fa-square-o
40
+ &.inactive
41
+ display: block
42
+ .optional-boolean-activator-toggle
43
+ color: $unchecked-color
44
+ @extend .fa, .fa-2x, .fa-toggle-off
45
+ .optional-boolean-label
46
+ color: $unchecked-color
47
+ .optional-boolean-toggle
48
+ display: none
@@ -0,0 +1,4 @@
1
+ module RecordCollection
2
+ class ApplicationController < ActionController::Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module RecordCollection
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>RecordCollection</title>
5
+ <%= stylesheet_link_tag "record_collection/application", media: "all" %>
6
+ <%= javascript_include_tag "record_collection/application" %>
7
+ <%= csrf_meta_tags %>
8
+ </head>
9
+ <body>
10
+
11
+ <%= yield %>
12
+
13
+ </body>
14
+ </html>
@@ -0,0 +1,12 @@
1
+ require 'active_attr'
2
+ require 'active_model'
3
+ require "record_collection/version"
4
+ require "record_collection/name"
5
+ require "record_collection/base"
6
+ require 'record_collection/rails/routes'
7
+ require 'record_collection/rails/form_options_helper'
8
+ require 'record_collection/engine'
9
+
10
+ module RecordCollection
11
+ # Your code goes here...
12
+ end
@@ -0,0 +1,98 @@
1
+ module RecordCollection
2
+ class Base
3
+ include Enumerable
4
+ include ActiveAttr::Model
5
+
6
+ attr_reader :collection
7
+ protected :collection
8
+ delegate :first, :last, :size, :length, :count, :empty?, :any?, to: :collection
9
+
10
+ class << self
11
+ def model_name
12
+ RecordCollection::Name.new(self)
13
+ end
14
+ # Find all attributes that are specified with type boolean
15
+ def boolean_attributes
16
+ attributes.select{|k,v| v[:type] == ActiveAttr::Typecasting::Boolean}.keys.map(&:to_sym)
17
+ end
18
+
19
+ def human_attribute_name(*args)
20
+ raise "No record_class defined and could not be inferred based on the inheritance namespace. Please define self.record_clas = ClassNameOfIndividualRecords in the collection" unless record_class.present?
21
+ record_class.human_attribute_name(*args)
22
+ end
23
+
24
+ def inherited(collection_class)
25
+ # Try to infer the baseclass from the collection inheritance and set it if possible
26
+ collection_class.send :cattr_accessor, :record_class
27
+ if base_class = collection_class.name.deconstantize.safe_constantize
28
+ collection_class.record_class = base_class
29
+ end
30
+ end
31
+
32
+ end
33
+
34
+ def initialize(collection = [], params = {})
35
+ super(params) # active attr initialize with params
36
+ @collection = collection
37
+ end
38
+
39
+ # implement enumerable logic for collection
40
+ def each(&block)
41
+ collection.each do |record|
42
+ if block_given?
43
+ block.call record
44
+ else
45
+ yield record
46
+ end
47
+ end
48
+ end
49
+
50
+ def save
51
+ valid? && update_collection_attributes!
52
+ end
53
+
54
+ def update_collection_attributes!
55
+ each { |r| r.update changed_attributes }
56
+ end
57
+
58
+ def changed_attributes
59
+ @changed_attributes ||= attributes.reject{|attr, val| val.nil? }
60
+ end
61
+
62
+ def persisted?
63
+ # Behave like an update in forms
64
+ true
65
+ end
66
+
67
+ def new_record?
68
+ # Behave like an update in forms
69
+ false
70
+ end
71
+
72
+
73
+ # This method returns nil when the values of `attr` in the collection
74
+ # are mixed. Otherwise the value itself. For boolean attributes the
75
+ # check is wether the values are truthy or falsy. If the
76
+ # set_if_nil: true
77
+ # option is given, a uniform value will be set as the collection value if
78
+ # it is not already set. This is important since there might be a uniform
79
+ # value in the collection, but the values of the collection are a result of
80
+ # an invalid form submission. In this case you want to keep the values of the
81
+ # submitted form as collection values, not the current uniform attribute.
82
+ def uniform_collection_attribute(attr, options = {})
83
+ attribute_spec = self.class.attributes[attr]
84
+ raise "Attribute #{attr} not defined on collection" unless attribute_spec
85
+ if attribute_spec[:type] == Boolean
86
+ # For boolean attributes presence is the true or false difference
87
+ # not the value itself
88
+ results = map{|r| r.public_send(attr).present? }.uniq
89
+ else
90
+ results = map{|r| r.public_send(attr) }.uniq
91
+ end
92
+ return nil unless results.size == 1 # one value found
93
+ public_send("#{attr}=", results.first) if options[:set_if_nil] and public_send(attr).nil? # set value on the object if it is uniform and not yet set
94
+ results.first
95
+ end
96
+
97
+ end
98
+ end
@@ -0,0 +1,5 @@
1
+ module RecordCollection
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace RecordCollection
4
+ end
5
+ end
@@ -0,0 +1,7 @@
1
+ module RecordCollection
2
+ class Name < ActiveModel::Name
3
+ def param_key
4
+ 'collection'
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,48 @@
1
+ require 'action_view/helpers/form_options_helper'
2
+ class ActionView::Helpers::FormBuilder
3
+ # This method adds the collection ids to the form if not already
4
+ # added. For the lazy peaple that forget to add them manually and
5
+ # use an optional tag
6
+ def add_collection_ids(content)
7
+ return content if @collection_ids_already_added
8
+ collection_ids + content
9
+ end
10
+
11
+ # Return inputs for the collection ids
12
+ def collection_ids
13
+ @collection_ids_already_added = true
14
+ object.map{|record| @template.hidden_field_tag('ids[]', record.id, id: nil) }.join.html_safe
15
+ end
16
+
17
+ def optional_boolean(attr)
18
+ classes = get_optional_classes(attr, base_class: 'optional-boolean')
19
+ label_tag = label(attr, options[:label])
20
+ check_box_tag = check_box(attr, options)
21
+ add_collection_ids @template.content_tag(:div, label_tag + check_box_tag , class: classes, data: { attribute: attr })
22
+ end
23
+
24
+ def optional_input(attr, options = {})
25
+ classes = get_optional_classes(attr, base_class: 'optional-input')
26
+ add_collection_ids @template.content_tag(:div, input(attr, options), class: classes, data: { attribute: attr })
27
+ end
28
+
29
+ def optional_text_field(attr, options={})
30
+ classes = get_optional_classes(attr, base_class: 'optional-text-field')
31
+ add_collection_ids @template.content_tag(:div, label(attr, options[:label]) + text_field(attr, options), class: classes, data: { attribute: attr })
32
+ end
33
+
34
+ def get_optional_classes(attr, options = {})
35
+ active = false
36
+ if object.respond_to?(:uniform_collection_attribute)
37
+ uniform_value = object.uniform_collection_attribute(attr, set_if_nil: true)
38
+ # The field is active when the uniform value is not nil
39
+ active = !uniform_value.nil?
40
+ end
41
+ active = true unless object.public_send(attr).nil? # Activate if collection attribute is not nil
42
+ classes = [options[:base_class] || 'optional-input', 'optional-attribute-container', attr]
43
+ classes << (active ? 'active' : 'inactive')
44
+ classes << 'error' if object.errors[attr].present?
45
+ classes
46
+ end
47
+ end
48
+
@@ -0,0 +1,21 @@
1
+ module ActionDispatch::Routing
2
+ class Mapper
3
+ # Add two collection routes to the normal resources definition.
4
+ # This call behaves exactly as the normal resources :... call,
5
+ # but adds:
6
+ # collection do
7
+ # get :batch_actions
8
+ # post :process_batch
9
+ # end
10
+ def batch_resources(*resources, &blk)
11
+ batch_blk = Proc.new do
12
+ collection do
13
+ get :batch_actions
14
+ match :process_batch, via: [:post, :patch, :put]
15
+ end
16
+ blk.call if blk
17
+ end
18
+ resources(*resources, &batch_blk)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,3 @@
1
+ module RecordCollection
2
+ VERSION = "0.3.2"
3
+ end
@@ -0,0 +1,43 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'record_collection/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "record_collection"
8
+ spec.version = RecordCollection::VERSION
9
+ spec.authors = ["Benjamin ter Kuile"]
10
+ spec.email = ["bterkuile@gmail.com"]
11
+ spec.summary = %q{Manage collections of records in Ruby on Rails}
12
+ spec.description = %q{This gem is there to aid you to work on subsets or rails models. This gem helps you to create forms and controllers that act on collections of models}
13
+ spec.homepage = "http://www.companytools.nl/"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.7"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ spec.add_development_dependency "rspec", ">= 3.1.0"
24
+ spec.add_development_dependency "rspec-rails", ">= 3.1.0"
25
+ spec.add_development_dependency "pry", ">= 0.1"
26
+ spec.add_development_dependency "rails", ">= 4.2.0"
27
+ spec.add_development_dependency "sqlite3"
28
+ spec.add_development_dependency "slim-rails"
29
+ spec.add_development_dependency "coffee-rails"
30
+ spec.add_development_dependency "sass-rails"
31
+ spec.add_development_dependency "font-awesome-rails"
32
+ spec.add_development_dependency "jquery-rails"
33
+ spec.add_development_dependency "js-routes"
34
+ spec.add_development_dependency "capybara"
35
+ spec.add_development_dependency "poltergeist"
36
+ spec.add_development_dependency "launchy"
37
+ spec.add_development_dependency "database_cleaner"
38
+
39
+
40
+ spec.add_runtime_dependency 'active_attr', '>= 0.8.5'
41
+ spec.add_runtime_dependency 'activemodel', '>= 4.1'
42
+ spec.add_runtime_dependency "railties", ">= 3.1"
43
+ end
@@ -0,0 +1,28 @@
1
+ == README
2
+
3
+ This README would normally document whatever steps are necessary to get the
4
+ application up and running.
5
+
6
+ Things you may want to cover:
7
+
8
+ * Ruby version
9
+
10
+ * System dependencies
11
+
12
+ * Configuration
13
+
14
+ * Database creation
15
+
16
+ * Database initialization
17
+
18
+ * How to run the test suite
19
+
20
+ * Services (job queues, cache servers, search engines, etc.)
21
+
22
+ * Deployment instructions
23
+
24
+ * ...
25
+
26
+
27
+ Please feel free to use a different markup language if you do not plan to run
28
+ <tt>rake doc:app</tt>.
@@ -0,0 +1,6 @@
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
+
6
+ Rails.application.load_tasks
File without changes
@@ -0,0 +1,10 @@
1
+ #= require jquery
2
+ #= require jquery_ujs
3
+ #= require js-routes
4
+ #= require record_collection/multi_select
5
+ #= require record_collection/optionals
6
+ #= require_tree .
7
+ #= require_self
8
+ $ ->
9
+ $(document).multi_select()
10
+ $(document).optionals()
@@ -0,0 +1,4 @@
1
+ //= require record_collection/multi_select
2
+ //= require record_collection/optionals
3
+ //= require scaffolds
4
+ @import ./components/*
@@ -0,0 +1,69 @@
1
+ body {
2
+ background-color: #fff;
3
+ color: #333;
4
+ font-family: verdana, arial, helvetica, sans-serif;
5
+ font-size: 13px;
6
+ line-height: 18px;
7
+ }
8
+
9
+ p, ol, ul, td {
10
+ font-family: verdana, arial, helvetica, sans-serif;
11
+ font-size: 13px;
12
+ line-height: 18px;
13
+ }
14
+
15
+ pre {
16
+ background-color: #eee;
17
+ padding: 10px;
18
+ font-size: 11px;
19
+ }
20
+
21
+ a {
22
+ color: #000;
23
+ &:visited {
24
+ color: #666;
25
+ }
26
+ &:hover {
27
+ color: #fff;
28
+ background-color: #000;
29
+ }
30
+ }
31
+
32
+ div {
33
+ &.field, &.actions {
34
+ margin-bottom: 10px;
35
+ }
36
+ }
37
+
38
+ #notice {
39
+ color: green;
40
+ }
41
+
42
+ .field_with_errors {
43
+ padding: 2px;
44
+ background-color: red;
45
+ display: table;
46
+ }
47
+
48
+ #error_explanation {
49
+ width: 450px;
50
+ border: 2px solid red;
51
+ padding: 7px;
52
+ padding-bottom: 0;
53
+ margin-bottom: 20px;
54
+ background-color: #f0f0f0;
55
+ h2 {
56
+ text-align: left;
57
+ font-weight: bold;
58
+ padding: 5px 5px 5px 15px;
59
+ font-size: 12px;
60
+ margin: -7px;
61
+ margin-bottom: 0px;
62
+ background-color: #c00;
63
+ color: #fff;
64
+ }
65
+ ul li {
66
+ font-size: 12px;
67
+ list-style: square;
68
+ }
69
+ }