assigns_has_many_through_relations 0.0.1 → 0.0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 091ce286a1c1e886f54fa1afaed43e33148ce45e
4
- data.tar.gz: 9a97881cf7f3b91b55de3be7ce3378403518b319
3
+ metadata.gz: 6dcecc6ee346ff60b3d5c0a6ad374a84f301c2c8
4
+ data.tar.gz: 60c2dbbeb838b89b3227a825d8d090fea409f6de
5
5
  SHA512:
6
- metadata.gz: 1c403f598488a5b190100f58bb6d1607b89fcd458ccefbb5b61f12ab03accc549224b7dc9e4f4db45bd507b528440a56033bda4abebac421fdcafe10f068869a
7
- data.tar.gz: 4a072185ea985fc2bb3933b4a94de76de1395ad85b87c9a908129605574805d6ab1ea5ee48a8698fa63863c9dea7c257e58fe7986109d98c9455c913c5ebfb02
6
+ metadata.gz: 9f6d1da9e0651b8cec30fab4e4c74579e3559ea83b2d5c972afe8f09b849cdc62e03872c4827821e97340a89246113e1d61b2eb1e31e71614fa40a84c425e458
7
+ data.tar.gz: 1dab5c419ac3dfbb844c4077712175076fc863223d2a26ee0d530522a35c1ca2fd740ce1f4d1ffb6dfd38ccadd666d6d1910de935540468675acf5972810e723
data/README.md CHANGED
@@ -4,10 +4,12 @@ Rails Engine that provides a management UI to assign models to each other in the
4
4
 
5
5
  For example, given a pair of models `Location` and `User`, we will consider `Location` to be the left side relation and `User` to be the right side relation. This UI provides a 3 column Bootstrap view where the left most column displays all `Location`s where the user can select one from the list. The 2 other columns are the Assigned and Unnassigned columns. These display `User`s that either have a join model with the selected `Location` or not, respectively. The user can then select `User`s from the Unnassigned column and the form PUT will create the join models necessary to move the Unnassigned `User`s to the middle Assigned column, effectively associating the selected `User`s from the right most column to the selected `Location`.
6
6
 
7
- ## Example UI
7
+ ## What You Get
8
8
  ![assigns_has_many_through_relations_screenshot](https://cloud.githubusercontent.com/assets/89930/6175967/0d86cf9e-b2c9-11e4-85d8-79c58d8570d6.png)
9
9
 
10
- In the above example, clicking on "Assign Selected" will move the selected `User`s to the middle column by creating the `locations_user` join model between them and the selected `Location` "Home".
10
+ In the above example, clicking on "Assign Selected" will move the selected `User`s to the middle column by creating the `locations_user` join model between them and the selected `Location` "Home".
11
+
12
+ You'll notice there's a Filter text input, this will filter the corresponding list as you type. Hitting ESC will clear the text field.
11
13
 
12
14
  ## Installation
13
15
 
@@ -25,6 +27,11 @@ Or install it yourself as:
25
27
 
26
28
  $ gem install assigns_has_many_through_relations
27
29
 
30
+ ## Dependencies
31
+
32
+ 1. [Twitter Bootstrap](http://getbootstrap.com) (if you want the UI to be pre-styled).
33
+ 2. [jQuery](http://jquery.com/download) (for the list Filter).
34
+
28
35
  ## Usage
29
36
 
30
37
  Declare the routes:
@@ -70,11 +77,19 @@ Finally, render the management UI partial in a view template in `app/views/locat
70
77
 
71
78
  You'll have to provide the user with a link to `locations_users_path`. And that's it. Now you'll be able to assign and unassign `User`s to `Location`s.
72
79
 
80
+ You can configure the engine to run a controller authorization method as you would a controller macro e.g like [Cancan's](https://github.com/CanCanCommunity/cancancan/wiki/Authorizing-Controller-Actions) `load_and_authorize_resource`:
81
+
82
+ ```ruby
83
+ # config/initializers/assigns_has_many_through_relations.rb
84
+
85
+ AHMTR.configure do
86
+ auth_filter :load_and_authorize_resource
87
+ end
88
+ ```
89
+
73
90
  ## Todo
74
91
 
75
- 1. Move the quick_list_filter.js file into the engine.
76
- 2. Write specs.
77
- 3. Clean up css classes and ids in the partial.
92
+ 1. Write specs.
78
93
 
79
94
  ## Contributing
80
95
 
Binary file
@@ -0,0 +1,59 @@
1
+ // Call this jQuery plugin on a text input field used to filter a ul as the user types
2
+ $.fn.quickListFilter = function(options) {
3
+ options = $.extend({
4
+ item: 'li'
5
+ }, options);
6
+
7
+ return this.each(function() {
8
+ var filter = $(this),
9
+ target = filter.data('target'),
10
+ items = $(target).find(options.item);
11
+
12
+ function filterList() {
13
+ items.each(function() {
14
+ var regex = new RegExp(filter.val(), 'i'),
15
+ li = $(this).parent('li');
16
+
17
+ if (regex.test(this.innerText)) {
18
+ li.slideDown(200);
19
+ } else {
20
+ li.slideUp(200);
21
+ }
22
+ });
23
+ }
24
+
25
+ filter.on('keyup', function(e) {
26
+ if (e.which === 27) { filter.val(null); }
27
+ filterList();
28
+ });
29
+ });
30
+ }
31
+
32
+ // Call this jQuery plugin on an element that you want to show
33
+ // when the target element has scrollbars, and hide if no scrollbars.
34
+ // Useful for a down arrow scroll indicator.
35
+ $.fn.hideWhenNoScrollBars = function() {
36
+ return this.each(function() {
37
+ var el = $(this),
38
+ target = $(el.data('target'));
39
+
40
+ function hideWhenNoScrollbars(target, el) {
41
+ if (target.scrollHeight > target.clientHeight) { // has vertical scrollbars
42
+ el.show();
43
+ } else {
44
+ el.hide();
45
+ }
46
+ }
47
+
48
+ $(window).on('resize', function() {
49
+ hideWhenNoScrollbars(target[0], el);
50
+ });
51
+
52
+ hideWhenNoScrollbars(target[0], el);
53
+ });
54
+ }
55
+
56
+ $(function() {
57
+ $('.quick-filter').quickListFilter({ item: 'li label, li a' });
58
+ $('.hide-when-no-scrollbars').hideWhenNoScrollBars();
59
+ });
Binary file
@@ -0,0 +1,38 @@
1
+ #ahmtr_wrap {
2
+ .nav {
3
+ font-size: 80%;
4
+
5
+ .active {
6
+ background-color: #ebebeb;
7
+ }
8
+ }
9
+
10
+ .fix-height {
11
+ max-height: 60vh;
12
+ overflow-y: scroll;
13
+ }
14
+
15
+ input[type="text"] {
16
+ width: 100%;
17
+ }
18
+
19
+ li {
20
+ overflow: hidden;
21
+
22
+ &:hover {
23
+ background-color: #ebebeb;
24
+ }
25
+
26
+ label {
27
+ padding: 5px 7px;
28
+ cursor: pointer;
29
+ display: inline-block;
30
+ width: 95%;
31
+ }
32
+
33
+ input[type="checkbox"] {
34
+ width: auto;
35
+ cursor: pointer;
36
+ }
37
+ }
38
+ }
@@ -1,14 +1,17 @@
1
1
  <div class="flash-messages row">
2
2
  <div class="col-sm-12">
3
3
  <% flash.each do |key, message| %>
4
- <div class="alert alert-<%= AssignsHasManyThroughRelations::BOOTSTRAP_FLASH_MAP[key] %>" role="alert">
5
- <%= message %>
4
+ <div class="alert alert-<%= AssignsHasManyThroughRelations::BOOTSTRAP_FLASH_MAP[key] %> alert-dismissible" role="alert">
5
+ <button type="button" class="close" data-dismiss="alert" aria-label="Close">
6
+ <span aria-hidden="true">&times;</span>
7
+ </button>
8
+ <p><%= message %></p>
6
9
  </div>
7
10
  <% end %>
8
11
  </div>
9
12
  </div>
10
13
 
11
- <div id="roles" class="row">
14
+ <div id="ahmtr_wrap" class="row">
12
15
  <div class="col-sm-3">
13
16
  <div class="panel panel-info">
14
17
  <div class="panel-heading">
@@ -44,15 +47,15 @@
44
47
  <div class="col-sm-9">
45
48
  <div class="row">
46
49
  <div class="col-sm-6">
47
- <div class="panel panel-primary roles-users-list pull-left">
50
+ <div class="panel panel-primary pull-left">
48
51
  <div class="panel-heading">
49
52
  <%= pluralize @selected_right_side_models.size, "#{@selected_left_side_model.name} User" %>
50
53
  </div>
51
54
 
52
55
  <div class="panel-body">
53
- <%= render 'shared/quick_list_filter', target: '#selected-users' %>
56
+ <%= render 'shared/quick_list_filter', target: '#selected-right-side' %>
54
57
 
55
- <ul id="selected-users" class="list-unstyled fix-height">
58
+ <ul id="selected-right-side" class="list-unstyled fix-height">
56
59
  <% @selected_right_side_models.each do |model| %>
57
60
  <li>
58
61
  <%= f.fields_for join_name, @selected_left_side_model.get_join_model_for(model) do |ruf| %>
@@ -64,28 +67,28 @@
64
67
  <% end %>
65
68
  </ul>
66
69
 
67
- <div class="text-center hide-when-no-scrollbars" data-target="#selected-users">
70
+ <div class="text-center hide-when-no-scrollbars" data-target="#selected-right-side">
68
71
  <span class="glyphicon glyphicon-chevron-down disabled"></span>
69
72
  </div>
70
73
  </div>
71
74
 
72
75
  <div class="panel-footer">
73
- <%= f.submit 'Unassign Selected', class: 'btn btn-warning btn-sm' %>
74
- <%= f.submit 'Unassign All', class: 'btn btn-danger btn-sm check-all-boxes', data: { target: '#selected-users :checkbox' } %>
76
+ <%= f.submit 'Unassign Selected', class: 'btn btn-primary btn-xs' %>
77
+ <%= f.submit 'Unassign All', class: 'btn btn-primary btn-xs check-all-boxes', data: { target: '#selected-right-side :checkbox', confirm: "Are you sure you want to Unassign all Users from the \"#{@selected_left_side_model.name}\" #{@selected_left_side_model.class}?" } %>
75
78
  </div>
76
79
  </div>
77
80
  </div>
78
81
 
79
82
  <div class="col-sm-6">
80
- <div class="panel panel-danger roles-users-list pull-left">
83
+ <div class="panel panel-danger pull-left">
81
84
  <div class="panel-heading">
82
85
  <%= pluralize @available_right_side_models.size, "Non #{@selected_left_side_model.name} User" %>
83
86
  </div>
84
87
 
85
88
  <div class="panel-body">
86
- <%= render 'shared/quick_list_filter', target: '#available-users' %>
89
+ <%= render 'shared/quick_list_filter', target: '#available-right-side' %>
87
90
 
88
- <ul id="available-users" class="list-unstyled fix-height">
91
+ <ul id="available-right-side" class="list-unstyled fix-height">
89
92
  <% @available_right_side_models.each do |model| %>
90
93
  <li>
91
94
  <%= f.fields_for join_name, model.send(join_name).build do |ruf| %>
@@ -96,14 +99,14 @@
96
99
  <% end %>
97
100
  </ul>
98
101
 
99
- <div class="text-center hide-when-no-scrollbars" data-target="#available-users">
102
+ <div class="text-center hide-when-no-scrollbars" data-target="#available-right-side">
100
103
  <span class="glyphicon glyphicon-chevron-down disabled"></span>
101
104
  </div>
102
105
  </div>
103
106
 
104
107
  <div class="panel-footer">
105
- <%= f.submit 'Assign Selected', class: 'btn btn-success btn-sm' %>
106
- <%= f.submit 'Assign All', class: 'btn btn-primary btn-sm check-all-boxes', data: { target: '#available-users :checkbox' } %>
108
+ <%= f.submit 'Assign Selected', class: 'btn btn-primary btn-xs' %>
109
+ <%= f.submit 'Assign All', class: 'btn btn-primary btn-xs check-all-boxes', data: { target: '#available-right-side :checkbox', confirm: "Are you sure you want to Assign all Users to the \"#{@selected_left_side_model.name}\" #{@selected_left_side_model.class}?" } %>
107
110
  </div>
108
111
  </div>
109
112
  </div>
@@ -1,5 +1,6 @@
1
1
  require "assigns_has_many_through_relations/version"
2
2
  require "assigns_has_many_through_relations/engine"
3
+ require "assigns_has_many_through_relations/configuration"
3
4
 
4
5
  module AssignsHasManyThroughRelations
5
6
  BOOTSTRAP_FLASH_MAP = {
@@ -11,6 +12,16 @@ module AssignsHasManyThroughRelations
11
12
  autoload :ControllerConcern, 'assigns_has_many_through_relations/controller_concern'
12
13
  autoload :ModelConcern, 'assigns_has_many_through_relations/model_concern'
13
14
 
15
+ class << self
16
+ attr_reader :config
17
+ delegate :auth_filter, to: :config
18
+
19
+ def configure(&block)
20
+ @config ||= Configuration.new
21
+ @config.instance_exec &block
22
+ end
23
+ end
24
+
14
25
  module_function
15
26
 
16
27
  def join_name_for(left_relation, right_relation)
@@ -26,3 +37,6 @@ module AssignsHasManyThroughRelations
26
37
  routes.put "/#{left_name}/#{right_name}/:id", to: "#{name}#update", as: "assign_#{name}"
27
38
  end
28
39
  end
40
+
41
+ AHMTR = AssignsHasManyThroughRelations
42
+ AHMTR.configure {} # so that @config is never nil
@@ -0,0 +1,10 @@
1
+ module AssignsHasManyThroughRelations
2
+ class Configuration
3
+ # Assign the name of a controller class method that you want to run for authorization
4
+ # e.g. Cancan's :load_and_authorize_resource, or a custom method you define
5
+ # in your ApplicationController
6
+ def auth_filter(val = nil)
7
+ @auth_filter ||= val
8
+ end
9
+ end
10
+ end
@@ -9,6 +9,8 @@ module AssignsHasManyThroughRelations
9
9
 
10
10
  include AssignsHasManyThroughRelations::ControllerInstanceMethods
11
11
  helper_method :left_relation_class_name, :join_name
12
+
13
+ send AHMTR.auth_filter if AHMTR.auth_filter
12
14
  end
13
15
 
14
16
  def left_relation_param_name; @left_relation_param_name end
@@ -1,3 +1,3 @@
1
1
  module AssignsHasManyThroughRelations
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: assigns_has_many_through_relations
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Diego Salazar
@@ -64,10 +64,15 @@ files:
64
64
  - LICENSE.txt
65
65
  - README.md
66
66
  - Rakefile
67
+ - app/assets/.DS_Store
68
+ - app/assets/javascripts/assigns_has_many_through_relations/plugins.js
69
+ - app/assets/stylesheets/.DS_Store
70
+ - app/assets/stylesheets/assigns_has_many_through_relations/application.css.scss
67
71
  - app/views/.DS_Store
68
72
  - app/views/_assigns_has_many_through_relations.html.erb
69
73
  - assigns_has_many_through_relations.gemspec
70
74
  - lib/assigns_has_many_through_relations.rb
75
+ - lib/assigns_has_many_through_relations/configuration.rb
71
76
  - lib/assigns_has_many_through_relations/controller_concern.rb
72
77
  - lib/assigns_has_many_through_relations/engine.rb
73
78
  - lib/assigns_has_many_through_relations/model_concern.rb