app_frame 0.5.5
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/.document +5 -0
- data/.rspec +1 -0
- data/Gemfile +26 -0
- data/Gemfile.lock +181 -0
- data/LICENSE.txt +20 -0
- data/README.md +25 -0
- data/Rakefile +47 -0
- data/VERSION +1 -0
- data/app/assets/javascripts/app_frame/application.js +9 -0
- data/app/assets/stylesheets/app_frame/_devise.scss +41 -0
- data/app/assets/stylesheets/app_frame/_tree.scss +31 -0
- data/app/assets/stylesheets/app_frame/application.scss +73 -0
- data/app/controllers/app_frame/devise/confirmations_controller.rb +3 -0
- data/app/controllers/app_frame/devise/passwords_controller.rb +3 -0
- data/app/controllers/app_frame/devise/registrations_controller.rb +3 -0
- data/app/controllers/app_frame/devise/sessions_controller.rb +3 -0
- data/app/helpers/app_frame/bootstrap_helper.rb +36 -0
- data/app/helpers/app_frame/breadcrumb_helper.rb +18 -0
- data/app/helpers/app_frame/menu_helper.rb +43 -0
- data/app/helpers/app_frame/pagination_helper.rb +12 -0
- data/app/helpers/app_frame/resources_helper.rb +63 -0
- data/app/helpers/app_frame/scopes_helper.rb +94 -0
- data/app/helpers/app_frame/select_helper.rb +8 -0
- data/app/helpers/app_frame/tree_nav_helper.rb +18 -0
- data/app/models/menu.rb +44 -0
- data/app/models/settings.rb +4 -0
- data/app/views/app_frame/devise/confirmations/new.html.haml +8 -0
- data/app/views/app_frame/devise/passwords/edit.html.haml +13 -0
- data/app/views/app_frame/devise/passwords/new.html.haml +8 -0
- data/app/views/app_frame/devise/registrations/edit.html.haml +19 -0
- data/app/views/app_frame/devise/registrations/new.html.haml +10 -0
- data/app/views/app_frame/devise/sessions/new.html.haml +12 -0
- data/app/views/app_frame/devise/shared/_links.html.haml +14 -0
- data/app/views/app_frame/devise/unlocks/new.html.haml +9 -0
- data/app/views/application/_brand.html.haml +1 -0
- data/app/views/application/_breadcrumb.html.haml +12 -0
- data/app/views/application/_flashes.html.haml +2 -0
- data/app/views/application/_form.html.haml +2 -0
- data/app/views/application/_head.html.haml +6 -0
- data/app/views/application/_secondary_menu.html.haml +3 -0
- data/app/views/application/_show.html.haml +5 -0
- data/app/views/application/_sidebar.html.haml +12 -0
- data/app/views/application/_sub_menu.html.haml +0 -0
- data/app/views/application/_table.html.haml +3 -0
- data/app/views/application/_toolbar.html.haml +7 -0
- data/app/views/application/_top_menu.html.haml +1 -0
- data/app/views/application/edit.html.haml +8 -0
- data/app/views/application/index.html.haml +8 -0
- data/app/views/application/new.html.haml +7 -0
- data/app/views/application/show.html.haml +10 -0
- data/app/views/kaminari/app_frame/_first_page.html.haml +9 -0
- data/app/views/kaminari/app_frame/_gap.html.haml +8 -0
- data/app/views/kaminari/app_frame/_last_page.html.haml +9 -0
- data/app/views/kaminari/app_frame/_next_page.html.haml +9 -0
- data/app/views/kaminari/app_frame/_page.html.haml +10 -0
- data/app/views/kaminari/app_frame/_paginator.html.haml +19 -0
- data/app/views/kaminari/app_frame/_prev_page.html.haml +9 -0
- data/app/views/layouts/app_frame/default.html.haml +33 -0
- data/app/views/layouts/app_frame/devise.html.haml +17 -0
- data/app_frame.gemspec +162 -0
- data/lib/app_frame.rb +27 -0
- data/lib/app_frame/controller_methods.rb +111 -0
- data/lib/app_frame/view_methods.rb +19 -0
- data/lib/assets/images/.gitkeep +0 -0
- data/lib/assets/javascripts/anytime.js +3716 -0
- data/lib/assets/stylesheets/anytime.css +777 -0
- data/lib/engine.rb +197 -0
- data/spec/app_frame_spec.rb +7 -0
- data/spec/spec_helper.rb +12 -0
- metadata +423 -0
@@ -0,0 +1,36 @@
|
|
1
|
+
module AppFrame
|
2
|
+
module BootstrapHelper
|
3
|
+
def alert(message, type = nil)
|
4
|
+
message = close_button + message
|
5
|
+
content_tag(:div, message, :class => "alert alert-#{alert_type_map(type)}")
|
6
|
+
end
|
7
|
+
|
8
|
+
def close_button
|
9
|
+
content_tag(:a, "×".html_safe, :class => 'close', :data => { :dismiss => "alert" })
|
10
|
+
end
|
11
|
+
|
12
|
+
def alert_type_map(type)
|
13
|
+
map = {
|
14
|
+
:alert => 'error',
|
15
|
+
:notice => 'success'
|
16
|
+
}
|
17
|
+
|
18
|
+
map[type.to_sym] || type
|
19
|
+
end
|
20
|
+
|
21
|
+
def icon(key, options = {})
|
22
|
+
css_class = "icon-#{key}"
|
23
|
+
css_class += " icon-white" if options[:invert]
|
24
|
+
content_tag(:i, '', :class => css_class).html_safe
|
25
|
+
end
|
26
|
+
|
27
|
+
def page_header(title, &block)
|
28
|
+
content = "".html_safe
|
29
|
+
content += content_tag(:div, capture(&block), :class => 'page-actions') if block_given?
|
30
|
+
content += content_tag(:h1, title)
|
31
|
+
|
32
|
+
content_tag(:div, content, :class => 'page-header')
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module AppFrame::BreadcrumbHelper
|
2
|
+
|
3
|
+
def breadcrumb(items = [])
|
4
|
+
|
5
|
+
yield items if block_given?
|
6
|
+
|
7
|
+
return unless items.any?
|
8
|
+
|
9
|
+
last = items.pop
|
10
|
+
|
11
|
+
result = items.map { |i| content_tag(:li, i + content_tag(:span, "/", :class => 'divider')) }
|
12
|
+
result << content_tag(:li, last, :class => 'active')
|
13
|
+
|
14
|
+
content_tag :ul, result.join().html_safe, :class => 'breadcrumb'
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module AppFrame
|
2
|
+
module MenuHelper
|
3
|
+
def menu_link(key, path = '#', options = {}, &block)
|
4
|
+
active = false
|
5
|
+
highlight = options.delete(:highlights_on) || /#{path}/
|
6
|
+
dropdown = options.delete(:dropdown)
|
7
|
+
|
8
|
+
Array(highlight).each do |regex|
|
9
|
+
active = true if request.path =~ regex
|
10
|
+
end
|
11
|
+
|
12
|
+
options[:class] ||= ""
|
13
|
+
options[:class] << ' active' if active
|
14
|
+
options[:class] << ' dropdown' if dropdown
|
15
|
+
options[:class] = options[:class].present? ? options[:class].strip : nil
|
16
|
+
|
17
|
+
key = t(:"menu.#{key}") if key.is_a?(Symbol)
|
18
|
+
|
19
|
+
a_class = dropdown ? 'dropdown-toggle' : nil
|
20
|
+
ul_class = dropdown ? 'dropdown-menu' : nil
|
21
|
+
|
22
|
+
content = link_to(key, path, :class => a_class)
|
23
|
+
content += content_tag(:ul, capture(&block), :class => ul_class) if block_given?
|
24
|
+
|
25
|
+
content_tag :li, content, options
|
26
|
+
end
|
27
|
+
|
28
|
+
def content_locale_switch(obj = nil)
|
29
|
+
return unless obj.respond_to?(:translated_locales)
|
30
|
+
|
31
|
+
current_locale = (params[:content_locale] || I18n.default_locale).to_sym
|
32
|
+
|
33
|
+
locales = I18n.available_locales.map do |s|
|
34
|
+
text = s.to_s.upcase
|
35
|
+
text << "*" if obj && !obj.translated_locales.include?(s)
|
36
|
+
content_tag :li, link_to(text, url_for(:content_locale => s)), :class => current_locale == s ? 'active' : nil
|
37
|
+
end
|
38
|
+
|
39
|
+
content_tag :ul, locales.join("").html_safe, :class => 'nav nav-pills'
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module AppFrame::PaginationHelper
|
2
|
+
|
3
|
+
def page_range
|
4
|
+
return nil if count == 0
|
5
|
+
|
6
|
+
range_start = (page - 1) * per_page + 1
|
7
|
+
range_end = (page) * per_page
|
8
|
+
range_end = count if range_end > count
|
9
|
+
|
10
|
+
"<div class='page-range'>Displaying <strong>#{range_start} - #{range_end}</strong> of <strong>#{count}</strong> in total</div>".html_safe
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module AppFrame::ResourcesHelper
|
2
|
+
|
3
|
+
def collection?
|
4
|
+
!!collection
|
5
|
+
rescue NameError
|
6
|
+
false
|
7
|
+
end
|
8
|
+
|
9
|
+
def resource?
|
10
|
+
!!resource
|
11
|
+
rescue NameError, ActiveRecord::RecordNotFound
|
12
|
+
false
|
13
|
+
end
|
14
|
+
|
15
|
+
def resource_name
|
16
|
+
if resource_class
|
17
|
+
resource_class.model_name.human
|
18
|
+
else
|
19
|
+
controller_name
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def parent?
|
24
|
+
controller.respond_to?(:parent?) && controller.send(:parent?)
|
25
|
+
end
|
26
|
+
|
27
|
+
def parent_resource_class
|
28
|
+
parent.class
|
29
|
+
end
|
30
|
+
|
31
|
+
def parent_resource_name
|
32
|
+
parent_resource_class.model_name.human
|
33
|
+
end
|
34
|
+
|
35
|
+
def controller_namespaces
|
36
|
+
result = controller.class.to_s.split('::')
|
37
|
+
result.pop
|
38
|
+
result.map(&:underscore)
|
39
|
+
end
|
40
|
+
|
41
|
+
def namespaced(string)
|
42
|
+
if controller_namespaces.any?
|
43
|
+
File.join(controller_namespaces, string)
|
44
|
+
else
|
45
|
+
string
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def resource_path_array
|
50
|
+
result = controller_namespaces
|
51
|
+
result << parent if parent?
|
52
|
+
result << resource
|
53
|
+
end
|
54
|
+
|
55
|
+
def parent_collection_url
|
56
|
+
return unless parent?
|
57
|
+
|
58
|
+
result = controller_namespaces
|
59
|
+
result << parent_resource_name.underscore.pluralize
|
60
|
+
polymorphic_url(result)
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module AppFrame::ScopesHelper
|
2
|
+
# return all scopes configured in the controller
|
3
|
+
def scopes
|
4
|
+
controller.class.scopes_configuration || {}
|
5
|
+
end
|
6
|
+
|
7
|
+
def default_scopes
|
8
|
+
@default_scopes ||= scopes.inject({}) do |result, element|
|
9
|
+
result[element.first] = element.last if element.last[:default] == true
|
10
|
+
result
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# return only the boolean scopes
|
15
|
+
def boolean_scopes
|
16
|
+
@boolean_scopes ||= scopes.inject({}) do |result, element|
|
17
|
+
result[element.first] = element.last if element.last[:type] == :boolean
|
18
|
+
result
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# return only the filter (non boolean) scopes
|
23
|
+
def filter_scopes
|
24
|
+
@filter_scopes ||= scopes.inject({}) do |result, element|
|
25
|
+
result[element.first] = element.last if element.last[:type] != :boolean
|
26
|
+
result
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def current_boolean_scopes
|
31
|
+
@current_boolean_scopes ||= current_scopes.inject({}) do |result, element|
|
32
|
+
result[element.first] = element.last if boolean_scopes.keys.include?(element.first)
|
33
|
+
result
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def current_filter_scopes
|
38
|
+
@current_filter_scopes ||= current_scopes.inject({}) do |result, element|
|
39
|
+
result[element.first] = element.last if filter_scopes.keys.include?(element.first)
|
40
|
+
result
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# create a link to a scope (or no scopes), wrapped in a list item
|
45
|
+
def scope_link(scope = nil)
|
46
|
+
if scope
|
47
|
+
as = scope[:as]
|
48
|
+
name = as.to_s.humanize
|
49
|
+
else
|
50
|
+
as = nil
|
51
|
+
name = "All"
|
52
|
+
end
|
53
|
+
|
54
|
+
link = current_filter_scopes.dup
|
55
|
+
|
56
|
+
if as
|
57
|
+
unless scope[:default]
|
58
|
+
link[as] = true
|
59
|
+
default_scopes.each { |dn, ds| link[ds[:as]] = false }
|
60
|
+
end
|
61
|
+
|
62
|
+
active = current_boolean_scopes.keys.include?(as)
|
63
|
+
else
|
64
|
+
active = current_boolean_scopes.empty?
|
65
|
+
end
|
66
|
+
|
67
|
+
# name = "#{name}: #{scope_count(scope)}"
|
68
|
+
|
69
|
+
content_tag(:li, link_to(name, link), :class => active ? 'active' : nil)
|
70
|
+
end
|
71
|
+
|
72
|
+
def scope_count(scope)
|
73
|
+
chain = controller.end_of_association_chain
|
74
|
+
chain = chain.send(scope) if scope
|
75
|
+
chain.count
|
76
|
+
end
|
77
|
+
|
78
|
+
# craete a navigation for all the boolean scopes
|
79
|
+
def scope_nav
|
80
|
+
return unless boolean_scopes.count > 0
|
81
|
+
|
82
|
+
if default_scopes.any?
|
83
|
+
result = []
|
84
|
+
else
|
85
|
+
result = [scope_link]
|
86
|
+
end
|
87
|
+
|
88
|
+
boolean_scopes.each do |name, s|
|
89
|
+
result << scope_link(s)
|
90
|
+
end
|
91
|
+
|
92
|
+
content_tag :ul, result.join("\n").html_safe, :class => 'nav nav-pills'
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module AppFrame::TreeNavHelper
|
2
|
+
def tree_nav(node)
|
3
|
+
result = []
|
4
|
+
result << tree_nav_link("↑", left_admin_section_page_path(parent, node), node.left_sibling)
|
5
|
+
result << tree_nav_link("↓", right_admin_section_page_path(parent, node), node.right_sibling)
|
6
|
+
result << tree_nav_link("→", down_admin_section_page_path(parent, node), node.left_sibling)
|
7
|
+
result << tree_nav_link("←", up_admin_section_page_path(parent, node), node.parent)
|
8
|
+
result << link_to("Edit", edit_admin_section_page_path(parent, node))
|
9
|
+
yield result if block_given?
|
10
|
+
result << link_to("Delete", admin_section_page_path(parent, node), :method => :delete)
|
11
|
+
content_tag :span, result.join('').html_safe, :class => 'tree_nav'
|
12
|
+
end
|
13
|
+
|
14
|
+
def tree_nav_link(text, link, condition = true)
|
15
|
+
condition ? link_to(text.html_safe, link) : content_tag(:span, text.html_safe)
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
data/app/models/menu.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
class MenuItem
|
2
|
+
attr_reader :name, :path, :namespace
|
3
|
+
|
4
|
+
def initialize(namespace, path)
|
5
|
+
@namespace = namespace
|
6
|
+
@path = path
|
7
|
+
@name = path.humanize
|
8
|
+
end
|
9
|
+
|
10
|
+
def url
|
11
|
+
"/#{namespace}/#{path}"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
class Menu
|
17
|
+
attr_reader :namespace
|
18
|
+
|
19
|
+
def initialize(namespace)
|
20
|
+
@namespace = namespace
|
21
|
+
end
|
22
|
+
|
23
|
+
def routes
|
24
|
+
@routes ||= Rails.application.routes.routes
|
25
|
+
end
|
26
|
+
|
27
|
+
def menu_routes
|
28
|
+
@menu_routes ||= routes.select { |r| r.defaults[:action] == 'index' }
|
29
|
+
end
|
30
|
+
|
31
|
+
def specs
|
32
|
+
@specs ||= menu_routes.map { |r| r.path.spec }.uniq
|
33
|
+
end
|
34
|
+
|
35
|
+
def menu_items
|
36
|
+
@menu_items = []
|
37
|
+
specs.each do |s|
|
38
|
+
matches = /\/#{@namespace}\/(\w*)/.match(s.to_s)
|
39
|
+
@menu_items << matches[1] if matches
|
40
|
+
end
|
41
|
+
@menu_items.uniq.map { |i| MenuItem.new(@namespace, i) }
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
= page_header "Resend confirmation instructions"
|
2
|
+
|
3
|
+
= simple_form_for(resource, :as => resource_name, :url => confirmation_path(resource_name), :html => { :method => :post }) do |f|
|
4
|
+
= f.error_notification
|
5
|
+
|
6
|
+
= f.input :email, :required => true
|
7
|
+
|
8
|
+
= f.button :submit, "Resend confirmation instructions", :class => 'btn'
|
@@ -0,0 +1,13 @@
|
|
1
|
+
= page_header "Change your password"
|
2
|
+
|
3
|
+
= simple_form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :put }) do |f|
|
4
|
+
= f.error_notification
|
5
|
+
|
6
|
+
= f.input :reset_password_token, :as => :hidden
|
7
|
+
= f.full_error :reset_password_token
|
8
|
+
|
9
|
+
= f.input :password, :label => "New password", :required => true
|
10
|
+
= f.input :password_confirmation, :label => "Confirm your new password", :required => true
|
11
|
+
|
12
|
+
= f.button :submit, "Change my password"
|
13
|
+
|
@@ -0,0 +1,8 @@
|
|
1
|
+
=page_header "Forgot your password?"
|
2
|
+
|
3
|
+
= simple_form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :post }) do |f|
|
4
|
+
= f.error_notification
|
5
|
+
|
6
|
+
= f.input :email, :required => true
|
7
|
+
|
8
|
+
= f.button :submit, "Send", :class => 'btn'
|
@@ -0,0 +1,19 @@
|
|
1
|
+
= page_header "Edit #{resource_name.to_s.humanize}"
|
2
|
+
|
3
|
+
= simple_form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :method => :put }) do |f|
|
4
|
+
= f.error_notification
|
5
|
+
|
6
|
+
= f.input :email, :required => true, :autofocus => true
|
7
|
+
= f.input :password, :hint => "leave it blank if you don't want to change it", :required => false
|
8
|
+
= f.input :password_confirmation, :required => false
|
9
|
+
= f.input :current_password, :hint => "we need your current password to confirm your changes", :required => true
|
10
|
+
|
11
|
+
= f.button :submit, "Update", :class => 'btn'
|
12
|
+
|
13
|
+
%h3 Cancel my account<
|
14
|
+
|
15
|
+
%p
|
16
|
+
Unhappy?
|
17
|
+
= link_to "Cancel my account", registration_path(resource_name), :confirm => "Are you sure?", :method => :delete
|
18
|
+
|
19
|
+
= link_to "Back", :back, :class => 'btn'
|
@@ -0,0 +1,10 @@
|
|
1
|
+
= page_header "Sign up"
|
2
|
+
|
3
|
+
= simple_form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f|
|
4
|
+
= f.error_notification
|
5
|
+
|
6
|
+
= f.input :email, :required => true, :autofocus => true
|
7
|
+
= f.input :password, :required => true
|
8
|
+
= f.input :password_confirmation, :required => true
|
9
|
+
|
10
|
+
= f.button :submit, "Sign up", :class => 'btn'
|
@@ -0,0 +1,12 @@
|
|
1
|
+
=page_header "Sign in"
|
2
|
+
|
3
|
+
= simple_form_for(resource, :as => resource_name, :url => session_path(resource_name)) do |f|
|
4
|
+
= f.input :email, :required => false, :autofocus => true
|
5
|
+
= f.input :password, :required => false
|
6
|
+
|
7
|
+
- if devise_mapping.rememberable?
|
8
|
+
= f.label :remember_me, :class => 'checkbox' do
|
9
|
+
= f.check_box :remember_me
|
10
|
+
Remember me
|
11
|
+
|
12
|
+
= f.button :submit, "Sign in", :class => 'btn'
|