best_boy 0.0.3 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/CHANGELOG.md +6 -0
- data/README.md +35 -1
- data/app/controllers/best_boy/best_boy_events_controller.rb +65 -0
- data/app/views/best_boy/best_boy_events/_navigation.html.erb +9 -0
- data/app/views/best_boy/best_boy_events/index.html.erb +6 -0
- data/app/views/best_boy/best_boy_events/lists.html.erb +43 -0
- data/app/views/best_boy/best_boy_events/stats.html.erb +60 -0
- data/app/views/layouts/best_boy_backend.html.erb +62 -0
- data/best_boy.gemspec +9 -6
- data/config/routes.rb +5 -0
- data/db/bestboy.db +0 -0
- data/lib/best_boy.rb +5 -1
- data/{app → lib/best_boy}/controllers/best_boy_controller.rb +1 -1
- data/lib/best_boy/engine.rb +3 -1
- data/lib/best_boy/models/active_record/best_boy/eventable.rb +4 -0
- data/lib/best_boy/models/active_record/best_boy_event.rb +11 -1
- data/lib/best_boy/version.rb +1 -1
- data/lib/generators/best_boy_generator.rb +6 -0
- data/lib/generators/templates/best_boy.rb +15 -3
- data/lib/generators/templates/bootstrap/bootstrap.css +4983 -0
- data/lib/generators/templates/bootstrap/bootstrap_datepicker.css +156 -0
- data/lib/generators/templates/bootstrap/bootstrap_datepicker.js +401 -0
- data/lib/generators/templates/bootstrap/glyphicons-halflings-white.png +0 -0
- data/lib/generators/templates/bootstrap/glyphicons-halflings.png +0 -0
- data/log/test.log +1 -0
- data/spec/best_boy/best_boy_controller_spec.rb +4 -4
- data/spec/best_boy/best_boy_event_spec.rb +49 -3
- data/spec/best_boy/eventable_spec.rb +4 -4
- data/spec/spec_helper.rb +8 -8
- metadata +54 -20
- data/app/models/best_boy/eventable.rb +0 -10
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -27,6 +27,20 @@ Generate the BestBoyEvent table migration
|
|
27
27
|
|
28
28
|
rails g best_boy
|
29
29
|
|
30
|
+
This will install following files into your application
|
31
|
+
|
32
|
+
config/initializers/best_boy.rb
|
33
|
+
|
34
|
+
db_migrate/create_best_boy_events_table.rb
|
35
|
+
|
36
|
+
public/javascripts/bootstrap_datepicker.js
|
37
|
+
|
38
|
+
public/stylesheets/bootstrap.css
|
39
|
+
|
40
|
+
public/stylesheets/bootstrap_datepicker.css
|
41
|
+
|
42
|
+
See usage section for version information.
|
43
|
+
|
30
44
|
Run the migration
|
31
45
|
|
32
46
|
rake db:migrate
|
@@ -46,6 +60,7 @@ In controller context:
|
|
46
60
|
best_boy_event object, event
|
47
61
|
|
48
62
|
This will log custom events for a object and a event phrase.
|
63
|
+
|
49
64
|
If no Object is given, it will raise an exception as well as if no event is provided.
|
50
65
|
|
51
66
|
|
@@ -61,7 +76,26 @@ BestBoyEvent table
|
|
61
76
|
Getting BestBoyEvents
|
62
77
|
---------------------
|
63
78
|
|
64
|
-
|
79
|
+
BestBoy comes with an admin interface, largely configurable to fit right into your existing application.
|
80
|
+
|
81
|
+
Following configurations can be done:
|
82
|
+
|
83
|
+
config.orm "symbole" # default: :active_record # for now only active_record is supported
|
84
|
+
config.base_controller "String" # default: "ApplicationController" # declare with Controller should be inherited
|
85
|
+
config.before_filter "comma separated symbols" # default: nil # declare before_filter to use inherited before_filters in admin section
|
86
|
+
config.skip_before_filter "comma separated symbols" # default: nil # declare skip_before_filter to skip inherited before_filters in admin section
|
87
|
+
config.custom_redirect "relative path as String" # default: nil # set a path to return back to your existing backend
|
88
|
+
|
89
|
+
After installation you can access it through:
|
90
|
+
|
91
|
+
<your application path>/best_boy_admin
|
92
|
+
|
93
|
+
|
94
|
+
Used gems and resource
|
95
|
+
----------------------
|
96
|
+
[Twitter Bootstrap](http://twitter.github.com/bootstrap/) in its version 2.0.4
|
97
|
+
|
98
|
+
[Stefan Petre](http://www.eyecon.ro/bootstrap-datepicker) for Datepicker in Twitter Bootstrap style
|
65
99
|
|
66
100
|
Thanks
|
67
101
|
------
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module BestBoy
|
2
|
+
class BestBoyEventsController < BestBoy.base_controller.constantize
|
3
|
+
|
4
|
+
before_filter BestBoy.before_filter if BestBoy.before_filter.present?
|
5
|
+
skip_before_filter BestBoy.skip_before_filter if BestBoy.skip_before_filter.present?
|
6
|
+
|
7
|
+
layout 'best_boy_backend'
|
8
|
+
|
9
|
+
helper_method :available_owner_types, :available_events, :available_years, :current_owner_type, :current_event, :current_year, :collection, :statistics, :stats_by_event_and_month
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def current_owner_type
|
14
|
+
@current_owner_type ||= available_owner_types.include?(params[:owner_type]) ? params[:owner_type] : available_owner_types.first
|
15
|
+
end
|
16
|
+
|
17
|
+
def current_event
|
18
|
+
@current_event ||= available_events.include?(params[:event]) ? params[:event] : nil
|
19
|
+
end
|
20
|
+
|
21
|
+
def current_date
|
22
|
+
@current_date ||= Date.strptime(params[:date], "%d-%m-%Y") if params[:date] rescue nil
|
23
|
+
end
|
24
|
+
|
25
|
+
def current_year
|
26
|
+
@current_year ||= available_years.include?(params[:year]) ? params[:year] : Time.zone.now.year
|
27
|
+
end
|
28
|
+
|
29
|
+
def available_owner_types
|
30
|
+
@available_owner_types ||= BestBoyEvent.select("DISTINCT best_boy_events.owner_type").order("best_boy_events.owner_type ASC").map(&:owner_type)
|
31
|
+
end
|
32
|
+
|
33
|
+
def available_events
|
34
|
+
@available_events ||= BestBoyEvent.where("best_boy_events.owner_type = ?", current_owner_type).select("best_boy_events.event").order("best_boy_events.event ASC").group("best_boy_events.event").map(&:event)
|
35
|
+
end
|
36
|
+
|
37
|
+
def available_years
|
38
|
+
@available_years = (BestBoyEvent.order("best_boy_events.created_at ASC").first.created_at.to_date.year..Time.zone.now.year).map{ |year| year.to_s } rescue [Time.zone.now.year]
|
39
|
+
end
|
40
|
+
|
41
|
+
def collection
|
42
|
+
@best_boy_events ||= (
|
43
|
+
scope = BestBoyEvent.where("best_boy_events.owner_type = ?", current_owner_type)
|
44
|
+
scope = scope.where("best_boy_events.event = ?", current_event) if current_event.present?
|
45
|
+
scope = scope.per_day(current_date) if current_date.present?
|
46
|
+
scope = scope.order("best_boy_events.created_at DESC, best_boy_events.event ASC")
|
47
|
+
scope.page(params[:page]).per(50)
|
48
|
+
)
|
49
|
+
end
|
50
|
+
|
51
|
+
def statistics
|
52
|
+
@statistics = Array.new
|
53
|
+
available_events.each do |event|
|
54
|
+
scope = BestBoyEvent.where("best_boy_events.owner_type = ? AND best_boy_events.event = ?", current_owner_type, event)
|
55
|
+
@statistics.push([event, scope.count] + %w(year month week day).map{ |delimiter| scope.send("per_#{delimiter}", Time.zone.now).count })
|
56
|
+
end
|
57
|
+
@statistics
|
58
|
+
end
|
59
|
+
|
60
|
+
def stats_by_event_and_month event, month
|
61
|
+
date = "1-#{month}-#{current_year}".to_time
|
62
|
+
BestBoyEvent.where("best_boy_events.owner_type = ? AND best_boy_events.event = ?", current_owner_type, event).per_month(date).count
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
<div class="span12">
|
2
|
+
<ul class="nav nav-tabs">
|
3
|
+
<% available_owner_types.each do |owner_type| %>
|
4
|
+
<li class="<%= (current_owner_type == owner_type) ? 'active' : nil %>">
|
5
|
+
<%= link_to owner_type, (referrer == "stats") ? best_boy_admin_stats_path(:owner_type => owner_type) : best_boy_admin_lists_path(:owner_type => owner_type) %>
|
6
|
+
</li>
|
7
|
+
<% end %>
|
8
|
+
</ul>
|
9
|
+
</div>
|
@@ -0,0 +1,6 @@
|
|
1
|
+
<div class="span12">
|
2
|
+
<div class="hero-unit">
|
3
|
+
<h2>BestBoy Administation Center</h1>
|
4
|
+
<p>BestBoy provides several statistics and logs for you. You can access yearly, monthly, weekly and day statistics for your logged events per owner class. In the list and logs section you can see realtime events and search for date and events for each owner class.</p>
|
5
|
+
</div>
|
6
|
+
</div>
|
@@ -0,0 +1,43 @@
|
|
1
|
+
<%= render "navigation", :referrer => "lists" %>
|
2
|
+
<% content_for :stylesheets do %>
|
3
|
+
<%= stylesheet_link_tag "bootstrap_datepicker" %>
|
4
|
+
<% end %>
|
5
|
+
<% content_for :javascripts do %>
|
6
|
+
<%= javascript_include_tag "https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" %>
|
7
|
+
<%= javascript_include_tag "bootstrap_datepicker" %>
|
8
|
+
<script type="text/javascript">
|
9
|
+
$(function(){$('#dp1').datepicker().on('changeDate', function(){$('#list-form').submit();})});
|
10
|
+
</script>
|
11
|
+
<% end %>
|
12
|
+
|
13
|
+
<div class="span12">
|
14
|
+
<div class="well">
|
15
|
+
<h3 class="pull-left">Log</h3>
|
16
|
+
<div class="pull-right">
|
17
|
+
<%= form_tag best_boy_admin_lists_path, :method => :get, :id => "list-form" do %>
|
18
|
+
<div class="input-append date pull-left span3" id="dp1" data-date="<%= params[:date].present? ? params[:date] : l(Time.zone.now, :format => "%d-%m-%Y") %>" data-date-format="dd-mm-yyyy">
|
19
|
+
<input name="date" class="span2" size="16" type="text" value="<%= params[:date] %>" readonly="readonly">
|
20
|
+
<span class="add-on"><i class="icon-th"></i></span>
|
21
|
+
</div>
|
22
|
+
<%= select_tag :event, options_for_select([["all Events", nil]] + available_events, :selected => current_event), :onchange => "submit();", :class => "span4" %>
|
23
|
+
<% end %>
|
24
|
+
</div>
|
25
|
+
<table class="table table-striped table-bordered">
|
26
|
+
<thead>
|
27
|
+
<tr>
|
28
|
+
<th>Event</th>
|
29
|
+
<th>created_at</th>
|
30
|
+
</tr>
|
31
|
+
</thead>
|
32
|
+
<tbody>
|
33
|
+
<% collection.each do |best_boy_event| %>
|
34
|
+
<tr>
|
35
|
+
<td><%= best_boy_event.event %></td>
|
36
|
+
<td><%= best_boy_event.created_at %></td>
|
37
|
+
</tr>
|
38
|
+
<% end %>
|
39
|
+
</tbody>
|
40
|
+
</table>
|
41
|
+
<%= paginate collection %>
|
42
|
+
</div>
|
43
|
+
</div>
|
@@ -0,0 +1,60 @@
|
|
1
|
+
<%= render "navigation", :referrer => "stats" %>
|
2
|
+
|
3
|
+
<div class="span12">
|
4
|
+
<div class="well">
|
5
|
+
<h3>Statistics</h3>
|
6
|
+
<table class="table table-striped table-bordered">
|
7
|
+
<thead>
|
8
|
+
<tr>
|
9
|
+
<th>Event</th>
|
10
|
+
<th>overall</th>
|
11
|
+
<th>year</th>
|
12
|
+
<th>month</th>
|
13
|
+
<th>week</th>
|
14
|
+
<th>day</th>
|
15
|
+
</tr>
|
16
|
+
</thead>
|
17
|
+
<tbody>
|
18
|
+
<% statistics.each do |best_boy_event| %>
|
19
|
+
<tr>
|
20
|
+
<% (0..5).each do |key| %>
|
21
|
+
<td><%= best_boy_event[key] %></td>
|
22
|
+
<% end %>
|
23
|
+
</tr>
|
24
|
+
<% end %>
|
25
|
+
</tbody>
|
26
|
+
</table>
|
27
|
+
</div>
|
28
|
+
</div>
|
29
|
+
|
30
|
+
<div class="span12">
|
31
|
+
<div class="well">
|
32
|
+
<h3 class="pull-left">Statistics for <%= current_year %> per month</h3>
|
33
|
+
|
34
|
+
<div class="pull-right">
|
35
|
+
<%= form_tag best_boy_admin_stats_path, :method => :get do %>
|
36
|
+
<%= select_tag :year, options_for_select(available_years, :selected => current_year), :onchange => "submit();" %>
|
37
|
+
<% end %>
|
38
|
+
</div>
|
39
|
+
<table class="table table-striped table-bordered">
|
40
|
+
<thead>
|
41
|
+
<tr>
|
42
|
+
<th>Event</th>
|
43
|
+
<% %w(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec).each do |month| %>
|
44
|
+
<th><%= month %></th>
|
45
|
+
<% end %>
|
46
|
+
</tr>
|
47
|
+
</thead>
|
48
|
+
<tbody>
|
49
|
+
<% available_events.each do |event| %>
|
50
|
+
<tr>
|
51
|
+
<td><%= event %></td>
|
52
|
+
<% %w(1 2 3 4 5 6 7 8 9 10 11 12).each do |month| %>
|
53
|
+
<td><%= stats_by_event_and_month event, month %></td>
|
54
|
+
<% end %>
|
55
|
+
</tr>
|
56
|
+
<% end %>
|
57
|
+
</tbody>
|
58
|
+
</table>
|
59
|
+
</div>
|
60
|
+
</div>
|
@@ -0,0 +1,62 @@
|
|
1
|
+
<!doctype html>
|
2
|
+
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7" lang="en"> <![endif]-->
|
3
|
+
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8" lang="en"> <![endif]-->
|
4
|
+
<!--[if IE 8]> <html class="no-js lt-ie9" lang="en"> <![endif]-->
|
5
|
+
<!-- Consider adding a manifest.appcache: h5bp.com/d/Offline -->
|
6
|
+
<!--[if gt IE 8]><!--> <html class="no-js" lang="en"> <!--<![endif]-->
|
7
|
+
<head>
|
8
|
+
<meta charset="utf-8">
|
9
|
+
<meta content='IE=edge,chrome=1' http-equiv='X-UA-Compatible'>
|
10
|
+
<meta name="description" content="">
|
11
|
+
<meta name="viewport" content="width=device-width">
|
12
|
+
<title>BBAC - BestBoy Administration Center</title>
|
13
|
+
<%= csrf_meta_tag %>
|
14
|
+
<%= stylesheet_link_tag("bootstrap", :media => "all") %>
|
15
|
+
<%= yield :stylesheets %>
|
16
|
+
<%= yield :javascripts %>
|
17
|
+
<style>
|
18
|
+
/* FIX for kaminari gem */
|
19
|
+
nav.pagination {
|
20
|
+
text-align: center;
|
21
|
+
}
|
22
|
+
span.current,
|
23
|
+
span.gap a {
|
24
|
+
float: left;
|
25
|
+
padding: 0 9px;
|
26
|
+
line-height: 34px;
|
27
|
+
text-decoration: none;
|
28
|
+
border: 1px solid #DDD;
|
29
|
+
border-left-width: 0;
|
30
|
+
}
|
31
|
+
span.current {
|
32
|
+
border: 1px solid #DDD;
|
33
|
+
color: #999;
|
34
|
+
cursor: default;
|
35
|
+
background-color: whiteSmoke;
|
36
|
+
}
|
37
|
+
</style>
|
38
|
+
</head>
|
39
|
+
<body style="padding-top: 60px;">
|
40
|
+
<div class="navbar navbar-fixed-top">
|
41
|
+
<div class="navbar-inner">
|
42
|
+
<div class="container">
|
43
|
+
<%= link_to "BBAC", best_boy_admin_path, :class => "brand" %>
|
44
|
+
<ul class="nav">
|
45
|
+
<li><%= link_to "Statistics", best_boy_admin_stats_path %></li>
|
46
|
+
<li><%= link_to "List & Logs", best_boy_admin_lists_path %></li>
|
47
|
+
</ul>
|
48
|
+
<% if BestBoy.custom_redirect.present? %>
|
49
|
+
<p class="navbar-text pull-right"><%= link_to("back", Rails.application.routes.recognize_path(BestBoy.custom_redirect)) %></p>
|
50
|
+
<% end %>
|
51
|
+
</div>
|
52
|
+
</div>
|
53
|
+
</div>
|
54
|
+
|
55
|
+
<div class="container">
|
56
|
+
<div class="row">
|
57
|
+
<%= yield %>
|
58
|
+
</div>
|
59
|
+
</div>
|
60
|
+
<%= yield :javascripts %>
|
61
|
+
</body>
|
62
|
+
</html>
|
data/best_boy.gemspec
CHANGED
@@ -16,12 +16,15 @@ Gem::Specification.new do |s|
|
|
16
16
|
s.rubyforge_project = "best_boy"
|
17
17
|
s.required_rubygems_version = ">= 1.3.6"
|
18
18
|
|
19
|
-
s.
|
20
|
-
|
21
|
-
s.add_development_dependency('
|
22
|
-
s.add_development_dependency('
|
23
|
-
s.add_development_dependency(
|
24
|
-
s.add_development_dependency(
|
19
|
+
s.add_dependency('kaminari')
|
20
|
+
|
21
|
+
s.add_development_dependency('bundler', '~> 1.1.0')
|
22
|
+
s.add_development_dependency('activerecord')
|
23
|
+
s.add_development_dependency('activesupport')
|
24
|
+
s.add_development_dependency('sqlite3', '~> 1.3.6')
|
25
|
+
s.add_development_dependency('rake', '~> 0.9.2.2')
|
26
|
+
s.add_development_dependency('rspec', '~> 2.10.0')
|
27
|
+
s.add_development_dependency('shoulda', '~> 3.0.1')
|
25
28
|
|
26
29
|
s.files = `git ls-files`.split("\n")
|
27
30
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
data/config/routes.rb
ADDED
@@ -0,0 +1,5 @@
|
|
1
|
+
Rails.application.routes.draw do |map|
|
2
|
+
get "best_boy_admin" => "best_boy/best_boy_events#index", :as => :best_boy_admin
|
3
|
+
get "best_boy_admin/stats" => "best_boy/best_boy_events#stats", :as => :best_boy_admin_stats
|
4
|
+
get "best_boy_admin/lists" => "best_boy/best_boy_events#lists", :as => :best_boy_admin_lists
|
5
|
+
end
|
data/db/bestboy.db
CHANGED
Binary file
|
data/lib/best_boy.rb
CHANGED
@@ -2,8 +2,12 @@ require "best_boy/engine.rb"
|
|
2
2
|
|
3
3
|
module BestBoy
|
4
4
|
# Define ORM
|
5
|
-
mattr_accessor :orm
|
5
|
+
mattr_accessor :orm, :base_controller, :before_filter, :skip_before_filter, :custom_redirect
|
6
6
|
@@orm = :active_record
|
7
|
+
@@base_controller = "ApplicationController"
|
8
|
+
@@before_filter = nil
|
9
|
+
@@skip_before_filter = nil
|
10
|
+
@@custom_redirect = nil
|
7
11
|
|
8
12
|
# Load configuration from initializer
|
9
13
|
def self.setup
|
data/lib/best_boy/engine.rb
CHANGED
@@ -3,8 +3,8 @@ require "rails"
|
|
3
3
|
|
4
4
|
module BestBoy
|
5
5
|
class Engine < Rails::Engine
|
6
|
+
|
6
7
|
initializer 'best_boy.model' do |app|
|
7
|
-
require "#{root}/app/models/best_boy/eventable.rb"
|
8
8
|
if BestBoy.orm == :active_record
|
9
9
|
require "best_boy/models/active_record/best_boy_event.rb"
|
10
10
|
require "best_boy/models/active_record/best_boy/eventable.rb"
|
@@ -15,9 +15,11 @@ module BestBoy
|
|
15
15
|
end
|
16
16
|
|
17
17
|
initializer 'best_boy.controller' do
|
18
|
+
require "best_boy/controllers/best_boy_controller.rb"
|
18
19
|
ActiveSupport.on_load(:action_controller) do
|
19
20
|
include BestBoyController::InstanceMethods
|
20
21
|
end
|
21
22
|
end
|
23
|
+
|
22
24
|
end
|
23
25
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
class BestBoyEvent < ActiveRecord::Base
|
2
|
+
|
2
3
|
# associations
|
3
4
|
#
|
4
5
|
#
|
@@ -9,9 +10,18 @@ class BestBoyEvent < ActiveRecord::Base
|
|
9
10
|
#
|
10
11
|
validates :event, :presence => true
|
11
12
|
|
13
|
+
# scopes
|
14
|
+
#
|
15
|
+
#
|
16
|
+
|
17
|
+
scope :per_day, lambda { |date| where("best_boy_events.created_at BETWEEN ? AND ?", date.beginning_of_day, date.end_of_day) }
|
18
|
+
scope :per_week, lambda { |date| where("best_boy_events.created_at BETWEEN ? AND ?", date.beginning_of_week, date.end_of_week) }
|
19
|
+
scope :per_month, lambda { |date| where("best_boy_events.created_at BETWEEN ? AND ?", date.beginning_of_month, date.end_of_month) }
|
20
|
+
scope :per_year, lambda { |date| where("best_boy_events.created_at BETWEEN ? AND ?", date.beginning_of_year, date.end_of_year) }
|
21
|
+
|
12
22
|
# attributes
|
13
23
|
#
|
14
24
|
#
|
15
|
-
attr_accessible :owner_id, :owner_type, :event
|
25
|
+
attr_accessible :owner_id, :owner_type, :event, :updated_at, :created_at
|
16
26
|
|
17
27
|
end
|
data/lib/best_boy/version.rb
CHANGED
@@ -6,6 +6,12 @@ module BestBoy
|
|
6
6
|
|
7
7
|
def copy_config_file
|
8
8
|
template 'best_boy.rb', 'config/initializers/best_boy.rb'
|
9
|
+
template 'bootstrap/glyphicons-halflings-white.png', 'public/images/bootstrap/glyphicons-halflings-white.png'
|
10
|
+
template 'bootstrap/glyphicons-halflings.png', 'public/images/bootstrap/glyphicons-halflings.png'
|
11
|
+
template 'bootstrap/bootstrap.css', 'public/stylesheets/bootstrap.css'
|
12
|
+
#datepicker by Stefan Petre (http://www.eyecon.ro/bootstrap-datepicker)
|
13
|
+
template 'bootstrap/bootstrap_datepicker.css', 'public/stylesheets/bootstrap_datepicker.css'
|
14
|
+
template 'bootstrap/bootstrap_datepicker.js', 'public/javascripts/bootstrap_datepicker.js'
|
9
15
|
end
|
10
16
|
end
|
11
17
|
end
|