adva-categories 0.0.3 → 0.0.4

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.
@@ -0,0 +1,4 @@
1
+ class Admin::CategoriesController < Admin::BaseController
2
+ belongs_to :site
3
+ belongs_to :section
4
+ end
@@ -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,11 @@
1
+ require_dependency 'url_helper'
2
+
3
+ UrlHelper.module_eval do
4
+ include do
5
+ def public_url_for(resources, options = {})
6
+ options[:category_id] = resources.pop.id if resources.last.class.name == 'Category'
7
+ super
8
+ end
9
+ end
10
+ end
11
+
@@ -0,0 +1,5 @@
1
+ class Categorization < ActiveRecord::Base
2
+ belongs_to :category
3
+ accepts_nested_attributes_for :category
4
+ end
5
+
@@ -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,2 @@
1
+ Content.categorizable
2
+
@@ -0,0 +1,4 @@
1
+ Section.class_eval do
2
+ has_many :categories, :foreign_key => :section_id
3
+ accepts_nested_attributes_for :categories
4
+ end
@@ -0,0 +1,17 @@
1
+ class Admin::Categories::Form < Adva::View::Form
2
+ include do
3
+ def fields
4
+ fieldset do
5
+ form.input :name
6
+ end
7
+ end
8
+
9
+ def sidebar
10
+ tab :options do
11
+ form.input :slug
12
+ end
13
+ end
14
+ end
15
+ end
16
+
17
+
@@ -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,9 @@
1
+ class Admin::Categories::Edit < Adva::View::Form
2
+ include do
3
+ def to_html
4
+ h2 :'.title'
5
+ render :partial => 'form'
6
+ end
7
+ end
8
+ end
9
+
@@ -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,10 @@
1
+ class Admin::Categories::New < Adva::View::Form
2
+ include do
3
+ def to_html
4
+ h2 :'.title'
5
+ render :partial => 'form'
6
+ end
7
+ end
8
+ end
9
+
10
+
@@ -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
@@ -0,0 +1,4 @@
1
+ Adva::Registry.set :redirect, {
2
+ 'admin/categories#create' => lambda { |c| c.edit_url },
3
+ 'admin/categories#update' => lambda { |c| c.edit_url }
4
+ }
data/config/routes.rb ADDED
@@ -0,0 +1,13 @@
1
+ require 'adva/routing_filters/categories'
2
+
3
+ Rails.application.routes.draw do
4
+ filter :categories
5
+
6
+ namespace :admin do
7
+ resources :sites do
8
+ resources :sections do
9
+ resources :categories
10
+ end
11
+ end
12
+ end
13
+ end
@@ -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,3 @@
1
+ module AdvaCategories
2
+ VERSION = "0.0.4"
3
+ end
@@ -0,0 +1,4 @@
1
+ Factory.define :category do |f|
2
+ f.section { Section.first || Factory(:section) }
3
+ f.name 'Category'
4
+ end
@@ -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: 25
4
+ hash: 23
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 3
10
- version: 0.0.3
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-11-19 00:00:00 +01:00
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
- - lib/bundler/repository.rb
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: []
@@ -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