adva-categories 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/app/controllers/admin/categories_controller.rb +4 -0
- data/app/controllers/base_controller_slice.rb +15 -0
- data/app/helpers/url_helper_slice.rb +11 -0
- data/app/models/categorization.rb +5 -0
- data/app/models/category.rb +13 -0
- data/app/models/content_slice.rb +2 -0
- data/app/models/section_slice.rb +4 -0
- data/app/views/admin/categories/_form.html.rb +17 -0
- data/app/views/admin/categories/_menu.html.rb +21 -0
- data/app/views/admin/categories/edit.html.rb +9 -0
- data/app/views/admin/categories/index.html.rb +20 -0
- data/app/views/admin/categories/new.html.rb +10 -0
- data/config/locales/en.yml +32 -0
- data/config/redirects.rb +4 -0
- data/config/routes.rb +13 -0
- data/lib/adva/active_record/categorizable.rb +26 -0
- data/lib/adva/categories.rb +13 -0
- data/lib/adva/routing_filters/categories.rb +81 -0
- data/lib/adva/views/categories_tab.rb +14 -0
- data/lib/adva-categories.rb +1 -0
- data/lib/adva_categories/version.rb +3 -0
- data/lib/testing/factories.rb +4 -0
- data/lib/testing/paths.rb +15 -0
- data/lib/testing/step_definitions.rb +21 -0
- metadata +28 -5
- data/lib/bundler/repository.rb +0 -118
@@ -0,0 +1,15 @@
|
|
1
|
+
require_dependency 'base_controller'
|
2
|
+
|
3
|
+
BaseController.class_eval do
|
4
|
+
include do
|
5
|
+
def collection
|
6
|
+
params[:category_id] ? super.categorized(params[:category_id]) : super
|
7
|
+
end
|
8
|
+
|
9
|
+
def category
|
10
|
+
params[:category_id] ? Category.find(params[:category_id]) : nil
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
helper_method :category
|
15
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class Category < ActiveRecord::Base
|
2
|
+
belongs_to :section
|
3
|
+
validates_presence_of :section, :name
|
4
|
+
|
5
|
+
acts_as_nested_set :scope => :section_id
|
6
|
+
has_slug :scope => :section_id
|
7
|
+
|
8
|
+
class << self
|
9
|
+
def categorizable(table)
|
10
|
+
Category.has_many_polymorphs :categorizables, :through => :categorizations, :foreign_key => 'category_id', :from => [table]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class Admin::Categories::Menu < Adva::View::Menu::Admin::Actions
|
2
|
+
include do
|
3
|
+
def main
|
4
|
+
parent_resource_label
|
5
|
+
index
|
6
|
+
edit_parent
|
7
|
+
end
|
8
|
+
|
9
|
+
def right
|
10
|
+
collection? ? new : destroy
|
11
|
+
reorder if index? && collection.size > 1
|
12
|
+
end
|
13
|
+
|
14
|
+
protected
|
15
|
+
|
16
|
+
def reorder
|
17
|
+
super( :'data-resource_type' => 'section', :'data-sortable_type' => 'categories')
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class Admin::Categories::Index < Minimal::Template
|
2
|
+
include do
|
3
|
+
def to_html
|
4
|
+
table_for collection, :class => 'categories list tree' do |t|
|
5
|
+
t.column :category, :actions
|
6
|
+
|
7
|
+
t.row do |r, category|
|
8
|
+
r.options[:id] = dom_id(category)
|
9
|
+
r.options[:class] = "level_#{category.level.to_i}"
|
10
|
+
r.cell capture { link_to_edit(category.name, category) }
|
11
|
+
r.cell links_to_actions([:edit, :destroy], category)
|
12
|
+
end
|
13
|
+
|
14
|
+
t.empty :p, :class => 'categories list empty' do
|
15
|
+
self.t(:'.empty', :link => capture { link_to(:'.create_item', new_path(:category)) }).html_safe
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
en:
|
2
|
+
menu:
|
3
|
+
categories: Categories
|
4
|
+
columns:
|
5
|
+
category: Category
|
6
|
+
actions:
|
7
|
+
categories:
|
8
|
+
columns:
|
9
|
+
category: Category
|
10
|
+
actions:
|
11
|
+
admin:
|
12
|
+
categories:
|
13
|
+
# menu:
|
14
|
+
# index: Categories
|
15
|
+
# products: Products
|
16
|
+
# new: New Category
|
17
|
+
# edit_parent: Settings
|
18
|
+
# destroy: Delete
|
19
|
+
# reorder: Reorder
|
20
|
+
# confirm_destroy: Do you really want to delete this category?
|
21
|
+
index:
|
22
|
+
create_item: Create Category
|
23
|
+
edit: Edit
|
24
|
+
destroy: Delete
|
25
|
+
confirm_destroy: Do you really want to delete this category?
|
26
|
+
empty: "There are no categories. %{link}"
|
27
|
+
new:
|
28
|
+
title: Create a Category
|
29
|
+
edit:
|
30
|
+
title: Edit Category
|
31
|
+
tabs:
|
32
|
+
categories: Categories
|
data/config/redirects.rb
ADDED
data/config/routes.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
module Adva
|
2
|
+
module Categorizable
|
3
|
+
def categorizable
|
4
|
+
unless categorizable?
|
5
|
+
Category.categorizable(name.underscore.pluralize.to_sym)
|
6
|
+
has_many :categories, :through => :categorizations, :as => :categorizable
|
7
|
+
accepts_nested_attributes_for :categorizations, :allow_destroy => true
|
8
|
+
extend ClassMethods
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def categorizable?
|
13
|
+
singleton_class.included_modules.include?(ClassMethods)
|
14
|
+
end
|
15
|
+
|
16
|
+
module ClassMethods
|
17
|
+
def categorized(category_id)
|
18
|
+
category = Category.find(category_id)
|
19
|
+
category_ids = category.self_and_descendants.map(&:id)
|
20
|
+
includes(:categorizations).where(:categorizations => { :category_id => category_ids })
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
ActiveRecord::Base.extend(self)
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'adva/core'
|
2
|
+
require 'adva/active_record/categorizable'
|
3
|
+
require 'adva/views/categories_tab'
|
4
|
+
|
5
|
+
module Adva
|
6
|
+
class Categories < ::Rails::Engine
|
7
|
+
include Adva::Engine
|
8
|
+
|
9
|
+
initializer 'adva-categories.configure_routing_filters' do
|
10
|
+
RoutingFilter::SectionRoot.anchors << 'categories'
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'routing_filter'
|
2
|
+
|
3
|
+
module RoutingFilter
|
4
|
+
class Categories < Filter
|
5
|
+
cattr_accessor :default_port
|
6
|
+
self.default_port = '80'
|
7
|
+
|
8
|
+
cattr_accessor :exclude
|
9
|
+
self.exclude = %r(^/admin)
|
10
|
+
|
11
|
+
def around_recognize(path, env, &block)
|
12
|
+
# p "#{self.class.name}: #{path}"
|
13
|
+
unless excluded?(path)
|
14
|
+
category_id = extract_category_id(env, path)
|
15
|
+
yield.tap do |params|
|
16
|
+
params[:category_id] = category_id if category_id
|
17
|
+
end
|
18
|
+
else
|
19
|
+
yield
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def around_generate(params, &block)
|
24
|
+
category_id = params.delete('category_id') || params.delete(:category_id)
|
25
|
+
yield.tap do |path|
|
26
|
+
# p "#{self.class.name}: #{path}"
|
27
|
+
insert_category_path(path, category_id) if !excluded?(path) && category_id
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
protected
|
32
|
+
|
33
|
+
def excluded?(path)
|
34
|
+
path =~ exclude
|
35
|
+
end
|
36
|
+
|
37
|
+
def extract_category_id(env, path)
|
38
|
+
if section = section_for(env, path) and path =~ recognition_pattern(section)
|
39
|
+
if category = section.categories.where(:path => $2).first
|
40
|
+
path.gsub!("#{$1}#{$2}", '')
|
41
|
+
path.replace('/') if path.blank?
|
42
|
+
category.id.to_s
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def recognition_pattern(section)
|
48
|
+
paths = section.categories.map(&:path).reject(&:blank?)
|
49
|
+
paths = paths.sort { |a, b| b.size <=> a.size }.join('|')
|
50
|
+
paths.empty? ? %r(^$) : %r(^.*(/categories/)(#{paths})(?=/|\.|\?|$))
|
51
|
+
end
|
52
|
+
|
53
|
+
def insert_category_path(path, category_id)
|
54
|
+
category = Category.find(category_id)
|
55
|
+
if path =~ section_pattern
|
56
|
+
path.sub!("/#{$1}/#{$2}", "/#{$1}/#{$2}/categories/#{category.path}")
|
57
|
+
elsif category.section.root?
|
58
|
+
path.sub!(%r(^/), "/categories/#{category.path}")
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def section_pattern
|
63
|
+
types = Section.types.map { |type| type.downcase.pluralize }.join('|')
|
64
|
+
%r(/(sections|#{types})/(\d+)(?=/|\.|\?|$))
|
65
|
+
end
|
66
|
+
|
67
|
+
def section_for(env, path)
|
68
|
+
if path =~ section_pattern
|
69
|
+
Section.find($2)
|
70
|
+
elsif path =~ %r(^/categories/\w+) && site = Site.by_host(host(env))
|
71
|
+
site.sections.root
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def host(env)
|
76
|
+
host, port = env.values_at('SERVER_NAME', 'SERVER_PORT')
|
77
|
+
port == default_port ? host : [host, port].compact.join(':')
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# this should rather be a code slice, shouldn't it? but slices don't support files
|
2
|
+
# that aren't loadable through require_dependency
|
3
|
+
|
4
|
+
require 'adva/view/form'
|
5
|
+
|
6
|
+
Adva::View::Form.class_eval do
|
7
|
+
def categories_tab(categories)
|
8
|
+
tab :categories do
|
9
|
+
fieldset do
|
10
|
+
form.has_many_through_collection_check_boxes(:categorizations, categories, :name)
|
11
|
+
end
|
12
|
+
end if categories.present?
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'adva/categories'
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Adva::Categories::Paths
|
2
|
+
def path_to(page)
|
3
|
+
case page
|
4
|
+
when /^the admin "([^"]*)" section categories page$/
|
5
|
+
site = Site.first
|
6
|
+
section = Section.where(:name => $1).first || raise("could not find section named #{$1}")
|
7
|
+
polymorphic_path([:admin, site, section, :categories])
|
8
|
+
else
|
9
|
+
super
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
World(Adva::Categories::Paths)
|
15
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
When /^I drag the category "([^"]*)" below the category "([^"]*)"$/ do |category, target|
|
2
|
+
category = Category.find_by_name(category)
|
3
|
+
target = Category.find_by_name(target)
|
4
|
+
section = Section.first
|
5
|
+
put(admin_site_section_path(section.site, section), {
|
6
|
+
:section => { :categories_attributes => [{ :id => category.id, :left_id => target.id }] },
|
7
|
+
:return_to => @request.url
|
8
|
+
})
|
9
|
+
follow_redirect!
|
10
|
+
end
|
11
|
+
|
12
|
+
Then /^the ([\w]+) (name|title)d "([^"]+)" should be categorized as "([^"]+)"$/ do |model, attribute, value, category|
|
13
|
+
record = model.classify.constantize.where(attribute => value).first
|
14
|
+
assert record.categories.map(&:name).include?(category), "expected #{record.inspect} to be categorized as #{category}"
|
15
|
+
end
|
16
|
+
|
17
|
+
Then /^the ([\w]+) (name|title)d "([^"]+)" should not be categorized as "([^"]+)"$/ do |model, attribute, value, category|
|
18
|
+
record = model.classify.constantize.where(attribute => value).first
|
19
|
+
assert !record.categories.map(&:name).include?(category), "expected #{record.inspect} not to be categorized as #{category}"
|
20
|
+
end
|
21
|
+
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: adva-categories
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 23
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 4
|
10
|
+
version: 0.0.4
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Ingo Weiss
|
@@ -16,7 +16,7 @@ autorequire:
|
|
16
16
|
bindir: bin
|
17
17
|
cert_chain: []
|
18
18
|
|
19
|
-
date: 2010-
|
19
|
+
date: 2010-12-03 00:00:00 +01:00
|
20
20
|
default_executable:
|
21
21
|
dependencies:
|
22
22
|
- !ruby/object:Gem::Dependency
|
@@ -42,7 +42,30 @@ extensions: []
|
|
42
42
|
extra_rdoc_files: []
|
43
43
|
|
44
44
|
files:
|
45
|
-
-
|
45
|
+
- app/controllers/admin/categories_controller.rb
|
46
|
+
- app/controllers/base_controller_slice.rb
|
47
|
+
- app/helpers/url_helper_slice.rb
|
48
|
+
- app/views/admin/categories/index.html.rb
|
49
|
+
- app/views/admin/categories/edit.html.rb
|
50
|
+
- app/views/admin/categories/new.html.rb
|
51
|
+
- app/views/admin/categories/_form.html.rb
|
52
|
+
- app/views/admin/categories/_menu.html.rb
|
53
|
+
- app/models/content_slice.rb
|
54
|
+
- app/models/category.rb
|
55
|
+
- app/models/categorization.rb
|
56
|
+
- app/models/section_slice.rb
|
57
|
+
- config/redirects.rb
|
58
|
+
- config/routes.rb
|
59
|
+
- config/locales/en.yml
|
60
|
+
- lib/adva-categories.rb
|
61
|
+
- lib/adva_categories/version.rb
|
62
|
+
- lib/adva/categories.rb
|
63
|
+
- lib/adva/active_record/categorizable.rb
|
64
|
+
- lib/adva/routing_filters/categories.rb
|
65
|
+
- lib/adva/views/categories_tab.rb
|
66
|
+
- lib/testing/factories.rb
|
67
|
+
- lib/testing/paths.rb
|
68
|
+
- lib/testing/step_definitions.rb
|
46
69
|
has_rdoc: true
|
47
70
|
homepage: http://github.com/svenfuchs/adva-cms2
|
48
71
|
licenses: []
|
data/lib/bundler/repository.rb
DELETED
@@ -1,118 +0,0 @@
|
|
1
|
-
require 'pathname'
|
2
|
-
|
3
|
-
# Bundler gemfile support for local/remote workspaces/repositories for work in
|
4
|
-
# development teams.
|
5
|
-
#
|
6
|
-
# Usage:
|
7
|
-
#
|
8
|
-
# # define paths to be searched for repositories:
|
9
|
-
# workspace '~/.projects ~/Development/{projects,work}'
|
10
|
-
#
|
11
|
-
# # define developer preferences for using local or remote repositories (uses ENV['user']):
|
12
|
-
# developer :sven, :prefer => :local
|
13
|
-
#
|
14
|
-
# # define repositories to be used for particular gems:
|
15
|
-
# adva_cms = repository('adva-cms2', :git => 'git@github.com:svenfuchs/adva-cms2.git', :ref => 'c2af0de')
|
16
|
-
# adva_shop = repository('adva-shop', :source => :local)
|
17
|
-
#
|
18
|
-
# # now use repositories to define gems:
|
19
|
-
# adva_cms.gem 'adva-core'
|
20
|
-
# adva_shop.gem 'adva-catalog'
|
21
|
-
#
|
22
|
-
# # The gem definition will now be proxied to Bundler with arguments according
|
23
|
-
# # to the setup defined earlier. E.g. as:
|
24
|
-
#
|
25
|
-
# gem 'adva-core', :path => 'Development/projects/adva-cms2/adva-core' # for developer 'sven'
|
26
|
-
# gem 'adva-core', :git => 'git@github.com:svenfuchs/adva-cms2.git', :ref => 'c2af0de' # for other developers
|
27
|
-
# gem 'adva-catalog', :path => 'Development/projects/adva-shop/adva-catalog' # for all developers
|
28
|
-
#
|
29
|
-
# One can also set an environment variable FORCE_REMOTE which will force remote
|
30
|
-
# repositories to be used *except* when a repository was defined with :source => :local
|
31
|
-
# which always forces the local repository to be used.
|
32
|
-
#
|
33
|
-
class Repository
|
34
|
-
class << self
|
35
|
-
def paths
|
36
|
-
@paths ||= []
|
37
|
-
end
|
38
|
-
|
39
|
-
def path(*paths)
|
40
|
-
paths.join(' ').split(' ').each do |path|
|
41
|
-
self.paths.concat(Pathname.glob(File.expand_path(path)))
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
def developer(name, preferences)
|
46
|
-
developers[name] = preferences
|
47
|
-
workspaces(preferences[:workspace])
|
48
|
-
end
|
49
|
-
|
50
|
-
def current_developer
|
51
|
-
developers[ENV['USER'].to_sym] || {}
|
52
|
-
end
|
53
|
-
|
54
|
-
def developers(developers = nil)
|
55
|
-
@developers ||= {}
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
class Gem < Array
|
60
|
-
def initialize(name, repository)
|
61
|
-
if repository.local?
|
62
|
-
sub_path = repository.path.join(name)
|
63
|
-
super([name, { :path => sub_path.exist? ? sub_path.to_s : repository.path.to_s }])
|
64
|
-
else
|
65
|
-
super([name, repository.options.dup])
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
attr_reader :bundler, :name, :options, :source
|
71
|
-
|
72
|
-
def initialize(bundler, name, options)
|
73
|
-
@bundler = bundler
|
74
|
-
@name = name
|
75
|
-
@source = options.delete(:source)
|
76
|
-
@options = options
|
77
|
-
end
|
78
|
-
|
79
|
-
def gem(name)
|
80
|
-
bundler.gem(*Gem.new(name, self))
|
81
|
-
end
|
82
|
-
|
83
|
-
def local?
|
84
|
-
source == :local # && path
|
85
|
-
end
|
86
|
-
|
87
|
-
def source
|
88
|
-
@source ||= forced_source || preferred_source || :remote
|
89
|
-
end
|
90
|
-
|
91
|
-
def forced_source
|
92
|
-
:remote if ENV['FORCE_REMOTE']
|
93
|
-
end
|
94
|
-
|
95
|
-
def preferred_source
|
96
|
-
self.class.current_developer[:prefer] || self.class.current_developer[name.to_sym]
|
97
|
-
end
|
98
|
-
|
99
|
-
def path
|
100
|
-
@path ||= begin
|
101
|
-
path = self.class.paths.detect { |path| path.join(name).exist? }
|
102
|
-
path ? path.join(name) : Pathname.new('.')
|
103
|
-
end
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
def workspace(*paths)
|
108
|
-
Repository.path(*paths)
|
109
|
-
end
|
110
|
-
alias :workspaces :workspace
|
111
|
-
|
112
|
-
def developer(name, preferences)
|
113
|
-
Repository.developer(name, preferences)
|
114
|
-
end
|
115
|
-
|
116
|
-
def repository(*args)
|
117
|
-
Repository.new(self, *args)
|
118
|
-
end
|