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 +4 -4
- data/README.md +20 -5
- data/app/assets/.DS_Store +0 -0
- data/app/assets/javascripts/assigns_has_many_through_relations/plugins.js +59 -0
- data/app/assets/stylesheets/.DS_Store +0 -0
- data/app/assets/stylesheets/assigns_has_many_through_relations/application.css.scss +38 -0
- data/app/views/_assigns_has_many_through_relations.html.erb +18 -15
- data/lib/assigns_has_many_through_relations.rb +14 -0
- data/lib/assigns_has_many_through_relations/configuration.rb +10 -0
- data/lib/assigns_has_many_through_relations/controller_concern.rb +2 -0
- data/lib/assigns_has_many_through_relations/version.rb +1 -1
- metadata +6 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6dcecc6ee346ff60b3d5c0a6ad374a84f301c2c8
|
4
|
+
data.tar.gz: 60c2dbbeb838b89b3227a825d8d090fea409f6de
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
##
|
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.
|
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
|
-
|
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">×</span>
|
7
|
+
</button>
|
8
|
+
<p><%= message %></p>
|
6
9
|
</div>
|
7
10
|
<% end %>
|
8
11
|
</div>
|
9
12
|
</div>
|
10
13
|
|
11
|
-
<div id="
|
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
|
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-
|
56
|
+
<%= render 'shared/quick_list_filter', target: '#selected-right-side' %>
|
54
57
|
|
55
|
-
<ul id="selected-
|
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-
|
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-
|
74
|
-
<%= f.submit 'Unassign All', class: 'btn btn-
|
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
|
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-
|
89
|
+
<%= render 'shared/quick_list_filter', target: '#available-right-side' %>
|
87
90
|
|
88
|
-
<ul id="available-
|
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-
|
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-
|
106
|
-
<%= f.submit 'Assign All', class: 'btn btn-primary btn-
|
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
|
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.
|
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
|