lolita-menu 0.3.6 → 0.4.0
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 +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
|