From 79e5a752cb226f02931d1fead4ad48cdaac1846b Mon Sep 17 00:00:00 2001 From: Jon Calhoun Date: Mon, 22 Sep 2014 09:29:55 -0400 Subject: [PATCH 1/8] not done yet, but committing so code is backed up on github --- .../stylesheets/upmin/instances.css.scss | 2 +- app/controllers/upmin/models_controller.rb | 11 +- app/views/upmin/models/dashboard.html.haml | 2 + app/views/upmin/models/new.html.haml | 6 +- app/views/upmin/models/show.html.haml | 6 +- .../associations/_associations.html.haml | 2 +- .../partials/attributes/_boolean.html.haml | 14 +- .../partials/attributes/_datetime.html.haml | 74 ++++++----- .../partials/attributes/_float.html.haml | 16 ++- .../partials/attributes/_integer.html.haml | 16 ++- .../partials/attributes/_nilable.html.haml | 14 -- .../attributes/_progress_bar.html.haml | 1 + .../partials/attributes/_string.html.haml | 23 +++- .../upmin/partials/attributes/_text.html.haml | 14 +- .../partials/attributes/_unknown.html.haml | 8 +- .../upmin/partials/models/_model.html.haml | 85 +++++------- .../partials/models/_new_model.html.haml | 37 +----- .../partials/search_results/_result.html.haml | 6 +- lib/upmin/admin.rb | 4 + lib/upmin/engine.rb | 2 + lib/upmin/klass.rb | 24 ++-- lib/upmin/railtie.rb | 2 + lib/upmin/railties/render.rb | 101 ++++---------- lib/upmin/railties/render_helpers.rb | 123 +++++++++++------- lib/upmin/version.rb | 2 +- 25 files changed, 278 insertions(+), 317 deletions(-) delete mode 100644 app/views/upmin/partials/attributes/_nilable.html.haml diff --git a/app/assets/stylesheets/upmin/instances.css.scss b/app/assets/stylesheets/upmin/instances.css.scss index e87ad53..ca0e551 100644 --- a/app/assets/stylesheets/upmin/instances.css.scss +++ b/app/assets/stylesheets/upmin/instances.css.scss @@ -2,7 +2,7 @@ .upmin-model { padding: 20px; - margin: 10px 0px 10px 0; + margin: 10px 0px 250px 0; border: 1px solid #eee; border-left-width: 5px; border-radius: 3px; diff --git a/app/controllers/upmin/models_controller.rb b/app/controllers/upmin/models_controller.rb index ca07999..2017daa 100644 --- a/app/controllers/upmin/models_controller.rb +++ b/app/controllers/upmin/models_controller.rb @@ -62,6 +62,7 @@ def create # PUT /:model_name/:id def update + instance = @model.instance updates = params[@klass.name.underscore] transforms = updates.delete(:transforms) || {} @@ -112,14 +113,12 @@ def action end private - - def set_klass - @klass = Upmin::Klass.find(params[:klass]) - raise "Invalid klass name" if @klass.nil? + def set_model + @model = @klass.new(id: params[:id]) end - def set_model - @model = @klass.find(params[:id]) + def set_klass + @klass = Upmin::AdminModel.find_class(params[:klass]) end def set_method diff --git a/app/views/upmin/models/dashboard.html.haml b/app/views/upmin/models/dashboard.html.haml index 032150f..e279942 100644 --- a/app/views/upmin/models/dashboard.html.haml +++ b/app/views/upmin/models/dashboard.html.haml @@ -7,3 +7,5 @@ This is embarrassing. :( %h3 ^ Click one of those links up top to use features that are working. ^ + %h5 + = Upmin::AdminModel.all diff --git a/app/views/upmin/models/new.html.haml b/app/views/upmin/models/new.html.haml index ef37cd9..2bb1cf6 100644 --- a/app/views/upmin/models/new.html.haml +++ b/app/views/upmin/models/new.html.haml @@ -9,13 +9,13 @@ %button.close{"data-dismiss" => "alert", type: "button"} %span{"aria-hidden" => "true"} × = alert - - if @model.instance.errors.any? + - if @model.errors.any? %ul - - @model.instance.errors.each do |field, error| + - @model.errors.each do |field, error| %li %b = field = error .row .col-sm-12 - = up_model(@model.instance) + = up_model(@model) diff --git a/app/views/upmin/models/show.html.haml b/app/views/upmin/models/show.html.haml index ef37cd9..2bb1cf6 100644 --- a/app/views/upmin/models/show.html.haml +++ b/app/views/upmin/models/show.html.haml @@ -9,13 +9,13 @@ %button.close{"data-dismiss" => "alert", type: "button"} %span{"aria-hidden" => "true"} × = alert - - if @model.instance.errors.any? + - if @model.errors.any? %ul - - @model.instance.errors.each do |field, error| + - @model.errors.each do |field, error| %li %b = field = error .row .col-sm-12 - = up_model(@model.instance) + = up_model(@model) diff --git a/app/views/upmin/partials/associations/_associations.html.haml b/app/views/upmin/partials/associations/_associations.html.haml index e1cc342..a434872 100644 --- a/app/views/upmin/partials/associations/_associations.html.haml +++ b/app/views/upmin/partials/associations/_associations.html.haml @@ -1,7 +1,7 @@ - if associations.any? %p - associations.each do |u| - - nested_upmin_model = Upmin::Model.new(u) + - nested_upmin_model = Upmin::AdminModel.new(u) %a.active-tag-link{href: upmin_model_path(nested_upmin_model.path_hash)} %span.label{class: nested_upmin_model.color} = nested_upmin_model.title diff --git a/app/views/upmin/partials/attributes/_boolean.html.haml b/app/views/upmin/partials/attributes/_boolean.html.haml index feb47cf..90441e9 100644 --- a/app/views/upmin/partials/attributes/_boolean.html.haml +++ b/app/views/upmin/partials/attributes/_boolean.html.haml @@ -1,8 +1,10 @@ -- boolean ||= "false" +.form-group{class: attribute.errors? ? "has-error" : ""} + %label{for: attribute.form_id} + = attribute.label_name -- if editable && f = form_builder - = f.check_box(attr_name, value: boolean, class: "boolean") + - if attribute.editable? && f = form_builder + = f.check_box(attribute.name, value: boolean, class: "boolean") -- else - %p.well - = f.check_box(attr_name, {value: boolean, class: "boolean", disabled: "disabled"}) + - else + %p.well + = attribute.value diff --git a/app/views/upmin/partials/attributes/_datetime.html.haml b/app/views/upmin/partials/attributes/_datetime.html.haml index 1c5e173..0af4994 100644 --- a/app/views/upmin/partials/attributes/_datetime.html.haml +++ b/app/views/upmin/partials/attributes/_datetime.html.haml @@ -1,36 +1,38 @@ -- datetime ||= nil -- datetime_str = datetime.utc.iso8601 if datetime -- if editable && f = form_builder - .row.datetime-attribute{class: form_id} - = f.hidden_field(attr_name, value: datetime_str) - - # TODO(jon): Figure out a better way to do transforms. This works for now though. - = f.hidden_field("transforms[#{attr_name}]", value: "DateTime#parse") - - .col-xs-12.col-md-4 - .input-group.pikadate - %input.form-control{type: :text, value: datetime_str, id: "#{form_id}-date"} - %span.input-group-addon - %span.glyphicon.glyphicon-calendar - - .col-xs-12.col-md-4 - .input-group.clockpicker{"data-align" => "top", "data-autoclose" => "true", "data-placement" => "right"} - %input.form-control{:type => "text", :value => datetime_str, id: "#{form_id}-time"} - %span.input-group-addon - %span.glyphicon.glyphicon-time - - .col-xs-12.col-md-4 - %small - Date & Time are shown in UTC. If not time is provided, the current time in UTC will be used. - - - - content_for(:javascript) do - :javascript - $(document).ready(function() { - window.Upmin.Attributes.DateTime("#{form_id}"); - }); - - -- else - %p.well - -# TODO(jon): Make this show an uneditable date and time field. - = datetime +- iso8601 = attribute.value.nil? ? nil : attribute.value.utc.iso8601 + +.form-group{class: attribute.errors? ? "has-error" : ""} + %label{for: attribute.form_id} + = attribute.label_name + + - if attribute.editable? && f = form_builder + .row.datetime-attribute{class: attribute.form_id} + = f.hidden_field(attribute.name, value: iso8601) + + .col-xs-12.col-md-4 + .input-group.pikadate + %input.form-control{type: :text, value: iso8601, id: "#{attribute.form_id}-date"} + %span.input-group-addon + %span.glyphicon.glyphicon-calendar + + .col-xs-12.col-md-4 + .input-group.clockpicker{"data-align" => "top", "data-autoclose" => "true", "data-placement" => "right"} + %input.form-control{type: "text", value: iso8601, id: "#{attribute.form_id}-time"} + %span.input-group-addon + %span.glyphicon.glyphicon-time + + .col-xs-12.col-md-4 + %small + Date & Time are shown in UTC. If no time is provided, the current time in UTC will be used. + + + - content_for(:javascript) do + :javascript + $(document).ready(function() { + window.Upmin.Attributes.DateTime("#{attribute.form_id}"); + }); + + + - else + %p.well + -# TODO(jon): Make this show an uneditable date and time field. + = iso8601 diff --git a/app/views/upmin/partials/attributes/_float.html.haml b/app/views/upmin/partials/attributes/_float.html.haml index 117c99e..9660a34 100644 --- a/app/views/upmin/partials/attributes/_float.html.haml +++ b/app/views/upmin/partials/attributes/_float.html.haml @@ -1,7 +1,11 @@ -- float ||= nil -- if editable && f = form_builder - = f.text_field(attr_name, value: float, class: "form-control") -- else - %p.well - = float +.form-group{class: attribute.errors? ? "has-error" : ""} + %label{for: attribute.form_id} + = attribute.label_name + + - if attribute.editable? && f = form_builder + = f.text_field(attribute.name, value: attribute.value, class: "form-control") + + - else + %p.well + = attribute.value diff --git a/app/views/upmin/partials/attributes/_integer.html.haml b/app/views/upmin/partials/attributes/_integer.html.haml index 035339a..825d6a3 100644 --- a/app/views/upmin/partials/attributes/_integer.html.haml +++ b/app/views/upmin/partials/attributes/_integer.html.haml @@ -1,7 +1,11 @@ -- integer ||= nil -- if editable && f = form_builder - = f.number_field(attr_name, value: integer, class: "form-control") -- else - %p.well - = integer +.form-group{class: attribute.errors? ? "has-error" : ""} + %label{for: attribute.form_id} + = attribute.label_name + + - if attribute.editable? && f = form_builder + = f.number_field(attribute.name, value: attribute.value, class: "form-control") + + - else + %p.well + = attribute.value diff --git a/app/views/upmin/partials/attributes/_nilable.html.haml b/app/views/upmin/partials/attributes/_nilable.html.haml deleted file mode 100644 index d46984a..0000000 --- a/app/views/upmin/partials/attributes/_nilable.html.haml +++ /dev/null @@ -1,14 +0,0 @@ -- if editable && f = form_builder - -# TODO(jon): Find a better place for code like this. Kinda sucks to have it in the view. - - is_nil = upmin_model.attribute(attr_name).nil? && !upmin_model.instance.new_record? - .input-group - = up_attribute(upmin_model.instance, attr_name, locals: { form_builder: form_builder }) - .input-group-addon.nilable-addon - .form-group - %label{for: "#{upmin_model.klass.name.underscore}_#{attr_name}_is_nil"} - Make This Nil - = check_box(upmin_model.klass.name.underscore, "#{attr_name}_is_nil", class: "boolean", checked: is_nil, value: is_nil) - -- else - %p.well - = nilable diff --git a/app/views/upmin/partials/attributes/_progress_bar.html.haml b/app/views/upmin/partials/attributes/_progress_bar.html.haml index 855256c..bbf55b4 100644 --- a/app/views/upmin/partials/attributes/_progress_bar.html.haml +++ b/app/views/upmin/partials/attributes/_progress_bar.html.haml @@ -1,3 +1,4 @@ +-# TODO(jon): Turn this into a widget - state ||= nil - states ||= [] %ol.progbar{data: { "progbar-steps" => states.length }} diff --git a/app/views/upmin/partials/attributes/_string.html.haml b/app/views/upmin/partials/attributes/_string.html.haml index 025b317..fece22b 100644 --- a/app/views/upmin/partials/attributes/_string.html.haml +++ b/app/views/upmin/partials/attributes/_string.html.haml @@ -1,7 +1,18 @@ -- string ||= nil -- if editable && f = form_builder - = f.text_field(attr_name, value: string, class: "form-control") +- is_nil = model.new_record? ? false : attribute.value.nil? -- else - %p.well - = string +.form-group{class: attribute.errors? ? "has-error" : ""} + %label{for: attribute.form_id} + = attribute.label_name + + - if attribute.editable? && f = form_builder + .input-group + = f.text_field(attribute.name, value: attribute.value, class: "form-control") + .input-group-addon.nilable-addon + .form-group + %label{for: "#{attribute.form_id}_is_nil"} + Make this Nil + = check_box(model.underscore_name, "#{attribute.name}_is_nil", class: "boolean", checked: is_nil, value: is_nil) + + - else + %p.well + = attribute.value diff --git a/app/views/upmin/partials/attributes/_text.html.haml b/app/views/upmin/partials/attributes/_text.html.haml index d0ed7fe..d2d7e28 100644 --- a/app/views/upmin/partials/attributes/_text.html.haml +++ b/app/views/upmin/partials/attributes/_text.html.haml @@ -1,7 +1,9 @@ -- text ||= nil -- if editable && f = form_builder - = f.text_area(attr_name, value: text, rows: 4, class: "form-control") +.form-group{class: attribute.errors? ? "has-error" : ""} + %label{for: attribute.form_id} + = attribute.label_name + - if attribute.editable? && f = form_builder + = f.text_area(attribute.name, value: attribute.value, rows: 4, class: "form-control") -- else - %p.well - = text + - else + %p.well + = attribute.value diff --git a/app/views/upmin/partials/attributes/_unknown.html.haml b/app/views/upmin/partials/attributes/_unknown.html.haml index efb20fc..a0c4e6a 100644 --- a/app/views/upmin/partials/attributes/_unknown.html.haml +++ b/app/views/upmin/partials/attributes/_unknown.html.haml @@ -1,3 +1,5 @@ -- unknown ||= nil -%p.well - = unknown +.form-group{class: attribute.errors? ? "has-error" : ""} + %label{for: attribute.form_id} + = attribute.label_name + %p.well + = attribute.value diff --git a/app/views/upmin/partials/models/_model.html.haml b/app/views/upmin/partials/models/_model.html.haml index fab8fd2..493b710 100644 --- a/app/views/upmin/partials/models/_model.html.haml +++ b/app/views/upmin/partials/models/_model.html.haml @@ -1,72 +1,47 @@ --# The following are all available to you here: --# unknown - This is the model passed into the up_model method. --# This attribute is always present in any partial rendered --# by Upmin, and will always match the name of the partial --# *UNLESS* nil, true, or false are passed in. --# upmin_model - This is an Upmin::Model instantiated with unknown --# and is always present. --# --# In general, the upmin_model makes it significantly easier to --# render a view, so I suggest using it. It is way simpler than --# trying to find all associations for a model on your own. --# --# Just an FYI: upmin_model.instance == unknown - -.upmin-model{class: upmin_model.color} - -# Display the model title as "Model # ID" +.upmin-model{class: model.color} %h3 - = upmin_model.title + = model.up_title %br %br %h3{style: "color: #333;"} Attributes %hr - -# Create a form to wrap the attributes in. - -# TODO(jon): Update the URL with a decent helper? - = form_for(upmin_model.instance, url: upmin_model_path(upmin_model.path_hash), html: { method: :put }) do |f| - - -# Render each attribute - - upmin_model.klass.attributes.each do |attribute| - - any_errors = model.errors[attribute].any? - .form-group{class: any_errors ? "has-error" : ""} - -# = f.label(attribute.to_s) # Not using this because it drops _id and this isn't always desirable - %label{for: upmin_model.attribute_form_id(attribute)} - = upmin_model.attribute_label_name(attribute) - - if upmin_model.attribute_type(attribute) == :string - = up_attribute(upmin_model.instance, attribute, locals: { form_builder: f }, as: :nilable) - - else - = up_attribute(upmin_model.instance, attribute, locals: { form_builder: f }) + -# Yes this is meant to be model.model - this is the raw rails model instance. + = form_for(model.model, url: model.path, html: { method: :put }) do |f| + -# Render each attribute + - model.up_attributes.each do |attribute| + = up_attribute(attribute, locals: { form_builder: f }) = f.submit("Save", class: "btn btn-primary") - - if upmin_model.klass.associations.any? - %br - %br - %br - %h3{style: "color: #333;"} - Associations - %hr - - upmin_model.klass.associations.each do |association| - %h5 - = association.to_s.humanize - = up_association(upmin_model.instance, association, limit: 5) - - - if upmin_model.klass.actions.any? - %br - %br - %br - %h3{style: "color: #333;"} - Actions - %hr - - upmin_model.klass.actions.each do |action| - %h4{style: "color: #333;"} - = action.to_s.capitalize.humanize - = up_action(upmin_model.instance, action) + -# - if model.klass.associations.any? + -# %br + -# %br + -# %br + -# %h3{style: "color: #333;"} + -# Associations + -# %hr + -# - model.klass.associations.each do |association| + -# %h5 + -# = association.to_s.humanize + -# = up_association(model.instance, association, limit: 5) + + -# - if model.klass.actions.any? + -# %br + -# %br + -# %br + -# %h3{style: "color: #333;"} + -# Actions + -# %hr + -# - model.klass.actions.each do |action| + -# %h4{style: "color: #333;"} + -# = action.to_s.capitalize.humanize + -# = up_action(model.instance, action) diff --git a/app/views/upmin/partials/models/_new_model.html.haml b/app/views/upmin/partials/models/_new_model.html.haml index 42c7a42..d5b8ee3 100644 --- a/app/views/upmin/partials/models/_new_model.html.haml +++ b/app/views/upmin/partials/models/_new_model.html.haml @@ -1,44 +1,21 @@ --# The following are all available to you here: --# unknown - This is the model passed into the up_model method. --# This attribute is always present in any partial rendered --# by Upmin, and will always match the name of the partial --# *UNLESS* nil, true, or false are passed in. --# upmin_model - This is an Upmin::Model instantiated with unknown --# and is always present. --# --# In general, the upmin_model makes it significantly easier to --# render a view, so I suggest using it. It is way simpler than --# trying to find all associations for a model on your own. --# --# Just an FYI: upmin_model.instance == unknown -.upmin-model{class: upmin_model.color} - -# Display the model title as "Model # ID" +.upmin-model{class: model.color} %h3 Create a - = upmin_model.klass.humanized_name(:singular) + = model.humanized_name(:singular) %br %br %h3{style: "color: #333;"} Attributes %hr - -# Create a form to wrap the attributes in. - -# TODO(jon): Update the URL with a decent helper? - = form_for(upmin_model.instance, url: upmin_create_model_path(klass: @klass.name), html: { method: :post }) do |f| - -# Render each attribute - - upmin_model.klass.attributes.each do |attribute| - - any_errors = new_model.errors[attribute].any? - .form-group{class: any_errors ? "has-error" : ""} - -# = f.label(attribute.to_s) # Not using this because it drops _id and this isn't always desirable + -# Yes this is meant to be model.model - this is the raw rails model instance. + = form_for(model.model, url: model.create_path, html: { method: :post }) do |f| - %label{for: upmin_model.attribute_form_id(attribute)} - = upmin_model.attribute_label_name(attribute) - - if upmin_model.attribute_type(attribute) == :string - = up_attribute(upmin_model.instance, attribute, locals: { form_builder: f }, as: :nilable) - - else - = up_attribute(upmin_model.instance, attribute, locals: { form_builder: f }) + -# Render each attribute + - model.up_attributes.each do |attribute| + = up_attribute(attribute, locals: { form_builder: f }) = f.submit("Create", class: "btn btn-primary") diff --git a/app/views/upmin/partials/search_results/_result.html.haml b/app/views/upmin/partials/search_results/_result.html.haml index 78d5f43..cec0ddb 100644 --- a/app/views/upmin/partials/search_results/_result.html.haml +++ b/app/views/upmin/partials/search_results/_result.html.haml @@ -1,8 +1,8 @@ -%a.search-result-link{href: upmin_model_path(upmin_model.path_hash)} +%a.search-result-link{href: upmin_model.path} .upmin-model{class: upmin_model.color} %dl.dl-horizontal - @klass.attributes.each do |attribute| %dt - = upmin_model.attribute_label_name(attribute) + = upmin_model.attribute_label(attribute) %dd - = upmin_model.attribute(attribute) + = upmin_model.send(attribute) diff --git a/lib/upmin/admin.rb b/lib/upmin/admin.rb index 59591fc..5359547 100644 --- a/lib/upmin/admin.rb +++ b/lib/upmin/admin.rb @@ -1,8 +1,12 @@ require "upmin" require "upmin/engine" +require "upmin/errors" require "upmin/klass" require "upmin/model" +require "upmin/automatic_delegation" +require "upmin/attribute" +require "upmin/admin_model" require "upmin/paginator" diff --git a/lib/upmin/engine.rb b/lib/upmin/engine.rb index a705703..ec62661 100644 --- a/lib/upmin/engine.rb +++ b/lib/upmin/engine.rb @@ -3,5 +3,7 @@ module Upmin class Engine < ::Rails::Engine isolate_namespace Upmin + + config.autoload_paths << "#{::Rails.root}/app/upmin/models" end end diff --git a/lib/upmin/klass.rb b/lib/upmin/klass.rb index cad5f94..9556e38 100644 --- a/lib/upmin/klass.rb +++ b/lib/upmin/klass.rb @@ -14,13 +14,13 @@ def initialize(model, options = {}) def new(*args) m = model.new(*args) - return Upmin::Model.new(m) + return Upmin::AdminModel.new(m) end # Exposing a model method, but wrapping the result in # an Upmin::Model def find(*args) - return Upmin::Model.new(model.find(*args)) + return Upmin::AdminModel.new(model.find(*args)) end def ransack(*args) @@ -96,17 +96,17 @@ def reflections - ## Methods for prettying up things to display them in views etc. + # ## Methods for prettying up things to display them in views etc. - # Returns the class name, split at camelCase, - # with the last word pluralized if it is plural. - def humanized_name(type = :plural) - names = model.name.split(/(?=[A-Z])/) - if type == :plural - names[names.length-1] = names.last.pluralize - end - return names.join(" ") - end + # # Returns the class name, split at camelCase, + # # with the last word pluralized if it is plural. + # def humanized_name(type = :plural) + # names = model.name.split(/(?=[A-Z])/) + # if type == :plural + # names[names.length-1] = names.last.pluralize + # end + # return names.join(" ") + # end # Returns the class name, capitalized as it would be with User.name or OrderShipment.name - "User", or "OrderShipment" def name diff --git a/lib/upmin/railtie.rb b/lib/upmin/railtie.rb index 2edae23..0a69495 100644 --- a/lib/upmin/railtie.rb +++ b/lib/upmin/railtie.rb @@ -1,3 +1,5 @@ +require 'rails/railtie' + module Upmin require 'rails' class Railtie < Rails::Railtie diff --git a/lib/upmin/railties/render.rb b/lib/upmin/railties/render.rb index 7996e31..a8acde0 100644 --- a/lib/upmin/railties/render.rb +++ b/lib/upmin/railties/render.rb @@ -2,85 +2,26 @@ module Upmin::Railties module Render - def up_model(model, options = {}) - options[:locals] ||= {} - - upmin_model = Upmin::Model.new(model) - options[:locals][:upmin_model] ||= upmin_model - - partials = RenderHelpers.model_partials(upmin_model, options) - return up_render(model, partials, options, :up_model) - end - - def up_attribute(model, attr_name, options = {}) - options[:locals] ||= {} - options[:locals][:model] ||= model - options[:locals][:attr_name] = attr_name - - upmin_model = Upmin::Model.new(model) - options[:locals][:upmin_model] ||= upmin_model - - options[:locals][:form_id] ||= upmin_model.attribute_form_id(attr_name) - # Only fill this in if it was never set so the user can override this. - if options[:locals][:editable].nil? - options[:locals][:editable] = upmin_model.attribute_editable?(attr_name) - end - - - partials = RenderHelpers.attribute_partials(upmin_model, attr_name, options) - - data = upmin_model.attribute(attr_name) - return up_render(data, partials, options, :up_attribute) - end - - def up_association(model, assoc_name, options = {}) - options[:locals] ||= {} - options[:locals][:model] ||= model - options[:locals][:assoc_name] = assoc_name - - upmin_model = Upmin::Model.new(model) - options[:locals][:upmin_model] ||= upmin_model - - partials = RenderHelpers.association_partials(upmin_model, assoc_name, options) - - data = upmin_model.association(assoc_name, options) - return up_render([data].flatten, partials, options, :up_association) - end - - def up_action(model, action_name, options = {}) - options[:locals] ||= {} - options[:locals][:model] ||= model - options[:locals][:action_name] = action_name - - upmin_model = Upmin::Model.new(model) - options[:locals][:upmin_model] ||= upmin_model - - partials = RenderHelpers.action_partials(upmin_model, action_name, options) - - data = upmin_model.action_parameters(action_name) - return up_render(data, partials, options, :up_action) - end - def up_search_results(ransack_search, ransack_results, options = {}) - options[:locals] ||= {} - options[:locals][:klass] ||= Upmin::Klass.find(ransack_search.klass) - options[:locals][:ransack_search] ||= ransack_search - options[:locals][:ransack_results] ||= ransack_results + # options[:locals] ||= {} + # options[:locals][:klass] ||= Upmin::Klass.find(ransack_search.klass) + # options[:locals][:ransack_search] ||= ransack_search + # options[:locals][:ransack_results] ||= ransack_results - partials = RenderHelpers.search_results_partials(ransack_search, options) + # partials = RenderHelpers.search_results_partials(ransack_search, options) - return up_render(ransack_results, partials, options, :up_search_results) + # return up_render(ransack_results, partials, options, :up_search_results) end def up_search_result(model, options = {}) - options[:locals] ||= {} + # options[:locals] ||= {} - upmin_model = Upmin::Model.new(model) - options[:locals][:upmin_model] ||= upmin_model + # upmin_model = Upmin::AdminModel.new(model) + # options[:locals][:upmin_model] ||= upmin_model - partials = RenderHelpers.search_result_partials(upmin_model, options) + # partials = RenderHelpers.search_result_partials(upmin_model, options) - return up_render(model, partials, options, :up_search_result) + # return up_render(model, partials, options, :up_search_result) end def up_search_box(klass, options = {}) @@ -99,8 +40,22 @@ def up_search_box(klass, options = {}) end - # Generic render method that is used by all of the up_ methods. Tries to render the partials in order, passing data in as the :object, along with options. - def up_render(data, partials, options = {}, calling_method = nil) + + # Generic render method that is used by upmin-admin. Tries to render partials in order, passing data in as the :object, along with options. + def up_render(data, options = {}) + if data.is_a?(Upmin::AdminModel) + options = RenderHelpers.model_options(data, options) + partials = RenderHelpers.model_partials(data, options) + elsif data.is_a?(Upmin::Attribute) + options = RenderHelpers.attribute_options(data, options) + partials = RenderHelpers.attribute_partials(data, options) + elsif data.is_a?(Upmin::Association) + options = RenderHelpers.association_options(data, options) + partials = RenderHelpers.association_partials(data, options) + else + raise Upmin::ArgumentError.new(data) + end + # Use options as the render hash, and set :object as the data being used for rendering. options[:object] = data @@ -113,7 +68,7 @@ def up_render(data, partials, options = {}, calling_method = nil) end # If we get here we tried all of the partials and nothing matched. This *shouldn't* be possible but might happen if partials are deleted. - raise "Failed to find a matching partial while trying to render `#{calling_method}` with the following data: #{data.inspect}" + raise Upmin::MissingPartial.new(data) end end diff --git a/lib/upmin/railties/render_helpers.rb b/lib/upmin/railties/render_helpers.rb index 37168f4..6a30c11 100644 --- a/lib/upmin/railties/render_helpers.rb +++ b/lib/upmin/railties/render_helpers.rb @@ -2,21 +2,33 @@ module Upmin::Railties module RenderHelpers - def RenderHelpers.model_partials(upmin_model, options) + def RenderHelpers.model_partials(model, options = {}) partials = [] # Add "new_" in front of any partial for the partial for new view. # # # model - prefix = upmin_model.new_record? ? "new_" : "" + prefix = model.new_record? ? "new_" : "" partials << build_model_path(options[:as]) if options[:as] - partials << build_model_path(upmin_model.klass.name.underscore, prefix) + partials << build_model_path(model.underscore_name, prefix) partials << build_model_path(:model, prefix) return partials end - def RenderHelpers.attribute_partials(upmin_model, attr_name, options) + def RenderHelpers.model_options(model, options = {}) + options[:locals] ||= {} + options[:locals][:model] ||= model + return options + end + + def RenderHelpers.build_model_path(partial_name, prefix = "") + return "#{root_path}/models/#{prefix}#{partial_name}" + end + + + + def RenderHelpers.attribute_partials(attribute, options = {}) partials = [] # # _, eg: user_name @@ -24,59 +36,92 @@ def RenderHelpers.attribute_partials(upmin_model, attr_name, options) # , eg: string # unknown - model_name = upmin_model.klass.name.underscore - attr_type = upmin_model.attribute_type(attr_name) + model_name = attribute.model.underscore_name + attr_type = attribute.type partials << build_attribute_path(options[:as]) if options[:as] - partials << build_attribute_path("#{model_name}_#{attr_name}") + partials << build_attribute_path("#{model_name}_#{attribute.name}") partials << build_attribute_path("#{model_name}_#{attr_type}") partials << build_attribute_path(attr_type) partials << build_attribute_path(:unknown) return partials end - # def RenderHelpers.action_partials(upmin_model, action, options) - # partials = [] - # partials << build_action_path(:unknown) - # return partials - # end + def RenderHelpers.attribute_options(attribute, options = {}) + options[:locals] ||= {} + options[:locals][:model] ||= attribute.model + options[:locals][:attribute] = attribute + return options + end + + def RenderHelpers.build_attribute_path(partial_name) + return "#{root_path}/attributes/#{partial_name}" + end + # NOTE: assoc_type is sketchy at best. It tries to determine it, but in some cases it has to be guessed at, so if you have polymorphic associations it will choose the data type of the first association it finds - eg if user.things returns [Order, Product, Review] it will use the type of "order" - def RenderHelpers.association_partials(upmin_model, assoc_name, options) + def RenderHelpers.association_partials(association, options = {}) partials = [] # # _, eg: user_recent_orders # _, eg: user_orders # , eg: orders # associations - model_name = upmin_model.klass.name.underscore - assoc_type = upmin_model.association_type(assoc_name) + model_name = association.model.underscore_name + assoc_type = association.type partials << build_association_path(options[:as]) if options[:as] - partials << build_association_path("#{model_name}_#{assoc_name}") + partials << build_association_path("#{model_name}_#{association.name}") partials << build_association_path("#{model_name}_#{assoc_type}") partials << build_association_path(assoc_type) partials << build_association_path(:associations) return partials end - def RenderHelpers.action_partials(upmin_model, action_name, options) + def RenderHelpers.association_options(association, options = {}) + options[:locals] ||= {} + options[:locals][:model] ||= association.model + options[:locals][:association] = association + return options + end + + def RenderHelpers.build_association_path(partial_name) + return "#{root_path}/associations/#{partial_name}" + end + + + + def RenderHelpers.action_partials(action, options = {}) partials = [] # # _, eg: order_refund # , eg: refund # action - model_name = upmin_model.klass.name.underscore + model_name = action.model.underscore_name partials << build_action_path(options[:as]) if options[:as] - partials << build_action_path("#{model_name}_#{action_name}") - partials << build_action_path(action_name) + partials << build_action_path("#{model_name}_#{action.name}") + partials << build_action_path(action.name) partials << build_action_path(:action) return partials end - def RenderHelpers.search_results_partials(ransack_search, options) + def RenderHelpers.action_options(action, options = {}) + options[:locals] ||= {} + options[:locals][:model] ||= model + options[:locals][:action] = action + return options + end + + def RenderHelpers.build_action_path(partial_name) + partial_name = partial_name.to_s.gsub(/[!?]/, "") + return "#{root_path}/actions/#{partial_name}" + end + + + + def RenderHelpers.search_results_partials(ransack_search, options = {}) partials = [] # # , eg: orders @@ -89,12 +134,12 @@ def RenderHelpers.search_results_partials(ransack_search, options) return partials end - def RenderHelpers.search_result_partials(upmin_model, options) + def RenderHelpers.search_result_partials(model, options = {}) partials = [] # # , eg: order # results - model_name = upmin_model.klass.name.underscore + model_name = model.klass.name.underscore partials << build_search_result_path(options[:as]) if options[:as] partials << build_search_result_path(model_name) @@ -102,7 +147,13 @@ def RenderHelpers.search_result_partials(upmin_model, options) return partials end - def RenderHelpers.search_box_partials(klass, options) + def RenderHelpers.build_search_result_path(partial_name) + return "#{root_path}/search_results/#{partial_name}" + end + + + + def RenderHelpers.search_box_partials(klass, options = {}) partials = [] # # ransack_search_box @@ -112,32 +163,12 @@ def RenderHelpers.search_box_partials(klass, options) return partials end - - def RenderHelpers.build_model_path(partial_name, prefix = "") - return "#{root_path}/models/#{prefix}#{partial_name}" - end - - def RenderHelpers.build_attribute_path(partial_name) - return "#{root_path}/attributes/#{partial_name}" - end - - def RenderHelpers.build_action_path(partial_name) - partial_name = partial_name.to_s.gsub(/[!?]/, "") - return "#{root_path}/actions/#{partial_name}" - end - - def RenderHelpers.build_association_path(partial_name) - return "#{root_path}/associations/#{partial_name}" - end - - def RenderHelpers.build_search_result_path(partial_name) - return "#{root_path}/search_results/#{partial_name}" - end - def RenderHelpers.build_search_box_path(partial_name) return "#{root_path}/search_boxes/#{partial_name}" end + + def RenderHelpers.root_path return "upmin/partials" end diff --git a/lib/upmin/version.rb b/lib/upmin/version.rb index 7c86ceb..347a9f6 100644 --- a/lib/upmin/version.rb +++ b/lib/upmin/version.rb @@ -1,3 +1,3 @@ module Upmin - VERSION = "0.0.39" + VERSION = "0.0.39dev10000058" end From 8a0e5eb330536d70f1f00e485047a1c4fdfba9a4 Mon Sep 17 00:00:00 2001 From: Jon Calhoun Date: Mon, 22 Sep 2014 09:30:13 -0400 Subject: [PATCH 2/8] not done yet, but committing so code is backed up on github --- .../partials/attributes/_decimal.html.haml | 11 ++ lib/upmin/admin_model.rb | 187 ++++++++++++++++++ lib/upmin/association.rb | 86 ++++++++ lib/upmin/attribute.rb | 86 ++++++++ lib/upmin/automatic_delegation.rb | 42 ++++ lib/upmin/errors.rb | 25 +++ 6 files changed, 437 insertions(+) create mode 100644 app/views/upmin/partials/attributes/_decimal.html.haml create mode 100644 lib/upmin/admin_model.rb create mode 100644 lib/upmin/association.rb create mode 100644 lib/upmin/attribute.rb create mode 100644 lib/upmin/automatic_delegation.rb create mode 100644 lib/upmin/errors.rb diff --git a/app/views/upmin/partials/attributes/_decimal.html.haml b/app/views/upmin/partials/attributes/_decimal.html.haml new file mode 100644 index 0000000..9660a34 --- /dev/null +++ b/app/views/upmin/partials/attributes/_decimal.html.haml @@ -0,0 +1,11 @@ + +.form-group{class: attribute.errors? ? "has-error" : ""} + %label{for: attribute.form_id} + = attribute.label_name + + - if attribute.editable? && f = form_builder + = f.text_field(attribute.name, value: attribute.value, class: "form-control") + + - else + %p.well + = attribute.value diff --git a/lib/upmin/admin_model.rb b/lib/upmin/admin_model.rb new file mode 100644 index 0000000..77e62af --- /dev/null +++ b/lib/upmin/admin_model.rb @@ -0,0 +1,187 @@ +module Upmin + class AdminModel + include Upmin::Engine.routes.url_helpers + include Upmin::AutomaticDelegation + + attr_reader :model + alias_method :object, :model # For delegation + + delegate(:color, to: :class) + delegate(:humanized_name, to: :class) + delegate(:underscore_name, to: :class) + delegate(:model_class_name, to: :class) + + def initialize(model = nil, options = {}) + if model.is_a?(Hash) + unless model.has_key?(:id) + raise ":id or model instance is required." + end + @model = self.class.find(model[:id]) + elsif model.nil? + @model = self.class.model_class.new + else + @model = model + end + end + + def path + return upmin_model_path(klass: model_class_name, id: id) + end + + def create_path + return upmin_create_model_path(klass: model_class_name) + end + + def up_title + return "#{humanized_name(:singular)} # #{id}" + end + + def up_attributes + attributes = [] + self.class.attributes.each do |attr_name| + attributes << Upmin::Attribute.new(self, attr_name) + end + return attributes + end + + + + + + + + + + + + # def color(color = nil) + # @color ||= color + # @color ||= klass.color + # return @color + # end + + ############################################# + #### Methods used to customize the model #### + ############################################# + + # Add a single attribute to upmin attributes. + # If this is called before upmin_attributes + # the attributes will not include any defaults + # attributes. + def AdminModel.attribute(attribute = nil) + @extra_attrs = [] unless defined?(@extra_attrs) + @extra_attrs << attribute.to_sym if attribute + end + + # Sets the attributes to the provided attributes # if any are any provided. + # If no attributes are provided then the + # attributes are set to the default attributes of + # the model class. + def AdminModel.attributes(*attributes) + @extra_attrs = [] unless defined?(@extra_attrs) + + if attributes.any? + @attributes = attributes.map{|a| a.to_sym} + end + + @attributes ||= model_class.attribute_names.map{|a| a.to_sym} + return (@attributes + @extra_attrs).uniq + end + + # Add a single action to upmin actions. If this is called + # before upmin_actions the actions will not include any defaults + # actions. + def AdminModel.action(action) + @actions ||= [] + + action = action.to_sym + @actions << action unless @actions.include?(action) + end + + # Sets the upmin_actions to the provided actions if any are + # provided. + # If no actions are provided, and upmin_actions hasn't been defined, + # then the upmin_actions are set to the default actions. + # Returns the upmin_actions + def AdminModel.actions(*actions) + if actions.any? + # set the actions + @actions = actions.map{|a| a.to_sym} + end + @actions ||= [] + return @actions + end + + + def AdminModel.find_class(model) + return find_or_create_class(model.to_s) + end + + def AdminModel.find_or_create_class(model_name) + ::Rails.application.eager_load! + return "Admin#{model_name}".constantize + rescue NameError + eval("class ::Admin#{model_name} < Upmin::AdminModel; end") + return "Admin#{model_name}".constantize + end + + # Returns all admin models. + def AdminModel.all + return @all if defined?(@all) + @all = [] + Upmin::Klass.models.each do |m| + @all << find_or_create_class(m.name) + end + return @all + end + + def AdminModel.model_class + @model_class ||= inferred_model_class + end + + def AdminModel.model_class? + return model_class + rescue Upmin::UninferrableSourceError + return false + end + + def AdminModel.model_class + return @model_class ||= inferred_model_class + end + + def AdminModel.inferred_model_class + name = model_class_name + return name.constantize + rescue NameError => error + raise if name && !error.missing_name?(name) + raise Upmin::UninferrableSourceError.new(self) + end + + def AdminModel.model_class_name + raise NameError if name.nil? || name.demodulize !~ /Admin.+$/ + return name.demodulize[5..-1] + end + + def AdminModel.humanized_name(type = :plural) + names = model_class_name.split(/(?=[A-Z])/) + if type == :plural + names[names.length-1] = names.last.pluralize + end + return names.join(" ") + end + + def AdminModel.underscore_name + return model_class_name.underscore + end + + def AdminModel.model_class_path + return Upmin::Engine.routes.url_helpers.upmin_search_path(klass: klass_name) + end + + def AdminModel.color + # TODO(jon)[v1.0]: Make colors dynamic again. + return :green + end + + end +end diff --git a/lib/upmin/association.rb b/lib/upmin/association.rb new file mode 100644 index 0000000..f673d0f --- /dev/null +++ b/lib/upmin/association.rb @@ -0,0 +1,86 @@ +module Upmin + class Association + attr_reader :model + attr_reader :name + + def initialize(model, assoc_name, options = {}) + @model = model + @name = assoc_name.to_sym + end + + def value + # TODO(jon): Add some way to handle exceptions. + return model.send(name) + end + + def type + return @type if defined?(@type) + + # Try to get it from the model_class' columns hash + if adapter = model.class.columns_hash[name.to_s] + return @type = adapter.type + else + return @type = :unknown + end + + # If we still don't know the type, try to infer it from the value + if @type == :unknown + @type = infer_type_from_value + end + + return @type + end + + def editable? + case name.to_sym + when :id + return false + when :created_at + return false + when :updated_at + return false + else + # TODO(jon): Add a way to declare which attributes are editable and which are not later. + return model.respond_to?("#{name}=") + end + end + + def errors? + return model.errors[name].any? + end + + def label_name + return name.to_s.gsub(/_/, " ").capitalize + end + + def form_id + return "#{model.underscore_name}_#{name}" + end + + def nilable_id + return "#{form_id}_is_nil" + end + + + private + + def infer_type_from_value + class_sym = value.class.to_s.underscore.to_sym + if class_sym == :false_class || class_sym == :true_class + return :boolean + elsif class_sym == :nil_class + return :unknown + elsif class_sym == :fixnum + return :integer + elsif class_sym == :big_decimal + return :decimal + elsif class_sym == :"active_support/time_with_zone" + return :datetime + else + # This should prevent any classes from being skipped, but we may not have an exhaustive list yet. + return class_sym + end + end + + end +end diff --git a/lib/upmin/attribute.rb b/lib/upmin/attribute.rb new file mode 100644 index 0000000..cb8f36b --- /dev/null +++ b/lib/upmin/attribute.rb @@ -0,0 +1,86 @@ +module Upmin + class Attribute + attr_reader :model + attr_reader :name + + def initialize(model, attr_name, options = {}) + @model = model + @name = attr_name.to_sym + end + + def value + # TODO(jon): Add some way to handle exceptions. + return model.send(name) + end + + def type + return @type if defined?(@type) + + # Try to get it from the model_class' columns hash + if adapter = model.class.columns_hash[name.to_s] + return @type = adapter.type + else + return @type = :unknown + end + + # If we still don't know the type, try to infer it from the value + if @type == :unknown + @type = infer_type_from_value + end + + return @type + end + + def editable? + case name.to_sym + when :id + return false + when :created_at + return false + when :updated_at + return false + else + # TODO(jon): Add a way to declare which attributes are editable and which are not later. + return model.respond_to?("#{name}=") + end + end + + def errors? + return model.errors[name].any? + end + + def label_name + return name.to_s.gsub(/_/, " ").capitalize + end + + def form_id + return "#{model.underscore_name}_#{name}" + end + + def nilable_id + return "#{form_id}_is_nil" + end + + + private + + def infer_type_from_value + class_sym = value.class.to_s.underscore.to_sym + if class_sym == :false_class || class_sym == :true_class + return :boolean + elsif class_sym == :nil_class + return :unknown + elsif class_sym == :fixnum + return :integer + elsif class_sym == :big_decimal + return :decimal + elsif class_sym == :"active_support/time_with_zone" + return :datetime + else + # This should prevent any classes from being skipped, but we may not have an exhaustive list yet. + return class_sym + end + end + + end +end diff --git a/lib/upmin/automatic_delegation.rb b/lib/upmin/automatic_delegation.rb new file mode 100644 index 0000000..01dd0ea --- /dev/null +++ b/lib/upmin/automatic_delegation.rb @@ -0,0 +1,42 @@ +module Upmin + module AutomaticDelegation + extend ActiveSupport::Concern + + # Delegates missing instance methods to the source model. + def method_missing(method, *args, &block) + if delegatable?(method) + self.class.delegate(method, to: :model) + send(method, *args, &block) + else + return super + end + end + + def delegatable?(method) + model.respond_to?(method) + end + + def respond_to?(method) + super || delegatable?(method) + end + + module ClassMethods + # Proxies missing class methods to the source class. + def method_missing(method, *args, &block) + return super unless delegatable?(method) + + model_class.send(method, *args, &block) + end + + def delegatable?(method) + model_class? && model_class.respond_to?(method) + end + + # Avoids reloading the model class when ActiveSupport clears autoloaded + # dependencies in development mode. + def before_remove_const + end + end + + end +end diff --git a/lib/upmin/errors.rb b/lib/upmin/errors.rb new file mode 100644 index 0000000..3f1679e --- /dev/null +++ b/lib/upmin/errors.rb @@ -0,0 +1,25 @@ +module Upmin + class ArgumentError < ArgumentError + def initialize(arg) + super("Invalid argument: #{arg}") + end + end + + class MissingPartial < ActionView::MissingTemplate + def initialize(data) + super("Could not find a matching partial with the following data: #{data}") + end + end + + class UninferrableAdminError < NameError + def initialize(klass) + super("Could not infer an Admin class for #{klass}.") + end + end + + class UninferrableSourceError < NameError + def initialize(klass) + super("Could not infer a source for #{klass}.") + end + end +end From 67323e51b7ab484260a70421b39213120d5b7b5a Mon Sep 17 00:00:00 2001 From: Jon Calhoun Date: Tue, 23 Sep 2014 21:19:52 -0400 Subject: [PATCH 3/8] basic association class works with value and type. I need to figure out how well limits etc work and update the views to use these --- app/controllers/upmin/models_controller.rb | 42 ++++------ app/views/upmin/models/new.html.haml | 2 +- app/views/upmin/models/search.html.haml | 2 +- app/views/upmin/models/show.html.haml | 2 +- .../upmin/partials/models/_model.html.haml | 29 ++++--- .../partials/models/_new_model.html.haml | 4 +- lib/upmin/admin.rb | 1 + lib/upmin/admin_model.rb | 29 ++++++- lib/upmin/association.rb | 77 ++++++++----------- lib/upmin/attribute.rb | 1 + lib/upmin/railties/active_record.rb | 55 ++----------- lib/upmin/railties/render.rb | 16 ++-- lib/upmin/version.rb | 2 +- 13 files changed, 105 insertions(+), 157 deletions(-) diff --git a/app/controllers/upmin/models_controller.rb b/app/controllers/upmin/models_controller.rb index 2017daa..edaef58 100644 --- a/app/controllers/upmin/models_controller.rb +++ b/app/controllers/upmin/models_controller.rb @@ -25,34 +25,28 @@ def new # POST /:model_name def create @model = @klass.new - instance = @model.instance + raw_model = @model.model - args = params[@klass.name.underscore] - transforms = args.delete(:transforms) || {} + args = params[@klass.underscore_name] args.each do |key, value| - # TODO(jon): Figure out a better way to do transforms. - # This could cause issues and is exploitable, but it - # should be fine for now since this is only on admin pages - if transforms[key] and not value.blank? - value = transform(transforms, key, value) - end + # TODO(jon): Figure out a way to do transforms. # TODO(jon): Remove duplicate code between update and create if args["#{key}_is_nil"] == "1" - instance.send("#{key}=", nil) + raw_model.send("#{key}=", nil) else if key.ends_with?("_is_nil") # Skip this, since we use the non _is_nil arg. else - instance.send("#{key}=", value) + raw_model.send("#{key}=", value) end end end - if instance.save - flash[:notice] = "#{@klass.humanized_name(:singular)} created successfully with id=#{instance.id}." - redirect_to(upmin_model_path(@model.path_hash)) + if raw_model.save + flash[:notice] = "#{@klass.humanized_name(:singular)} created successfully with id=#{raw_model.id}." + redirect_to(@model.path) else flash.now[:alert] = "#{@klass.humanized_name(:singular)} was NOT created." render(:new) @@ -63,33 +57,25 @@ def create # PUT /:model_name/:id def update - instance = @model.instance - updates = params[@klass.name.underscore] - transforms = updates.delete(:transforms) || {} + raw_model = @model.model + updates = params[@klass.underscore_name] updates.each do |key, value| - # TODO(jon): Figure out a better way to do transforms. - # This could cause issues and is exploitable, but it - # should be fine for now since this is only on admin pages - if transforms[key] and not value.blank? - value = transform(transforms, key, value) - end - # TODO(jon): Remove duplicate code between update and create if updates["#{key}_is_nil"] == "1" - instance.send("#{key}=", nil) + raw_model.send("#{key}=", nil) else if key.ends_with?("_is_nil") # Skip this, since we use the non _is_nil arg. else - instance.send("#{key}=", value) + raw_model.send("#{key}=", value) end end end - if instance.save + if raw_model.save flash[:notice] = "#{@klass.humanized_name(:singular)} updated successfully." - redirect_to(upmin_model_path(@model.path_hash)) + redirect_to(@model.path) else flash.now[:alert] = "#{@klass.humanized_name(:singular)} was NOT updated." render(:show) diff --git a/app/views/upmin/models/new.html.haml b/app/views/upmin/models/new.html.haml index 2bb1cf6..7ee9738 100644 --- a/app/views/upmin/models/new.html.haml +++ b/app/views/upmin/models/new.html.haml @@ -18,4 +18,4 @@ = error .row .col-sm-12 - = up_model(@model) + = up_render(@model) diff --git a/app/views/upmin/models/search.html.haml b/app/views/upmin/models/search.html.haml index c32213a..973a442 100644 --- a/app/views/upmin/models/search.html.haml +++ b/app/views/upmin/models/search.html.haml @@ -11,7 +11,7 @@ -# TODO(jon): Implement up_search_box = up_search_box(@klass) .new-button-wrapper - %a.btn.btn-block.btn-success{href: upmin_new_model_path(klass: @klass.name)} + %a.btn.btn-block.btn-success{href: upmin_new_model_path(klass: @klass.model_class_name)} Create a new = @klass.humanized_name(:singular) %br diff --git a/app/views/upmin/models/show.html.haml b/app/views/upmin/models/show.html.haml index 2bb1cf6..7ee9738 100644 --- a/app/views/upmin/models/show.html.haml +++ b/app/views/upmin/models/show.html.haml @@ -18,4 +18,4 @@ = error .row .col-sm-12 - = up_model(@model) + = up_render(@model) diff --git a/app/views/upmin/partials/models/_model.html.haml b/app/views/upmin/partials/models/_model.html.haml index 493b710..8249520 100644 --- a/app/views/upmin/partials/models/_model.html.haml +++ b/app/views/upmin/partials/models/_model.html.haml @@ -1,6 +1,6 @@ .upmin-model{class: model.color} %h3 - = model.up_title + = model.title %br %br @@ -12,24 +12,23 @@ = form_for(model.model, url: model.path, html: { method: :put }) do |f| -# Render each attribute - - model.up_attributes.each do |attribute| - = up_attribute(attribute, locals: { form_builder: f }) + - model.attributes.each do |attribute| + = up_render(attribute, locals: { form_builder: f }) = f.submit("Save", class: "btn btn-primary") - - -# - if model.klass.associations.any? - -# %br - -# %br - -# %br - -# %h3{style: "color: #333;"} - -# Associations - -# %hr - -# - model.klass.associations.each do |association| - -# %h5 - -# = association.to_s.humanize - -# = up_association(model.instance, association, limit: 5) + - if model.associations.any? + %br + %br + %br + %h3{style: "color: #333;"} + Associations + %hr + - model.associations.each do |association| + %h5 + = association.title + = up_render(association, limit: 5) -# - if model.klass.actions.any? -# %br diff --git a/app/views/upmin/partials/models/_new_model.html.haml b/app/views/upmin/partials/models/_new_model.html.haml index d5b8ee3..e848a56 100644 --- a/app/views/upmin/partials/models/_new_model.html.haml +++ b/app/views/upmin/partials/models/_new_model.html.haml @@ -14,8 +14,8 @@ = form_for(model.model, url: model.create_path, html: { method: :post }) do |f| -# Render each attribute - - model.up_attributes.each do |attribute| - = up_attribute(attribute, locals: { form_builder: f }) + - model.attributes.each do |attribute| + = up_render(attribute, locals: { form_builder: f }) = f.submit("Create", class: "btn btn-primary") diff --git a/lib/upmin/admin.rb b/lib/upmin/admin.rb index 5359547..e886635 100644 --- a/lib/upmin/admin.rb +++ b/lib/upmin/admin.rb @@ -6,6 +6,7 @@ require "upmin/model" require "upmin/automatic_delegation" require "upmin/attribute" +require "upmin/association" require "upmin/admin_model" require "upmin/paginator" diff --git a/lib/upmin/admin_model.rb b/lib/upmin/admin_model.rb index 77e62af..9b8d3bb 100644 --- a/lib/upmin/admin_model.rb +++ b/lib/upmin/admin_model.rb @@ -9,6 +9,7 @@ class AdminModel delegate(:color, to: :class) delegate(:humanized_name, to: :class) delegate(:underscore_name, to: :class) + delegate(:model_class, to: :class) delegate(:model_class_name, to: :class) def initialize(model = nil, options = {}) @@ -16,9 +17,9 @@ def initialize(model = nil, options = {}) unless model.has_key?(:id) raise ":id or model instance is required." end - @model = self.class.find(model[:id]) + @model = self.model_class.find(model[:id]) elsif model.nil? - @model = self.class.model_class.new + @model = self.model_class.new else @model = model end @@ -32,11 +33,11 @@ def create_path return upmin_create_model_path(klass: model_class_name) end - def up_title + def title return "#{humanized_name(:singular)} # #{id}" end - def up_attributes + def attributes attributes = [] self.class.attributes.each do |attr_name| attributes << Upmin::Attribute.new(self, attr_name) @@ -44,13 +45,33 @@ def up_attributes return attributes end + def associations + associations = [] + self.class.associations.each do |assoc_name| + associations << Upmin::Association.new(self, assoc_name) + end + return associations + end + def AdminModel.associations + return @associations if defined?(@associations) + all = [] + ignored = [] + model_class.reflect_on_all_associations.each do |reflection| + all << reflection.name.to_sym + # We need to remove the ignored later because we don't know the order they come in. + if reflection.is_a?(::ActiveRecord::Reflection::ThroughReflection) + ignored << reflection.options[:through] + end + end + return @associations = all - ignored + end diff --git a/lib/upmin/association.rb b/lib/upmin/association.rb index f673d0f..4646b42 100644 --- a/lib/upmin/association.rb +++ b/lib/upmin/association.rb @@ -16,69 +16,54 @@ def value def type return @type if defined?(@type) - # Try to get it from the model_class' columns hash - if adapter = model.class.columns_hash[name.to_s] - return @type = adapter.type + if reflection + @type = reflection.foreign_type.to_s.gsub(/_type$/, "") + if collection? + @type = @type.pluralize.to_sym + else + @type = @type.to_sym + end else - return @type = :unknown + @type = :unknown end - # If we still don't know the type, try to infer it from the value - if @type == :unknown - @type = infer_type_from_value + if @type == :unknown && first = [value].flatten.first + @type = first.class.name.underscore + if collection? || value.responds_to?(:each) + @type = @type.pluralize.to_sym + else + @type = @type.to_sym + end end return @type end - def editable? - case name.to_sym - when :id - return false - when :created_at - return false - when :updated_at - return false - else - # TODO(jon): Add a way to declare which attributes are editable and which are not later. - return model.respond_to?("#{name}=") - end - end - - def errors? - return model.errors[name].any? - end - - def label_name - return name.to_s.gsub(/_/, " ").capitalize + def collection? + return reflection.collection? end - def form_id - return "#{model.underscore_name}_#{name}" - end - - def nilable_id - return "#{form_id}_is_nil" + def reflection + return @reflection if defined?(@reflection) + @reflection = model.model_class.reflect_on_all_associations.select do |r| + r.name == name + end.first + return @reflection end private def infer_type_from_value - class_sym = value.class.to_s.underscore.to_sym - if class_sym == :false_class || class_sym == :true_class - return :boolean - elsif class_sym == :nil_class - return :unknown - elsif class_sym == :fixnum - return :integer - elsif class_sym == :big_decimal - return :decimal - elsif class_sym == :"active_support/time_with_zone" - return :datetime + if reflection + type = reflection.foreign_type.to_s.gsub(/_type$/, "") + if collection? + return type.pluralize.to_sym + else + return type.to_sym + end else - # This should prevent any classes from being skipped, but we may not have an exhaustive list yet. - return class_sym + return :unknown end end diff --git a/lib/upmin/attribute.rb b/lib/upmin/attribute.rb index cb8f36b..a946987 100644 --- a/lib/upmin/attribute.rb +++ b/lib/upmin/attribute.rb @@ -14,6 +14,7 @@ def value end def type + # TODO(jon): Add a way to override with widgets? return @type if defined?(@type) # Try to get it from the model_class' columns hash diff --git a/lib/upmin/railties/active_record.rb b/lib/upmin/railties/active_record.rb index eeaa987..fa1de9a 100644 --- a/lib/upmin/railties/active_record.rb +++ b/lib/upmin/railties/active_record.rb @@ -4,60 +4,15 @@ module Upmin::Railties module ActiveRecord extend ::ActiveSupport::Concern + def upmin_model + klass = Upmin::AdminModel.find_class(self.class) + return klass.new(self) + end + included do end module ClassMethods - - # Add a single attribute to upmin attributes. If this is called - # before upmin_attributes the attributes will not include any defaults - # attributes. - def upmin_attribute(attribute = nil) - @upmin_extra_attrs = [] unless defined?(@upmin_extra_attrs) - @upmin_extra_attrs << attribute.to_sym if attribute - end - - # Sets the upmin_attributes to the provided attributes if any are - # provided. - # If no attributes are provided, and upmin_attributes hasn't been defined, - # then the upmin_attributes are set to the default attributes. - # Returns the upmin_attributes - def upmin_attributes(*attributes) - @upmin_extra_attrs = [] unless defined?(@upmin_extra_attrs) - - if attributes.any? - @upmin_attributes = attributes.map{|a| a.to_sym} - end - - @upmin_attributes ||= attribute_names.map{|a| a.to_sym} - return (@upmin_attributes + @upmin_extra_attrs).uniq - end - - - # Add a single action to upmin actions. If this is called - # before upmin_actions the actions will not include any defaults - # actions. - def upmin_action(action) - @upmin_actions ||= [] - - action = action.to_sym - @upmin_actions << action unless @upmin_actions.include?(action) - end - - # Sets the upmin_actions to the provided actions if any are - # provided. - # If no actions are provided, and upmin_actions hasn't been defined, - # then the upmin_actions are set to the default actions. - # Returns the upmin_actions - def upmin_actions(*actions) - if actions.any? - # set the actions - @upmin_actions = actions.map{|a| a.to_sym} - end - @upmin_actions ||= [] - return @upmin_actions - end - end end end diff --git a/lib/upmin/railties/render.rb b/lib/upmin/railties/render.rb index a8acde0..1685c7e 100644 --- a/lib/upmin/railties/render.rb +++ b/lib/upmin/railties/render.rb @@ -25,18 +25,18 @@ def up_search_result(model, options = {}) end def up_search_box(klass, options = {}) - options[:locals] ||= {} + # options[:locals] ||= {} - klass = Upmin::Klass.find(klass) unless klass.is_a?(Upmin::Klass) - if klass.nil? - raise "Invalid klass provided in `up_search_box`" - end + # klass = Upmin::Klass.find(klass) unless klass.is_a?(Upmin::Klass) + # if klass.nil? + # raise "Invalid klass provided in `up_search_box`" + # end - options[:locals][:klass] = klass + # options[:locals][:klass] = klass - partials = RenderHelpers.search_box_partials(klass, options) + # partials = RenderHelpers.search_box_partials(klass, options) - return up_render(klass, partials, options, :up_search_box) + # return up_render(klass, partials, options, :up_search_box) end diff --git a/lib/upmin/version.rb b/lib/upmin/version.rb index 347a9f6..d20b394 100644 --- a/lib/upmin/version.rb +++ b/lib/upmin/version.rb @@ -1,3 +1,3 @@ module Upmin - VERSION = "0.0.39dev10000058" + VERSION = "0.0.39dev10000068" end From 309439e123c7f35040dc1be91ecffaca91b06179 Mon Sep 17 00:00:00 2001 From: Jon Calhoun Date: Wed, 24 Sep 2014 23:01:03 -0400 Subject: [PATCH 4/8] Associations are rendering correctly --- .../associations/_associations.html.haml | 18 +++---- .../upmin/partials/models/_model.html.haml | 5 +- lib/upmin/association.rb | 52 +++++++++++++------ lib/upmin/version.rb | 2 +- 4 files changed, 47 insertions(+), 30 deletions(-) diff --git a/app/views/upmin/partials/associations/_associations.html.haml b/app/views/upmin/partials/associations/_associations.html.haml index a434872..b1dc93b 100644 --- a/app/views/upmin/partials/associations/_associations.html.haml +++ b/app/views/upmin/partials/associations/_associations.html.haml @@ -1,14 +1,12 @@ -- if associations.any? - %p - - associations.each do |u| - - nested_upmin_model = Upmin::AdminModel.new(u) - %a.active-tag-link{href: upmin_model_path(nested_upmin_model.path_hash)} - %span.label{class: nested_upmin_model.color} - = nested_upmin_model.title +%h5 + = association.title -- else +- if association.empty? %p.well None - - +- else + - association.upmin_values.each do |m| + %a.active-tag-link{href: m.path} + %span.label{class: m.color} + = m.title diff --git a/app/views/upmin/partials/models/_model.html.haml b/app/views/upmin/partials/models/_model.html.haml index 8249520..a56b089 100644 --- a/app/views/upmin/partials/models/_model.html.haml +++ b/app/views/upmin/partials/models/_model.html.haml @@ -25,10 +25,9 @@ %h3{style: "color: #333;"} Associations %hr + - model.associations.each do |association| - %h5 - = association.title - = up_render(association, limit: 5) + = up_render(association) -# - if model.klass.actions.any? -# %br diff --git a/lib/upmin/association.rb b/lib/upmin/association.rb index 4646b42..d6655c0 100644 --- a/lib/upmin/association.rb +++ b/lib/upmin/association.rb @@ -13,6 +13,21 @@ def value return model.send(name) end + def title + return name.to_s.humanize + end + + def upmin_values(options = {}) + options[:limit] ||= 5 + if reflection.collection? + vals = [value.limit(options[:limit])].flatten + else + vals = [value] + end + + return vals.map{ |m| Upmin::AdminModel.find_class(m.class).new(m) } + end + def type return @type if defined?(@type) @@ -27,36 +42,41 @@ def type @type = :unknown end - if @type == :unknown && first = [value].flatten.first - @type = first.class.name.underscore - if collection? || value.responds_to?(:each) - @type = @type.pluralize.to_sym - else - @type = @type.to_sym - end + if @type == :unknown + @type = infer_type_from_value end return @type end def collection? - return reflection.collection? + if reflection + return reflection.collection? + elsif value + return value.responds_to?(:each) + else + return false + end end - def reflection - return @reflection if defined?(@reflection) - @reflection = model.model_class.reflect_on_all_associations.select do |r| - r.name == name - end.first - return @reflection + def empty? + return ![value].flatten.any? end private + def reflection + return @reflection if defined?(@reflection) + @reflection = model.model_class.reflect_on_all_associations.select do |r| + r.name == name + end.first + return @reflection + end + def infer_type_from_value - if reflection - type = reflection.foreign_type.to_s.gsub(/_type$/, "") + if first = [value].flatten.first + type = first.class.name.underscore if collection? return type.pluralize.to_sym else diff --git a/lib/upmin/version.rb b/lib/upmin/version.rb index d20b394..fa8540c 100644 --- a/lib/upmin/version.rb +++ b/lib/upmin/version.rb @@ -1,3 +1,3 @@ module Upmin - VERSION = "0.0.39dev10000068" + VERSION = "0.0.39dev10000073" end From cc53b7bc63bc30bcaf4ca35d35d95e5d144a41ca Mon Sep 17 00:00:00 2001 From: Jon Calhoun Date: Thu, 25 Sep 2014 03:05:44 -0400 Subject: [PATCH 5/8] actions are working. Next up is search --- app/controllers/upmin/models_controller.rb | 20 +++--- app/helpers/upmin/instances_helper.rb | 13 ---- .../upmin/partials/actions/_action.html.haml | 27 ++------ .../upmin/partials/models/_model.html.haml | 21 +++---- .../partials/parameters/_block_parameter.haml | 0 .../parameters/_opt_parameter.html.haml | 14 +++++ .../parameters/_req_parameter.html.haml | 4 ++ lib/upmin/action.rb | 50 +++++++++++++++ lib/upmin/admin.rb | 2 + lib/upmin/admin_model.rb | 33 +++++++--- lib/upmin/automatic_delegation.rb | 30 ++++++++- lib/upmin/errors.rb | 16 ++++- lib/upmin/parameter.rb | 43 +++++++++++++ lib/upmin/railties/render.rb | 6 ++ lib/upmin/railties/render_helpers.rb | 63 ++++++++++++++----- lib/upmin/version.rb | 2 +- 16 files changed, 263 insertions(+), 81 deletions(-) delete mode 100644 app/helpers/upmin/instances_helper.rb create mode 100644 app/views/upmin/partials/parameters/_block_parameter.haml create mode 100644 app/views/upmin/partials/parameters/_opt_parameter.html.haml create mode 100644 app/views/upmin/partials/parameters/_req_parameter.html.haml create mode 100644 lib/upmin/action.rb create mode 100644 lib/upmin/parameter.rb diff --git a/app/controllers/upmin/models_controller.rb b/app/controllers/upmin/models_controller.rb index edaef58..7128a09 100644 --- a/app/controllers/upmin/models_controller.rb +++ b/app/controllers/upmin/models_controller.rb @@ -7,7 +7,7 @@ class ModelsController < ApplicationController before_filter :set_page, only: [:search] - before_filter :set_method, only: [:action] + before_filter :set_action, only: [:action] before_filter :set_arguments, only: [:action] def dashboard @@ -88,10 +88,10 @@ def search end def action - # begin - response = @model.perform_action(params[:method], @arguments) - flash[:notice] = "Action successfully performed with a response of: #{response}" - redirect_to(upmin_model_path(@model.path_hash)) + @response = @action.perform(@arguments) + + flash[:notice] = "Action successfully performed with a response of: #{@response}" + redirect_to(@model.path) # rescue Exception => e # flash.now[:alert] = "Action failed with the error message: #{e.message}" # render(:show) @@ -107,12 +107,15 @@ def set_klass @klass = Upmin::AdminModel.find_class(params[:klass]) end - def set_method - @method = params[:method].to_sym + def set_action + action_name = params[:method].to_sym + @action = @model.actions.select{ |action| action.name == action_name }.first + + raise Upmin::InvalidAction.new(params[:method]) unless @action end def set_arguments - arguments = params[@method] || {} + arguments = params[@action.name] || {} @arguments = {} arguments.each do |k, v| unless k.ends_with?("_is_nil") @@ -121,6 +124,7 @@ def set_arguments end end end + @arguments = ActiveSupport::HashWithIndifferentAccess.new(@arguments) end def set_page diff --git a/app/helpers/upmin/instances_helper.rb b/app/helpers/upmin/instances_helper.rb deleted file mode 100644 index e07c7bd..0000000 --- a/app/helpers/upmin/instances_helper.rb +++ /dev/null @@ -1,13 +0,0 @@ -module Upmin - module InstancesHelper - - def models_path - return "#{root_path}models/" - end - - def instance_path(instance) - return "#{models_path}#{instance.class.name}/#{instance.id}" - end - - end -end diff --git a/app/views/upmin/partials/actions/_action.html.haml b/app/views/upmin/partials/actions/_action.html.haml index 0a81baa..88d9761 100644 --- a/app/views/upmin/partials/actions/_action.html.haml +++ b/app/views/upmin/partials/actions/_action.html.haml @@ -1,24 +1,9 @@ +%h4{style: "color: #333;"} + = action.title .well.action-well - = form_tag(upmin_action_path(upmin_model.path_hash.merge(method: action_name))) do - - upmin_model.action_parameters(action_name).each do |param_type, param_name| - - - next if param_type == :block # Skip blocks - - .form-group - = label(action_name, param_name, param_name.to_s.capitalize.gsub("_", " ")) - - - if param_type == :opt - .input-group - = text_field(action_name, param_name, class: "form-control") - .input-group-addon.nilable-addon - .form-group - %label{for: "#{action_name}_#{param_name}_is_nil"} - Do Not Provide - = check_box(action_name, "#{param_name}_is_nil", class: "boolean") - %small - * Optional - - else - = text_field(action_name, param_name, class: "form-control") - + = form_tag(action.path) do + - action.parameters.each do |parameter| + -# = Upmin::Railties::RenderHelpers.parameter_partials(parameter) + = up_render(parameter) = submit_tag("Submit", class: "btn btn-primary") diff --git a/app/views/upmin/partials/models/_model.html.haml b/app/views/upmin/partials/models/_model.html.haml index a56b089..ec4bc78 100644 --- a/app/views/upmin/partials/models/_model.html.haml +++ b/app/views/upmin/partials/models/_model.html.haml @@ -29,17 +29,14 @@ - model.associations.each do |association| = up_render(association) - -# - if model.klass.actions.any? - -# %br - -# %br - -# %br - -# %h3{style: "color: #333;"} - -# Actions - -# %hr - -# - model.klass.actions.each do |action| - -# %h4{style: "color: #333;"} - -# = action.to_s.capitalize.humanize - -# = up_action(model.instance, action) - + - if model.actions.any? + %br + %br + %br + %h3{style: "color: #333;"} + Actions + %hr + - model.actions.each do |action| + = up_render(action) diff --git a/app/views/upmin/partials/parameters/_block_parameter.haml b/app/views/upmin/partials/parameters/_block_parameter.haml new file mode 100644 index 0000000..e69de29 diff --git a/app/views/upmin/partials/parameters/_opt_parameter.html.haml b/app/views/upmin/partials/parameters/_opt_parameter.html.haml new file mode 100644 index 0000000..560ccae --- /dev/null +++ b/app/views/upmin/partials/parameters/_opt_parameter.html.haml @@ -0,0 +1,14 @@ +.form-group + %label{for: parameter.form_id} + = parameter.label_name + + .input-group + = text_field(action.name, parameter.name, class: "form-control") + .input-group-addon.nilable-addon + .form-group + %label{for: parameter.nil_form_id} + Do Not Provide + = check_box(parameter.action.name, "#{parameter.name}_is_nil", class: "boolean") + + %small + * Optional diff --git a/app/views/upmin/partials/parameters/_req_parameter.html.haml b/app/views/upmin/partials/parameters/_req_parameter.html.haml new file mode 100644 index 0000000..db7068e --- /dev/null +++ b/app/views/upmin/partials/parameters/_req_parameter.html.haml @@ -0,0 +1,4 @@ +.form-group + %label{for: parameter.form_id} + = parameter.label_name + = text_field(action.name, parameter.name, class: "form-control") diff --git a/lib/upmin/action.rb b/lib/upmin/action.rb new file mode 100644 index 0000000..475eab7 --- /dev/null +++ b/lib/upmin/action.rb @@ -0,0 +1,50 @@ +module Upmin + class Action + include Upmin::AutomaticDelegation + + attr_reader :model + attr_reader :name + + def initialize(model, action_name, options = {}) + @model = model + @name = action_name.to_sym + end + + def title + return name.to_s.humanize + end + + def path + return upmin_action_path(klass: model.model_class_name, id: model.id, method: name) + end + + def parameters + return @parameters if defined?(@parameters) + @parameters = [] + model.method(name).parameters.each do |param_type, param_name| + @parameters << Upmin::Parameter.new(self, param_name, type: param_type) + end + return @parameters + end + alias_method :arguments, :parameters + + def perform(arguments) + array = [] + parameters.each do |parameter| + if parameter.type == :req + unless arguments[parameter.name] + raise Upmin::MissingArgument.new(parameter.name) + end + array << arguments[parameter.name] + elsif parameter.type == :opt + array << arguments[parameter.name] if arguments[parameter.name] + else # :block - skip it + end + end + return model.send(name, *array) + end + + private + + end +end diff --git a/lib/upmin/admin.rb b/lib/upmin/admin.rb index e886635..ee78225 100644 --- a/lib/upmin/admin.rb +++ b/lib/upmin/admin.rb @@ -7,6 +7,8 @@ require "upmin/automatic_delegation" require "upmin/attribute" require "upmin/association" +require "upmin/action" +require "upmin/parameter" require "upmin/admin_model" require "upmin/paginator" diff --git a/lib/upmin/admin_model.rb b/lib/upmin/admin_model.rb index 9b8d3bb..30ee992 100644 --- a/lib/upmin/admin_model.rb +++ b/lib/upmin/admin_model.rb @@ -26,7 +26,11 @@ def initialize(model = nil, options = {}) end def path - return upmin_model_path(klass: model_class_name, id: id) + if new_record? + return upmin_new_model_path(klass: model_class_name) + else + return upmin_model_path(klass: model_class_name, id: id) + end end def create_path @@ -38,21 +42,34 @@ def title end def attributes - attributes = [] + return @attributes if defined?(@attributes) + @attributes = [] self.class.attributes.each do |attr_name| - attributes << Upmin::Attribute.new(self, attr_name) + @attributes << Upmin::Attribute.new(self, attr_name) end - return attributes + return @attributes end def associations - associations = [] + return @associations if defined?(@associations) + @associations = [] self.class.associations.each do |assoc_name| - associations << Upmin::Association.new(self, assoc_name) + @associations << Upmin::Association.new(self, assoc_name) end - return associations + return @associations end + def actions + return @actions if defined?(@actions) + @actions = [] + self.class.actions.each do |action_name| + @actions << Upmin::Action.new(self, action_name) + end + return @actions + end + + + @@ -73,8 +90,6 @@ def AdminModel.associations return @associations = all - ignored end - - # def color(color = nil) # @color ||= color # @color ||= klass.color diff --git a/lib/upmin/automatic_delegation.rb b/lib/upmin/automatic_delegation.rb index 01dd0ea..16d6069 100644 --- a/lib/upmin/automatic_delegation.rb +++ b/lib/upmin/automatic_delegation.rb @@ -13,13 +13,30 @@ def method_missing(method, *args, &block) end def delegatable?(method) - model.respond_to?(method) + return model.respond_to?(method) end + delegate(:delegated?, to: :class) + def respond_to?(method) super || delegatable?(method) end + def method(method_name) + if delegated?(method_name) + return model.method(method_name) + else + return super(method_name) + end + rescue NameError => e + if delegatable?(method_name) + self.class.delegate(method_name, to: :model) + return method(method_name) + else + super(method_name) + end + end + module ClassMethods # Proxies missing class methods to the source class. def method_missing(method, *args, &block) @@ -32,6 +49,17 @@ def delegatable?(method) model_class? && model_class.respond_to?(method) end + def delegate(method, *args) + @delegated ||= [] + @delegated << method.to_sym + super(method, *args) + end + + def delegated?(method) + @delegated ||= [] + return @delegated.include?(method.to_sym) + end + # Avoids reloading the model class when ActiveSupport clears autoloaded # dependencies in development mode. def before_remove_const diff --git a/lib/upmin/errors.rb b/lib/upmin/errors.rb index 3f1679e..66bdd17 100644 --- a/lib/upmin/errors.rb +++ b/lib/upmin/errors.rb @@ -1,13 +1,25 @@ module Upmin + class InvalidAction < ArgumentError + def initialize(action) + super("Invalid action: #{action}") + end + end + + class MissingArgument < ArgumentError + def initialize(arg) + super("Missing argument: #{arg}") + end + end + class ArgumentError < ArgumentError def initialize(arg) super("Invalid argument: #{arg}") end end - class MissingPartial < ActionView::MissingTemplate + class MissingPartial < ::StandardError def initialize(data) - super("Could not find a matching partial with the following data: #{data}") + super("Could not find a matching partial with the following data: #{data.as_json}") end end diff --git a/lib/upmin/parameter.rb b/lib/upmin/parameter.rb new file mode 100644 index 0000000..c5d4dc9 --- /dev/null +++ b/lib/upmin/parameter.rb @@ -0,0 +1,43 @@ +module Upmin + class Parameter + attr_reader :action + attr_reader :name + + def initialize(action, parameter_name, options = {}) + @action = action + @name = parameter_name.to_sym + @type = options[:type] if options[:type] + end + + def model + return action.model + end + + def title + return name.to_s.humanize + end + + def label_name + name.to_s.capitalize.gsub("_", " ") + end + + def type + return @type if defined?(@type) + @type = action.model.method(action.name).parameters.select do |param_type, param_name| + param_name == name + end.first.first || :req + return @type + end + + def form_id + return "#{action.name}_#{name}" + end + + def nil_form_id + return "#{form_id}_is_nil" + end + + private + + end +end diff --git a/lib/upmin/railties/render.rb b/lib/upmin/railties/render.rb index 1685c7e..1dc5b86 100644 --- a/lib/upmin/railties/render.rb +++ b/lib/upmin/railties/render.rb @@ -52,6 +52,12 @@ def up_render(data, options = {}) elsif data.is_a?(Upmin::Association) options = RenderHelpers.association_options(data, options) partials = RenderHelpers.association_partials(data, options) + elsif data.is_a?(Upmin::Action) + options = RenderHelpers.action_options(data, options) + partials = RenderHelpers.action_partials(data, options) + elsif data.is_a?(Upmin::Parameter) + options = RenderHelpers.parameter_options(data, options) + partials = RenderHelpers.parameter_partials(data, options) else raise Upmin::ArgumentError.new(data) end diff --git a/lib/upmin/railties/render_helpers.rb b/lib/upmin/railties/render_helpers.rb index 6a30c11..1670654 100644 --- a/lib/upmin/railties/render_helpers.rb +++ b/lib/upmin/railties/render_helpers.rb @@ -22,8 +22,8 @@ def RenderHelpers.model_options(model, options = {}) return options end - def RenderHelpers.build_model_path(partial_name, prefix = "") - return "#{root_path}/models/#{prefix}#{partial_name}" + def RenderHelpers.build_model_path(partial, prefix = "") + return build_path("models", "#{prefix}#{partial}") end @@ -54,8 +54,8 @@ def RenderHelpers.attribute_options(attribute, options = {}) return options end - def RenderHelpers.build_attribute_path(partial_name) - return "#{root_path}/attributes/#{partial_name}" + def RenderHelpers.build_attribute_path(partial) + return build_path("attributes", partial) end @@ -86,8 +86,8 @@ def RenderHelpers.association_options(association, options = {}) return options end - def RenderHelpers.build_association_path(partial_name) - return "#{root_path}/associations/#{partial_name}" + def RenderHelpers.build_association_path(partial) + return build_path("associations", partial) end @@ -109,14 +109,45 @@ def RenderHelpers.action_partials(action, options = {}) def RenderHelpers.action_options(action, options = {}) options[:locals] ||= {} - options[:locals][:model] ||= model + options[:locals][:model] ||= action.model options[:locals][:action] = action return options end - def RenderHelpers.build_action_path(partial_name) - partial_name = partial_name.to_s.gsub(/[!?]/, "") - return "#{root_path}/actions/#{partial_name}" + def RenderHelpers.build_action_path(partial) + return build_path("actions", partial) + end + + + + def RenderHelpers.parameter_partials(parameter, options = {}) + partials = [] + # + # __, eg: order_refund_amount + # _, eg: refund_amount + # , eg: amount + # _parameter, eg: opt_parameter and req_parameter + model_name = parameter.model.underscore_name + action_name = parameter.action.name + + partials << build_parameter_path(options[:as]) if options[:as] + partials << build_parameter_path("#{model_name}_#{action_name}_#{parameter.name}") + partials << build_parameter_path("#{action_name}_#{parameter.name}") + partials << build_parameter_path(parameter.name) + partials << build_parameter_path("#{parameter.type}_parameter") + return partials + end + + def RenderHelpers.parameter_options(parameter, options = {}) + options[:locals] ||= {} + options[:locals][:model] ||= parameter.model + options[:locals][:action] ||= parameter.action + options[:locals][:parameter] = parameter + return options + end + + def RenderHelpers.build_parameter_path(partial) + return build_path("parameters", partial) end @@ -147,8 +178,8 @@ def RenderHelpers.search_result_partials(model, options = {}) return partials end - def RenderHelpers.build_search_result_path(partial_name) - return "#{root_path}/search_results/#{partial_name}" + def RenderHelpers.build_search_result_path(partial) + return build_path("search_results", partial) end @@ -163,11 +194,15 @@ def RenderHelpers.search_box_partials(klass, options = {}) return partials end - def RenderHelpers.build_search_box_path(partial_name) - return "#{root_path}/search_boxes/#{partial_name}" + def RenderHelpers.build_search_box_path(partial) + return build_path("search_boxes", partial) end + def RenderHelpers.build_path(folder, partial) + partial = partial.to_s.gsub(/[!?]/, "") + "#{root_path}/#{folder}/#{partial}" + end def RenderHelpers.root_path return "upmin/partials" diff --git a/lib/upmin/version.rb b/lib/upmin/version.rb index fa8540c..7132820 100644 --- a/lib/upmin/version.rb +++ b/lib/upmin/version.rb @@ -1,3 +1,3 @@ module Upmin - VERSION = "0.0.39dev10000073" + VERSION = "0.0.39dev10000087" end From dc12f99aeb524647bf5e53c748dd720264763058 Mon Sep 17 00:00:00 2001 From: Jon Calhoun Date: Thu, 25 Sep 2014 04:34:01 -0400 Subject: [PATCH 6/8] rewrite appears to be working. need to test --- .../stylesheets/upmin/instances.css.scss | 6 ++ app/controllers/upmin/models_controller.rb | 9 ++- app/views/upmin/models/search.html.haml | 6 +- .../_ransack_search_box.html.haml | 5 +- .../partials/search_results/_result.html.haml | 8 --- .../search_results/_results.html.haml | 11 +++- lib/upmin/admin.rb | 1 + lib/upmin/admin_model.rb | 20 +++++-- lib/upmin/attribute.rb | 8 +-- lib/upmin/query.rb | 42 ++++++++++++++ lib/upmin/railties/render.rb | 55 +++++-------------- lib/upmin/railties/render_helpers.rb | 34 ++++++------ lib/upmin/version.rb | 2 +- 13 files changed, 121 insertions(+), 86 deletions(-) delete mode 100644 app/views/upmin/partials/search_results/_result.html.haml create mode 100644 lib/upmin/query.rb diff --git a/app/assets/stylesheets/upmin/instances.css.scss b/app/assets/stylesheets/upmin/instances.css.scss index ca0e551..cb39c3b 100644 --- a/app/assets/stylesheets/upmin/instances.css.scss +++ b/app/assets/stylesheets/upmin/instances.css.scss @@ -1,5 +1,11 @@ @import "colors"; +.search-result-link { + .upmin-model { + margin-bottom: 10px; + } +} + .upmin-model { padding: 20px; margin: 10px 0px 250px 0; diff --git a/app/controllers/upmin/models_controller.rb b/app/controllers/upmin/models_controller.rb index 7128a09..a006b29 100644 --- a/app/controllers/upmin/models_controller.rb +++ b/app/controllers/upmin/models_controller.rb @@ -6,6 +6,7 @@ class ModelsController < ApplicationController before_filter :set_model, only: [:show, :update, :action] before_filter :set_page, only: [:search] + before_filter :set_query, only: [:search] before_filter :set_action, only: [:action] before_filter :set_arguments, only: [:action] @@ -83,8 +84,8 @@ def update end def search - @q = @klass.ransack(params[:q]) - @results = Upmin::Paginator.paginate(@q.result(distinct: true), @page, 30) + # @q = @klass.ransack(params[:q]) + # @results = Upmin::Paginator.paginate(@q.result(distinct: true), @page, 30) end def action @@ -99,6 +100,10 @@ def action end private + def set_query + @query = Upmin::Query.new(@klass, params[:q], page: @page, per_page: 30) + end + def set_model @model = @klass.new(id: params[:id]) end diff --git a/app/views/upmin/models/search.html.haml b/app/views/upmin/models/search.html.haml index 973a442..6618eea 100644 --- a/app/views/upmin/models/search.html.haml +++ b/app/views/upmin/models/search.html.haml @@ -2,14 +2,14 @@ .row .col-md-8 -# TODO(jon): Add pagination w/ search results - = up_search_results(@q, @results) + = up_render(@query) %br - = up_paginate(@results) + = up_paginate(@query.paginated_results) %br %br .col-md-4 -# TODO(jon): Implement up_search_box - = up_search_box(@klass) + = up_render(@klass) .new-button-wrapper %a.btn.btn-block.btn-success{href: upmin_new_model_path(klass: @klass.model_class_name)} Create a new diff --git a/app/views/upmin/partials/search_boxes/_ransack_search_box.html.haml b/app/views/upmin/partials/search_boxes/_ransack_search_box.html.haml index 115ea72..a0886a3 100644 --- a/app/views/upmin/partials/search_boxes/_ransack_search_box.html.haml +++ b/app/views/upmin/partials/search_boxes/_ransack_search_box.html.haml @@ -3,11 +3,10 @@ = klass.humanized_name -# Upmin default search uses the ransack gem, see: https://github.com/activerecord-hackery/ransack -= form_tag(upmin_search_path(klass: klass.name), method: :get) do += form_tag(klass.search_path, method: :get) do - klass.attributes.each do |attr_name| - type = klass.attribute_type(attr_name) - -# TODO(jon): Make sure these retain their data on a search -# TODO(jon): Break these into partials possibly? - if type == :string .form-group @@ -34,4 +33,4 @@ = date_field(:q, "#{attr_name}_lteq", class: "form-control") = submit_tag("Search", class: "btn btn-primary btn-block") - = link_to("Clear All", upmin_search_path(klass: klass.name), class: "btn btn-default btn-block") + = link_to("Clear All", klass.search_path, class: "btn btn-default btn-block") diff --git a/app/views/upmin/partials/search_results/_result.html.haml b/app/views/upmin/partials/search_results/_result.html.haml deleted file mode 100644 index cec0ddb..0000000 --- a/app/views/upmin/partials/search_results/_result.html.haml +++ /dev/null @@ -1,8 +0,0 @@ -%a.search-result-link{href: upmin_model.path} - .upmin-model{class: upmin_model.color} - %dl.dl-horizontal - - @klass.attributes.each do |attribute| - %dt - = upmin_model.attribute_label(attribute) - %dd - = upmin_model.send(attribute) diff --git a/app/views/upmin/partials/search_results/_results.html.haml b/app/views/upmin/partials/search_results/_results.html.haml index bb12f03..20fd48a 100644 --- a/app/views/upmin/partials/search_results/_results.html.haml +++ b/app/views/upmin/partials/search_results/_results.html.haml @@ -1,2 +1,9 @@ -- results.each do |result| - = up_search_result(result) +- query.upmin_results.each do |result| + %a.search-result-link{href: result.path} + .upmin-model{class: result.color} + %dl.dl-horizontal + - result.attributes.each do |attribute| + %dt + = attribute.label_name + %dd + = attribute.value diff --git a/lib/upmin/admin.rb b/lib/upmin/admin.rb index ee78225..fa15e30 100644 --- a/lib/upmin/admin.rb +++ b/lib/upmin/admin.rb @@ -9,6 +9,7 @@ require "upmin/association" require "upmin/action" require "upmin/parameter" +require "upmin/query" require "upmin/admin_model" require "upmin/paginator" diff --git a/lib/upmin/admin_model.rb b/lib/upmin/admin_model.rb index 30ee992..bcac4df 100644 --- a/lib/upmin/admin_model.rb +++ b/lib/upmin/admin_model.rb @@ -124,6 +124,14 @@ def AdminModel.attributes(*attributes) return (@attributes + @extra_attrs).uniq end + def AdminModel.attribute_type(attribute) + if adapter = model_class.columns_hash[attribute.to_s] + return adapter.type + else + return :unknown + end + end + # Add a single action to upmin actions. If this is called # before upmin_actions the actions will not include any defaults # actions. @@ -206,12 +214,16 @@ def AdminModel.humanized_name(type = :plural) return names.join(" ") end - def AdminModel.underscore_name - return model_class_name.underscore + def AdminModel.underscore_name(type = :singular) + if type == :singular + return model_class_name.underscore + else + return model_class_name.pluralize.underscore + end end - def AdminModel.model_class_path - return Upmin::Engine.routes.url_helpers.upmin_search_path(klass: klass_name) + def AdminModel.search_path + return Upmin::Engine.routes.url_helpers.upmin_search_path(klass: model_class_name) end def AdminModel.color diff --git a/lib/upmin/attribute.rb b/lib/upmin/attribute.rb index a946987..a6cb8ef 100644 --- a/lib/upmin/attribute.rb +++ b/lib/upmin/attribute.rb @@ -17,12 +17,8 @@ def type # TODO(jon): Add a way to override with widgets? return @type if defined?(@type) - # Try to get it from the model_class' columns hash - if adapter = model.class.columns_hash[name.to_s] - return @type = adapter.type - else - return @type = :unknown - end + # Try to get it from the model_class + @type = model.class.attribute_type(name) # If we still don't know the type, try to infer it from the value if @type == :unknown diff --git a/lib/upmin/query.rb b/lib/upmin/query.rb new file mode 100644 index 0000000..52c140b --- /dev/null +++ b/lib/upmin/query.rb @@ -0,0 +1,42 @@ +module Upmin + class Query + + attr_reader :klass + attr_reader :search_options + attr_reader :page + attr_reader :per_page + + delegate(:underscore_name, to: :klass) + + def initialize(klass, search_options = {}, options = {}) + @klass = klass + @search_options = search_options + @page = options[:page] + @per_page = options[:per_page] + end + + def results + return klass.ransack(search_options).result(distinct: true) + end + + def paginated_results + return @paginated_results if defined?(@paginated_results) + if page && per_page + pr = Upmin::Paginator.paginate(results, page, per_page) + else + pr = Upmin::Paginator.paginate(results) + end + @paginated_results = pr + return @paginated_results + end + + def upmin_results + return @upmin_results if defined?(@upmin_results) + @upmin_results = paginated_results.map{ |r| Upmin::AdminModel.find_class(r.class).new(r) } + return @upmin_results + end + + private + + end +end diff --git a/lib/upmin/railties/render.rb b/lib/upmin/railties/render.rb index 1dc5b86..c2a348d 100644 --- a/lib/upmin/railties/render.rb +++ b/lib/upmin/railties/render.rb @@ -2,62 +2,37 @@ module Upmin::Railties module Render - def up_search_results(ransack_search, ransack_results, options = {}) - # options[:locals] ||= {} - # options[:locals][:klass] ||= Upmin::Klass.find(ransack_search.klass) - # options[:locals][:ransack_search] ||= ransack_search - # options[:locals][:ransack_results] ||= ransack_results - - # partials = RenderHelpers.search_results_partials(ransack_search, options) - - # return up_render(ransack_results, partials, options, :up_search_results) - end - - def up_search_result(model, options = {}) - # options[:locals] ||= {} - - # upmin_model = Upmin::AdminModel.new(model) - # options[:locals][:upmin_model] ||= upmin_model - - # partials = RenderHelpers.search_result_partials(upmin_model, options) - - # return up_render(model, partials, options, :up_search_result) - end - - def up_search_box(klass, options = {}) - # options[:locals] ||= {} - - # klass = Upmin::Klass.find(klass) unless klass.is_a?(Upmin::Klass) - # if klass.nil? - # raise "Invalid klass provided in `up_search_box`" - # end - - # options[:locals][:klass] = klass - - # partials = RenderHelpers.search_box_partials(klass, options) - - # return up_render(klass, partials, options, :up_search_box) - end - - - - # Generic render method that is used by upmin-admin. Tries to render partials in order, passing data in as the :object, along with options. + # Render method that is used by upmin-admin. Tries to render partials in order, passing data in as the :object, along with options. def up_render(data, options = {}) if data.is_a?(Upmin::AdminModel) options = RenderHelpers.model_options(data, options) partials = RenderHelpers.model_partials(data, options) + elsif data.is_a?(Upmin::Attribute) options = RenderHelpers.attribute_options(data, options) partials = RenderHelpers.attribute_partials(data, options) + elsif data.is_a?(Upmin::Association) options = RenderHelpers.association_options(data, options) partials = RenderHelpers.association_partials(data, options) + elsif data.is_a?(Upmin::Action) options = RenderHelpers.action_options(data, options) partials = RenderHelpers.action_partials(data, options) + elsif data.is_a?(Upmin::Parameter) options = RenderHelpers.parameter_options(data, options) partials = RenderHelpers.parameter_partials(data, options) + + elsif data.is_a?(Upmin::Query) + options = RenderHelpers.search_results_options(data, options) + partials = RenderHelpers.search_results_partials(data, options) + + elsif Upmin::AdminModel.all.include?(data) + # Probably rendering a search box + options = RenderHelpers.search_box_options(data, options) + partials = RenderHelpers.search_box_partials(data, options) + else raise Upmin::ArgumentError.new(data) end diff --git a/lib/upmin/railties/render_helpers.rb b/lib/upmin/railties/render_helpers.rb index 1670654..ffc5122 100644 --- a/lib/upmin/railties/render_helpers.rb +++ b/lib/upmin/railties/render_helpers.rb @@ -152,33 +152,26 @@ def RenderHelpers.build_parameter_path(partial) - def RenderHelpers.search_results_partials(ransack_search, options = {}) + def RenderHelpers.search_results_partials(query, options = {}) partials = [] # # , eg: orders # results - model_name_plural = ransack_search.klass.name.underscore.pluralize + model_name_plural = query.underscore_name(:plural) - partials << build_search_result_path(options[:as]) if options[:as] - partials << build_search_result_path(model_name_plural) - partials << build_search_result_path(:results) + partials << build_search_results_path(options[:as]) if options[:as] + partials << build_search_results_path(model_name_plural) + partials << build_search_results_path(:results) return partials end - def RenderHelpers.search_result_partials(model, options = {}) - partials = [] - # - # , eg: order - # results - model_name = model.klass.name.underscore - - partials << build_search_result_path(options[:as]) if options[:as] - partials << build_search_result_path(model_name) - partials << build_search_result_path(:result) - return partials + def RenderHelpers.search_results_options(query, options = {}) + options[:locals] ||= {} + options[:locals][:query] = query + return options end - def RenderHelpers.build_search_result_path(partial) + def RenderHelpers.build_search_results_path(partial) return build_path("search_results", partial) end @@ -194,11 +187,18 @@ def RenderHelpers.search_box_partials(klass, options = {}) return partials end + def RenderHelpers.search_box_options(klass, options = {}) + options[:locals] ||= {} + options[:locals][:klass] = klass + return options + end + def RenderHelpers.build_search_box_path(partial) return build_path("search_boxes", partial) end + def RenderHelpers.build_path(folder, partial) partial = partial.to_s.gsub(/[!?]/, "") "#{root_path}/#{folder}/#{partial}" diff --git a/lib/upmin/version.rb b/lib/upmin/version.rb index 7132820..90a82ad 100644 --- a/lib/upmin/version.rb +++ b/lib/upmin/version.rb @@ -1,3 +1,3 @@ module Upmin - VERSION = "0.0.39dev10000087" + VERSION = "0.0.39dev10000092" end From 192395ba533d8bb937e7e6785917bfdf23a0e790 Mon Sep 17 00:00:00 2001 From: Jon Calhoun Date: Thu, 25 Sep 2014 06:01:19 -0400 Subject: [PATCH 7/8] tests are working. new model rewrite is solid. --- Rakefile | 21 ++- app/assets/stylesheets/upmin/base.css.scss | 1 + .../stylesheets/upmin/instances.css.scss | 4 + .../upmin/partials/actions/_action.html.haml | 2 +- .../upmin/partials/models/_model.html.haml | 24 +-- .../partials/models/_new_model.html.haml | 13 +- lib/upmin/admin_model.rb | 166 ++++++++++-------- lib/upmin/automatic_delegation.rb | 4 +- lib/upmin/version.rb | 2 +- spec/features/action_spec.rb | 85 ++++----- spec/features/search_spec.rb | 1 - spec/spec_helper.rb | 13 +- test_app_upmin/models/admin_product.rb | 11 ++ test_app_upmin/models/admin_shipment.rb | 11 ++ test_app_upmin/models/admin_user.rb | 5 + .../app/upmin/models/admin_product.rb | 11 ++ .../app/upmin/models/admin_shipment.rb | 11 ++ .../app/upmin/models/admin_user.rb | 5 + .../app/upmin/models/admin_product.rb | 11 ++ .../app/upmin/models/admin_shipment.rb | 11 ++ .../app/upmin/models/admin_user.rb | 5 + .../app/upmin/models/admin_product.rb | 11 ++ .../app/upmin/models/admin_shipment.rb | 11 ++ .../app/upmin/models/admin_user.rb | 5 + .../app/upmin/models/admin_product.rb | 11 ++ .../app/upmin/models/admin_shipment.rb | 11 ++ .../app/upmin/models/admin_user.rb | 5 + .../app/upmin/models/admin_product.rb | 11 ++ .../app/upmin/models/admin_shipment.rb | 11 ++ .../app/upmin/models/admin_user.rb | 5 + {seed => test_seeders}/names.json | 0 {seed => test_seeders}/order_seeder.rb | 0 {seed => test_seeders}/product_seeder.rb | 0 {seed => test_seeders}/products.json | 0 {seed => test_seeders}/seeder.rb | 0 {seed => test_seeders}/user_seeder.rb | 0 36 files changed, 344 insertions(+), 154 deletions(-) create mode 100644 test_app_upmin/models/admin_product.rb create mode 100644 test_app_upmin/models/admin_shipment.rb create mode 100644 test_app_upmin/models/admin_user.rb create mode 100644 test_apps/active_record_32/app/upmin/models/admin_product.rb create mode 100644 test_apps/active_record_32/app/upmin/models/admin_shipment.rb create mode 100644 test_apps/active_record_32/app/upmin/models/admin_user.rb create mode 100644 test_apps/active_record_40/app/upmin/models/admin_product.rb create mode 100644 test_apps/active_record_40/app/upmin/models/admin_shipment.rb create mode 100644 test_apps/active_record_40/app/upmin/models/admin_user.rb create mode 100644 test_apps/active_record_41/app/upmin/models/admin_product.rb create mode 100644 test_apps/active_record_41/app/upmin/models/admin_shipment.rb create mode 100644 test_apps/active_record_41/app/upmin/models/admin_user.rb create mode 100644 test_apps/active_record_42/app/upmin/models/admin_product.rb create mode 100644 test_apps/active_record_42/app/upmin/models/admin_shipment.rb create mode 100644 test_apps/active_record_42/app/upmin/models/admin_user.rb create mode 100644 test_apps/will_paginate/app/upmin/models/admin_product.rb create mode 100644 test_apps/will_paginate/app/upmin/models/admin_shipment.rb create mode 100644 test_apps/will_paginate/app/upmin/models/admin_user.rb rename {seed => test_seeders}/names.json (100%) rename {seed => test_seeders}/order_seeder.rb (100%) rename {seed => test_seeders}/product_seeder.rb (100%) rename {seed => test_seeders}/products.json (100%) rename {seed => test_seeders}/seeder.rb (100%) rename {seed => test_seeders}/user_seeder.rb (100%) diff --git a/Rakefile b/Rakefile index b6041c9..168eef4 100644 --- a/Rakefile +++ b/Rakefile @@ -13,6 +13,17 @@ end task :default => "spec:all" +def update_files + # Drop and reload spec files + sh "rm -rf spec/" + sh "cp -R ../../spec spec" + sh "cp ../../.rspec .rspec" + + # Drop and reloda Upmin::Model files + sh "rm -rf app/upmin/" + sh "cp -R ../../test_app_upmin app/upmin" +end + namespace :spec do # Full bundle install & test. %w(active_record_32 active_record_40 active_record_41 active_record_42 will_paginate).each do |gemfile| @@ -33,10 +44,7 @@ namespace :spec do sh "RAILS_ENV=test bundle exec rake db:drop db:create db:migrate --quiet" - # Drop and reload spec files - sh "rm -rf spec/" - sh "cp -R ../../spec spec" - sh "cp ../../.rspec .rspec" + update_files # Run tests sh "bundle exec rake" @@ -50,10 +58,7 @@ namespace :spec do Dir.chdir("test_apps/#{gemfile}") puts "Re-testing in #{`pwd`}. Bundle install and migration updates will NOT happen!" - # Drop and reload spec files - sh "rm -rf spec/" - sh "cp -R ../../spec spec" - sh "cp ../../.rspec .rspec" + update_files # Run tests sh "bundle exec rake" diff --git a/app/assets/stylesheets/upmin/base.css.scss b/app/assets/stylesheets/upmin/base.css.scss index 081164c..6c4bf92 100644 --- a/app/assets/stylesheets/upmin/base.css.scss +++ b/app/assets/stylesheets/upmin/base.css.scss @@ -53,6 +53,7 @@ body { } + .wizard span {padding: 12px 12px 10px 12px; margin-right:5px; background:#efefef; position:relative; display:inline-block; } .wizard span:before {width:0px; height:0px; border-top: 20px inset transparent; border-bottom: 20px inset transparent; border-left: 20px solid #fff; position: absolute; content: ""; top: 0; left: 0;} .wizard span:after {width:0px; height:0px; border-top: 20px inset transparent; border-bottom: 20px inset transparent; border-left: 20px solid #efefef; position: absolute; content: ""; top: 0; right: -20px; z-index:2;} diff --git a/app/assets/stylesheets/upmin/instances.css.scss b/app/assets/stylesheets/upmin/instances.css.scss index cb39c3b..1c5a451 100644 --- a/app/assets/stylesheets/upmin/instances.css.scss +++ b/app/assets/stylesheets/upmin/instances.css.scss @@ -71,6 +71,10 @@ padding: 9px; margin-bottom: 0; min-height: 40px; + + &.action-well { + margin-bottom: 32px; + } } a.active-tag-link { diff --git a/app/views/upmin/partials/actions/_action.html.haml b/app/views/upmin/partials/actions/_action.html.haml index 88d9761..23e7aee 100644 --- a/app/views/upmin/partials/actions/_action.html.haml +++ b/app/views/upmin/partials/actions/_action.html.haml @@ -1,7 +1,7 @@ %h4{style: "color: #333;"} = action.title .well.action-well - = form_tag(action.path) do + = form_tag(action.path, class: action.name) do - action.parameters.each do |parameter| -# = Upmin::Railties::RenderHelpers.parameter_partials(parameter) = up_render(parameter) diff --git a/app/views/upmin/partials/models/_model.html.haml b/app/views/upmin/partials/models/_model.html.haml index ec4bc78..80f3502 100644 --- a/app/views/upmin/partials/models/_model.html.haml +++ b/app/views/upmin/partials/models/_model.html.haml @@ -8,14 +8,15 @@ Attributes %hr - -# Yes this is meant to be model.model - this is the raw rails model instance. - = form_for(model.model, url: model.path, html: { method: :put }) do |f| + .attributes + -# Yes this is meant to be model.model - this is the raw rails model instance. + = form_for(model.model, url: model.path, html: { method: :put }) do |f| - -# Render each attribute - - model.attributes.each do |attribute| - = up_render(attribute, locals: { form_builder: f }) + -# Render each attribute + - model.attributes.each do |attribute| + = up_render(attribute, locals: { form_builder: f }) - = f.submit("Save", class: "btn btn-primary") + = f.submit("Save", class: "btn btn-primary") - if model.associations.any? @@ -25,9 +26,9 @@ %h3{style: "color: #333;"} Associations %hr - - - model.associations.each do |association| - = up_render(association) + .associations + - model.associations.each do |association| + = up_render(association) - if model.actions.any? @@ -37,6 +38,7 @@ %h3{style: "color: #333;"} Actions %hr - - model.actions.each do |action| - = up_render(action) + .actions + - model.actions.each do |action| + = up_render(action) diff --git a/app/views/upmin/partials/models/_new_model.html.haml b/app/views/upmin/partials/models/_new_model.html.haml index e848a56..31a32a0 100644 --- a/app/views/upmin/partials/models/_new_model.html.haml +++ b/app/views/upmin/partials/models/_new_model.html.haml @@ -10,12 +10,13 @@ Attributes %hr - -# Yes this is meant to be model.model - this is the raw rails model instance. - = form_for(model.model, url: model.create_path, html: { method: :post }) do |f| + .attributes + -# Yes this is meant to be model.model - this is the raw rails model instance. + = form_for(model.model, url: model.create_path, html: { method: :post }) do |f| - -# Render each attribute - - model.attributes.each do |attribute| - = up_render(attribute, locals: { form_builder: f }) + -# Render each attribute + - model.attributes.each do |attribute| + = up_render(attribute, locals: { form_builder: f }) - = f.submit("Create", class: "btn btn-primary") + = f.submit("Create", class: "btn btn-primary") diff --git a/lib/upmin/admin_model.rb b/lib/upmin/admin_model.rb index bcac4df..6bc9961 100644 --- a/lib/upmin/admin_model.rb +++ b/lib/upmin/admin_model.rb @@ -6,12 +6,6 @@ class AdminModel attr_reader :model alias_method :object, :model # For delegation - delegate(:color, to: :class) - delegate(:humanized_name, to: :class) - delegate(:underscore_name, to: :class) - delegate(:model_class, to: :class) - delegate(:model_class_name, to: :class) - def initialize(model = nil, options = {}) if model.is_a?(Hash) unless model.has_key?(:id) @@ -70,9 +64,39 @@ def actions + ############################################# + ### Delegated instance methods ### + ############################################# + + # TODO(jon): Figure out why delegations here weren't working in 3.2 tests + # delegate(:color, to: :class) + def color + return self.class.color + end + # delegate(:humanized_name, to: :class) + def humanized_name(type = :plural) + return self.class.humanized_name(type) + end + # delegate(:underscore_name, to: :class) + def underscore_name + return self.class.underscore_name + end + # delegate(:model_class, to: :class) + def model_class + return self.class.model_class + end + # delegate(:model_class_name, to: :class) + def model_class_name + return self.class.model_class_name + end + + ############################################# + ### Class methods ### + ############################################# + def AdminModel.associations return @associations if defined?(@associations) @@ -90,73 +114,6 @@ def AdminModel.associations return @associations = all - ignored end - # def color(color = nil) - # @color ||= color - # @color ||= klass.color - # return @color - # end - - ############################################# - #### Methods used to customize the model #### - ############################################# - - # Add a single attribute to upmin attributes. - # If this is called before upmin_attributes - # the attributes will not include any defaults - # attributes. - def AdminModel.attribute(attribute = nil) - @extra_attrs = [] unless defined?(@extra_attrs) - @extra_attrs << attribute.to_sym if attribute - end - - # Sets the attributes to the provided attributes # if any are any provided. - # If no attributes are provided then the - # attributes are set to the default attributes of - # the model class. - def AdminModel.attributes(*attributes) - @extra_attrs = [] unless defined?(@extra_attrs) - - if attributes.any? - @attributes = attributes.map{|a| a.to_sym} - end - - @attributes ||= model_class.attribute_names.map{|a| a.to_sym} - return (@attributes + @extra_attrs).uniq - end - - def AdminModel.attribute_type(attribute) - if adapter = model_class.columns_hash[attribute.to_s] - return adapter.type - else - return :unknown - end - end - - # Add a single action to upmin actions. If this is called - # before upmin_actions the actions will not include any defaults - # actions. - def AdminModel.action(action) - @actions ||= [] - - action = action.to_sym - @actions << action unless @actions.include?(action) - end - - # Sets the upmin_actions to the provided actions if any are - # provided. - # If no actions are provided, and upmin_actions hasn't been defined, - # then the upmin_actions are set to the default actions. - # Returns the upmin_actions - def AdminModel.actions(*actions) - if actions.any? - # set the actions - @actions = actions.map{|a| a.to_sym} - end - @actions ||= [] - return @actions - end - - def AdminModel.find_class(model) return find_or_create_class(model.to_s) end @@ -231,5 +188,68 @@ def AdminModel.color return :green end + + + ############################################# + ### Customization methods called in ### + ### Admin classes ### + ############################################# + + # Add a single attribute to upmin attributes. + # If this is called before upmin_attributes + # the attributes will not include any defaults + # attributes. + def AdminModel.attribute(attribute = nil) + @extra_attrs = [] unless defined?(@extra_attrs) + @extra_attrs << attribute.to_sym if attribute + end + + # Sets the attributes to the provided attributes # if any are any provided. + # If no attributes are provided then the + # attributes are set to the default attributes of + # the model class. + def AdminModel.attributes(*attributes) + @extra_attrs = [] unless defined?(@extra_attrs) + + if attributes.any? + @attributes = attributes.map{|a| a.to_sym} + end + + @attributes ||= model_class.attribute_names.map{|a| a.to_sym} + return (@attributes + @extra_attrs).uniq + end + + def AdminModel.attribute_type(attribute) + if adapter = model_class.columns_hash[attribute.to_s] + return adapter.type + else + return :unknown + end + end + + # Add a single action to upmin actions. If this is called + # before upmin_actions the actions will not include any defaults + # actions. + def AdminModel.action(action) + @actions ||= [] + + action = action.to_sym + @actions << action unless @actions.include?(action) + end + + # Sets the upmin_actions to the provided actions if any are + # provided. + # If no actions are provided, and upmin_actions hasn't been defined, + # then the upmin_actions are set to the default actions. + # Returns the upmin_actions + def AdminModel.actions(*actions) + if actions.any? + # set the actions + @actions = actions.map{|a| a.to_sym} + end + @actions ||= [] + return @actions + end + end end diff --git a/lib/upmin/automatic_delegation.rb b/lib/upmin/automatic_delegation.rb index 16d6069..86cb3e0 100644 --- a/lib/upmin/automatic_delegation.rb +++ b/lib/upmin/automatic_delegation.rb @@ -16,7 +16,9 @@ def delegatable?(method) return model.respond_to?(method) end - delegate(:delegated?, to: :class) + def delegated?(method) + return self.class.delegated?(method) + end def respond_to?(method) super || delegatable?(method) diff --git a/lib/upmin/version.rb b/lib/upmin/version.rb index 90a82ad..1bdb97d 100644 --- a/lib/upmin/version.rb +++ b/lib/upmin/version.rb @@ -1,3 +1,3 @@ module Upmin - VERSION = "0.0.39dev10000092" + VERSION = "0.0.39dev10000093" end diff --git a/spec/features/action_spec.rb b/spec/features/action_spec.rb index ac74ed1..f6fbafc 100644 --- a/spec/features/action_spec.rb +++ b/spec/features/action_spec.rb @@ -6,63 +6,56 @@ # Setup BG Stuff end - scenario("with a valid user") do - visit("/upmin/m/User/new") - - expected_user = build(:user) - - fill_in("user_name", with: expected_user.name) - fill_in("user_email", with: expected_user.email) - fill_in("user_stripe_card_id", with: expected_user.stripe_card_id) - - expect { click_button("Create") }.to(change(User, :count).by(1)) + scenario("with an existing method") do + user = User.first + visit("/upmin/m/User/i/#{user.id}") + + within(".actions") do + expect(page).to(have_selector("h4", text: "Issue coupon")) + within("form.issue_coupon") do + fill_in("issue_coupon_percent", with: "20") + click_button("Submit") + end + end - expect(page).to(have_selector("input#user_name[value='#{expected_user.name}']")) - expect(page).to(have_selector("input#user_email[value='#{expected_user.email}']")) - expect(page).to(have_selector("input#user_stripe_card_id[value='#{expected_user.stripe_card_id}']")) + within(".alert.alert-info") do + expect(page).to(have_content("CPN_FJALDKF01Z1")) + end end - scenario("with an invalid user") do - visit("/upmin/m/User/new") - - invalid_user = build(:user, email: "invalid") + scenario("without an optional parameter") do + user = User.first + visit("/upmin/m/User/i/#{user.id}") - fill_in("user_name", with: invalid_user.name) - fill_in("user_email", with: invalid_user.email) - fill_in("user_stripe_card_id", with: invalid_user.stripe_card_id) - - expect { click_button("Create") }.not_to(change(User, :count)) - - within(".alert.alert-danger") do - expect(page).to(have_content("User was NOT created.")) + within(".actions") do + expect(page).to(have_selector("h4", text: "Issue coupon")) + within("form.issue_coupon") do + check("issue_coupon_percent_is_nil") + click_button("Submit") + end end - within(".field_with_errors") do - expect(page).to(have_selector("input#user_email")) + within(".alert.alert-info") do + expect(page).to(have_content("CPN_FJALDKF01Z1")) end - - # Make sure the inputs have the values typed in. - expect(page).to(have_selector("input#user_name[value='#{invalid_user.name}']")) - expect(page).to(have_selector("input#user_email[value='#{invalid_user.email}']")) - expect(page).to(have_selector("input#user_stripe_card_id[value='#{invalid_user.stripe_card_id}']")) end - scenario("with a nil attribute") do - product = build(:product) - visit("/upmin/m/Product/new") + scenario("with an admin only method") do + product = Product.first + visit("/upmin/m/Product/i/#{product.id}") - check("product_name_is_nil") - fill_in("product_short_desc", with: product.short_desc) - fill_in("product_manufacturer", with: product.manufacturer) + new_price = "10.15" - expect { click_button("Create") }.to(change(Product, :count).by(1)) - - box = find("#product_name_is_nil") - expect(box).to(be_checked) + within(".actions") do + expect(page).to(have_selector("h4", text: "Update price")) + within("form.update_price") do + fill_in("update_price_price", with: new_price) + click_button("Submit") + end + end - created = Product.last - expect(created.name).to(be_nil) - expect(created.short_desc).to(eq(product.short_desc)) - expect(created.manufacturer).to(eq(product.manufacturer)) + product.reload + expect(product.price.to_s[0..4]).to(eq(new_price)) end + end diff --git a/spec/features/search_spec.rb b/spec/features/search_spec.rb index 15c6e1a..ee3da3f 100644 --- a/spec/features/search_spec.rb +++ b/spec/features/search_spec.rb @@ -73,5 +73,4 @@ expect(page).to(have_content(expected_user.name)) end - end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 757dbb2..62ddaff 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -9,7 +9,7 @@ if defined?(ActiveRecord) - require File.expand_path('../../../../seed/seeder', __FILE__) + require File.expand_path('../../../../test_seeders/seeder', __FILE__) end @@ -41,11 +41,12 @@ end # Uncomment this if you want to the page to be saved and opened after any test failure. - # config.after do |example| - # if example.metadata[:type] == :feature && example.exception.present? - # save_and_open_page - # end - # end + config.after do |example| + if example.metadata[:type] == :feature && example.exception.present? + save_and_open_page + end + end config.include(FactoryGirl::Syntax::Methods) + config.include(ActionView::Helpers::NumberHelper, type: :view) end diff --git a/test_app_upmin/models/admin_product.rb b/test_app_upmin/models/admin_product.rb new file mode 100644 index 0000000..a2585d3 --- /dev/null +++ b/test_app_upmin/models/admin_product.rb @@ -0,0 +1,11 @@ +class AdminProduct < Upmin::AdminModel + attributes :name, :short_desc, :price, :manufacturer, :free_shipping + + action :update_price + + def update_price(price) + model.price = price + model.save! + end + +end diff --git a/test_app_upmin/models/admin_shipment.rb b/test_app_upmin/models/admin_shipment.rb new file mode 100644 index 0000000..c6be749 --- /dev/null +++ b/test_app_upmin/models/admin_shipment.rb @@ -0,0 +1,11 @@ +class AdminShipment < Upmin::AdminModel + + attribute :status + actions :update_shipment + + def status + return "TestStatus" + # return Upmin::Widget::ProgressBar.new(model.status, model.tracking_states) + end + +end diff --git a/test_app_upmin/models/admin_user.rb b/test_app_upmin/models/admin_user.rb new file mode 100644 index 0000000..65f8d20 --- /dev/null +++ b/test_app_upmin/models/admin_user.rb @@ -0,0 +1,5 @@ +class AdminUser < Upmin::AdminModel + + action :issue_coupon + +end diff --git a/test_apps/active_record_32/app/upmin/models/admin_product.rb b/test_apps/active_record_32/app/upmin/models/admin_product.rb new file mode 100644 index 0000000..a2585d3 --- /dev/null +++ b/test_apps/active_record_32/app/upmin/models/admin_product.rb @@ -0,0 +1,11 @@ +class AdminProduct < Upmin::AdminModel + attributes :name, :short_desc, :price, :manufacturer, :free_shipping + + action :update_price + + def update_price(price) + model.price = price + model.save! + end + +end diff --git a/test_apps/active_record_32/app/upmin/models/admin_shipment.rb b/test_apps/active_record_32/app/upmin/models/admin_shipment.rb new file mode 100644 index 0000000..c6be749 --- /dev/null +++ b/test_apps/active_record_32/app/upmin/models/admin_shipment.rb @@ -0,0 +1,11 @@ +class AdminShipment < Upmin::AdminModel + + attribute :status + actions :update_shipment + + def status + return "TestStatus" + # return Upmin::Widget::ProgressBar.new(model.status, model.tracking_states) + end + +end diff --git a/test_apps/active_record_32/app/upmin/models/admin_user.rb b/test_apps/active_record_32/app/upmin/models/admin_user.rb new file mode 100644 index 0000000..65f8d20 --- /dev/null +++ b/test_apps/active_record_32/app/upmin/models/admin_user.rb @@ -0,0 +1,5 @@ +class AdminUser < Upmin::AdminModel + + action :issue_coupon + +end diff --git a/test_apps/active_record_40/app/upmin/models/admin_product.rb b/test_apps/active_record_40/app/upmin/models/admin_product.rb new file mode 100644 index 0000000..a2585d3 --- /dev/null +++ b/test_apps/active_record_40/app/upmin/models/admin_product.rb @@ -0,0 +1,11 @@ +class AdminProduct < Upmin::AdminModel + attributes :name, :short_desc, :price, :manufacturer, :free_shipping + + action :update_price + + def update_price(price) + model.price = price + model.save! + end + +end diff --git a/test_apps/active_record_40/app/upmin/models/admin_shipment.rb b/test_apps/active_record_40/app/upmin/models/admin_shipment.rb new file mode 100644 index 0000000..c6be749 --- /dev/null +++ b/test_apps/active_record_40/app/upmin/models/admin_shipment.rb @@ -0,0 +1,11 @@ +class AdminShipment < Upmin::AdminModel + + attribute :status + actions :update_shipment + + def status + return "TestStatus" + # return Upmin::Widget::ProgressBar.new(model.status, model.tracking_states) + end + +end diff --git a/test_apps/active_record_40/app/upmin/models/admin_user.rb b/test_apps/active_record_40/app/upmin/models/admin_user.rb new file mode 100644 index 0000000..65f8d20 --- /dev/null +++ b/test_apps/active_record_40/app/upmin/models/admin_user.rb @@ -0,0 +1,5 @@ +class AdminUser < Upmin::AdminModel + + action :issue_coupon + +end diff --git a/test_apps/active_record_41/app/upmin/models/admin_product.rb b/test_apps/active_record_41/app/upmin/models/admin_product.rb new file mode 100644 index 0000000..a2585d3 --- /dev/null +++ b/test_apps/active_record_41/app/upmin/models/admin_product.rb @@ -0,0 +1,11 @@ +class AdminProduct < Upmin::AdminModel + attributes :name, :short_desc, :price, :manufacturer, :free_shipping + + action :update_price + + def update_price(price) + model.price = price + model.save! + end + +end diff --git a/test_apps/active_record_41/app/upmin/models/admin_shipment.rb b/test_apps/active_record_41/app/upmin/models/admin_shipment.rb new file mode 100644 index 0000000..c6be749 --- /dev/null +++ b/test_apps/active_record_41/app/upmin/models/admin_shipment.rb @@ -0,0 +1,11 @@ +class AdminShipment < Upmin::AdminModel + + attribute :status + actions :update_shipment + + def status + return "TestStatus" + # return Upmin::Widget::ProgressBar.new(model.status, model.tracking_states) + end + +end diff --git a/test_apps/active_record_41/app/upmin/models/admin_user.rb b/test_apps/active_record_41/app/upmin/models/admin_user.rb new file mode 100644 index 0000000..65f8d20 --- /dev/null +++ b/test_apps/active_record_41/app/upmin/models/admin_user.rb @@ -0,0 +1,5 @@ +class AdminUser < Upmin::AdminModel + + action :issue_coupon + +end diff --git a/test_apps/active_record_42/app/upmin/models/admin_product.rb b/test_apps/active_record_42/app/upmin/models/admin_product.rb new file mode 100644 index 0000000..a2585d3 --- /dev/null +++ b/test_apps/active_record_42/app/upmin/models/admin_product.rb @@ -0,0 +1,11 @@ +class AdminProduct < Upmin::AdminModel + attributes :name, :short_desc, :price, :manufacturer, :free_shipping + + action :update_price + + def update_price(price) + model.price = price + model.save! + end + +end diff --git a/test_apps/active_record_42/app/upmin/models/admin_shipment.rb b/test_apps/active_record_42/app/upmin/models/admin_shipment.rb new file mode 100644 index 0000000..c6be749 --- /dev/null +++ b/test_apps/active_record_42/app/upmin/models/admin_shipment.rb @@ -0,0 +1,11 @@ +class AdminShipment < Upmin::AdminModel + + attribute :status + actions :update_shipment + + def status + return "TestStatus" + # return Upmin::Widget::ProgressBar.new(model.status, model.tracking_states) + end + +end diff --git a/test_apps/active_record_42/app/upmin/models/admin_user.rb b/test_apps/active_record_42/app/upmin/models/admin_user.rb new file mode 100644 index 0000000..65f8d20 --- /dev/null +++ b/test_apps/active_record_42/app/upmin/models/admin_user.rb @@ -0,0 +1,5 @@ +class AdminUser < Upmin::AdminModel + + action :issue_coupon + +end diff --git a/test_apps/will_paginate/app/upmin/models/admin_product.rb b/test_apps/will_paginate/app/upmin/models/admin_product.rb new file mode 100644 index 0000000..a2585d3 --- /dev/null +++ b/test_apps/will_paginate/app/upmin/models/admin_product.rb @@ -0,0 +1,11 @@ +class AdminProduct < Upmin::AdminModel + attributes :name, :short_desc, :price, :manufacturer, :free_shipping + + action :update_price + + def update_price(price) + model.price = price + model.save! + end + +end diff --git a/test_apps/will_paginate/app/upmin/models/admin_shipment.rb b/test_apps/will_paginate/app/upmin/models/admin_shipment.rb new file mode 100644 index 0000000..c6be749 --- /dev/null +++ b/test_apps/will_paginate/app/upmin/models/admin_shipment.rb @@ -0,0 +1,11 @@ +class AdminShipment < Upmin::AdminModel + + attribute :status + actions :update_shipment + + def status + return "TestStatus" + # return Upmin::Widget::ProgressBar.new(model.status, model.tracking_states) + end + +end diff --git a/test_apps/will_paginate/app/upmin/models/admin_user.rb b/test_apps/will_paginate/app/upmin/models/admin_user.rb new file mode 100644 index 0000000..65f8d20 --- /dev/null +++ b/test_apps/will_paginate/app/upmin/models/admin_user.rb @@ -0,0 +1,5 @@ +class AdminUser < Upmin::AdminModel + + action :issue_coupon + +end diff --git a/seed/names.json b/test_seeders/names.json similarity index 100% rename from seed/names.json rename to test_seeders/names.json diff --git a/seed/order_seeder.rb b/test_seeders/order_seeder.rb similarity index 100% rename from seed/order_seeder.rb rename to test_seeders/order_seeder.rb diff --git a/seed/product_seeder.rb b/test_seeders/product_seeder.rb similarity index 100% rename from seed/product_seeder.rb rename to test_seeders/product_seeder.rb diff --git a/seed/products.json b/test_seeders/products.json similarity index 100% rename from seed/products.json rename to test_seeders/products.json diff --git a/seed/seeder.rb b/test_seeders/seeder.rb similarity index 100% rename from seed/seeder.rb rename to test_seeders/seeder.rb diff --git a/seed/user_seeder.rb b/test_seeders/user_seeder.rb similarity index 100% rename from seed/user_seeder.rb rename to test_seeders/user_seeder.rb From 221a412d453f1f506828b8d33e53b33749790cff Mon Sep 17 00:00:00 2001 From: Jon Calhoun Date: Thu, 25 Sep 2014 08:00:43 -0400 Subject: [PATCH 8/8] removing files that should be git ignored and making colors work. Also dropped AdminModel and Klass and just made Model work --- .gitignore | 1 + Rakefile | 2 +- app/assets/javascripts/upmin/application.js | 1 - app/controllers/upmin/models_controller.rb | 2 +- app/views/layouts/upmin/_navbar.html.haml | 6 +- app/views/upmin/models/dashboard.html.haml | 2 - lib/upmin/admin.rb | 7 +- lib/upmin/admin_model.rb | 255 ------------- lib/upmin/association.rb | 2 +- lib/upmin/klass.rb | 170 --------- lib/upmin/model.rb | 345 +++++++++++++----- lib/upmin/query.rb | 2 +- lib/upmin/railties/active_record.rb | 2 +- lib/upmin/railties/render.rb | 4 +- lib/upmin/version.rb | 2 +- spec/spec_helper.rb | 10 +- test_app_upmin/models/admin_product.rb | 2 +- test_app_upmin/models/admin_shipment.rb | 2 +- test_app_upmin/models/admin_user.rb | 2 +- .../app/upmin/models/admin_product.rb | 11 - .../app/upmin/models/admin_shipment.rb | 11 - .../app/upmin/models/admin_user.rb | 5 - .../app/upmin/models/admin_product.rb | 11 - .../app/upmin/models/admin_shipment.rb | 11 - .../app/upmin/models/admin_user.rb | 5 - .../app/upmin/models/admin_product.rb | 11 - .../app/upmin/models/admin_shipment.rb | 11 - .../app/upmin/models/admin_user.rb | 5 - .../app/upmin/models/admin_product.rb | 11 - .../app/upmin/models/admin_shipment.rb | 11 - .../app/upmin/models/admin_user.rb | 5 - .../app/upmin/models/admin_product.rb | 11 - .../app/upmin/models/admin_shipment.rb | 11 - .../app/upmin/models/admin_user.rb | 5 - 34 files changed, 271 insertions(+), 683 deletions(-) delete mode 100644 lib/upmin/admin_model.rb delete mode 100644 lib/upmin/klass.rb delete mode 100644 test_apps/active_record_32/app/upmin/models/admin_product.rb delete mode 100644 test_apps/active_record_32/app/upmin/models/admin_shipment.rb delete mode 100644 test_apps/active_record_32/app/upmin/models/admin_user.rb delete mode 100644 test_apps/active_record_40/app/upmin/models/admin_product.rb delete mode 100644 test_apps/active_record_40/app/upmin/models/admin_shipment.rb delete mode 100644 test_apps/active_record_40/app/upmin/models/admin_user.rb delete mode 100644 test_apps/active_record_41/app/upmin/models/admin_product.rb delete mode 100644 test_apps/active_record_41/app/upmin/models/admin_shipment.rb delete mode 100644 test_apps/active_record_41/app/upmin/models/admin_user.rb delete mode 100644 test_apps/active_record_42/app/upmin/models/admin_product.rb delete mode 100644 test_apps/active_record_42/app/upmin/models/admin_shipment.rb delete mode 100644 test_apps/active_record_42/app/upmin/models/admin_user.rb delete mode 100644 test_apps/will_paginate/app/upmin/models/admin_product.rb delete mode 100644 test_apps/will_paginate/app/upmin/models/admin_shipment.rb delete mode 100644 test_apps/will_paginate/app/upmin/models/admin_user.rb diff --git a/.gitignore b/.gitignore index 728011c..b8d01f0 100644 --- a/.gitignore +++ b/.gitignore @@ -50,3 +50,4 @@ spec/dummy/.sass-cache test_apps/**/db/migrate/**/*.rb test_apps/**/db/* test_apps/**/spec/**/*.rb +test_apps/**/app/upmin/**/*.rb diff --git a/Rakefile b/Rakefile index 168eef4..9bb7684 100644 --- a/Rakefile +++ b/Rakefile @@ -19,7 +19,7 @@ def update_files sh "cp -R ../../spec spec" sh "cp ../../.rspec .rspec" - # Drop and reloda Upmin::Model files + # Drop and reload Upmin::Model files sh "rm -rf app/upmin/" sh "cp -R ../../test_app_upmin app/upmin" end diff --git a/app/assets/javascripts/upmin/application.js b/app/assets/javascripts/upmin/application.js index d2e1ed3..41d5b67 100644 --- a/app/assets/javascripts/upmin/application.js +++ b/app/assets/javascripts/upmin/application.js @@ -13,7 +13,6 @@ //= require jquery //= require jquery_ujs //= require ./jquery-clockpicker -//= require ./moment //= require ./pikaday //= require ./helpers //= require_tree . diff --git a/app/controllers/upmin/models_controller.rb b/app/controllers/upmin/models_controller.rb index a006b29..c8a34c7 100644 --- a/app/controllers/upmin/models_controller.rb +++ b/app/controllers/upmin/models_controller.rb @@ -109,7 +109,7 @@ def set_model end def set_klass - @klass = Upmin::AdminModel.find_class(params[:klass]) + @klass = Upmin::Model.find_class(params[:klass]) end def set_action diff --git a/app/views/layouts/upmin/_navbar.html.haml b/app/views/layouts/upmin/_navbar.html.haml index a9f56c9..1c86532 100644 --- a/app/views/layouts/upmin/_navbar.html.haml +++ b/app/views/layouts/upmin/_navbar.html.haml @@ -9,7 +9,7 @@ %span.icon-bar #navbar-main.navbar-collapse.collapse %ul.nav.navbar-nav - - Upmin::Klass.all.each do |m| + - Upmin::Model.all.each do |m| %li - %a{href: upmin_search_path(klass: m.name)} - = m.name.pluralize + %a{href: m.search_path} + = m.humanized_name(:plural) diff --git a/app/views/upmin/models/dashboard.html.haml b/app/views/upmin/models/dashboard.html.haml index e279942..032150f 100644 --- a/app/views/upmin/models/dashboard.html.haml +++ b/app/views/upmin/models/dashboard.html.haml @@ -7,5 +7,3 @@ This is embarrassing. :( %h3 ^ Click one of those links up top to use features that are working. ^ - %h5 - = Upmin::AdminModel.all diff --git a/lib/upmin/admin.rb b/lib/upmin/admin.rb index fa15e30..64574d9 100644 --- a/lib/upmin/admin.rb +++ b/lib/upmin/admin.rb @@ -1,18 +1,17 @@ require "upmin" require "upmin/engine" +require "upmin/automatic_delegation" require "upmin/errors" -require "upmin/klass" +require "upmin/paginator" + require "upmin/model" -require "upmin/automatic_delegation" require "upmin/attribute" require "upmin/association" require "upmin/action" require "upmin/parameter" require "upmin/query" -require "upmin/admin_model" -require "upmin/paginator" # Monkey patch code into rails require "upmin/railties/active_record" diff --git a/lib/upmin/admin_model.rb b/lib/upmin/admin_model.rb deleted file mode 100644 index 6bc9961..0000000 --- a/lib/upmin/admin_model.rb +++ /dev/null @@ -1,255 +0,0 @@ -module Upmin - class AdminModel - include Upmin::Engine.routes.url_helpers - include Upmin::AutomaticDelegation - - attr_reader :model - alias_method :object, :model # For delegation - - def initialize(model = nil, options = {}) - if model.is_a?(Hash) - unless model.has_key?(:id) - raise ":id or model instance is required." - end - @model = self.model_class.find(model[:id]) - elsif model.nil? - @model = self.model_class.new - else - @model = model - end - end - - def path - if new_record? - return upmin_new_model_path(klass: model_class_name) - else - return upmin_model_path(klass: model_class_name, id: id) - end - end - - def create_path - return upmin_create_model_path(klass: model_class_name) - end - - def title - return "#{humanized_name(:singular)} # #{id}" - end - - def attributes - return @attributes if defined?(@attributes) - @attributes = [] - self.class.attributes.each do |attr_name| - @attributes << Upmin::Attribute.new(self, attr_name) - end - return @attributes - end - - def associations - return @associations if defined?(@associations) - @associations = [] - self.class.associations.each do |assoc_name| - @associations << Upmin::Association.new(self, assoc_name) - end - return @associations - end - - def actions - return @actions if defined?(@actions) - @actions = [] - self.class.actions.each do |action_name| - @actions << Upmin::Action.new(self, action_name) - end - return @actions - end - - - - ############################################# - ### Delegated instance methods ### - ############################################# - - # TODO(jon): Figure out why delegations here weren't working in 3.2 tests - # delegate(:color, to: :class) - def color - return self.class.color - end - # delegate(:humanized_name, to: :class) - def humanized_name(type = :plural) - return self.class.humanized_name(type) - end - # delegate(:underscore_name, to: :class) - def underscore_name - return self.class.underscore_name - end - # delegate(:model_class, to: :class) - def model_class - return self.class.model_class - end - # delegate(:model_class_name, to: :class) - def model_class_name - return self.class.model_class_name - end - - - - - ############################################# - ### Class methods ### - ############################################# - - def AdminModel.associations - return @associations if defined?(@associations) - - all = [] - ignored = [] - model_class.reflect_on_all_associations.each do |reflection| - all << reflection.name.to_sym - - # We need to remove the ignored later because we don't know the order they come in. - if reflection.is_a?(::ActiveRecord::Reflection::ThroughReflection) - ignored << reflection.options[:through] - end - end - - return @associations = all - ignored - end - - def AdminModel.find_class(model) - return find_or_create_class(model.to_s) - end - - def AdminModel.find_or_create_class(model_name) - ::Rails.application.eager_load! - return "Admin#{model_name}".constantize - rescue NameError - eval("class ::Admin#{model_name} < Upmin::AdminModel; end") - return "Admin#{model_name}".constantize - end - - # Returns all admin models. - def AdminModel.all - return @all if defined?(@all) - @all = [] - Upmin::Klass.models.each do |m| - @all << find_or_create_class(m.name) - end - return @all - end - - def AdminModel.model_class - @model_class ||= inferred_model_class - end - - def AdminModel.model_class? - return model_class - rescue Upmin::UninferrableSourceError - return false - end - - def AdminModel.model_class - return @model_class ||= inferred_model_class - end - - def AdminModel.inferred_model_class - name = model_class_name - return name.constantize - rescue NameError => error - raise if name && !error.missing_name?(name) - raise Upmin::UninferrableSourceError.new(self) - end - - def AdminModel.model_class_name - raise NameError if name.nil? || name.demodulize !~ /Admin.+$/ - return name.demodulize[5..-1] - end - - def AdminModel.humanized_name(type = :plural) - names = model_class_name.split(/(?=[A-Z])/) - if type == :plural - names[names.length-1] = names.last.pluralize - end - return names.join(" ") - end - - def AdminModel.underscore_name(type = :singular) - if type == :singular - return model_class_name.underscore - else - return model_class_name.pluralize.underscore - end - end - - def AdminModel.search_path - return Upmin::Engine.routes.url_helpers.upmin_search_path(klass: model_class_name) - end - - def AdminModel.color - # TODO(jon)[v1.0]: Make colors dynamic again. - return :green - end - - - - ############################################# - ### Customization methods called in ### - ### Admin classes ### - ############################################# - - # Add a single attribute to upmin attributes. - # If this is called before upmin_attributes - # the attributes will not include any defaults - # attributes. - def AdminModel.attribute(attribute = nil) - @extra_attrs = [] unless defined?(@extra_attrs) - @extra_attrs << attribute.to_sym if attribute - end - - # Sets the attributes to the provided attributes # if any are any provided. - # If no attributes are provided then the - # attributes are set to the default attributes of - # the model class. - def AdminModel.attributes(*attributes) - @extra_attrs = [] unless defined?(@extra_attrs) - - if attributes.any? - @attributes = attributes.map{|a| a.to_sym} - end - - @attributes ||= model_class.attribute_names.map{|a| a.to_sym} - return (@attributes + @extra_attrs).uniq - end - - def AdminModel.attribute_type(attribute) - if adapter = model_class.columns_hash[attribute.to_s] - return adapter.type - else - return :unknown - end - end - - # Add a single action to upmin actions. If this is called - # before upmin_actions the actions will not include any defaults - # actions. - def AdminModel.action(action) - @actions ||= [] - - action = action.to_sym - @actions << action unless @actions.include?(action) - end - - # Sets the upmin_actions to the provided actions if any are - # provided. - # If no actions are provided, and upmin_actions hasn't been defined, - # then the upmin_actions are set to the default actions. - # Returns the upmin_actions - def AdminModel.actions(*actions) - if actions.any? - # set the actions - @actions = actions.map{|a| a.to_sym} - end - @actions ||= [] - return @actions - end - - end -end diff --git a/lib/upmin/association.rb b/lib/upmin/association.rb index d6655c0..53ae5e1 100644 --- a/lib/upmin/association.rb +++ b/lib/upmin/association.rb @@ -25,7 +25,7 @@ def upmin_values(options = {}) vals = [value] end - return vals.map{ |m| Upmin::AdminModel.find_class(m.class).new(m) } + return vals.map{ |m| m.upmin_model } end def type diff --git a/lib/upmin/klass.rb b/lib/upmin/klass.rb deleted file mode 100644 index 9556e38..0000000 --- a/lib/upmin/klass.rb +++ /dev/null @@ -1,170 +0,0 @@ -module Upmin - class Klass - - attr_accessor :model - attr_accessor :color - - def initialize(model, options = {}) - self.model = model - - if options[:color] - self.color = options[:color] - end - end - - def new(*args) - m = model.new(*args) - return Upmin::AdminModel.new(m) - end - - # Exposing a model method, but wrapping the result in - # an Upmin::Model - def find(*args) - return Upmin::AdminModel.new(model.find(*args)) - end - - def ransack(*args) - return model.ransack(*args) - end - - - # Returns all of the upmin attributes for the ActiveRecord model - # referenced by this Klass object. - def attributes - return model.upmin_attributes - end - - # Returns the type for an attribute by checking the columns - # hash. If no match can be found, :unknown is returned. - # NOTE - the Upmin::Model version of this will look at the - # actual contents of the attr_name if :unknown is returned, - # so this version is more accurate if you can use it. - def attribute_type(attr_name) - if connection_adapter = model.columns_hash[attr_name.to_s] - return connection_adapter.type - else - return :unknown - end - end - - - # Returns all of the upmin actions for the ActiveRecord model - # referenced by this Klass object. - def actions - return model.upmin_actions - end - - # Returns all associations that are not used in through associations - # eg - an Order's products, but not an order's product_orders that link the two. - def associations - return @associations if defined?(@associations) - - all = [] - ignored = [] - model.reflect_on_all_associations.each do |reflection| - all << reflection.name.to_sym - - # We need to remove the ignored later because we don't know the order they come in. - if reflection.is_a?(::ActiveRecord::Reflection::ThroughReflection) - ignored << reflection.options[:through] - end - end - - return @associations = all - ignored - end - - # Tries to find an association type based on the reflection - def association_type(assoc_name) - reflection = reflections.select { |r| r.name == assoc_name.to_sym }.first - - if reflection - return reflection.foreign_type.to_s.gsub(/_type$/, "").pluralize.to_sym - else - return :unknown - end - end - - def plural_associations - return model.reflect_on_all_associations - .select{ |r| r.collection? } - .map{ |r| r.name.to_sym } - end - - def reflections - return model.reflect_on_all_associations - end - - - - # ## Methods for prettying up things to display them in views etc. - - # # Returns the class name, split at camelCase, - # # with the last word pluralized if it is plural. - # def humanized_name(type = :plural) - # names = model.name.split(/(?=[A-Z])/) - # if type == :plural - # names[names.length-1] = names.last.pluralize - # end - # return names.join(" ") - # end - - # Returns the class name, capitalized as it would be with User.name or OrderShipment.name - "User", or "OrderShipment" - def name - return model.name - end - - def path_hash - return { - klass: klass.name - } - end - - - - ## Class Methods - - # Takes a Rails ActiveRecord or the name of one and returns an - # Upmin::Klass instance of the model. - def Klass.find(model) - return all.select{|k| k.name == model.to_s}.first - end - - # Returns an array of all Klass instances - def Klass.all - return @all if defined?(@all) - all = [] - - models.each_with_index do |model, i| - klass = Klass.new(model, color: colors[i % colors.length]) - all << klass - end - - return @all = all - end - - def Klass.colors - return [ - :light_blue, - :blue_green, - :red, - :yellow, - :orange, - :purple, - :dark_blue, - :dark_red, - :green - ] - end - - def Klass.models - # If Rails - ::Rails.application.eager_load! - rails_models = ::ActiveRecord::Base.descendants.select do |m| - m.to_s != "ActiveRecord::SchemaMigration" - end - - return rails_models - end - - end -end diff --git a/lib/upmin/model.rb b/lib/upmin/model.rb index f5e7307..220a909 100644 --- a/lib/upmin/model.rb +++ b/lib/upmin/model.rb @@ -1,137 +1,288 @@ module Upmin class Model + include Upmin::Engine.routes.url_helpers + include Upmin::AutomaticDelegation - attr_accessor :instance - attr_accessor :klass + attr_reader :model + alias_method :object, :model # For delegation - def initialize(instance, options = {}) - self.instance = instance - self.klass = Upmin::Klass.find(instance.class.name) + def initialize(model = nil, options = {}) + if model.is_a?(Hash) + unless model.has_key?(:id) + raise ":id or model instance is required." + end + @model = self.model_class.find(model[:id]) + elsif model.nil? + @model = self.model_class.new + else + @model = model + end + end + + def path + if new_record? + return upmin_new_model_path(klass: model_class_name) + else + return upmin_model_path(klass: model_class_name, id: id) + end + end + + def create_path + return upmin_create_model_path(klass: model_class_name) end - ## Methods for rendering in views def title - return "#{klass.humanized_name(:singular)} # #{instance.id}" + return "#{humanized_name(:singular)} # #{id}" + end + + def attributes + return @attributes if defined?(@attributes) + @attributes = [] + self.class.attributes.each do |attr_name| + @attributes << Upmin::Attribute.new(self, attr_name) + end + return @attributes + end + + def associations + return @associations if defined?(@associations) + @associations = [] + self.class.associations.each do |assoc_name| + @associations << Upmin::Association.new(self, assoc_name) + end + return @associations + end + + def actions + return @actions if defined?(@actions) + @actions = [] + self.class.actions.each do |action_name| + @actions << Upmin::Action.new(self, action_name) + end + return @actions end + + + ########################################################### + ### Delegated instance methods ### + ########################################################### + + # TODO(jon): Figure out why delegations here weren't working in 3.2 tests + # delegate(:color, to: :class) def color - return klass.color - end - - def path_hash - return { - klass: klass.name, - id: instance.id - } - end - - def new_record? - return instance.new_record? - end - - ## Methods for getting attributes, associations, etc and anything relevant to them. - - - # Returns the type of an attribute. If it is nil and we can't - # figure it out from the db columns we just fall back to - # :unknown - def attribute_type(attr_name) - type = klass.attribute_type(attr_name) - - if type == :unknown - # See if we can deduce it by looking at the data - data = attribute(attr_name) - class_sym = data.class.to_s.underscore.to_sym - if class_sym == :false_class || class_sym == :true_class - type = :boolean - elsif class_sym == :nil_class - type = :unknown - elsif class_sym == :fixnum - type = :integer - elsif class_sym == :big_decimal - type = :decimal - elsif class_sym == :"active_support/time_with_zone" - type = :datetime - else - # This should prevent any classes from being skipped, but we may not have an exhaustive list yet. - type = class_sym + return self.class.color + end + # delegate(:humanized_name, to: :class) + def humanized_name(type = :plural) + return self.class.humanized_name(type) + end + # delegate(:underscore_name, to: :class) + def underscore_name + return self.class.underscore_name + end + # delegate(:model_class, to: :class) + def model_class + return self.class.model_class + end + # delegate(:model_class_name, to: :class) + def model_class_name + return self.class.model_class_name + end + + + + + ########################################################### + ### Class methods ### + ########################################################### + + def Model.associations + return @associations if defined?(@associations) + + all = [] + ignored = [] + model_class.reflect_on_all_associations.each do |reflection| + all << reflection.name.to_sym + + # We need to remove the ignored later because we don't know the order they come in. + if reflection.is_a?(::ActiveRecord::Reflection::ThroughReflection) + ignored << reflection.options[:through] end end - return type + return @associations = all - ignored + end + + def Model.find_class(model) + return find_or_create_class(model.to_s) + end + + def Model.find_or_create_class(model_name) + ::Rails.application.eager_load! + return "Admin#{model_name}".constantize + rescue NameError + eval("class ::Admin#{model_name} < Upmin::Model; end") + return "Admin#{model_name}".constantize + end + + # Returns all admin models. + def Model.all + return @all if defined?(@all) + @all = [] + all_models.each do |m| + @all << find_or_create_class(m.name) + end + return @all + end + + def Model.all_models + # If Rails + ::Rails.application.eager_load! + rails_models = ::ActiveRecord::Base.descendants.select do |m| + m.to_s != "ActiveRecord::SchemaMigration" + end + + return rails_models end - # Returns whether or not the attr_name is an attribute that can be edited. - def attribute_editable?(attr_name) - attr_name = attr_name.to_sym - return false if attr_name == :id - return false if attr_name == :created_at - return false if attr_name == :updated_at - # TODO(jon): Add a way to declare which attributes are editable and which are not later. - return instance.respond_to?("#{attr_name}=") + def Model.model_class + @model_class ||= inferred_model_class end - # Returns the value of the attr_name method - def attribute(attr_name) - attr_name = attr_name.to_sym - # TODO(jon): Add some way to handle exceptions. Probably a custom error that we display. - return instance.send(attr_name) + def Model.model_class? + return model_class + rescue Upmin::UninferrableSourceError + return false end - def attribute_form_id(attr_name) - return "#{klass.name.underscore}_#{attr_name}" + def Model.model_class + return @model_class ||= inferred_model_class end - def attribute_label_name(attr_name) - return attr_name.to_s.gsub(/_/, " ").capitalize + def Model.inferred_model_class + name = model_class_name + return name.constantize + rescue NameError => error + raise if name && !error.missing_name?(name) + raise Upmin::UninferrableSourceError.new(self) end + def Model.model_class_name + raise NameError if name.nil? || name.demodulize !~ /Admin.+$/ + return name.demodulize[5..-1] + end - # Returns the type of an association. If we can't figure it - # out we fall back to :unknown - def association_type(assoc_name) - type = klass.association_type(assoc_name) - if type == :unknown && data = association(assoc_name).first - type = data.class.name.underscore + def Model.humanized_name(type = :plural) + names = model_class_name.split(/(?=[A-Z])/) + if type == :plural + names[names.length-1] = names.last.pluralize end - return type + return names.join(" ") end - def association(assoc_name, options = {}) - association = instance.send(assoc_name) - if association.respond_to?(:each) - # We have a collection, at least we hope we do. - if options[:limit] && association.respond_to?(:limit) - association = association.limit(5) - end + def Model.underscore_name(type = :singular) + if type == :singular + return model_class_name.underscore + else + return model_class_name.pluralize.underscore end - return association end - def action_parameters(action) - instance.method(action).parameters + def Model.search_path + return Upmin::Engine.routes.url_helpers.upmin_search_path(klass: model_class_name) + end + + def Model.color + return @color if defined?(@color) + @color = Model.next_color + return @color + end + + def Model.next_color + @color_index ||= 0 + next_color = colors[@color_index] + @color_index = (@color_index + 1) % colors.length + return next_color end - def perform_action(action, arguments) - unless klass.actions.include?(action.to_sym) - raise "Invalid action: #{action}" + def Model.colors(*colors) + @colors = colors if colors.any? + @colors ||= [ + :light_blue, + :blue_green, + :red, + :yellow, + :orange, + :purple, + :dark_blue, + :dark_red, + :green + ] + return @colors + end + + + + ########################################################### + ### Customization methods for Admin classes ### + ########################################################### + + # Add a single attribute to upmin attributes. + # If this is called before upmin_attributes + # the attributes will not include any defaults + # attributes. + def Model.attribute(attribute = nil) + @extra_attrs = [] unless defined?(@extra_attrs) + @extra_attrs << attribute.to_sym if attribute + end + + # Sets the attributes to the provided attributes # if any are any provided. + # If no attributes are provided then the + # attributes are set to the default attributes of + # the model class. + def Model.attributes(*attributes) + @extra_attrs = [] unless defined?(@extra_attrs) + + if attributes.any? + @attributes = attributes.map{|a| a.to_sym} end - params = action_parameters(action) - params_array = [] - params.each do |param_type, param_name| - if param_type == :req - raise "Missing argument: #{param_name}" unless arguments[param_name] - params_array << arguments[param_name] - elsif param_type == :opt - params_array << arguments[param_name] if arguments[param_name] - else # :block or ?? - next - end + @attributes ||= model_class.attribute_names.map{|a| a.to_sym} + return (@attributes + @extra_attrs).uniq + end + + def Model.attribute_type(attribute) + if adapter = model_class.columns_hash[attribute.to_s] + return adapter.type + else + return :unknown end - return instance.send(action, *params_array) end + # Add a single action to upmin actions. If this is called + # before upmin_actions the actions will not include any defaults + # actions. + def Model.action(action) + @actions ||= [] + + action = action.to_sym + @actions << action unless @actions.include?(action) + end + + # Sets the upmin_actions to the provided actions if any are + # provided. + # If no actions are provided, and upmin_actions hasn't been defined, + # then the upmin_actions are set to the default actions. + # Returns the upmin_actions + def Model.actions(*actions) + if actions.any? + # set the actions + @actions = actions.map{|a| a.to_sym} + end + @actions ||= [] + return @actions + end end end diff --git a/lib/upmin/query.rb b/lib/upmin/query.rb index 52c140b..2a7f16d 100644 --- a/lib/upmin/query.rb +++ b/lib/upmin/query.rb @@ -32,7 +32,7 @@ def paginated_results def upmin_results return @upmin_results if defined?(@upmin_results) - @upmin_results = paginated_results.map{ |r| Upmin::AdminModel.find_class(r.class).new(r) } + @upmin_results = paginated_results.map{ |r| r.upmin_model } return @upmin_results end diff --git a/lib/upmin/railties/active_record.rb b/lib/upmin/railties/active_record.rb index fa1de9a..5d8580d 100644 --- a/lib/upmin/railties/active_record.rb +++ b/lib/upmin/railties/active_record.rb @@ -5,7 +5,7 @@ module ActiveRecord extend ::ActiveSupport::Concern def upmin_model - klass = Upmin::AdminModel.find_class(self.class) + klass = Upmin::Model.find_class(self.class) return klass.new(self) end diff --git a/lib/upmin/railties/render.rb b/lib/upmin/railties/render.rb index c2a348d..51e72a1 100644 --- a/lib/upmin/railties/render.rb +++ b/lib/upmin/railties/render.rb @@ -4,7 +4,7 @@ module Render # Render method that is used by upmin-admin. Tries to render partials in order, passing data in as the :object, along with options. def up_render(data, options = {}) - if data.is_a?(Upmin::AdminModel) + if data.is_a?(Upmin::Model) options = RenderHelpers.model_options(data, options) partials = RenderHelpers.model_partials(data, options) @@ -28,7 +28,7 @@ def up_render(data, options = {}) options = RenderHelpers.search_results_options(data, options) partials = RenderHelpers.search_results_partials(data, options) - elsif Upmin::AdminModel.all.include?(data) + elsif Upmin::Model.all.include?(data) # Probably rendering a search box options = RenderHelpers.search_box_options(data, options) partials = RenderHelpers.search_box_partials(data, options) diff --git a/lib/upmin/version.rb b/lib/upmin/version.rb index 1bdb97d..529026c 100644 --- a/lib/upmin/version.rb +++ b/lib/upmin/version.rb @@ -1,3 +1,3 @@ module Upmin - VERSION = "0.0.39dev10000093" + VERSION = "0.0.39dev10000094" end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 62ddaff..9a9cbbf 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -41,11 +41,11 @@ end # Uncomment this if you want to the page to be saved and opened after any test failure. - config.after do |example| - if example.metadata[:type] == :feature && example.exception.present? - save_and_open_page - end - end + # config.after do |example| + # if example.metadata[:type] == :feature && example.exception.present? + # save_and_open_page + # end + # end config.include(FactoryGirl::Syntax::Methods) config.include(ActionView::Helpers::NumberHelper, type: :view) diff --git a/test_app_upmin/models/admin_product.rb b/test_app_upmin/models/admin_product.rb index a2585d3..719c0ee 100644 --- a/test_app_upmin/models/admin_product.rb +++ b/test_app_upmin/models/admin_product.rb @@ -1,4 +1,4 @@ -class AdminProduct < Upmin::AdminModel +class AdminProduct < Upmin::Model attributes :name, :short_desc, :price, :manufacturer, :free_shipping action :update_price diff --git a/test_app_upmin/models/admin_shipment.rb b/test_app_upmin/models/admin_shipment.rb index c6be749..26b7416 100644 --- a/test_app_upmin/models/admin_shipment.rb +++ b/test_app_upmin/models/admin_shipment.rb @@ -1,4 +1,4 @@ -class AdminShipment < Upmin::AdminModel +class AdminShipment < Upmin::Model attribute :status actions :update_shipment diff --git a/test_app_upmin/models/admin_user.rb b/test_app_upmin/models/admin_user.rb index 65f8d20..e5455bd 100644 --- a/test_app_upmin/models/admin_user.rb +++ b/test_app_upmin/models/admin_user.rb @@ -1,4 +1,4 @@ -class AdminUser < Upmin::AdminModel +class AdminUser < Upmin::Model action :issue_coupon diff --git a/test_apps/active_record_32/app/upmin/models/admin_product.rb b/test_apps/active_record_32/app/upmin/models/admin_product.rb deleted file mode 100644 index a2585d3..0000000 --- a/test_apps/active_record_32/app/upmin/models/admin_product.rb +++ /dev/null @@ -1,11 +0,0 @@ -class AdminProduct < Upmin::AdminModel - attributes :name, :short_desc, :price, :manufacturer, :free_shipping - - action :update_price - - def update_price(price) - model.price = price - model.save! - end - -end diff --git a/test_apps/active_record_32/app/upmin/models/admin_shipment.rb b/test_apps/active_record_32/app/upmin/models/admin_shipment.rb deleted file mode 100644 index c6be749..0000000 --- a/test_apps/active_record_32/app/upmin/models/admin_shipment.rb +++ /dev/null @@ -1,11 +0,0 @@ -class AdminShipment < Upmin::AdminModel - - attribute :status - actions :update_shipment - - def status - return "TestStatus" - # return Upmin::Widget::ProgressBar.new(model.status, model.tracking_states) - end - -end diff --git a/test_apps/active_record_32/app/upmin/models/admin_user.rb b/test_apps/active_record_32/app/upmin/models/admin_user.rb deleted file mode 100644 index 65f8d20..0000000 --- a/test_apps/active_record_32/app/upmin/models/admin_user.rb +++ /dev/null @@ -1,5 +0,0 @@ -class AdminUser < Upmin::AdminModel - - action :issue_coupon - -end diff --git a/test_apps/active_record_40/app/upmin/models/admin_product.rb b/test_apps/active_record_40/app/upmin/models/admin_product.rb deleted file mode 100644 index a2585d3..0000000 --- a/test_apps/active_record_40/app/upmin/models/admin_product.rb +++ /dev/null @@ -1,11 +0,0 @@ -class AdminProduct < Upmin::AdminModel - attributes :name, :short_desc, :price, :manufacturer, :free_shipping - - action :update_price - - def update_price(price) - model.price = price - model.save! - end - -end diff --git a/test_apps/active_record_40/app/upmin/models/admin_shipment.rb b/test_apps/active_record_40/app/upmin/models/admin_shipment.rb deleted file mode 100644 index c6be749..0000000 --- a/test_apps/active_record_40/app/upmin/models/admin_shipment.rb +++ /dev/null @@ -1,11 +0,0 @@ -class AdminShipment < Upmin::AdminModel - - attribute :status - actions :update_shipment - - def status - return "TestStatus" - # return Upmin::Widget::ProgressBar.new(model.status, model.tracking_states) - end - -end diff --git a/test_apps/active_record_40/app/upmin/models/admin_user.rb b/test_apps/active_record_40/app/upmin/models/admin_user.rb deleted file mode 100644 index 65f8d20..0000000 --- a/test_apps/active_record_40/app/upmin/models/admin_user.rb +++ /dev/null @@ -1,5 +0,0 @@ -class AdminUser < Upmin::AdminModel - - action :issue_coupon - -end diff --git a/test_apps/active_record_41/app/upmin/models/admin_product.rb b/test_apps/active_record_41/app/upmin/models/admin_product.rb deleted file mode 100644 index a2585d3..0000000 --- a/test_apps/active_record_41/app/upmin/models/admin_product.rb +++ /dev/null @@ -1,11 +0,0 @@ -class AdminProduct < Upmin::AdminModel - attributes :name, :short_desc, :price, :manufacturer, :free_shipping - - action :update_price - - def update_price(price) - model.price = price - model.save! - end - -end diff --git a/test_apps/active_record_41/app/upmin/models/admin_shipment.rb b/test_apps/active_record_41/app/upmin/models/admin_shipment.rb deleted file mode 100644 index c6be749..0000000 --- a/test_apps/active_record_41/app/upmin/models/admin_shipment.rb +++ /dev/null @@ -1,11 +0,0 @@ -class AdminShipment < Upmin::AdminModel - - attribute :status - actions :update_shipment - - def status - return "TestStatus" - # return Upmin::Widget::ProgressBar.new(model.status, model.tracking_states) - end - -end diff --git a/test_apps/active_record_41/app/upmin/models/admin_user.rb b/test_apps/active_record_41/app/upmin/models/admin_user.rb deleted file mode 100644 index 65f8d20..0000000 --- a/test_apps/active_record_41/app/upmin/models/admin_user.rb +++ /dev/null @@ -1,5 +0,0 @@ -class AdminUser < Upmin::AdminModel - - action :issue_coupon - -end diff --git a/test_apps/active_record_42/app/upmin/models/admin_product.rb b/test_apps/active_record_42/app/upmin/models/admin_product.rb deleted file mode 100644 index a2585d3..0000000 --- a/test_apps/active_record_42/app/upmin/models/admin_product.rb +++ /dev/null @@ -1,11 +0,0 @@ -class AdminProduct < Upmin::AdminModel - attributes :name, :short_desc, :price, :manufacturer, :free_shipping - - action :update_price - - def update_price(price) - model.price = price - model.save! - end - -end diff --git a/test_apps/active_record_42/app/upmin/models/admin_shipment.rb b/test_apps/active_record_42/app/upmin/models/admin_shipment.rb deleted file mode 100644 index c6be749..0000000 --- a/test_apps/active_record_42/app/upmin/models/admin_shipment.rb +++ /dev/null @@ -1,11 +0,0 @@ -class AdminShipment < Upmin::AdminModel - - attribute :status - actions :update_shipment - - def status - return "TestStatus" - # return Upmin::Widget::ProgressBar.new(model.status, model.tracking_states) - end - -end diff --git a/test_apps/active_record_42/app/upmin/models/admin_user.rb b/test_apps/active_record_42/app/upmin/models/admin_user.rb deleted file mode 100644 index 65f8d20..0000000 --- a/test_apps/active_record_42/app/upmin/models/admin_user.rb +++ /dev/null @@ -1,5 +0,0 @@ -class AdminUser < Upmin::AdminModel - - action :issue_coupon - -end diff --git a/test_apps/will_paginate/app/upmin/models/admin_product.rb b/test_apps/will_paginate/app/upmin/models/admin_product.rb deleted file mode 100644 index a2585d3..0000000 --- a/test_apps/will_paginate/app/upmin/models/admin_product.rb +++ /dev/null @@ -1,11 +0,0 @@ -class AdminProduct < Upmin::AdminModel - attributes :name, :short_desc, :price, :manufacturer, :free_shipping - - action :update_price - - def update_price(price) - model.price = price - model.save! - end - -end diff --git a/test_apps/will_paginate/app/upmin/models/admin_shipment.rb b/test_apps/will_paginate/app/upmin/models/admin_shipment.rb deleted file mode 100644 index c6be749..0000000 --- a/test_apps/will_paginate/app/upmin/models/admin_shipment.rb +++ /dev/null @@ -1,11 +0,0 @@ -class AdminShipment < Upmin::AdminModel - - attribute :status - actions :update_shipment - - def status - return "TestStatus" - # return Upmin::Widget::ProgressBar.new(model.status, model.tracking_states) - end - -end diff --git a/test_apps/will_paginate/app/upmin/models/admin_user.rb b/test_apps/will_paginate/app/upmin/models/admin_user.rb deleted file mode 100644 index 65f8d20..0000000 --- a/test_apps/will_paginate/app/upmin/models/admin_user.rb +++ /dev/null @@ -1,5 +0,0 @@ -class AdminUser < Upmin::AdminModel - - action :issue_coupon - -end