lolita-menu 0.0.6

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.
Files changed (42) hide show
  1. data/.document +5 -0
  2. data/Gemfile +15 -0
  3. data/History.rdoc +24 -0
  4. data/LICENSE.txt +20 -0
  5. data/README.rdoc +17 -0
  6. data/Rakefile +53 -0
  7. data/VERSION +1 -0
  8. data/app/controllers/menu_items_controller.rb +41 -0
  9. data/app/models/menu.rb +139 -0
  10. data/app/models/menu_item.rb +130 -0
  11. data/app/views/components/lolita/menu/_columns.html.haml +2 -0
  12. data/app/views/components/lolita/menu/_columns_body.html.haml +11 -0
  13. data/app/views/components/lolita/menu/_list.html.haml +11 -0
  14. data/app/views/components/lolita/menu_items/_display.html.haml +7 -0
  15. data/app/views/components/lolita/menu_items/_row.html.haml +10 -0
  16. data/app/views/components/lolita/menu_items/_subtree.html.haml +2 -0
  17. data/config/locales/en.yml +5 -0
  18. data/config/locales/lv.yml +5 -0
  19. data/config/routes.rb +8 -0
  20. data/lib/generators/lolita_menu/assets_generator.rb +13 -0
  21. data/lib/generators/lolita_menu/install_generator.rb +19 -0
  22. data/lib/generators/lolita_menu/templates/migrations/create_menu_items.rb +27 -0
  23. data/lib/generators/lolita_menu/templates/migrations/create_menus.rb +14 -0
  24. data/lib/lolita-menu.rb +13 -0
  25. data/lib/lolita-menu/module.rb +1 -0
  26. data/lib/lolita-menu/rails.rb +5 -0
  27. data/lolita-menu.gemspec +113 -0
  28. data/public/javascripts/jquery-ui-nested-sortables.js +356 -0
  29. data/public/javascripts/lolita/menu-items.js +86 -0
  30. data/public/stylesheets/lolita/menu/style.css +16 -0
  31. data/spec/controllers/menu_items_controller_spec.rb +5 -0
  32. data/spec/models/menu_item_spec.rb +47 -0
  33. data/spec/models/menu_spec.rb +79 -0
  34. data/spec/spec_helper.rb +22 -0
  35. data/test_orm/active_record.rb +9 -0
  36. data/test_orm/boot.rb +9 -0
  37. data/test_orm/config/active_record.yml +2 -0
  38. data/test_orm/db/migrate/01_create_lolita_menus.rb +14 -0
  39. data/test_orm/db/migrate/02_create_lolita_menu_items.rb +27 -0
  40. data/test_orm/rails/config/application.rb +8 -0
  41. data/test_orm/rails/config/enviroment.rb +5 -0
  42. metadata +213 -0
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/Gemfile ADDED
@@ -0,0 +1,15 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem 'lolita','~>3.1.6'
4
+ gem 'haml', :require=>true
5
+
6
+ group :development, :test do
7
+ gem 'rspec', '~>2.6.0'
8
+ gem "sqlite3"
9
+ gem 'rspec-rails', '~>2.6.0'
10
+ gem 'haml-rails'
11
+ gem "shoulda", ">= 0"
12
+ gem "bundler", "~> 1.0.0"
13
+ gem "jeweler", "~> 1.5.2"
14
+ gem "rcov", ">= 0"
15
+ end
@@ -0,0 +1,24 @@
1
+ === Version 0.0.6 / 2011-05-19
2
+ * Bug fixes
3
+ * Can't recognize HAML files in projects without haml.
4
+
5
+ === Version 0.0.5 / 2011-05-19
6
+ * Bug fixes
7
+ * HAML dependencies not supported
8
+
9
+ === Version 0.0.4 / 2011-05-19
10
+ * Bug fixes
11
+ * Wrong table names for models
12
+
13
+ === Version 0.0.3 / 2011-05-19
14
+ * Enhancements
15
+ * README updated
16
+
17
+ === Version 0.0.2 / 2011-05-19
18
+ * Bug fixes
19
+ * migrations have one timestamp
20
+ * migration names does not match class names
21
+
22
+ === Version 0.0.1 / 2011-05-19
23
+ * Basic models, controllers, views
24
+ * JavaScripts, Style, Migrations
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 ITHouse, Arturs Meisters
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,17 @@
1
+ = Lolita Menu
2
+ Create, sort and publish menus with Lolita.
3
+
4
+ ==Instalation
5
+ In your project Gemfile put
6
+ gem 'lolita-menu'
7
+ And run
8
+ bundle install
9
+ Like with other Lolita gems call
10
+ rails g lolita_menu:install
11
+ Since this gem comes with migrations
12
+ rake db:migrate
13
+
14
+ Enjoy!
15
+
16
+ ==Copyright
17
+ Copyright © 2011 ITHouse and Arturs Meisters. See LICENSE.txt for further details.
@@ -0,0 +1,53 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'rake'
11
+
12
+ require 'jeweler'
13
+ Jeweler::Tasks.new do |gem|
14
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
15
+ gem.name = "lolita-menu"
16
+ gem.homepage = "http://github.com/ithouse/lolita-menu"
17
+ gem.license = "MIT"
18
+ gem.summary = %Q{Menu managing plugin for Lolita.}
19
+ gem.description = %Q{Manage public menus for each project inside Lolita.}
20
+ gem.email = "support@ithouse.lv"
21
+ gem.authors = ["ITHouse","Arturs Meisters"]
22
+ # Include your dependencies below. Runtime dependencies are required when using your gem,
23
+ # and development dependencies are only needed for development (ie running rake tasks, tests, etc)
24
+ # gem.add_runtime_dependency 'jabber4r', '> 0.1'
25
+ # gem.add_development_dependency 'rspec', '> 1.2.3'
26
+ end
27
+ Jeweler::RubygemsDotOrgTasks.new
28
+
29
+ require 'rake/testtask'
30
+ Rake::TestTask.new(:test) do |test|
31
+ test.libs << 'lib' << 'test'
32
+ test.pattern = 'test/**/test_*.rb'
33
+ test.verbose = true
34
+ end
35
+
36
+ require 'rcov/rcovtask'
37
+ Rcov::RcovTask.new do |test|
38
+ test.libs << 'test'
39
+ test.pattern = 'test/**/test_*.rb'
40
+ test.verbose = true
41
+ end
42
+
43
+ task :default => :test
44
+
45
+ require 'rake/rdoctask'
46
+ Rake::RDocTask.new do |rdoc|
47
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
48
+
49
+ rdoc.rdoc_dir = 'rdoc'
50
+ rdoc.title = "lolita-menu #{version}"
51
+ rdoc.rdoc_files.include('README*')
52
+ rdoc.rdoc_files.include('lib/**/*.rb')
53
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.6
@@ -0,0 +1,41 @@
1
+ # encoding: utf-8
2
+ class MenuItemsController < ActionController::Base
3
+ include Lolita::Controllers::UserHelpers
4
+
5
+ before_filter :authenticate_lolita_user!
6
+
7
+ def create
8
+ menu=Menu.find_by_id(params[:menu_id])
9
+ item=MenuItem.create!(:name=>"new item",:url=>"/",:menu_id=>params[:menu_id])
10
+ menu.append(item)
11
+ render :partial=>"row", :locals=>{:item=>item}
12
+ end
13
+
14
+ def update
15
+ if item=MenuItem.find_by_id(params[:id])
16
+ item.send(:"#{params[:attribute]}=",params[:value])
17
+ item.save
18
+ render :json=>{:status=>item.errors.any? ? "error" : "saved"}
19
+ else
20
+ render :json=>{:status=>"error"}
21
+ end
22
+ end
23
+
24
+ def update_tree
25
+ menu=Menu.find_by_id(params[:menu_id])
26
+
27
+ if menu && menu.update_whole_tree(params[:items])
28
+ response.headers["Lolita-Notice"] = I18n.t("lolita.menu.notice")
29
+ else
30
+ response.headers["Lolita-Error"] = I18n.t("lolita.menu.error")
31
+ end
32
+
33
+ render :nothing=>true
34
+ end
35
+
36
+ def destroy
37
+ item=MenuItem.find_by_id(params[:id])
38
+ item.destroy
39
+ render :json=>{:id=>item.id}
40
+ end
41
+ end
@@ -0,0 +1,139 @@
1
+ class Menu < ActiveRecord::Base
2
+ set_table_name "lolita_menus"
3
+
4
+ has_many :items, :class_name => "MenuItem", :dependent => :destroy
5
+
6
+ validates :name, :presence => true
7
+
8
+ after_create :create_root, :unless=>:have_root?
9
+
10
+ include Lolita::Configuration
11
+
12
+ lolita do
13
+ list do
14
+ column :name
15
+ builder :name=>"lolita/menu",:state=>"list"
16
+ end
17
+ tab(:content) do
18
+ field :name
19
+ end
20
+ end
21
+
22
+ POSITION_ATTRIBUTES = {:left => :lft,:right => :rgt,:depth => :depth,:parent_id => :parent_id}
23
+ ROOT = "root"
24
+ NONE = "none"
25
+
26
+ def position_attributes
27
+ POSITION_ATTRIBUTES
28
+ end
29
+
30
+ def root
31
+ if item=self.items.first
32
+ item.root
33
+ end
34
+ end
35
+
36
+ def children
37
+ self.root.children
38
+ end
39
+
40
+ def append(item)
41
+ unless item.menu_id == self.id
42
+ item.menu_id = self.id
43
+ item.save!
44
+ end
45
+ self.root.append(item)
46
+ item.reload
47
+ end
48
+
49
+ def have_root?
50
+ !!self.root
51
+ end
52
+
53
+ def update_whole_tree(items)
54
+ begin
55
+ self.class.transaction do
56
+ update_all_items(items)
57
+ true
58
+ end
59
+ end
60
+ end
61
+
62
+ protected
63
+
64
+
65
+ def update_all_items(items)
66
+ items_ids=items_with_acumulated_ids(items) do |item|
67
+ update_item(item)
68
+ end
69
+ remove_deleted_items(items_ids)
70
+ end
71
+
72
+ def items_with_acumulated_ids(items)
73
+ item_ids=[]
74
+ items.each do |key,item|
75
+ item_ids << item[:item_id]
76
+ yield item
77
+ end
78
+ return item_ids
79
+ end
80
+
81
+ def update_item(item)
82
+ if is_root?(item)
83
+ update_root(item)
84
+ else
85
+ update_branch(item)
86
+ end
87
+ end
88
+
89
+ def is_root?(item)
90
+ item[:item_id]==ROOT
91
+ end
92
+
93
+ def update_root(item)
94
+ MenuItem.update_all(update_values_for(item),"id=#{root.id}")
95
+ end
96
+
97
+ def update_branch(item)
98
+ MenuItem.update_all(update_values_for(item),"id=#{item[:item_id].to_i}")
99
+ end
100
+
101
+ def update_values_for(item)
102
+ values=[]
103
+ position_attributes.each do |attr_name,field|
104
+ value = value_for_attribute(attr_name,item)
105
+ values<<"#{field}=#{value}"
106
+ end
107
+ values.join(", ")
108
+ end
109
+
110
+ def value_for_attribute(attr_name,item)
111
+ if item[attr_name]==NONE || !item[attr_name]
112
+ "NULL"
113
+ elsif item[attr_name]==ROOT
114
+ root.id
115
+ else
116
+ item[attr_name].to_i
117
+ end
118
+ end
119
+
120
+ def remove_deleted_items(ids)
121
+ deleted_ids=deleted_items_ids(ids)
122
+ if deleted_ids.any?
123
+ MenuItem.delete_all("id IN (#{deleted_ids.join(",")})")
124
+ end
125
+ end
126
+
127
+ def deleted_items_ids(ids)
128
+ ids = ids.map{|id| id=="root" ? root.id : id.to_i}
129
+ all_ids = MenuItem.where(:menu_id=>self.id).map{|item| item.id}
130
+ all_ids - ids
131
+ end
132
+
133
+ private
134
+
135
+ def create_root
136
+ MenuItem.create_root!(self)
137
+ end
138
+
139
+ end
@@ -0,0 +1,130 @@
1
+ class MenuItem < ActiveRecord::Base
2
+ set_table_name "lolita_menu_items"
3
+
4
+ attr_accessor :place
5
+
6
+ belongs_to :menu, :class_name => "Menu"
7
+
8
+ validates :name,:presence => true
9
+ validates :url, :format => {:with => /^\/.*/}, :unless=>:root?
10
+
11
+ before_create :set_default_positions
12
+ after_create :put_in_place
13
+ before_validation :set_default_values, :if=>:new_record?
14
+
15
+ class << self
16
+ def create_root!(menu)
17
+ new_item = self.create!({:menu_id => menu.id}.merge(default_root_position))
18
+ menu.items << new_item
19
+ end
20
+
21
+ def default_root_position
22
+ {
23
+ :lft=>1,
24
+ :rgt=>2,
25
+ :depth=>0,
26
+ :parent_id=>nil
27
+ }
28
+ end
29
+ end
30
+ # instance methods
31
+
32
+ def visible?
33
+ self.is_visible
34
+ end
35
+
36
+ def root?
37
+ self.parent_id.nil?
38
+ end
39
+
40
+ def parent
41
+ @parent||=self.class.find_by_id(self.parent_id)
42
+ @parent
43
+ end
44
+
45
+ def parent=(parent_item)
46
+ @parent=parent_item
47
+ end
48
+
49
+ def children
50
+ @children||=self.class.where("lft>:left AND rgt<:right AND depth=:depth AND menu_id=:menu",{
51
+ :left => self.lft,:right => self.rgt,:depth => self.depth+1, :menu=>self.menu_id
52
+ }).order("lft asc")
53
+ @children
54
+ end
55
+
56
+ def root
57
+ if self.parent_id.nil?
58
+ self
59
+ else
60
+ self.class.where(:menu_id=>self.menu_id,:parent_id=>nil).first
61
+ end
62
+ end
63
+
64
+ def append(item)
65
+ append_item(item)
66
+ recalculate_positions_after(:append)
67
+ end
68
+
69
+ private
70
+
71
+ def append_item(item)
72
+ position=position_for_append
73
+ self.class.update_all(positions_to_update_string(position),"id=#{item.id}")
74
+ end
75
+
76
+ def recalculate_positions_after(action)
77
+ if action==:append
78
+ self.class.update_all("rgt=#{self.rgt+2}","id=#{self.id}")
79
+ end
80
+ end
81
+
82
+ def positions_to_update_string(positions)
83
+ result=[]
84
+ positions.each do |field,value|
85
+ result<<"#{field}=#{value}"
86
+ end
87
+ result.join(", ")
88
+ end
89
+
90
+ def position_for_append
91
+ {
92
+ :lft => self.rgt,
93
+ :rgt => self.rgt+1,
94
+ :depth => self.depth+1,
95
+ :parent_id => self.id
96
+ }
97
+ end
98
+
99
+ def set_default_positions
100
+ if am_i_new_root? || !menu_id
101
+ set_root_position
102
+ else
103
+ self.place=:append
104
+ end
105
+ end
106
+
107
+ def am_i_new_root?
108
+ self.menu_id && Menu.exists?(menu_id) && !MenuItem.where(:menu_id=>menu_id).first
109
+ end
110
+
111
+ def set_root_position
112
+ self.class.default_root_position.each do |method,value|
113
+ unless self.send(method)
114
+ self.send(:"#{method}=",value)
115
+ end
116
+ end
117
+ end
118
+
119
+ def put_in_place
120
+ if place==:append
121
+ self.menu.root.append(self)
122
+ end
123
+ end
124
+
125
+ def set_default_values
126
+ self.name||="root"
127
+ self.url||="/"
128
+ end
129
+
130
+ end