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.
Files changed (48) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.md +223 -0
  3. data/Rakefile +40 -0
  4. data/app/assets/javascripts/xhive/application.js +15 -0
  5. data/app/assets/javascripts/xhive/loader.js.coffee +22 -0
  6. data/app/assets/javascripts/xhive/pages.js +2 -0
  7. data/app/assets/stylesheets/xhive/application.css +13 -0
  8. data/app/assets/stylesheets/xhive/pages.css +4 -0
  9. data/app/controllers/xhive/application_controller.rb +5 -0
  10. data/app/controllers/xhive/pages_controller.rb +24 -0
  11. data/app/controllers/xhive/stylesheets_controller.rb +16 -0
  12. data/app/controllers/xhive/widgets_controller.rb +16 -0
  13. data/app/helpers/xhive/application_helper.rb +39 -0
  14. data/app/models/xhive/anonymous_user.rb +6 -0
  15. data/app/models/xhive/mapper.rb +19 -0
  16. data/app/models/xhive/page.rb +19 -0
  17. data/app/models/xhive/site.rb +14 -0
  18. data/app/models/xhive/stylesheet.rb +29 -0
  19. data/app/presenters/xhive/base_presenter.rb +31 -0
  20. data/app/presenters/xhive/page_presenter.rb +23 -0
  21. data/app/presenters/xhive/stylesheet_presenter.rb +31 -0
  22. data/app/presenters/xhive/user_presenter.rb +8 -0
  23. data/app/views/layouts/xhive/application.html.erb +15 -0
  24. data/app/views/xhive/pages/show.html.erb +2 -0
  25. data/config/routes.rb +13 -0
  26. data/db/migrate/20121012191751_create_xhive_sites.rb +10 -0
  27. data/db/migrate/20121012193105_create_xhive_pages.rb +14 -0
  28. data/db/migrate/20121013235227_add_home_page_to_xhive_sites.rb +5 -0
  29. data/db/migrate/20121013235306_add_site_to_xhive_pages.rb +6 -0
  30. data/db/migrate/20121016224054_create_xhive_stylesheets.rb +13 -0
  31. data/db/migrate/20121016224326_add_slug_index_to_pages.rb +5 -0
  32. data/db/migrate/20121019185702_create_xhive_mappers.rb +13 -0
  33. data/lib/tasks/xhive_tasks.rake +4 -0
  34. data/lib/xhive/base_tag.rb +89 -0
  35. data/lib/xhive/controller.rb +24 -0
  36. data/lib/xhive/engine.rb +24 -0
  37. data/lib/xhive/hashy.rb +42 -0
  38. data/lib/xhive/presentable.rb +28 -0
  39. data/lib/xhive/router/base.rb +61 -0
  40. data/lib/xhive/router/cells.rb +36 -0
  41. data/lib/xhive/router/error.rb +14 -0
  42. data/lib/xhive/router/route.rb +77 -0
  43. data/lib/xhive/router.rb +10 -0
  44. data/lib/xhive/tag_factory.rb +21 -0
  45. data/lib/xhive/version.rb +3 -0
  46. data/lib/xhive/widgify.rb +53 -0
  47. data/lib/xhive.rb +12 -0
  48. metadata +271 -0
@@ -0,0 +1,10 @@
1
+ class CreateXhiveSites < ActiveRecord::Migration
2
+ def change
3
+ create_table :xhive_sites do |t|
4
+ t.string :name
5
+ t.string :domain
6
+
7
+ t.timestamps
8
+ end
9
+ end
10
+ end
@@ -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,5 @@
1
+ class AddHomePageToXhiveSites < ActiveRecord::Migration
2
+ def change
3
+ add_column :xhive_sites, :home_page_id, :integer
4
+ end
5
+ end
@@ -0,0 +1,6 @@
1
+ class AddSiteToXhivePages < ActiveRecord::Migration
2
+ def change
3
+ add_column :xhive_pages, :site_id, :integer
4
+ add_index :xhive_pages, :site_id
5
+ end
6
+ 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,5 @@
1
+ class AddSlugIndexToPages < ActiveRecord::Migration
2
+ def change
3
+ add_index :xhive_pages, [:site_id, :slug], :unique => true
4
+ end
5
+ 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,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :xhive do
3
+ # # Task goes here
4
+ # 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
@@ -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
@@ -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,14 @@
1
+ module Xhive
2
+ module Router
3
+ class Error < StandardError
4
+ def initialize(action)
5
+ @action = action
6
+ super
7
+ end
8
+
9
+ def message
10
+ "No route was found for action #{@action}"
11
+ end
12
+ end
13
+ end
14
+ 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
@@ -0,0 +1,10 @@
1
+ module Xhive
2
+ module Router
3
+ end
4
+ end
5
+
6
+ require 'xhive/router/error'
7
+ require 'xhive/router/base'
8
+ require 'xhive/router/route'
9
+ require 'xhive/router/cells'
10
+
@@ -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,3 @@
1
+ module Xhive
2
+ VERSION = "1.0.0.pre"
3
+ 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
@@ -0,0 +1,12 @@
1
+ require "xhive/engine"
2
+
3
+ module Xhive
4
+ end
5
+
6
+ require 'xhive/hashy'
7
+ require 'xhive/controller'
8
+ require 'xhive/presentable'
9
+ require 'xhive/router'
10
+ require 'xhive/base_tag'
11
+ require 'xhive/tag_factory'
12
+ require 'xhive/widgify'