upmin-admin 0.1.01 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
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