CSC/ECE 517 Spring 2014/oss E1401 lmn: Difference between revisions

From Expertiza_Wiki
Jump to navigation Jump to search
(→‎Future Work: struck out things completed in final projects)
 
(5 intermediate revisions by 2 users not shown)
Line 9: Line 9:
* Add the ability to sort Due Dates
* Add the ability to sort Due Dates
====Classes====
====Classes====
Changed existent classes
* controllers/assignments_controller.rb
* controllers/assignments_controller.rb
* controllers/due_date_controller.rb
* controllers/sign_up_sheet_controller.rb
* models/due_date.rb
* models/due_date.rb
* views/assignments/new.html.erb
* models/sign_up_topic.rb
* views/assignments/edit.html.erb
* views/assignments/edit/_add_signup_topics.html.erb
* views/assignments/edit/_due_dates.html.erb
* views/assignments/edit/_general.html.erb
* views/assignments/edit/_rubrics.html.erb
 
Added classes
* models/AssignmentFormObject.rb
* controllers/assignment_form_object_controller.rb
* helpers/assignment_form_object_helper.rb
* views/assignment_form_object/new.erb
* views/assignments/new/_add_signup_topics.html.erb
* views/assignments/new/_due_dates.html.erb
* views/assignments/new/_general.html.erb
* views/assignments/new/_late_policy.html.erb
* views/assignments/new/_review_strategy.html.erb
* views/assignments/new/_rubrics.html.erb


====Objectives====
====Objectives====
Line 35: Line 19:
* Assignment, Due Date, and Topic views and controllers should be subsumed into a combined AssignmentFormObject view and controller
* Assignment, Due Date, and Topic views and controllers should be subsumed into a combined AssignmentFormObject view and controller
==Design and Implementation==
==Design and Implementation==
As a bit of background, form objects[http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/] could be thought of as a fake model that is not itself persisted but persists all its component models. A form object acts like a model in that it can have validations, have persistence strategies, and return whether or not it is persisted. Behind the scenes it is intelligently manipulating other models to provide this functionality transparently, encapsulating the required logic.
As a bit of background, form objects<ref>http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/</ref> could be thought of as a fake model that is not itself persisted but persists all its component models. The name comes from the idea that in many cases forms require information for several models at a time, rather than just one. Creating a series of forms to work with each model at a time is tedious and frustrating, both for the coder and for the end user. A form object fixes this problem by acting like a model. It can have validations, persistence strategies, and a save method that returns the object if and when it is saved, just like a model. Behind the scenes it is intelligently manipulating other models to provide this functionality transparently, encapsulating the required logic.


Our design uses one form object, AssignmentFormObject, to store data about the base Assignment model and Due Date and Topic models associated to that Assignment. In this way we can achieve both objectives, utilizing the form object to create, update, and delete Assignments and associated Due Date and Topics without having to have separate controllers and views for each.
Our design uses one form object, AssignmentFormObject, to store data about the base Assignment model, Due Date, and Topic models associated to that Assignment. In this way we can achieve both objectives, utilizing the form object to create, update, and delete Assignments and associated Due Date and Topics without having to have separate controllers and views for each.


Without knowing anything about form objects, it is easy to tell what is going on when you attempt to persist an AssignmentFormObject:
The form object inherits from ActiveRecord (though is not a subclass of) in order to be able to mass-assign attributes and create custom validations and Virtus<ref>https://github.com/solnic/virtus</ref> to handle attributes like a normal model. We need these custom validations because the attributes of the form object include arrays of attributes for models, something the default validations cannot handle on their own. In addition, we need to run validations on the parameters passed in for each of the models contained within the form object, again something that the default validations cannot do.
 
The logic behind persisting a form object is somewhat more complicated than a normal model (in which you very rarely have to override the default save operation). This is because form objects must persist several models at once, as shown below.
  <nowiki>Assignment.transaction do
  <nowiki>Assignment.transaction do
       if @assignment.save
       if @assignment.save
Line 56: Line 42:
           end
           end
         end</nowiki>
         end</nowiki>
The form object starts a transaction (in order to guarantee that either everything or nothing is saved) then attempts to create (or update) the Assignment. If this succeeds, it moves on to the components of the Assignment, doing the same. These inputs are passed to the form object previously, using Virtus[https://github.com/solnic/virtus] to create validations on the attributes passed.
The form object starts a transaction (in order to guarantee that either everything or nothing is saved) then attempts to create (or update) the Assignment. If this succeeds, it moves on to the components of the Assignment, doing the same. If any one of these operations fails (for instance, if a topic is not able to be saved), the entire operation is rolled back, removing all of the partially saved changes and reverting to before the form object attempted to persist. In this way we make sure that the save operation is atomic like a normal model's save operation.


Because the form object can be treated like a model, writing a controller to populate views with it is just as easy as with a normal model, as shown.
Because the form object can be treated like a model, writing a controller to populate views with it is just as easy as with a normal model, as shown.
Line 69: Line 55:
   end
   end
</nowiki>
</nowiki>
===Changes to the View===
Basically, we have added the ability to add most of the same attributes to a new assignment as when editing an existing assignment. Previously, you could only
provide an assignment name, then you had to add due dates, etc. There are still some limitations to this process due to the complicated relationships between
models. To view the changes, navigate to "~/assignments/new". The addition of Rubrics and Review Strategies is currently not implemented for new assignments, and
this should be addressed in future works. In addition, you may only add one topic to a new assignment, then you must add more topics at the edit page.
The addition of due dates uses a partial view that is in need of refactoring. In addition some validation is necessary for date times. For submission due dates
and review due dates, please provide a date time.
===Affected Classes===
Changed existent classes
* controllers/assignments_controller.rb
* models/due_date.rb
* views/assignments/new.html.erb
* views/assignments/edit.html.erb
* views/assignments/edit/_add_signup_topics.html.erb
* views/assignments/edit/_due_dates.html.erb
* views/assignments/edit/_general.html.erb
* views/assignments/edit/_rubrics.html.erb
Added classes
* models/AssignmentFormObject.rb
* controllers/assignment_form_object_controller.rb
* helpers/assignment_form_object_helper.rb
* views/assignment_form_object/new.erb
* views/assignments/new/_add_signup_topics.html.erb
* views/assignments/new/_due_dates.html.erb
* views/assignments/new/_general.html.erb
* views/assignments/new/_late_policy.html.erb
* views/assignments/new/_review_strategy.html.erb
* views/assignments/new/_rubrics.html.erb
==Running the Project Locally==
In order to run the project locally (for feature testing or for running unit tests) please follow
these steps:
1. Clone the GitHub repository (https://github.com/losmescaleros/expertiza)
2. Run "bundle install"
3. Run "rake db:create:all"
4. If you have a database dump, run it. For this project, we used the one located here:
http://courses.ncsu.edu/csc517/common/homework/OSS/expertiza_scrubbed_2014_03_14.sql.zip
"mysql -u root pg_development < expertiza-scrubbed.sql"
Note that this may not be the name of the zipped dump, but you can customize to your needs. You may
also need to specify your mysql password using "mysql -u root -p ..." depending on your mysql setup.
5. Run "exec rake db:migrate"
6. Run the rails server using "rails server -p 3000". You may substitute 3000 with a port number of
your choice.
===Running Tests===
For this project, we used RSpec for unit tests. These are located at spec/models/assignment_form_object_spec.rb. The tests
are primarily unit tests for the model, and it required a lot of testing of other models that were not well tested (or tested
at all) elsewhere.
==Future Work==
==Future Work==
As with every project there are always more things to be done. With the use of a form object there are side benefits that we can take advantage of with more effort that we just didn't have time for. There are also some problematic areas we uncovered during the project that could use cleaning up. The following additional steps (or features) should be taken (or implemented) by anyone continuing with the project.
Struck out lines indicate features that have already been added in continuing work.
* <del>Ability to add multiple sign up topics from a new form</del>
* Ability to add rubric details from a new form
* Redo the due dates partial to be more readable and use rails conventions rather than JavaScript
* Redo the due dates partial in general, it is very messy and difficult to work with
* Relocate JavaScript scripts to another.js resource file rather than in-line in the html
* <del>Eliminate the need to have multiple calls to the same controller methods for every table entry for due dates on the edit pages</del>
==References==
<references/>

Latest revision as of 13:18, 5 May 2014

Introduction

Background

Assignments in Expertiza have many components--due dates and topics, for example. Due dates and topics are objects in their own right (DueDate, SignupTopic); we need a way to have sets of due dates and topics associated with an assignment. There are also separate forms to create and edit an Assignment and its components (Topics, Due Dates), these forms should be unified into one form for every component of an Assignment. Also, it'd be nice if we could sort assignment due dates in order of next due.

What Needs to be Done

  • Remove the separate New and Edit forms for assignment
  • Create a form object to encapsulate the creation and editing of Assignments, Due Dates, and Topics
  • Add the ability to sort Due Dates

Classes

  • controllers/assignments_controller.rb
  • controllers/due_date_controller.rb
  • controllers/sign_up_sheet_controller.rb
  • models/due_date.rb
  • models/sign_up_topic.rb

Objectives

  • All CUD (Create, Update, Delete) operations on Assignments, Due Dates, and Topics should be performed through an AssignmentFormObject
  • Assignment, Due Date, and Topic views and controllers should be subsumed into a combined AssignmentFormObject view and controller

Design and Implementation

As a bit of background, form objects<ref>http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/</ref> could be thought of as a fake model that is not itself persisted but persists all its component models. The name comes from the idea that in many cases forms require information for several models at a time, rather than just one. Creating a series of forms to work with each model at a time is tedious and frustrating, both for the coder and for the end user. A form object fixes this problem by acting like a model. It can have validations, persistence strategies, and a save method that returns the object if and when it is saved, just like a model. Behind the scenes it is intelligently manipulating other models to provide this functionality transparently, encapsulating the required logic.

Our design uses one form object, AssignmentFormObject, to store data about the base Assignment model, Due Date, and Topic models associated to that Assignment. In this way we can achieve both objectives, utilizing the form object to create, update, and delete Assignments and associated Due Date and Topics without having to have separate controllers and views for each.

The form object inherits from ActiveRecord (though is not a subclass of) in order to be able to mass-assign attributes and create custom validations and Virtus<ref>https://github.com/solnic/virtus</ref> to handle attributes like a normal model. We need these custom validations because the attributes of the form object include arrays of attributes for models, something the default validations cannot handle on their own. In addition, we need to run validations on the parameters passed in for each of the models contained within the form object, again something that the default validations cannot do.

The logic behind persisting a form object is somewhat more complicated than a normal model (in which you very rarely have to override the default save operation). This is because form objects must persist several models at once, as shown below.

Assignment.transaction do
      if @assignment.save

        topics_list.each do |a|
          a.assignment = @assignment
          if !a.save
            raise ActiveRecord::Rollback
          end
        end

        due_dates_list.each do |a|
          a.assignment = @assignment
          if !a.save
            raise ActiveRecord::Rollback
          end
        end

The form object starts a transaction (in order to guarantee that either everything or nothing is saved) then attempts to create (or update) the Assignment. If this succeeds, it moves on to the components of the Assignment, doing the same. If any one of these operations fails (for instance, if a topic is not able to be saved), the entire operation is rolled back, removing all of the partially saved changes and reverting to before the form object attempted to persist. In this way we make sure that the save operation is atomic like a normal model's save operation.

Because the form object can be treated like a model, writing a controller to populate views with it is just as easy as with a normal model, as shown.

  def create
    @assignment_form_object = AssignmentFormObject.new(params)
    if @assignment_form_object.save
      alert("Form saved")
    else
      alert("Error saving form")
    end
  end

Changes to the View

Basically, we have added the ability to add most of the same attributes to a new assignment as when editing an existing assignment. Previously, you could only provide an assignment name, then you had to add due dates, etc. There are still some limitations to this process due to the complicated relationships between models. To view the changes, navigate to "~/assignments/new". The addition of Rubrics and Review Strategies is currently not implemented for new assignments, and this should be addressed in future works. In addition, you may only add one topic to a new assignment, then you must add more topics at the edit page. The addition of due dates uses a partial view that is in need of refactoring. In addition some validation is necessary for date times. For submission due dates and review due dates, please provide a date time.

Affected Classes

Changed existent classes

  • controllers/assignments_controller.rb
  • models/due_date.rb
  • views/assignments/new.html.erb
  • views/assignments/edit.html.erb
  • views/assignments/edit/_add_signup_topics.html.erb
  • views/assignments/edit/_due_dates.html.erb
  • views/assignments/edit/_general.html.erb
  • views/assignments/edit/_rubrics.html.erb

Added classes

  • models/AssignmentFormObject.rb
  • controllers/assignment_form_object_controller.rb
  • helpers/assignment_form_object_helper.rb
  • views/assignment_form_object/new.erb
  • views/assignments/new/_add_signup_topics.html.erb
  • views/assignments/new/_due_dates.html.erb
  • views/assignments/new/_general.html.erb
  • views/assignments/new/_late_policy.html.erb
  • views/assignments/new/_review_strategy.html.erb
  • views/assignments/new/_rubrics.html.erb

Running the Project Locally

In order to run the project locally (for feature testing or for running unit tests) please follow these steps: 1. Clone the GitHub repository (https://github.com/losmescaleros/expertiza) 2. Run "bundle install" 3. Run "rake db:create:all" 4. If you have a database dump, run it. For this project, we used the one located here: http://courses.ncsu.edu/csc517/common/homework/OSS/expertiza_scrubbed_2014_03_14.sql.zip "mysql -u root pg_development < expertiza-scrubbed.sql" Note that this may not be the name of the zipped dump, but you can customize to your needs. You may also need to specify your mysql password using "mysql -u root -p ..." depending on your mysql setup. 5. Run "exec rake db:migrate" 6. Run the rails server using "rails server -p 3000". You may substitute 3000 with a port number of your choice.

Running Tests

For this project, we used RSpec for unit tests. These are located at spec/models/assignment_form_object_spec.rb. The tests are primarily unit tests for the model, and it required a lot of testing of other models that were not well tested (or tested at all) elsewhere.

Future Work

As with every project there are always more things to be done. With the use of a form object there are side benefits that we can take advantage of with more effort that we just didn't have time for. There are also some problematic areas we uncovered during the project that could use cleaning up. The following additional steps (or features) should be taken (or implemented) by anyone continuing with the project. Struck out lines indicate features that have already been added in continuing work.

  • Ability to add multiple sign up topics from a new form
  • Ability to add rubric details from a new form
  • Redo the due dates partial to be more readable and use rails conventions rather than JavaScript
  • Redo the due dates partial in general, it is very messy and difficult to work with
  • Relocate JavaScript scripts to another.js resource file rather than in-line in the html
  • Eliminate the need to have multiple calls to the same controller methods for every table entry for due dates on the edit pages

References

<references/>