upmin-admin 0.1.01 → 0.1.3
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.
- checksums.yaml +4 -4
- data/README.md +10 -18
- data/Rakefile +18 -18
- data/app/assets/javascripts/upmin/attributes/datetime.js +37 -11
- data/app/assets/stylesheets/upmin/application.css +0 -1
- data/app/assets/stylesheets/upmin/base.css.scss +8 -1
- data/app/assets/stylesheets/upmin/dashboard.css +16 -0
- data/app/assets/stylesheets/upmin/instances.css.scss +7 -0
- data/app/controllers/upmin/dashboard_controller.rb +11 -0
- data/app/controllers/upmin/models_controller.rb +28 -9
- data/app/views/layouts/upmin/application.html.haml +6 -2
- data/app/views/upmin/dashboard/_chart.html.haml +9 -0
- data/app/views/upmin/dashboard/index.html.haml +14 -0
- data/app/views/upmin/models/search.html.haml +20 -4
- data/app/views/upmin/partials/attributes/_boolean.html.haml +1 -6
- data/app/views/upmin/partials/attributes/_date.html.haml +26 -0
- data/app/views/upmin/partials/attributes/_enum.html.haml +9 -0
- data/app/views/upmin/partials/attributes/_money_cents.html.haml +10 -0
- data/app/views/upmin/partials/models/_model.html.haml +5 -6
- data/app/views/upmin/partials/models/_new_model.html.haml +1 -1
- data/app/views/upmin/partials/search_boxes/_ransack_search_box.html.haml +6 -1
- data/config/routes.rb +3 -5
- data/lib/generators/upmin/install_generator.rb +24 -0
- data/lib/generators/upmin/templates/initializer.rb +13 -0
- data/lib/upmin/active_record/model.rb +15 -1
- data/lib/upmin/admin.rb +2 -1
- data/lib/upmin/attribute.rb +4 -1
- data/lib/upmin/configuration.rb +1 -0
- data/lib/upmin/data_mapper/model.rb +10 -0
- data/lib/upmin/engine.rb +4 -3
- data/lib/upmin/model.rb +37 -5
- data/lib/upmin/railtie.rb +1 -0
- data/lib/upmin/railties/dashboard.rb +88 -0
- data/lib/upmin/railties/render_helpers.rb +2 -0
- data/lib/upmin/version.rb +1 -1
- data/spec/features/delete_model_spec.rb +38 -0
- data/spec/features/edit_model_spec.rb +1 -1
- data/spec/features/enum_attributes_spec.rb +24 -0
- data/spec/features/navbar_spec.rb +4 -4
- data/spec/features/new_model_spec.rb +1 -1
- data/spec/features/search_spec.rb +17 -27
- data/spec/lib/attribute_spec.rb +15 -0
- data/spec/lib/upmin/active_record/model_spec.rb +15 -0
- data/spec/spec_helper.rb +8 -0
- metadata +49 -4
- data/app/views/upmin/models/dashboard.html.haml +0 -9
@@ -0,0 +1,26 @@
|
|
1
|
+
- date = attribute.value.nil? ? nil : attribute.value
|
2
|
+
|
3
|
+
.form-group{class: attribute.errors? ? "has-error" : ""}
|
4
|
+
%label{for: attribute.form_id}
|
5
|
+
= attribute.label_name
|
6
|
+
|
7
|
+
- if attribute.editable? && f = form_builder
|
8
|
+
.row.date-attribute{class: attribute.form_id}
|
9
|
+
= f.hidden_field(attribute.name, value: date)
|
10
|
+
|
11
|
+
.col-md-12
|
12
|
+
.input-group.pikadate
|
13
|
+
%input.form-control{type: :text, value: date, id: "#{attribute.form_id}-date"}
|
14
|
+
%span.input-group-addon
|
15
|
+
%span.glyphicon.glyphicon-calendar
|
16
|
+
|
17
|
+
- content_for(:javascript) do
|
18
|
+
:javascript
|
19
|
+
$(document).ready(function() {
|
20
|
+
window.Upmin.Attributes.Date("#{attribute.form_id}");
|
21
|
+
});
|
22
|
+
|
23
|
+
- else
|
24
|
+
%p.well
|
25
|
+
-# TODO(jon): Make this show an uneditable date.
|
26
|
+
= date
|
@@ -0,0 +1,9 @@
|
|
1
|
+
.form-group{class: attribute.errors? ? "has-error" : ""}
|
2
|
+
%label{for: attribute.form_id}
|
3
|
+
= attribute.label_name
|
4
|
+
|
5
|
+
.enum
|
6
|
+
- attribute.enum_options.each do |option|
|
7
|
+
%label.radio-inline
|
8
|
+
= form_builder.radio_button attribute.name, option, disabled: !attribute.editable?
|
9
|
+
= option.humanize
|
@@ -0,0 +1,10 @@
|
|
1
|
+
-# based on "unknown" attribute type partial; does not allow editing (would
|
2
|
+
-# have to parse the string back into cents, which is fraught with danger)
|
3
|
+
.form-group{class: attribute.errors? ? "has-error" : ""}
|
4
|
+
%label{for: attribute.form_id}
|
5
|
+
= attribute.label_name.gsub('cents', '')
|
6
|
+
%p.well
|
7
|
+
- if attribute.value.respond_to?(:/)
|
8
|
+
= number_to_currency(attribute.value / 100.to_f)
|
9
|
+
- else
|
10
|
+
= attribute.value
|
@@ -2,23 +2,24 @@
|
|
2
2
|
%h3
|
3
3
|
= model.title
|
4
4
|
|
5
|
-
|
5
|
+
= link_to(model.path, method: :delete, class: "btn btn-sm btn-danger delete pull-right", title: "Delete #{model.title}.", data: {confirm: "Are you sure?"}) do
|
6
|
+
%span.glyphicon.glyphicon-trash.white
|
7
|
+
|
6
8
|
%br
|
7
9
|
%h3{style: "color: #333;"}
|
8
10
|
Attributes
|
9
|
-
%hr
|
10
11
|
|
12
|
+
%hr
|
11
13
|
.attributes
|
12
14
|
-# Yes this is meant to be model.model - this is the raw rails model instance.
|
13
15
|
= form_for(model, url: model.path, html: { method: :put }) do |f|
|
14
16
|
|
15
17
|
-# Render each attribute
|
16
|
-
- model.
|
18
|
+
- model.form_attributes.each do |attribute|
|
17
19
|
= up_render(attribute, locals: { form_builder: f })
|
18
20
|
|
19
21
|
= f.submit("Save", class: "btn btn-primary")
|
20
22
|
|
21
|
-
|
22
23
|
- if model.associations.any?
|
23
24
|
%br
|
24
25
|
%br
|
@@ -30,7 +31,6 @@
|
|
30
31
|
- model.associations.each do |association|
|
31
32
|
= up_render(association)
|
32
33
|
|
33
|
-
|
34
34
|
- if model.actions.any?
|
35
35
|
%br
|
36
36
|
%br
|
@@ -41,4 +41,3 @@
|
|
41
41
|
.actions
|
42
42
|
- model.actions.each do |action|
|
43
43
|
= up_render(action)
|
44
|
-
|
@@ -15,7 +15,7 @@
|
|
15
15
|
= form_for(model, url: model.create_path, html: { method: :post }) do |f|
|
16
16
|
|
17
17
|
-# Render each attribute
|
18
|
-
- model.
|
18
|
+
- model.form_attributes.each do |attribute|
|
19
19
|
= up_render(attribute, locals: { form_builder: f })
|
20
20
|
|
21
21
|
= f.submit("Create", class: "btn btn-primary")
|
@@ -22,7 +22,7 @@
|
|
22
22
|
.input-group-addon to
|
23
23
|
= number_field(:q, "#{attr_name}_lteq", class: "form-control")
|
24
24
|
|
25
|
-
- if
|
25
|
+
- if [:datetime, :date].include?(type) && Rails::VERSION::MAJOR == 4
|
26
26
|
-# TODO(jon): Add date fields to search boxes for Rails 3
|
27
27
|
.form-group
|
28
28
|
= label(:q, "#{attr_name}_cont", attr_name.to_s.capitalize.gsub("_", " "))
|
@@ -32,5 +32,10 @@
|
|
32
32
|
To
|
33
33
|
= date_field(:q, "#{attr_name}_lteq", class: "form-control")
|
34
34
|
|
35
|
+
- if type == :enum
|
36
|
+
.form-group
|
37
|
+
= label(:q, "#{attr_name}_cont", attr_name.to_s.capitalize.gsub("_", " "))
|
38
|
+
= select(:q, "#{attr_name}_eq", klass.model_class.defined_enums[attr_name.to_s], { include_blank: true }, { class: "form-control" })
|
39
|
+
|
35
40
|
= submit_tag("Search", class: "btn btn-primary btn-block")
|
36
41
|
= link_to("Clear All", klass.search_path, class: "btn btn-default btn-block")
|
data/config/routes.rb
CHANGED
@@ -1,9 +1,6 @@
|
|
1
1
|
Upmin::Engine.routes.draw do
|
2
|
-
root to: "
|
3
|
-
|
4
|
-
# TODO(jon): Add support for dashboards (or some other main page).
|
5
|
-
# TODO(jon): Move dashboards to an appropriate controller
|
6
|
-
get "/", as: :upmin_dashboard, controller: :models, action: :dashboard
|
2
|
+
root to: "dashboard#index"
|
3
|
+
get "/", as: :upmin_dashboard, controller: :dashboard, action: :index
|
7
4
|
|
8
5
|
scope "m" do
|
9
6
|
scope "/:klass" do
|
@@ -15,6 +12,7 @@ Upmin::Engine.routes.draw do
|
|
15
12
|
scope "/i/:id" do
|
16
13
|
get "/", as: :upmin_model, controller: :models, action: :show
|
17
14
|
put "/", controller: :models, action: :update
|
15
|
+
delete "/", controller: :models, action: :destroy
|
18
16
|
|
19
17
|
post "/:method", as: :upmin_action, controller: :models, action: :action
|
20
18
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Upmin
|
2
|
+
module Generators
|
3
|
+
class InstallGenerator < Rails::Generators::Base
|
4
|
+
|
5
|
+
desc 'Creates an initializer file at config/initializers/upmin.rb and adds Upmin route to config/routes.rb'
|
6
|
+
|
7
|
+
source_root File.expand_path('../templates', __FILE__)
|
8
|
+
|
9
|
+
def copy_initializer
|
10
|
+
template 'initializer.rb', 'config/initializers/upmin.rb'
|
11
|
+
end
|
12
|
+
|
13
|
+
def add_route
|
14
|
+
route "mount Upmin::Engine => '/admin'"
|
15
|
+
end
|
16
|
+
|
17
|
+
def messages
|
18
|
+
log "\n # Global configuration can be edited in 'config/initializers/upmin.rb'\n"
|
19
|
+
log "\n # You can access Upmin at: '/admin'. Modify config/routes.rb if this conflicts with an existing route.\n\n"
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# Upmin Initializer
|
2
|
+
|
3
|
+
Upmin.configure do |config|
|
4
|
+
# List of models you want to appear in your Upmin Admin pages - default is all Models
|
5
|
+
# config.models = [:user, :order, :product]
|
6
|
+
|
7
|
+
# Overrides the default list of colors used to highlight models.
|
8
|
+
# config.colors = [::light_blue, :blue_green, :red, :yellow, :orange, :purple, :dark_blue, :dark_red, :green]
|
9
|
+
|
10
|
+
# Overrides the default of number of items displayed per page.
|
11
|
+
# This can also be set per model, see: https://github.com/upmin/upmin-admin-ruby/wiki/Customizing-Models
|
12
|
+
# items_per_page = 30
|
13
|
+
end
|
@@ -27,10 +27,24 @@ module Upmin::ActiveRecord
|
|
27
27
|
|
28
28
|
def attribute_type(attribute)
|
29
29
|
adapter = model_class.columns_hash[attribute.to_s]
|
30
|
-
|
30
|
+
if adapter
|
31
|
+
if attribute.in? enum_attributes
|
32
|
+
return :enum
|
33
|
+
else
|
34
|
+
return adapter.type
|
35
|
+
end
|
36
|
+
end
|
31
37
|
return :unknown
|
32
38
|
end
|
33
39
|
|
40
|
+
def enum_attributes
|
41
|
+
if model_class.respond_to? :defined_enums
|
42
|
+
model_class.defined_enums.keys.map(&:to_sym)
|
43
|
+
else
|
44
|
+
[]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
34
48
|
def associations
|
35
49
|
return @associations if defined?(@associations)
|
36
50
|
|
data/lib/upmin/admin.rb
CHANGED
@@ -29,6 +29,7 @@ require "upmin/railties/active_record"
|
|
29
29
|
require "upmin/railties/paginator"
|
30
30
|
require "upmin/railties/render"
|
31
31
|
require "upmin/railties/render_helpers"
|
32
|
+
require "upmin/railties/dashboard"
|
32
33
|
require "upmin/railtie"
|
33
34
|
|
34
35
|
# gems and stuff we use
|
@@ -36,7 +37,7 @@ require "jquery-rails"
|
|
36
37
|
require "ransack"
|
37
38
|
require "haml"
|
38
39
|
require "sass-rails"
|
39
|
-
|
40
|
+
require "chartkick"
|
40
41
|
|
41
42
|
require "ostruct"
|
42
43
|
|
data/lib/upmin/attribute.rb
CHANGED
@@ -10,7 +10,7 @@ module Upmin
|
|
10
10
|
|
11
11
|
def value
|
12
12
|
# TODO(jon): Add some way to handle exceptions.
|
13
|
-
return model.send(name)
|
13
|
+
return model.model.send(name)
|
14
14
|
end
|
15
15
|
|
16
16
|
def type
|
@@ -62,6 +62,9 @@ module Upmin
|
|
62
62
|
return "#{form_id}_is_nil"
|
63
63
|
end
|
64
64
|
|
65
|
+
def enum_options
|
66
|
+
model.class.model_class.defined_enums[name.to_s].keys
|
67
|
+
end
|
65
68
|
|
66
69
|
private
|
67
70
|
|
data/lib/upmin/configuration.rb
CHANGED
@@ -60,3 +60,13 @@ module Upmin::DataMapper
|
|
60
60
|
|
61
61
|
end
|
62
62
|
end
|
63
|
+
|
64
|
+
module DataMapper
|
65
|
+
module ClassMethods
|
66
|
+
#Returns an ActiveModel::Name object needed by Kaminari to render page_entries_info.
|
67
|
+
def model_name
|
68
|
+
return@_model_name ||= ActiveModel::Name.new(self)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
Model.append_extensions(ClassMethods)
|
72
|
+
end
|
data/lib/upmin/engine.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
# require
|
2
|
-
#
|
3
|
-
require 'kaminari'
|
1
|
+
# require kaminari here to affect load order so that custom Kaminari (bootstrap) views load from upmin,
|
2
|
+
# unless will_paginate is used in the parent Rails app.
|
3
|
+
require 'kaminari' unless defined?(WillPaginate)
|
4
4
|
|
5
5
|
module Upmin
|
6
6
|
class Engine < ::Rails::Engine
|
@@ -9,3 +9,4 @@ module Upmin
|
|
9
9
|
config.autoload_paths << "#{::Rails.root}/app/upmin/models"
|
10
10
|
end
|
11
11
|
end
|
12
|
+
|
data/lib/upmin/model.rb
CHANGED
@@ -50,6 +50,15 @@ module Upmin
|
|
50
50
|
return @attributes
|
51
51
|
end
|
52
52
|
|
53
|
+
def form_attributes
|
54
|
+
return @form_attributes if defined?(@form_attributes)
|
55
|
+
@form_attributes = []
|
56
|
+
self.class.form_attributes.each do |attr_name|
|
57
|
+
@form_attributes << Upmin::Attribute.new(self, attr_name)
|
58
|
+
end
|
59
|
+
return @form_attributes
|
60
|
+
end
|
61
|
+
|
53
62
|
def associations
|
54
63
|
return @associations if defined?(@associations)
|
55
64
|
@associations = []
|
@@ -170,10 +179,12 @@ module Upmin
|
|
170
179
|
end
|
171
180
|
|
172
181
|
def Model.humanized_name(type = :plural)
|
173
|
-
names = model_class_name.split(/(?=[A-Z])/).map{|n| n.gsub(":", "")}
|
182
|
+
names = @display_name ? [@display_name] : model_class_name.split(/(?=[A-Z])/).map{|n| n.gsub(":", "")}
|
183
|
+
|
174
184
|
if type == :plural
|
175
185
|
names[names.length-1] = names.last.pluralize
|
176
186
|
end
|
187
|
+
|
177
188
|
return names.join(" ")
|
178
189
|
end
|
179
190
|
|
@@ -243,21 +254,38 @@ module Upmin
|
|
243
254
|
@extra_attrs << attribute.to_sym if attribute
|
244
255
|
end
|
245
256
|
|
257
|
+
def Model.form_attribute(attribute = nil)
|
258
|
+
@extra_form_attrs = [] unless defined?(@extra_form_attrs)
|
259
|
+
@extra_form_attrs << attribute.to_sym if attribute
|
260
|
+
end
|
261
|
+
|
246
262
|
# Sets the attributes to the provided attributes # if any are any provided.
|
247
263
|
# If no attributes are provided then the
|
248
264
|
# attributes are set to the default attributes of
|
249
265
|
# the model class.
|
250
|
-
def Model.attributes(*
|
266
|
+
def Model.attributes(*attrs)
|
251
267
|
@extra_attrs = [] unless defined?(@extra_attrs)
|
252
268
|
|
253
|
-
if
|
254
|
-
@attributes =
|
269
|
+
if attrs.any?
|
270
|
+
@attributes = attrs.map{|a| a.to_sym}
|
255
271
|
end
|
256
272
|
@attributes ||= default_attributes
|
257
273
|
|
258
274
|
return (@attributes + @extra_attrs).uniq
|
259
275
|
end
|
260
276
|
|
277
|
+
# Edit/Create form specific attributes
|
278
|
+
def Model.form_attributes(*attrs)
|
279
|
+
@extra_form_attrs = [] unless defined?(@extra_form_attrs)
|
280
|
+
|
281
|
+
if attrs.any?
|
282
|
+
@form_attributes = attrs.map{|a| a.to_sym}
|
283
|
+
end
|
284
|
+
@form_attributes ||= default_attributes
|
285
|
+
|
286
|
+
return (@form_attributes + @extra_form_attrs).uniq
|
287
|
+
end
|
288
|
+
|
261
289
|
# Add a single action to upmin actions. If this is called
|
262
290
|
# before upmin_actions the actions will not include any defaults
|
263
291
|
# actions.
|
@@ -276,7 +304,7 @@ module Upmin
|
|
276
304
|
def Model.actions(*actions)
|
277
305
|
if actions.any?
|
278
306
|
# set the actions
|
279
|
-
@actions = actions.map
|
307
|
+
@actions = actions.map(&:to_sym)
|
280
308
|
end
|
281
309
|
@actions ||= []
|
282
310
|
return @actions
|
@@ -286,6 +314,10 @@ module Upmin
|
|
286
314
|
return @items_per_page ||= items
|
287
315
|
end
|
288
316
|
|
317
|
+
def Model.display_name (name)
|
318
|
+
return @display_name ||= name
|
319
|
+
end
|
320
|
+
|
289
321
|
|
290
322
|
###########################################################
|
291
323
|
### Methods that need to be to be overridden. If the
|
data/lib/upmin/railtie.rb
CHANGED
@@ -0,0 +1,88 @@
|
|
1
|
+
module Upmin::Railties
|
2
|
+
module Dashboard
|
3
|
+
|
4
|
+
def group_by_best_fit(limit = 30)
|
5
|
+
return send "group_by_#{grouping limit}"
|
6
|
+
end
|
7
|
+
|
8
|
+
# Selects the time period with no more than <limit> entries
|
9
|
+
def grouping(limit = 30)
|
10
|
+
seconds = range_in_seconds
|
11
|
+
if seconds/1.day < limit
|
12
|
+
return 'day'
|
13
|
+
elsif seconds/1.week < limit
|
14
|
+
return 'week'
|
15
|
+
elsif seconds/1.month < limit
|
16
|
+
return 'month'
|
17
|
+
else
|
18
|
+
return 'year'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
#
|
23
|
+
# Date range manipulation
|
24
|
+
#
|
25
|
+
def range_in_seconds
|
26
|
+
return last_date - first_date
|
27
|
+
end
|
28
|
+
|
29
|
+
def first_date
|
30
|
+
order('date(created_at) ASC').first.try(:created_at) || Time.now
|
31
|
+
end
|
32
|
+
|
33
|
+
def last_date
|
34
|
+
order('date(created_at) ASC').last.try(:created_at) || Time.now
|
35
|
+
end
|
36
|
+
|
37
|
+
#
|
38
|
+
# Group by
|
39
|
+
#
|
40
|
+
def group_by_day
|
41
|
+
dates = where.not(created_at: nil).group('date(created_at)').order('date(created_at) ASC').count
|
42
|
+
# Convert sqlite String date keys to Date keys
|
43
|
+
dates.map { |k, v| [Date.parse(k), v] } if dates.keys.first.is_a? String
|
44
|
+
end
|
45
|
+
|
46
|
+
def group_by_week
|
47
|
+
result = Hash.new(0)
|
48
|
+
group_by_day.each_with_object(result) { |i, a| a[i[0].beginning_of_week.strftime] += i[1] }
|
49
|
+
return result
|
50
|
+
end
|
51
|
+
|
52
|
+
def group_by_month
|
53
|
+
return group_by_strftime('%b %Y')
|
54
|
+
end
|
55
|
+
|
56
|
+
def group_by_year
|
57
|
+
return group_by_strftime('%Y')
|
58
|
+
end
|
59
|
+
|
60
|
+
#
|
61
|
+
# Aggregate by
|
62
|
+
#
|
63
|
+
def group_by_day_of_week
|
64
|
+
template = Hash[Date::ABBR_DAYNAMES.map {|x| [x, 0]}]
|
65
|
+
return group_by_strftime('%a', template)
|
66
|
+
end
|
67
|
+
|
68
|
+
def group_by_day_of_month
|
69
|
+
return Hash[group_by_strftime('%d').sort]
|
70
|
+
end
|
71
|
+
|
72
|
+
def group_by_week_of_year
|
73
|
+
return Hash[group_by_strftime('%W').sort]
|
74
|
+
end
|
75
|
+
|
76
|
+
def group_by_month_of_year
|
77
|
+
template = Hash[Date::ABBR_MONTHNAMES.map {|x| [x, 0]}]
|
78
|
+
template.shift
|
79
|
+
return group_by_strftime( '%b', template)
|
80
|
+
end
|
81
|
+
|
82
|
+
def group_by_strftime(filter, result = Hash.new(0))
|
83
|
+
group_by_day.each_with_object(result) { |i, a| a[i[0].strftime(filter)] += i[1] }
|
84
|
+
return result
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
end
|