lolita-menu 0.0.6

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