assigns_has_many_through_relations 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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