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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +10 -18
  3. data/Rakefile +18 -18
  4. data/app/assets/javascripts/upmin/attributes/datetime.js +37 -11
  5. data/app/assets/stylesheets/upmin/application.css +0 -1
  6. data/app/assets/stylesheets/upmin/base.css.scss +8 -1
  7. data/app/assets/stylesheets/upmin/dashboard.css +16 -0
  8. data/app/assets/stylesheets/upmin/instances.css.scss +7 -0
  9. data/app/controllers/upmin/dashboard_controller.rb +11 -0
  10. data/app/controllers/upmin/models_controller.rb +28 -9
  11. data/app/views/layouts/upmin/application.html.haml +6 -2
  12. data/app/views/upmin/dashboard/_chart.html.haml +9 -0
  13. data/app/views/upmin/dashboard/index.html.haml +14 -0
  14. data/app/views/upmin/models/search.html.haml +20 -4
  15. data/app/views/upmin/partials/attributes/_boolean.html.haml +1 -6
  16. data/app/views/upmin/partials/attributes/_date.html.haml +26 -0
  17. data/app/views/upmin/partials/attributes/_enum.html.haml +9 -0
  18. data/app/views/upmin/partials/attributes/_money_cents.html.haml +10 -0
  19. data/app/views/upmin/partials/models/_model.html.haml +5 -6
  20. data/app/views/upmin/partials/models/_new_model.html.haml +1 -1
  21. data/app/views/upmin/partials/search_boxes/_ransack_search_box.html.haml +6 -1
  22. data/config/routes.rb +3 -5
  23. data/lib/generators/upmin/install_generator.rb +24 -0
  24. data/lib/generators/upmin/templates/initializer.rb +13 -0
  25. data/lib/upmin/active_record/model.rb +15 -1
  26. data/lib/upmin/admin.rb +2 -1
  27. data/lib/upmin/attribute.rb +4 -1
  28. data/lib/upmin/configuration.rb +1 -0
  29. data/lib/upmin/data_mapper/model.rb +10 -0
  30. data/lib/upmin/engine.rb +4 -3
  31. data/lib/upmin/model.rb +37 -5
  32. data/lib/upmin/railtie.rb +1 -0
  33. data/lib/upmin/railties/dashboard.rb +88 -0
  34. data/lib/upmin/railties/render_helpers.rb +2 -0
  35. data/lib/upmin/version.rb +1 -1
  36. data/spec/features/delete_model_spec.rb +38 -0
  37. data/spec/features/edit_model_spec.rb +1 -1
  38. data/spec/features/enum_attributes_spec.rb +24 -0
  39. data/spec/features/navbar_spec.rb +4 -4
  40. data/spec/features/new_model_spec.rb +1 -1
  41. data/spec/features/search_spec.rb +17 -27
  42. data/spec/lib/attribute_spec.rb +15 -0
  43. data/spec/lib/upmin/active_record/model_spec.rb +15 -0
  44. data/spec/spec_helper.rb +8 -0
  45. metadata +49 -4
  46. 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
- %br
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.attributes.each do |attribute|
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.attributes.each do |attribute|
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 type == :datetime && Rails::VERSION::MAJOR == 4
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")
@@ -1,9 +1,6 @@
1
1
  Upmin::Engine.routes.draw do
2
- root to: "models#dashboard"
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
- return adapter.type if adapter
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
 
@@ -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
 
@@ -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
 
@@ -76,6 +76,7 @@ module Upmin
76
76
  def_models += ::ActiveRecord::Base.descendants
77
77
  .map(&:to_s)
78
78
  .select{ |m| m != "ActiveRecord::SchemaMigration" }
79
+ .select{ |m| m.exclude? "HABTM" }
79
80
  .sort
80
81
  .map(&:underscore)
81
82
  .map(&:to_sym)
@@ -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
@@ -1,6 +1,6 @@
1
- # require 'haml-rails'
2
- # require kaminari here to affect load order so that custom kaminari views load from upmin
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
+
@@ -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(*attributes)
266
+ def Model.attributes(*attrs)
251
267
  @extra_attrs = [] unless defined?(@extra_attrs)
252
268
 
253
- if attributes.any?
254
- @attributes = attributes.map{|a| a.to_sym}
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{|a| a.to_sym}
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
@@ -7,6 +7,7 @@ module Upmin
7
7
  ActiveSupport.on_load(:active_record) do
8
8
  if defined?(ActiveRecord)
9
9
  ::ActiveRecord::Base.send(:include, Upmin::Railties::ActiveRecord)
10
+ ::ActiveRecord::Base.send(:extend, Upmin::Railties::Dashboard)
10
11
  end
11
12
 
12
13
  if defined?(DataMapper)
@@ -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