xhive 1.0.0.pre
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.
- data/MIT-LICENSE +20 -0
- data/README.md +223 -0
- data/Rakefile +40 -0
- data/app/assets/javascripts/xhive/application.js +15 -0
- data/app/assets/javascripts/xhive/loader.js.coffee +22 -0
- data/app/assets/javascripts/xhive/pages.js +2 -0
- data/app/assets/stylesheets/xhive/application.css +13 -0
- data/app/assets/stylesheets/xhive/pages.css +4 -0
- data/app/controllers/xhive/application_controller.rb +5 -0
- data/app/controllers/xhive/pages_controller.rb +24 -0
- data/app/controllers/xhive/stylesheets_controller.rb +16 -0
- data/app/controllers/xhive/widgets_controller.rb +16 -0
- data/app/helpers/xhive/application_helper.rb +39 -0
- data/app/models/xhive/anonymous_user.rb +6 -0
- data/app/models/xhive/mapper.rb +19 -0
- data/app/models/xhive/page.rb +19 -0
- data/app/models/xhive/site.rb +14 -0
- data/app/models/xhive/stylesheet.rb +29 -0
- data/app/presenters/xhive/base_presenter.rb +31 -0
- data/app/presenters/xhive/page_presenter.rb +23 -0
- data/app/presenters/xhive/stylesheet_presenter.rb +31 -0
- data/app/presenters/xhive/user_presenter.rb +8 -0
- data/app/views/layouts/xhive/application.html.erb +15 -0
- data/app/views/xhive/pages/show.html.erb +2 -0
- data/config/routes.rb +13 -0
- data/db/migrate/20121012191751_create_xhive_sites.rb +10 -0
- data/db/migrate/20121012193105_create_xhive_pages.rb +14 -0
- data/db/migrate/20121013235227_add_home_page_to_xhive_sites.rb +5 -0
- data/db/migrate/20121013235306_add_site_to_xhive_pages.rb +6 -0
- data/db/migrate/20121016224054_create_xhive_stylesheets.rb +13 -0
- data/db/migrate/20121016224326_add_slug_index_to_pages.rb +5 -0
- data/db/migrate/20121019185702_create_xhive_mappers.rb +13 -0
- data/lib/tasks/xhive_tasks.rake +4 -0
- data/lib/xhive/base_tag.rb +89 -0
- data/lib/xhive/controller.rb +24 -0
- data/lib/xhive/engine.rb +24 -0
- data/lib/xhive/hashy.rb +42 -0
- data/lib/xhive/presentable.rb +28 -0
- data/lib/xhive/router/base.rb +61 -0
- data/lib/xhive/router/cells.rb +36 -0
- data/lib/xhive/router/error.rb +14 -0
- data/lib/xhive/router/route.rb +77 -0
- data/lib/xhive/router.rb +10 -0
- data/lib/xhive/tag_factory.rb +21 -0
- data/lib/xhive/version.rb +3 -0
- data/lib/xhive/widgify.rb +53 -0
- data/lib/xhive.rb +12 -0
- metadata +271 -0
@@ -0,0 +1,14 @@
|
|
1
|
+
class CreateXhivePages < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :xhive_pages do |t|
|
4
|
+
t.string :name
|
5
|
+
t.string :title
|
6
|
+
t.text :content
|
7
|
+
t.string :meta_keywords
|
8
|
+
t.text :meta_description
|
9
|
+
t.string :slug
|
10
|
+
|
11
|
+
t.timestamps
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class CreateXhiveStylesheets < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :xhive_stylesheets do |t|
|
4
|
+
t.belongs_to :site
|
5
|
+
t.string :name
|
6
|
+
t.text :content
|
7
|
+
t.string :slug
|
8
|
+
|
9
|
+
t.timestamps
|
10
|
+
end
|
11
|
+
add_index :xhive_stylesheets, [:site_id, :slug], :unique => true
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class CreateXhiveMappers < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :xhive_mappers do |t|
|
4
|
+
t.string :resource
|
5
|
+
t.string :action
|
6
|
+
t.integer :page_id
|
7
|
+
t.integer :site_id
|
8
|
+
|
9
|
+
t.timestamps
|
10
|
+
end
|
11
|
+
add_index :xhive_mappers, [:site_id, :resource, :action]
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'liquid'
|
3
|
+
|
4
|
+
module Xhive
|
5
|
+
class BaseTag < ::Liquid::Tag
|
6
|
+
# Public: initializes the tag. Also stores the passed attributes.
|
7
|
+
#
|
8
|
+
# Example: transforms this liquid meta tag {% some_tag attr1:foo attr2:bar %}
|
9
|
+
# into #<SomeTag attributes: { :attr1 => 'foo', :attr2 => 'bar' }>
|
10
|
+
#
|
11
|
+
def initialize(tag_name, markup, tokens)
|
12
|
+
@attributes = {}
|
13
|
+
markup.scan(::Liquid::TagAttributes) do |key, value|
|
14
|
+
# Remove single or double quotes around the quoted fragment before storing them in attributes
|
15
|
+
@attributes[key.to_sym] = value.gsub("'",'').gsub('"','')
|
16
|
+
end
|
17
|
+
super
|
18
|
+
end
|
19
|
+
|
20
|
+
# Public: renders the tag.
|
21
|
+
#
|
22
|
+
# context - The hash containing a set of external attributes.
|
23
|
+
#
|
24
|
+
# Returns: the rendered tag or a list of errors.
|
25
|
+
#
|
26
|
+
def render(context)
|
27
|
+
if (errors = check_parameters).empty?
|
28
|
+
process_parameters(context)
|
29
|
+
html = "<div "
|
30
|
+
html << " data-widget='true'"
|
31
|
+
html << " data-url='#{rest_url}'"
|
32
|
+
html << " data-params='#{parameters_to_args}'"
|
33
|
+
html << "></div>"
|
34
|
+
else
|
35
|
+
errors
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
# Private: checks the attributes to see if there is any required
|
42
|
+
# param missing.
|
43
|
+
#
|
44
|
+
# Returns: a list with the error descriptions.
|
45
|
+
#
|
46
|
+
def check_parameters
|
47
|
+
errors = ""
|
48
|
+
@url.scan(/:(\w*)/).flatten.map(&:to_sym).each do |arg|
|
49
|
+
errors += "<li><p>parameter '#{arg.to_s}' is required</p></li>" unless @attributes.has_key?(arg)
|
50
|
+
end
|
51
|
+
errors.empty? ? "" : "<ul>#{errors}</ul>"
|
52
|
+
end
|
53
|
+
|
54
|
+
# Private: replaces any REST-like attributes in the url.
|
55
|
+
#
|
56
|
+
# Returns: a new url with populated parameters.
|
57
|
+
#
|
58
|
+
def rest_url
|
59
|
+
URI.encode(@url.gsub(/:(\w*)/) { "%{#{$1}}" } % @attributes)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Private: replaces each attribute with its actual value.
|
63
|
+
#
|
64
|
+
def process_parameters(context)
|
65
|
+
@attributes.each {|k, v| @attributes[k] = process_value(v, context)}
|
66
|
+
end
|
67
|
+
|
68
|
+
# Private: transforms a parameters hash into a query string.
|
69
|
+
#
|
70
|
+
def parameters_to_args
|
71
|
+
@attributes.except(:id).map {|k,v| "#{k.to_s}=#{CGI.escape(v.to_s.gsub(/\'/,'').gsub(/\"/,''))}"}.join("&")
|
72
|
+
end
|
73
|
+
|
74
|
+
# Private: checks if the value is a meta variable and grabs its value from the context if so.
|
75
|
+
#
|
76
|
+
# Returns: the raw attribute value or the context value.
|
77
|
+
#
|
78
|
+
def process_value(value, context)
|
79
|
+
# If the value contains a meta variable, get it from the context
|
80
|
+
if value =~ /\{\{(.*)\}\}/
|
81
|
+
context[$1]
|
82
|
+
else
|
83
|
+
value
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Xhive
|
2
|
+
module Controller
|
3
|
+
def current_controller
|
4
|
+
self.class_variable_get "@@current_controller"
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.extended(base)
|
8
|
+
base.class_eval do
|
9
|
+
@@current_controller = nil
|
10
|
+
|
11
|
+
include InstanceMethods
|
12
|
+
|
13
|
+
prepend_before_filter :set_current_controller
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module InstanceMethods
|
19
|
+
# This is to access the current_controller from within presenters
|
20
|
+
def set_current_controller
|
21
|
+
@@current_controller = self
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/xhive/engine.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require "cells"
|
2
|
+
|
3
|
+
module Xhive
|
4
|
+
class Engine < ::Rails::Engine
|
5
|
+
isolate_namespace Xhive
|
6
|
+
::Cell::Base.prepend_view_path('app/cells')
|
7
|
+
initializer "xhive.extend_application_controller" do
|
8
|
+
ActiveSupport.on_load(:action_controller) do
|
9
|
+
extend Xhive::Widgify
|
10
|
+
extend Xhive::Controller
|
11
|
+
|
12
|
+
include Xhive::ApplicationHelper
|
13
|
+
|
14
|
+
helper_method :initialize_widgets_loader
|
15
|
+
end
|
16
|
+
end
|
17
|
+
initializer "xhive.load_all_controller_classes" do
|
18
|
+
ActiveSupport.on_load(:action_controller) do
|
19
|
+
Rails.application.reload_routes!
|
20
|
+
Dir[Rails.root.join("app/controllers/**/*.rb")].each {|f| require f}
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/xhive/hashy.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
module Xhive
|
2
|
+
# Adds the ability to get/set an attribute using the dot operator
|
3
|
+
#
|
4
|
+
# Examples
|
5
|
+
#
|
6
|
+
# a = Hashy.new
|
7
|
+
# a.foo # => nil
|
8
|
+
# a.foo = 'bar'
|
9
|
+
# a.foo # => 'bar'
|
10
|
+
#
|
11
|
+
# a = Hashy.new(:foo => 'bar')
|
12
|
+
# a.foo # => 'bar'
|
13
|
+
# a.duu # => nil
|
14
|
+
#
|
15
|
+
class Hashy < Hash
|
16
|
+
# Public: initializer
|
17
|
+
#
|
18
|
+
# attrs - The Hash containing the initial elements (default: {})
|
19
|
+
#
|
20
|
+
# Returns an instance of Hashy
|
21
|
+
def initialize(attrs={})
|
22
|
+
super
|
23
|
+
attrs.each {|k,v| self[k.to_sym] = v }
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def method_missing(sym, *args, &block)
|
29
|
+
# If I receive a method with the form 'foo='
|
30
|
+
if sym.to_s =~ /=/
|
31
|
+
attr = sym.to_s.scan(/(\w*)=/)
|
32
|
+
self[attr[0][0].to_sym] = args[0] unless attr.empty?
|
33
|
+
elsif sym.to_s =~ /^.*\?$/
|
34
|
+
attr = sym.to_s.scan(/(\w*)\?/)
|
35
|
+
self.fetch(attr[0][0].to_sym, false) ? true : false
|
36
|
+
else
|
37
|
+
# This should be the standard behavior with []
|
38
|
+
self.fetch(sym, nil)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Xhive
|
2
|
+
module Presentable
|
3
|
+
def presenter
|
4
|
+
klass = presenter_class.constantize
|
5
|
+
klass.new(self)
|
6
|
+
end
|
7
|
+
|
8
|
+
def presenter_class
|
9
|
+
self.class.klass_name || "#{self.class.name}Presenter"
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
@klass_name = nil
|
14
|
+
|
15
|
+
def presenter_class=(class_name)
|
16
|
+
@klass_name = class_name
|
17
|
+
end
|
18
|
+
|
19
|
+
def klass_name
|
20
|
+
@klass_name
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.included(base)
|
25
|
+
base.extend(ClassMethods)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Xhive
|
2
|
+
module Router
|
3
|
+
class Base
|
4
|
+
# Public: returns the route that matches the url.
|
5
|
+
#
|
6
|
+
# url - The String containing the url to match.
|
7
|
+
#
|
8
|
+
# Returns: an Route object.
|
9
|
+
#
|
10
|
+
def self.match(url)
|
11
|
+
Route.find(url)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Public: returns the route definition for a controller#action pair.
|
15
|
+
#
|
16
|
+
# controller - The String containing the controller name.
|
17
|
+
# action - The String containing the action name.
|
18
|
+
#
|
19
|
+
# Returns: the route definition. e.g. 'pages/:id/show'.
|
20
|
+
#
|
21
|
+
def self.route_for(controller, action)
|
22
|
+
routes = normalized_routes
|
23
|
+
url = routes.to_a.select {|route| route[:end_point] == "#{controller}##{action}"}.first.fetch(:path)
|
24
|
+
url = url.gsub('(.:format)', '')
|
25
|
+
url
|
26
|
+
rescue
|
27
|
+
fail Error, "#{controller}##{action}"
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
# Private: joins the Rails application routes and the Xhive::Engine routes.
|
33
|
+
#
|
34
|
+
# Returns: an array of normalized routes.
|
35
|
+
#
|
36
|
+
def self.normalized_routes
|
37
|
+
load_routes_for(Rails.application.routes) | load_routes_for(Xhive::Engine.routes)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Private: returns an array of normalized routes.
|
41
|
+
#
|
42
|
+
# Example: [{:end_point => 'pages#show', :path => '/pages/:id/show'}]
|
43
|
+
#
|
44
|
+
# Returns: an array with a set of normalized routes.
|
45
|
+
#
|
46
|
+
def self.load_routes_for(router)
|
47
|
+
# TODO: find another way to get the routes prefix. This might get changed as is not public API.
|
48
|
+
mount_point = router._generate_prefix({}).to_s
|
49
|
+
routes = router.routes.to_a
|
50
|
+
routes.collect do |route|
|
51
|
+
if route.requirements[:controller] && route.requirements[:action]
|
52
|
+
controller = route.requirements[:controller].gsub(/rails\/|xhive\//, '')
|
53
|
+
action = route.requirements[:action]
|
54
|
+
path = route.path.spec.to_s
|
55
|
+
{ :end_point => "#{controller}##{action}", :path => "#{mount_point}#{path}" }
|
56
|
+
end
|
57
|
+
end.compact
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Xhive
|
2
|
+
module Router
|
3
|
+
class Cells
|
4
|
+
# Public: draws the internal widget routes for cell based widgets.
|
5
|
+
#
|
6
|
+
# Example:
|
7
|
+
#
|
8
|
+
# Xhive::Routes.draw do |router|
|
9
|
+
# mount 'my_widget', :to => 'my_cell#action'
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
def self.draw
|
13
|
+
Rails.application.reload_routes!
|
14
|
+
yield self
|
15
|
+
end
|
16
|
+
|
17
|
+
# Public: mounts a cell based route and creates the Liquid tag.
|
18
|
+
#
|
19
|
+
# route - The String containing the route.
|
20
|
+
# options - The Hash containing the route endpoint.
|
21
|
+
#
|
22
|
+
# Example: mount 'my_widget', :to => 'my_cell#action'
|
23
|
+
#
|
24
|
+
def self.mount(route, options)
|
25
|
+
cell, action = options[:to].split('#')
|
26
|
+
widgets_base_route = Base.route_for('widgets', 'show').gsub(/\/\*\w*$/, '')
|
27
|
+
widget_route = "#{widgets_base_route}/#{route}"
|
28
|
+
tag_class_name = "#{cell}_#{action}".classify
|
29
|
+
|
30
|
+
Route.add(widget_route, cell.classify, action)
|
31
|
+
|
32
|
+
Xhive::TagFactory.create_class(tag_class_name, widget_route)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module Xhive
|
2
|
+
module Router
|
3
|
+
class Route
|
4
|
+
@@routes = []
|
5
|
+
|
6
|
+
attr :route, :klass, :action, :args
|
7
|
+
|
8
|
+
def initialize(route, klass, action)
|
9
|
+
@route = route
|
10
|
+
@klass = klass
|
11
|
+
@action = action
|
12
|
+
@args = extract_args(route)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Public: adds a new route to the collection.
|
16
|
+
#
|
17
|
+
# route - The String containing the route definition.
|
18
|
+
# klass - The String containing the Controller/Cell class name.
|
19
|
+
# action - The String containing the action name.
|
20
|
+
#
|
21
|
+
def self.add(route, klass, action)
|
22
|
+
@@routes << new(route, klass, action)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Public: finds the route that matches the url.
|
26
|
+
#
|
27
|
+
# url - The String containing the url.
|
28
|
+
#
|
29
|
+
# Returns: a Route object that matches the url.
|
30
|
+
#
|
31
|
+
def self.find(url)
|
32
|
+
@@routes.select {|r| r.matches?(url) }.first
|
33
|
+
end
|
34
|
+
|
35
|
+
# Public: checks if the route matches the url.
|
36
|
+
#
|
37
|
+
# url - The String containing the url to match.
|
38
|
+
#
|
39
|
+
# Returns: true or false.
|
40
|
+
#
|
41
|
+
def matches?(url)
|
42
|
+
!!(matcher =~ url)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Public: returns the parameters present in the url.
|
46
|
+
#
|
47
|
+
# url - The String containing the url.
|
48
|
+
#
|
49
|
+
# Returns: a hash of the form { :id => '1', :name => 'john' }
|
50
|
+
#
|
51
|
+
def params_from(url)
|
52
|
+
values = url.scan(matcher).flatten
|
53
|
+
params = Hash[args.zip values]
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
# Private: returns a regexp matcher for the route.
|
59
|
+
#
|
60
|
+
# Returns: a Regexp object with the route matcher.
|
61
|
+
#
|
62
|
+
def matcher
|
63
|
+
Regexp.new("^#{route.gsub(/\:[-\w]*/, '([-\w]*)')}$")
|
64
|
+
end
|
65
|
+
|
66
|
+
# Private: extracts the argument names from a given url.
|
67
|
+
#
|
68
|
+
# url - The String containing the url.
|
69
|
+
#
|
70
|
+
# Returns: an array of argument names.
|
71
|
+
#
|
72
|
+
def extract_args(url)
|
73
|
+
variables = url.scan(/:([-\w]*)/).flatten.map(&:to_sym)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
data/lib/xhive/router.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
module Xhive
|
2
|
+
class TagFactory
|
3
|
+
# Public: creates and registers the tag class for a specific name/url pair.
|
4
|
+
#
|
5
|
+
# name - The String containing the widget name.
|
6
|
+
# url - The String containing the widget url.
|
7
|
+
#
|
8
|
+
def self.create_class(name, url)
|
9
|
+
klass = Class.new(Xhive::BaseTag) do
|
10
|
+
const_set 'URL', url
|
11
|
+
def initialize(tag_name, markup, tokens)
|
12
|
+
@url = self.class.const_get 'URL'
|
13
|
+
super
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
Liquid.const_set name, klass
|
18
|
+
Liquid::Template.register_tag(name.underscore, "Liquid::#{name}".constantize)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Xhive
|
2
|
+
module Widgify
|
3
|
+
# Public: remove layout from action and create Liquid tag class.
|
4
|
+
#
|
5
|
+
# actions - The array of actions to widgify.
|
6
|
+
#
|
7
|
+
def widgify(*actions)
|
8
|
+
actions.each do |action|
|
9
|
+
remove_layout_for_action(action)
|
10
|
+
Xhive::TagFactory.create_class(tag_name_for(action), route_for(action))
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# Public: creates an array with the actions to exclude from layout.
|
15
|
+
#
|
16
|
+
def self.extended(base)
|
17
|
+
base.class_variable_set "@@widgets_list", []
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
# Private: returns the route definition for a controller#action pair.
|
23
|
+
#
|
24
|
+
# action - The string with the action name.
|
25
|
+
#
|
26
|
+
# Returns: the route definition. e.g. 'pages/:id/show'.
|
27
|
+
#
|
28
|
+
def route_for(action)
|
29
|
+
Xhive::Router::Base.route_for(controller_name, action)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Private: builds the tag name for a given action.
|
33
|
+
#
|
34
|
+
# action - The string with the action name.
|
35
|
+
#
|
36
|
+
# Returns: the tag name.
|
37
|
+
#
|
38
|
+
def tag_name_for(action)
|
39
|
+
controller_name = self.name.gsub(/Controller|Xhive|::/, '')
|
40
|
+
tag_name = "#{controller_name}#{action.capitalize}"
|
41
|
+
end
|
42
|
+
|
43
|
+
# Private: adds the action to the widgets list and sets the layout to false.
|
44
|
+
#
|
45
|
+
# action - The string with the action name.
|
46
|
+
#
|
47
|
+
def remove_layout_for_action(action)
|
48
|
+
self.class_variable_get("@@widgets_list") << action.to_sym
|
49
|
+
self.layout :false, :only => self.class_variable_get("@@widgets_list")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
data/lib/xhive.rb
ADDED