rhyhann-merb_stathyc_slice 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +21 -0
- data/README.markdown +106 -0
- data/Rakefile +55 -0
- data/TODO +5 -0
- data/app/controllers/application.rb +3 -0
- data/app/controllers/pages.rb +62 -0
- data/app/helpers/application_helper.rb +136 -0
- data/app/helpers/pages_helper.rb +49 -0
- data/app/models/page.rb +49 -0
- data/app/views/layout/static_slice.html.haml +27 -0
- data/app/views/pages/_cud.html.haml +11 -0
- data/app/views/pages/_sidebar.html.haml +2 -0
- data/app/views/pages/delete.html.haml +2 -0
- data/app/views/pages/edit.html.haml +6 -0
- data/app/views/pages/index.html.haml +5 -0
- data/app/views/pages/new.html.haml +6 -0
- data/app/views/pages/show.html.haml +5 -0
- data/lib/static_slice.rb +85 -0
- data/lib/static_slice/merbtasks.rb +103 -0
- data/lib/static_slice/slicetasks.rb +18 -0
- data/lib/static_slice/spectasks.rb +65 -0
- data/public/images/css/banner_mountains.jpg +0 -0
- data/public/images/css/bg.png +0 -0
- data/public/images/css/bg02-blue-left.png +0 -0
- data/public/images/css/bg02-blue-right.png +0 -0
- data/public/images/css/bg02-left.png +0 -0
- data/public/images/css/bg02-right.png +0 -0
- data/public/images/css/bg02-white-left.png +0 -0
- data/public/images/css/bg02-white-right.png +0 -0
- data/public/images/css/logo.png +0 -0
- data/public/images/css/menu.png +0 -0
- data/public/images/css/menuleft.png +0 -0
- data/public/images/css/menuright.png +0 -0
- data/public/stylesheets/master.css +138 -0
- data/spec/models/page_spec.rb +7 -0
- data/spec/requests/pages_spec.rb +110 -0
- data/spec/spec_helper.rb +43 -0
- data/spec/static_slice_spec.rb +19 -0
- data/stubs/app/controllers/application.rb +2 -0
- data/stubs/app/controllers/main.rb +2 -0
- metadata +118 -0
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
(The MIT/X11 License)
|
2
|
+
|
3
|
+
Copyright (c) <2008> <Othmane Benkirane>
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
/!\ My version numbers mean that they are stable when they are pair (2,4,6,8…) and
|
2
|
+
nearly bugfree when they finish with a 0 (10,20,30) so if you see version 1, just know
|
3
|
+
that it's not meant for production now!
|
4
|
+
|
5
|
+
StaticSlice
|
6
|
+
===========
|
7
|
+
|
8
|
+
A slice for the Merb framework that provides the core static pages functionnalities.
|
9
|
+
Just see the views for edit.
|
10
|
+
|
11
|
+
To see all available tasks for StaticSlice run:
|
12
|
+
rake -T slices:static_slice
|
13
|
+
|
14
|
+
CUD Customization
|
15
|
+
-----------------
|
16
|
+
|
17
|
+
If you want to protect the CUD actions, you must know that: ALL of them are located in
|
18
|
+
a content named *for_cud*. simple.
|
19
|
+
If you want to change the look of the CUD operations, it's stored in a partial named *_cud*.
|
20
|
+
But _please see the default one first_.
|
21
|
+
|
22
|
+
Sidebar
|
23
|
+
-------
|
24
|
+
|
25
|
+
A partial called *_sidebar* may help you with Sidebar customization.
|
26
|
+
|
27
|
+
Installation
|
28
|
+
============
|
29
|
+
|
30
|
+
* dependency (config/init.rb)
|
31
|
+
|
32
|
+
\# add the slice as a regular dependency
|
33
|
+
dependency 'static_slice'
|
34
|
+
|
35
|
+
* router
|
36
|
+
|
37
|
+
\# example: /static_slice/:controller/:action/:id
|
38
|
+
add_slice(:StaticSlice)
|
39
|
+
\# example: /foo/:controller/:action/:id
|
40
|
+
add_slice(:StaticSlice, 'foo') # same as :path => 'foo'
|
41
|
+
\# example: /:lang/:controller/:action/:id
|
42
|
+
add_slice(:StaticSlice, :path => ':lang')
|
43
|
+
\# example: /:controller/:action/:id
|
44
|
+
slice(:StaticSlice)
|
45
|
+
|
46
|
+
* rake
|
47
|
+
|
48
|
+
rake slices:static_slice:install
|
49
|
+
|
50
|
+
Uses
|
51
|
+
====
|
52
|
+
|
53
|
+
You can put your overrides here:
|
54
|
+
host-app/slices/static_slice/app - controllers, models, views ...
|
55
|
+
|
56
|
+
Templates are located in this order:
|
57
|
+
1. host-app/slices/static_slice/app/views/*
|
58
|
+
2. gems/static_slice/app/views/*
|
59
|
+
3. host-app/app/views/*
|
60
|
+
|
61
|
+
You can use the host application's layout by configuring the
|
62
|
+
static_slice slice in a before_app_loads block:
|
63
|
+
|
64
|
+
Merb::Slices.config[:static_slice] = { :layout => :application }
|
65
|
+
|
66
|
+
By default :static_slice is used. If you need to override
|
67
|
+
stylesheets or javascripts, just specify your own files in your layout
|
68
|
+
instead/in addition to the ones supplied (if any) in
|
69
|
+
host-app/public/slices/static_slice.
|
70
|
+
|
71
|
+
In any case don't edit those files directly as they may be clobbered any time
|
72
|
+
rake static_slice:install is run.
|
73
|
+
|
74
|
+
About Slices
|
75
|
+
============
|
76
|
+
|
77
|
+
Merb-Slices is a Merb plugin for using and creating application 'slices' which
|
78
|
+
help you modularize your application. Usually these are reuseable extractions
|
79
|
+
from your main app. In effect, a Slice is just like a regular Merb MVC
|
80
|
+
application, both in functionality as well as in structure.
|
81
|
+
|
82
|
+
When you generate a Slice stub structure, a module is setup to serve as a
|
83
|
+
namespace for your controller, models, helpers etc. This ensures maximum
|
84
|
+
encapsulation. You could say a Slice is a mixture between a Merb plugin (a
|
85
|
+
Gem) and a Merb application, reaping the benefits of both.
|
86
|
+
|
87
|
+
A host application can 'mount' a Slice inside the router, which means you have
|
88
|
+
full over control how it integrates. By default a Slice's routes are prefixed
|
89
|
+
by its name (a router :namespace), but you can easily provide your own prefix
|
90
|
+
or leave it out, mounting it at the root of your url-schema. You can even
|
91
|
+
mount a Slice multiple times and give extra parameters to customize an
|
92
|
+
instance's behaviour.
|
93
|
+
|
94
|
+
A Slice's Application controller uses controller_for_slice to setup slice
|
95
|
+
specific behaviour, which mainly affects cascaded view handling. Additionaly,
|
96
|
+
this method is available to any kind of controller, so it can be used for
|
97
|
+
Merb Mailer too for example.
|
98
|
+
|
99
|
+
There are many ways which let you customize a Slice's functionality and
|
100
|
+
appearance without ever touching the Gem-level code itself. It's not only easy
|
101
|
+
to add template/layout overrides, you can also add/modify controllers, models
|
102
|
+
and other runtime code from within the host application.
|
103
|
+
|
104
|
+
To create your own Slice run this (somewhere outside of your merb app):
|
105
|
+
|
106
|
+
merb-gen slice <your-lowercase-slice-name>
|
data/Rakefile
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake/gempackagetask'
|
3
|
+
|
4
|
+
require 'merb-core'
|
5
|
+
require 'merb-core/tasks/merb'
|
6
|
+
|
7
|
+
GEM_NAME = "merb_stathyc_slice"
|
8
|
+
AUTHOR = "Othmane Benkirane (rhyhann)"
|
9
|
+
EMAIL = "eo-in-rhyhann-net"
|
10
|
+
HOMEPAGE = "http://github.com/rhyhann/merb-stathyc_slice"
|
11
|
+
SUMMARY = "Merb Slice that provides core static pages functionnalities"
|
12
|
+
GEM_VERSION = "0.1"
|
13
|
+
|
14
|
+
spec = Gem::Specification.new do |s|
|
15
|
+
s.rubyforge_project = 'merb'
|
16
|
+
s.name = GEM_NAME
|
17
|
+
s.version = GEM_VERSION
|
18
|
+
s.platform = Gem::Platform::RUBY
|
19
|
+
s.has_rdoc = true
|
20
|
+
s.extra_rdoc_files = ["README.markdown", "LICENSE", 'TODO']
|
21
|
+
s.summary = SUMMARY
|
22
|
+
s.description = s.summary
|
23
|
+
s.author = AUTHOR
|
24
|
+
s.email = EMAIL
|
25
|
+
s.homepage = HOMEPAGE
|
26
|
+
s.add_dependency('merb-slices', '>= 1.0')
|
27
|
+
s.require_path = 'lib'
|
28
|
+
s.files = %w(LICENSE README.markdown Rakefile TODO) + Dir.glob("{lib,spec,app,public,stubs}/**/*")
|
29
|
+
end
|
30
|
+
|
31
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
32
|
+
pkg.gem_spec = spec
|
33
|
+
end
|
34
|
+
|
35
|
+
desc "Install the gem"
|
36
|
+
task :install do
|
37
|
+
Merb::RakeHelper.install(GEM_NAME, :version => GEM_VERSION)
|
38
|
+
end
|
39
|
+
|
40
|
+
desc "Uninstall the gem"
|
41
|
+
task :uninstall do
|
42
|
+
Merb::RakeHelper.uninstall(GEM_NAME, :version => GEM_VERSION)
|
43
|
+
end
|
44
|
+
|
45
|
+
desc "Create a gemspec file"
|
46
|
+
task :gemspec do
|
47
|
+
File.open("#{GEM_NAME}.gemspec", "w") do |file|
|
48
|
+
file.puts spec.to_ruby
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
require 'spec/rake/spectask'
|
53
|
+
require 'merb-core/test/tasks/spectasks'
|
54
|
+
desc 'Default: run spec examples'
|
55
|
+
task :default => 'spec'
|
@@ -0,0 +1,62 @@
|
|
1
|
+
class StaticSlice::Pages < StaticSlice::Application
|
2
|
+
# provides :xml, :yaml, :js
|
3
|
+
def index
|
4
|
+
@pages = Page.all
|
5
|
+
display @pages
|
6
|
+
end
|
7
|
+
|
8
|
+
def show(id)
|
9
|
+
@page = Page.get(id)
|
10
|
+
raise NotFound unless @page
|
11
|
+
display @page
|
12
|
+
end
|
13
|
+
|
14
|
+
def new
|
15
|
+
only_provides :html
|
16
|
+
@page = Page.new
|
17
|
+
display @page
|
18
|
+
end
|
19
|
+
|
20
|
+
def edit(id)
|
21
|
+
only_provides :html
|
22
|
+
@page = Page.get(id)
|
23
|
+
raise NotFound unless @page
|
24
|
+
display @page
|
25
|
+
end
|
26
|
+
|
27
|
+
def create(page)
|
28
|
+
@page = Page.new(page)
|
29
|
+
if @page.save
|
30
|
+
redirect resource(@page), :message => {:notice => "Page was successfully created"}
|
31
|
+
else
|
32
|
+
message[:error] = "Page failed to be created"
|
33
|
+
render :new
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def update(id, page)
|
38
|
+
@page = Page.get(id)
|
39
|
+
raise NotFound unless @page
|
40
|
+
if @page.update_attributes(page)
|
41
|
+
redirect resource(@page)
|
42
|
+
else
|
43
|
+
display @page, :edit
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def delete(id)
|
48
|
+
@page = Page.get(id)
|
49
|
+
raise NotFound unless @page
|
50
|
+
display @page
|
51
|
+
end
|
52
|
+
def destroy(id)
|
53
|
+
@page = Page.get(id)
|
54
|
+
raise NotFound unless @page
|
55
|
+
if @page.destroy
|
56
|
+
redirect resource(:pages)
|
57
|
+
else
|
58
|
+
raise InternalServerError
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end # Pages
|
@@ -0,0 +1,136 @@
|
|
1
|
+
module Merb
|
2
|
+
module StaticSlice
|
3
|
+
module ApplicationHelper
|
4
|
+
|
5
|
+
# @param *segments<Array[#to_s]> Path segments to append.
|
6
|
+
#
|
7
|
+
# @return <String>
|
8
|
+
# A path relative to the public directory, with added segments.
|
9
|
+
def image_path(*segments)
|
10
|
+
public_path_for(:image, *segments)
|
11
|
+
end
|
12
|
+
|
13
|
+
# @param *segments<Array[#to_s]> Path segments to append.
|
14
|
+
#
|
15
|
+
# @return <String>
|
16
|
+
# A path relative to the public directory, with added segments.
|
17
|
+
def javascript_path(*segments)
|
18
|
+
public_path_for(:javascript, *segments)
|
19
|
+
end
|
20
|
+
|
21
|
+
# @param *segments<Array[#to_s]> Path segments to append.
|
22
|
+
#
|
23
|
+
# @return <String>
|
24
|
+
# A path relative to the public directory, with added segments.
|
25
|
+
def stylesheet_path(*segments)
|
26
|
+
public_path_for(:stylesheet, *segments)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Construct a path relative to the public directory
|
30
|
+
#
|
31
|
+
# @param <Symbol> The type of component.
|
32
|
+
# @param *segments<Array[#to_s]> Path segments to append.
|
33
|
+
#
|
34
|
+
# @return <String>
|
35
|
+
# A path relative to the public directory, with added segments.
|
36
|
+
def public_path_for(type, *segments)
|
37
|
+
::StaticSlice.public_path_for(type, *segments)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Construct an app-level path.
|
41
|
+
#
|
42
|
+
# @param <Symbol> The type of component.
|
43
|
+
# @param *segments<Array[#to_s]> Path segments to append.
|
44
|
+
#
|
45
|
+
# @return <String>
|
46
|
+
# A path within the host application, with added segments.
|
47
|
+
def app_path_for(type, *segments)
|
48
|
+
::StaticSlice.app_path_for(type, *segments)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Construct a slice-level path.
|
52
|
+
#
|
53
|
+
# @param <Symbol> The type of component.
|
54
|
+
# @param *segments<Array[#to_s]> Path segments to append.
|
55
|
+
#
|
56
|
+
# @return <String>
|
57
|
+
# A path within the slice source (Gem), with added segments.
|
58
|
+
def slice_path_for(type, *segments)
|
59
|
+
::StaticSlice.slice_path_for(type, *segments)
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
|
64
|
+
# This function takes the Model.sexhy hash and guesses the untyped variables, sorts it
|
65
|
+
# and then returns the incredible formù!
|
66
|
+
def sexify(object, f_d = {:properties => {}})
|
67
|
+
f = ''
|
68
|
+
f_d = object_labels(object, object_types(object))
|
69
|
+
f_d[:properties].delete_if {|k,v| [:id,:created_at,:updated_at].include?(k)}
|
70
|
+
f_d[:properties].to_a.sort_by {|a|a[1][:order]}.each do |element|
|
71
|
+
type, options = element[0], element[1]
|
72
|
+
# TODO: default hashes
|
73
|
+
html = object.class.sexhy[:html]
|
74
|
+
f << nl(open_tag(:div))
|
75
|
+
f << nl(html[:label][:tag],
|
76
|
+
options[:label],
|
77
|
+
html[:label][:attributes].merge(
|
78
|
+
{:for => "#{object.class.to_s.downcase}_#{type.to_s}"}
|
79
|
+
)
|
80
|
+
)
|
81
|
+
eval "f << nl(html[:input][:tag],
|
82
|
+
(#{options[:input]}(:#{type})).to_s,
|
83
|
+
html[:input][:attributes]
|
84
|
+
)"
|
85
|
+
f << nl(close_tag(:div))
|
86
|
+
end
|
87
|
+
f
|
88
|
+
end
|
89
|
+
def order(hash)
|
90
|
+
n = 0
|
91
|
+
hash[:properties].each do |k,v|
|
92
|
+
n+=1
|
93
|
+
hash[:properties][k][:order] = n if hash[:properties][k][:order].nil?
|
94
|
+
end
|
95
|
+
hash
|
96
|
+
end
|
97
|
+
# This function returns the list of the inputs.
|
98
|
+
# It will first try to merge it to the given hash, then to the model's hash
|
99
|
+
# then to nothing.
|
100
|
+
def object_types(o, c_d = {:properties => {}})
|
101
|
+
c_d = o.class.sexhy if c_d == {:properties => {}} && o.class.methods.include?('sexhy')
|
102
|
+
c_d = order c_d
|
103
|
+
properties = {}
|
104
|
+
o.class.properties.each do |pr|
|
105
|
+
t = DataMapper::Types
|
106
|
+
properties[pr.name] = {}
|
107
|
+
properties[pr.name][:input] = if pr.type.is_a?(t::Boolean) ||
|
108
|
+
pr.type.is_a?(TrueClass); 'check_box'
|
109
|
+
elsif [String, Float, Fixnum,
|
110
|
+
Integer, BigDecimal,
|
111
|
+
].include?(pr.type); 'text_field'
|
112
|
+
elsif pr.type == t::Text; 'text_area'
|
113
|
+
elsif pr.type.is_a?(Date); 'date'
|
114
|
+
end
|
115
|
+
end
|
116
|
+
c_d[:properties].each {|k,v|
|
117
|
+
properties[k].merge!(v)}
|
118
|
+
{:properties => properties}
|
119
|
+
end
|
120
|
+
def object_labels(o, c_d = {:properties => {}})
|
121
|
+
c_d = o.class.sexhy if c_d == {} && o.class.methods.include?('sexhy')
|
122
|
+
properties = {}
|
123
|
+
o.class.properties.each do |pr|
|
124
|
+
properties[pr.name] = {}
|
125
|
+
properties[pr.name][:label] = pr.name.to_s.capitalize
|
126
|
+
end
|
127
|
+
c_d[:properties].each {|k,v| properties[k].merge!(v)}
|
128
|
+
{:properties => properties}
|
129
|
+
end
|
130
|
+
# This function returns its arguments with new lines
|
131
|
+
def nl(*arg)
|
132
|
+
arg[0].is_a?(Symbol) ? nl(tag(*arg)) : " \n #{arg[0]}"
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Merb
|
2
|
+
module PagesHelper
|
3
|
+
def sexify(object, form_definition = {})
|
4
|
+
f = "\n"
|
5
|
+
object.class.properties.each do |pr|
|
6
|
+
t = DataMapper::Types
|
7
|
+
if pr.type.is_a?(t::Boolean) || pr.type.is_a?(TrueClass)
|
8
|
+
f << nl(label(:"#{pr.name}"))
|
9
|
+
f << nl(check_box(:"#{pr.name}"))
|
10
|
+
elsif [String, Float, Fixnum, Integer, BigDecimal].include?(pr.type)
|
11
|
+
f << nl(label(:"#{pr.name}"))
|
12
|
+
f << nl(text_field(:"#{pr.name}"))
|
13
|
+
elsif pr.type == t::Text
|
14
|
+
f << nl(label( :"#{pr.name}" ))
|
15
|
+
f << nl(text_area(:"#{pr.name}"))
|
16
|
+
elsif pr.type.is_a?(Date)
|
17
|
+
dater = "\n"
|
18
|
+
f << tag(:label, :class => :date) do
|
19
|
+
yearc = (Time.now.year-100..Time.now.year)
|
20
|
+
dater << select(:"#{pr.name}_month", :collection => (1..12).to_a); dater << " / "
|
21
|
+
dater << select(:"#{pr.name}_day" , :collection => (1..31).to_a); dater << " / "
|
22
|
+
dater << select(:"#{pr.name}_year" , :collection => (yearc).to_a); dater << " / "
|
23
|
+
dater
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
f
|
28
|
+
end
|
29
|
+
def hy_guess_type(object)
|
30
|
+
object.class.properties.each do |pr|
|
31
|
+
t = DataMapper::Types
|
32
|
+
inputs = {}
|
33
|
+
inputs[:"#{pr.name}"] = if pr.type.is_a?(t::Boolean) ||
|
34
|
+
pr.type.is_a?(TrueClass); :check_box
|
35
|
+
elsif [String, Float, Fixnum,
|
36
|
+
Integer, BigDecimal,
|
37
|
+
].include?(pr.type); :text_field
|
38
|
+
elsif pr.type == t::Text; :text_area
|
39
|
+
elsif pr.type.is_a?(Date); :date
|
40
|
+
end
|
41
|
+
inputs
|
42
|
+
end
|
43
|
+
end
|
44
|
+
# This function returns its arguments with new lines
|
45
|
+
def nl(arg)
|
46
|
+
tag(:div, "\n #{arg}\n")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end # Merb
|