lolita-menu 0.3.6 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +47 -0
- data/Gemfile +8 -11
- data/Rakefile +4 -43
- data/app/assets/javascripts/lolita/menu/autocomplete-rails.js +1 -1
- data/app/assets/javascripts/lolita/menu/menu-items.js +5 -5
- data/app/assets/stylesheets/lolita/menu/application.css +21 -0
- data/app/controllers/menu_items_controller.rb +4 -0
- data/app/controllers/nested_trees_controller.rb +46 -46
- data/app/models/menu.rb +1 -7
- data/app/models/menu_item.rb +13 -17
- data/app/views/components/lolita/menu/nested_tree/_display.html.haml +0 -1
- data/lib/generators/lolita/menu/templates/migrations/create_menu_items.rb +1 -1
- data/lib/lolita-menu/nested_tree.rb +253 -253
- data/lib/lolita-menu/nested_tree/branch_builder.rb +58 -58
- data/lib/lolita-menu/nested_tree/configuration.rb +72 -72
- data/lib/lolita-menu/nested_tree/scope.rb +52 -52
- data/lib/lolita-menu/nested_tree/tree_builder.rb +29 -29
- data/lolita-menu.gemspec +6 -110
- data/spec/models/menu_item_spec.rb +6 -6
- data/spec/models/menu_spec.rb +0 -1
- data/test_orm/active_record.rb +4 -6
- data/test_orm/log/.gitkeep +0 -0
- data/test_orm/rails/config/application.rb +2 -3
- data/test_orm/rails/log/.gitkeep +0 -0
- metadata +34 -129
@@ -1,70 +1,70 @@
|
|
1
1
|
module Lolita
|
2
|
-
|
3
|
-
|
4
|
-
|
2
|
+
module Menu
|
3
|
+
module NestedTree
|
4
|
+
class BranchBuilder
|
5
5
|
|
6
|
-
|
7
|
-
|
6
|
+
ROOT = "root"
|
7
|
+
NONE = "none"
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
9
|
+
def initialize(root, attributes)
|
10
|
+
@root = root
|
11
|
+
@attributes = {}
|
12
|
+
(attributes || {}).each do |key,value|
|
13
|
+
@attributes[key.to_sym] = value
|
14
|
+
end
|
15
|
+
end
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
|
17
|
+
def root?
|
18
|
+
@attributes[:item_id] == "root"
|
19
|
+
end
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
|
21
|
+
def value_for(attribute)
|
22
|
+
convert(@attributes[attribute.to_sym])
|
23
|
+
end
|
24
24
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
25
|
+
def attribute_value_pairs
|
26
|
+
@attributes.map do |attr, value|
|
27
|
+
[mapping[attr], convert(value)]
|
28
|
+
end
|
29
|
+
end
|
30
30
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
31
|
+
def attribute_value_pairs_hash
|
32
|
+
hash = {}
|
33
|
+
attribute_value_pairs.each do |pair|
|
34
|
+
next if pair[0] == :item_id
|
35
|
+
hash[pair[0]] = pair[1]
|
36
|
+
end
|
37
|
+
hash
|
38
|
+
end
|
39
39
|
|
40
|
-
|
40
|
+
private
|
41
41
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
42
|
+
def mapping
|
43
|
+
unless @mapping
|
44
|
+
@mapping = {
|
45
|
+
:left => :lft,
|
46
|
+
:right => :rgt
|
47
|
+
}
|
48
|
+
class << @mapping
|
49
|
+
def [](key)
|
50
|
+
self.keys.include?(key) ? self.fetch(key) : key
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
@mapping
|
55
|
+
end
|
56
56
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
57
|
+
def convert(value)
|
58
|
+
if value == ROOT
|
59
|
+
@root.id
|
60
|
+
elsif value == NONE || !value
|
61
|
+
nil
|
62
|
+
else
|
63
|
+
value.to_i
|
64
|
+
end
|
65
|
+
end
|
66
66
|
|
67
|
-
|
68
|
-
|
69
|
-
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
70
|
end
|
@@ -1,87 +1,87 @@
|
|
1
1
|
module Lolita
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
2
|
+
module Menu
|
3
|
+
module NestedTree
|
4
|
+
module ClassMethods
|
5
|
+
# Allows to use tree structure relationship between model records
|
6
|
+
#
|
7
|
+
# ===
|
8
|
+
#
|
9
|
+
# Usage:
|
10
|
+
#
|
11
|
+
# lolita_nested_tree :scope => "User"
|
12
|
+
#
|
13
|
+
# ===
|
14
|
+
#
|
15
|
+
# Configuration options:
|
16
|
+
# * <tt>:scope</tt> - model name (as String)
|
17
|
+
#
|
18
|
+
def lolita_nested_tree(options = {})
|
19
|
+
@lolita_nested_tree ||= Lolita::Menu::NestedTree::Configuration.new(self, options)
|
20
|
+
end
|
21
|
+
end
|
22
22
|
|
23
|
-
|
23
|
+
class Configuration
|
24
24
|
|
25
|
-
|
25
|
+
attr_reader :klass, :options, :scope, :build_method
|
26
26
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
27
|
+
def initialize(base_class, options = {})
|
28
|
+
@klass = base_class
|
29
|
+
@options = options || {}
|
30
|
+
initialize_options
|
31
|
+
normalize_attributes
|
32
|
+
validate
|
33
|
+
extend_scope_classes
|
34
|
+
end
|
35
35
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
36
|
+
def scope_keys
|
37
|
+
@scope.map do |scope|
|
38
|
+
@klass.reflect_on_association(scope.to_sym).foreign_key.to_sym
|
39
|
+
end
|
40
|
+
end
|
41
41
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
42
|
+
def scope_key_for(klass)
|
43
|
+
scope_reflections.detect do |reflection|
|
44
|
+
reflection.klass == klass
|
45
|
+
end.foreign_key.to_sym
|
46
|
+
end
|
47
47
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
48
|
+
def scope_classes
|
49
|
+
@scope.map do |scope|
|
50
|
+
@klass.reflect_on_association(scope.to_sym).klass
|
51
|
+
end
|
52
|
+
end
|
53
53
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
54
|
+
def scope_reflections
|
55
|
+
@scope.map do |scope|
|
56
|
+
@klass.reflect_on_association(scope.to_sym)
|
57
|
+
end
|
58
|
+
end
|
59
59
|
|
60
|
-
|
60
|
+
private
|
61
61
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
62
|
+
def initialize_options
|
63
|
+
@options.each do |attr, value|
|
64
|
+
self.respond_to?(attr.to_sym) && instance_variable_set(:"@#{attr}", value)
|
65
|
+
end
|
66
|
+
end
|
67
67
|
|
68
|
-
|
69
|
-
|
70
|
-
|
68
|
+
def normalize_attributes
|
69
|
+
@scope = (@scope.is_a?(Array) && @scope || [@scope]).compact
|
70
|
+
end
|
71
71
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
72
|
+
def validate
|
73
|
+
@scope.each do |scope|
|
74
|
+
raise ArgumentError, "#{@klass} should reflect on association #{scope}" unless @klass.reflect_on_association(scope.to_sym)
|
75
|
+
end
|
76
|
+
end
|
77
77
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
78
|
+
def extend_scope_classes
|
79
|
+
scope_classes.each do |klass|
|
80
|
+
klass.send(:include, Lolita::Menu::NestedTree::Scope)
|
81
|
+
end
|
82
|
+
end
|
83
83
|
|
84
|
-
|
85
|
-
|
86
|
-
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
87
|
end
|
@@ -1,54 +1,54 @@
|
|
1
1
|
module Lolita
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
2
|
+
module Menu
|
3
|
+
module NestedTree
|
4
|
+
module Scope
|
5
|
+
|
6
|
+
def self.included(base_class)
|
7
|
+
base_class.extend(ClassMethods)
|
8
|
+
base_class.send(:include, InstanceMethods)
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
def sees?(tree_class)
|
13
|
+
tree_class.lolita_nested_tree.scope_classes.include?(self)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
module InstanceMethods
|
18
|
+
def sees?(tree_class)
|
19
|
+
self.class.sees?(tree_class)
|
20
|
+
end
|
21
|
+
|
22
|
+
def root(tree_class, scope_attributes = {})
|
23
|
+
tree_class.find_or_create_root(merge_scope_with_self(tree_class, scope_attributes))
|
24
|
+
end
|
25
|
+
|
26
|
+
def children(*args)
|
27
|
+
self.root(*args).children
|
28
|
+
end
|
29
|
+
|
30
|
+
def append(item, scope_attributes = {})
|
31
|
+
scope_attributes = merge_scope_with_self(item.class, scope_attributes)
|
32
|
+
scope_root = self.root(item.class, scope_attributes)
|
33
|
+
item.class.with_tree_scope(scope_attributes) do
|
34
|
+
scope_root.append(item)
|
35
|
+
item.reload
|
36
|
+
end
|
37
|
+
item
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def merge_scope_with_self(tree_class, scope_attributes)
|
43
|
+
scope_attributes.merge(tree_key(tree_class) => self.id)
|
44
|
+
end
|
45
|
+
|
46
|
+
def tree_key(klass)
|
47
|
+
klass.lolita_nested_tree.scope_key_for(self.class)
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
54
|
end
|
@@ -1,38 +1,38 @@
|
|
1
1
|
module Lolita
|
2
|
-
|
3
|
-
|
4
|
-
|
2
|
+
module Menu
|
3
|
+
module NestedTree
|
4
|
+
class TreeBuilder
|
5
5
|
|
6
|
-
|
6
|
+
attr_reader :items, :root
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
8
|
+
def initialize(klass, items, scope_attributes = {})
|
9
|
+
@klass = klass
|
10
|
+
@scope_attributes = scope_attributes
|
11
|
+
@root = @klass.find_or_create_root(@scope_attributes)
|
12
|
+
@items = items.is_a?(Hash) ? items.values : items
|
13
|
+
@items.map! do |item|
|
14
|
+
Lolita::Menu::NestedTree::BranchBuilder.new(@root, item)
|
15
|
+
end
|
16
|
+
end
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
|
18
|
+
def item_ids
|
19
|
+
@items.map { |item| item.value_for(:item_id) }
|
20
|
+
end
|
21
21
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
22
|
+
def update_items
|
23
|
+
@items.each do |item|
|
24
|
+
@klass.update_item(item)
|
25
|
+
end
|
26
|
+
@klass.remove_items(deleted_items)
|
27
|
+
end
|
28
28
|
|
29
|
-
|
29
|
+
private
|
30
30
|
|
31
|
-
|
32
|
-
|
33
|
-
|
31
|
+
def deleted_items
|
32
|
+
@klass.with_tree_scope(@scope_attributes).all_tree_item_ids - item_ids
|
33
|
+
end
|
34
34
|
|
35
|
-
|
36
|
-
|
37
|
-
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
38
|
end
|