Showing posts with label Rails. Show all posts
Showing posts with label Rails. Show all posts

Monday, July 7, 2014

Respecting Owner Limits in Belongs To Associations

So, here's the scenario: I have a rails 4.0.4 application. This application has many shares, each share has many share_users which each have an 'approved' boolean attribute. However each share also has a user_limit. So how does one ensure that the number of a share's approved share_users does not exceed the share's user_limit?

If you just want the solution, skip down to Attempt Three.

Attempt One - Overwrite the share_user's approved=(value) method
New approved=(value) method:

 def approved=(value)  
      share = self.share  
      if value == true  
           if share.share_users.where("approved = true").count < share.user_limit  
                write_attribute(:approved, value)  
           else  
                raise Exceptions::ShareUserLimitReached  
           end  
      end  
      if value == false  
           write_attribute(:approved, value)  
      end  
 end  

Supporting share_user spec:

 describe "approved=(value) > " do  
           it {should respond_to :approved=}  
           let(:approved_share) {FactoryGirl.create(:basic_share, user_limit: 1)}  
           let!(:approved_share_user) {FactoryGirl.create(:share_user, share_id: approved_share.id, approved: true)}  
           let!(:disapproved_share_user) {FactoryGirl.create(:share_user, share_id: approved_share.id, approved: false)}  
           it "should not throw a ShareUserLimitReached error when the share's user_limit is not being exceeded" do  
                expect{share_user.approved=true}.not_to raise_error  
           end  
           it "should throw a ShareUserLimitReached error when the share's user_limit attempts to be exceeded" do  
                approved_share.user_limit = 2  
                approved_share.save  
                expect{disapproved_share_user.approved=true}.to raise_error Exceptions::ShareUserLimitReached  
           end  
           it "should allow share_users to be disapproved without issue" do  
                expect{approved_share_user.approved=false}.not_to raise_error  
           end  
      end  

Now, if you look at the code, you'll see a couple of things.

  • share_user#approved=(value) is not encapsulated. It's reaching through to the share, retrieving attributes from it and then executing upon those attributes.
  • share_user#approved=(value) raises a nasty error that then has to be handled specially everywhere it is likely to be invoked.
  • share_user#approved=(value) heavily complicates creating new share_users as it requires the share_user to already have a share at point of creation. This requires telling the relationship to retrieve it from memory as opposed to the database. More on this can be found here.
All in all, not a great solution, it's messy, doesn't quite do the job and requires modifying a lot of other code.

Attempt Two - validates_associated + validates_with
  1. Create a new validation for the Share called ApprovedShareUserCount and invoke it with validates_with.
  2.  class ApprovedShareUserCountValidator < ActiveModel::Validator  
          def validate(record)  
               #Required to ensure that the presence validation spec for user_limit doesn't trigger this validation to throw a nil error.
               return if record.user_limit.nil?  
    
               count = record.share_users.where("approved = true").count  
               if count > record.user_limit  
                    record.errors.add(:share_users, 'The share has reached its user limit, please contact your CEO or Tech Admin about this issue.')  
               end  
          end  
     end  
    
    Note: I stored my validators in a new folder; app/validators and added a custom eager loading path for it.
     class Share < ActiveRecord::Base       
          has_many :share_users, foreign_key: "share_id", dependent: :destroy  
          validates :user_limit, presence: true  
          validates_with ApprovedShareUserCountValidator  
     end  
    

  3. Invoke this new validation whenever a share_user is saved by adding 'validates_associated :share' to it.
  4.  class ShareUser < ActiveRecord::Base  
          belongs_to :share  
          validates :share_id, presence: true  
          validates_associated :share  
     end  
    
Supporting Share Spec
 describe "should ensure the user_limit is never exceeded" do  
                let!(:user_limit_share) {FactoryGirl.create(:share, user_limit: 1)}  
                let!(:approved_share_user) {FactoryGirl.create(:share_user, approved: true, share_id: user_limit_share.id)}  
                let!(:disapproved_share_user) {FactoryGirl.create(:share_user, approved: false, share_id: user_limit_share.id)}  
                subject{user_limit_share}  
                it "should be valid if the number of approved share_users is <= the share's user_limit" do  
                     should be_valid  
                end  
           end  

Supporting Share_User spec
 describe "validate_association :share" do  
      let!(:user_limit_share) {FactoryGirl.create(:share, user_limit: 1)}  
      let!(:approved_share_user) {FactoryGirl.create(:share_user, approved: true, share_id: user_limit_share.id)}  
      let!(:disapproved_share_user) {FactoryGirl.create(:share_user, approved: false, share_id: user_limit_share.id)}  
      it "should not be valid if approving it would exceed the share's user_limit" do  
           disapproved_share_user.approved = true  
           expect(disapproved_share_user.valid?).to_not be_true  
      end  
 end  

The allure of this is that it ties into the existing validation framework. Unfortunately, there is one critical problem; it doesn't work. The validations all trigger and work perfectly fine! But the share's validation is triggered against the database, and does NOT use the share_user in memory that triggered it.

So to use this solution, the share_user would have to be saved to the database, thus defeating the entire purpose.

Attempt Three - Share#respect_share?(share_user) + ShareUser#validates_with custom validator
Attempt two was definitely far more elegant then attempt one and was certainly moving in the right direction. So how do we build on that with something that respects encapsulation, can be easily expanded on and that uses the existing validation framework?


  1. Create a public method on Share that determines if a given share_user would violate the share's rules.
  2.  def respect_share?(share_user)  
               #Ensure the share's user_limit is respected.  
               if share_user.approved == true  
                    count = self.share_users.where("approved = true").count  
                    #This assumes that in the event this returns true, the share_user will be added to the share.  
                    #Thus it must count up, so that a share with 10 users and a 10 user limit will return false.  
                    if count+1<=self.user_limit  
                         return true  
                    else  
                         return false  
                    end  
               end  
               #This method can be expanded as the share grows.  
          end  
    
    At this point we only care about respecting the user_limit. However as you can see this can be easily expanded by breaking out the if statement (and any additional if statements) into supporting private methods.
  3. Create a custom validator for share_user that invokes the owning share's respect_share? method.
  4.  class RespectShareValidator < ActiveModel::Validator  
          def validate(record)  
               #Required to ensure that the presence validation spec for share_id doesn't trigger this validation to throw a nil error.  
               return if record.share_id.nil?  
               #This simply calls the record's owning share and then passes the record to it.  
               if record.share.respect_share?(record) == false  
                    record.errors.add(:share_users, '<Error Message Here>')  
               end  
          end  
     end  
    

Supporting Share Spec
 describe "Public Methods > " do  
      describe "respect_share?(share_user)" do  
           let!(:user_limit_share) {FactoryGirl.create(:share, user_limit: 1)}  
           let!(:approved_share_user) {FactoryGirl.create(:share_user, approved: true, share_id: user_limit_share.id)}  
           let!(:disapproved_share_user) {FactoryGirl.create(:share_user, approved: false, share_id: user_limit_share.id)}  
           it "should return false if the share's user_limit would be exceeded by saving the passed in share_user" do  
                disapproved_share_user.approved = true  
                expect(user_limit_share.respect_share?(disapproved_share_user)).to be false  
           end  
      end  
 end  

Supporting Share_User Spec
 describe "RespectShareValidator" do  
      let!(:user_limit_share) {FactoryGirl.create(:share, user_limit: 1)}  
      let!(:approved_share_user) {FactoryGirl.create(:share_user, approved: true, share_id: user_limit_share.id)}  
      let!(:disapproved_share_user) {FactoryGirl.create(:share_user, approved: false, share_id: user_limit_share.id)}  
      it "should not be valid if approving it would exceed the share's user_limit" do  
           disapproved_share_user.approved = true  
           expect(disapproved_share_user.valid?).to_not be_true  
      end  
 end  

Unlike Attempt Two this one actually works. This is because the current in-memory representation of the share_user is passed to Share#respect_share? and the method knows that it is dealing with an in memory model instead of a database record. Now while this knowledge isn't ideal, it is much cleaner and functional then either of the other two attempts.

The existing validation mechanisms are used, and there are no custom errors to be caught. This means that the majority of existing code will function without issue. On top of this, because all of the code necessary to determine if a Share would be broken is contained within the Share class itself, this solution doesn't violate encapsulation.


Monday, November 4, 2013

Asynchronously Rendering Partials with custom jQuery/AJAX and Rails 3.2.14

I recently spent a while learning how to do custom AJAX queries with jQuery and Rails 3.2.14. While I'm well within the realm of a Junior Rails developer, I knew NOTHING about jQuery and only slightly more about AJAX. This post is going to tell a story, but I'm also going to show you some things I learned and haven't seen well documented elsewhere.

While researching and rapid prototyping code, I realized that there are a lot of pitfalls when you're glueing rails and jquery's AJAX functionality together. The biggest pitfall is silent failure in your .js.erb file, but there are plenty of others along the way, which Steve Schwartz over at AlfaJango.com has a great guide on. As I compiled information from the various sources, I realized the documentation for rails AJAX functionality is firmly couched in form_for and "remote: true" in particular. In fact, "remote: true" works for links, buttons, and even form_for's cousin, form_tag. Sadly "remote: true" doesn't work for anything custom, so almost all the documentation out there leaves you wondering. Sure there are some good guides out there like Steve Schwartz's over at AlfaJango.com but even they leave some of the components out.

Custom AJAX methods rely on several components in rails 3.2.14; the parent view, the jQuery & AJAX component itself, the controller method, the supporting .js.erb file and the partial being injected into the page. I won't go so far as to say these are all necessary and this is the only way, but these are the pieces that had to come together for me. These combined to form a nice little loop complete with silent failures and some very loud failures.

Using jQuery, an AJAX call may be made by any function for any reason, this is delightfully powerful, but can also be troublesome. I was trying to fire an AJAX call when the user changed the value in a select box. This required that the parent view containing the select boxes render first. You can see an example select box below taken directly from my app.

1:  <select id="station_select">  
2:    <% station_list = current_user.market_item_summaries.pluck('station_id').uniq %>  
3:    <option value="All">All Stations</option> <!-- The top option is the default option -->  
4:    <% station_list.each do |s|%>  
5:     <option value="<%= s.to_s %>"> <%=Station.where("station_id = ?", s).pluck("name")[0]%> </option>  
6:    <% end %>  
7:   </select>  

So this select box is rendered when the user visits the parent view. You'll see that it has an 'id' value and each option has a dynamic value. The 'id' value is standard HTML and is used by the jQuery to determine what to watch. jQuery can also watch css classes and a few other things, but I prefer ids personally. In order to fire off a jQuery AJAX call I elected to use the 'change' bind, though jQuery has a pile of really nifty events you can use for your functions. So when a user selects an option, the jQuery below is invoked by the browser.

1:  $(document).ready(function(){  
2:      //On change, trigger this function  
3:      $("#station_select, #listing_character, #owner, #type").change(function(){  
4:          //Load all select's values into variables  
5:          var ss = $("#station_select").val();  
6:          var ls = $("#listing_character").val();  
7:          var ow = $("#owner").val();  
8:          var ty = $("#type").val();  
9:          //Fire an AJAX call to marketsummaries/filter  
10:        $.ajax({  
11:            url: "marketsummaries/filter", type: "GET",  
12:            //Pass in each variable as a parameter.  
13:            data: { station_id: ss,   
14:                listing_character_id: ls,   
15:                owner_id: ow,   
16:                type: ty }  
17:        });  
18:      });  
19:  });  

You can see the comments explaining the cycle of events in rough detail, but let's break it down even more.
1:  $(document).ready(function(){  
2:  });  

The above function wraps our entire jQuery function set. This ensures that the document is fully loaded before any functions are called. While this may seem ancillary at first, ensuring the page is fully loaded is an excellent means of heading off bugs at the pass.

1:      $("#station_select, #listing_character, #owner, #type").change(function(){  
2:      });  

After the document is ready, you see the function declaration. jQuery distinguishes itself from JavaScript with a dollar sign before all functions. Knowing this you can see that my jQuery function actually includes several plain JavaScript elements. That aside, within my function declaration you can see that I am calling the selector "#station_select", jQuery uses the number sign to distinguish an id from a class, which would be represented with a dot. ".station_select" for class vs "#station_select" for id. You also see that a comma may be used as a delineator between multiple selectors. Each selector in the above snippet selects an id. The function is then bound to the change event, so when the value of either of the <select>'s changes, this function will fire.

1:          var ss = $("#station_select").val();  
2:          var ls = $("#listing_character").val();  
3:          var ow = $("#owner").val();  
4:          var ty = $("#type").val();  

In the above snippet you can see a combination of regular Javascript and jQuery. "var ss =" is JavaScript, declaring a variable, meanwhile everything right of the equals operator is jQuery. Each variable is assigned the current value of the <select> with the listed id. While this may be rather straight forward, the fact that jQuery and JavaScript can be so easily intermingled may not be as forthcoming as one might expect.

1:        $.ajax({  
2:            url: "marketsummaries/filter", type: "GET",  
3:            //Pass in each variable as a parameter.  
4:            data: { station_id: ss,   
5:                listing_character_id: ls,   
6:                owner_id: ow,   
7:                type: ty }  
8:        });  

Here you can see the actual AJAX call itself. You'll notice that ajax is actually a function within jQuery. The ajax function has a series of arguments that it takes, the primary arguments are; 'url', 'type', 'data' and 'success', the last of which I will explain in greater detail at the end. All arguments are comma delineated. The url arg determines where the AJAX call will be pointed towards, you'll notice this makes it very simple to dovetail it with a controller method. The type arg supports all four REST types and defaults to a GET call, I chose to explicitly define it. The data arg takes a comma delineated hash and will pass the data as query string parameters to the target url. Once again, you'll notice that this dovetails quite well with rails controllers.

1:            data: { station_id: ss,   
2:                listing_character_id: ls,   
3:                owner_id: ow,   
4:                type: ty }  

Each variable is passed as an element of the hash, this means that 'ss' will be accessible in the controller as params[:station_id]. The exact query passed to the server will look like;
1:  marketsummaries/filter?station_id=60000025&listing_character_id=All&owner_id=All&type=All  

Once the AJAX call is made, your controller must be waiting to receive it. This requires that you have a supporting route in your routes.rb file and a properly named method in your controller.
1:  routes.rb  
2:  match "/marketsummaries/filter", to: 'MarketItemSummaries#filter', via: :get  

1:  market_item_summaries_controller.rb  
2:  def filter()  
3:          @input = {"station_id" => nil, "listing_character_id" => nil, "owner_id" => nil, "type" => nil}  
4:          @input["station_id"] = params[:station_id]  
5:          @input["listing_character_id"] = params[:listing_character_id]  
6:          @input["owner_id"] = params[:owner_id]  
7:          @input["type"] = params[:type]  
8:
9:          (Whatever your method needs to do, build a query, what have you.)
10:
11:          respond_to do |format|  
12:              format.js  
13:          end  
14:      end  

Without the routes entry, the rails router will reject the AJAX call with a 403 forbidden leaving your controller's method untouched. The controller method itself must share the name specified in the AJAX call's url. Above you can see me taking each parameter and passing it to an instance hash. From there I can work with the hash more easily then individual parameters.

1:          respond_to do |format|  
2:              format.js  
3:          end  

This last function is extremely important and often missed. "respond_to" allows you to specify what type of response the controller is to send to a given call. You can specify html, xml, pretty much any format. In this case, the controller is going to respond with JavaScript. Because there is no additional block after "format.js", rails defaults to checking for a file "<method_name>.js.erb", in this case "filter.js.erb".

1:  filter.js.erb  
2:  $('#corporationtable').empty().append("<%= escape_javascript(render(:partial => 'corporation_table', locals: {mis: @mis})) %>");  

Like .html.erb files, .js.erb files are run through the embedded ruby pre-processor. This means you can dynamically embed different JavaScript commands into the page before it is actually run through the user's browser. This particular .js.erb file has some important parts though;

1:  $('#corporationtable')  

The above component uses jQuery to target a specific id. While it is possible to target an id on a partial, the jQuery selectors break when dom elements are reloaded through AJAX. For this reason "#corporationtable" is a div on the parent view, not on the partial that is going to be loaded.

1:  .empty().append()  

Normally when you go looking for proper syntax in a jQuery/AJAX .js.erb file, you'll see .html(), not .empty().append(). During my research I stumbled across an excellent, if tangential, blog post by Joe Zim explaining that while .html() does work, the reason it works is not officially supported. This means the jQuery team may randomly patch that functionality out and not tell anyone, so like him, I've elected to use .empty().append() directly rather then through .html(). It is important to note that .empty() destroys all child elements within the specified dom element.

1:  .append("<%= escape_javascript(render(:partial => 'corporation_table', locals: {mis: @mis})) %>");  

The .append() method takes html dom elements as well as html strings as arguments. Combining this with erb to use rails' render to generate a partial and append it to the div specified earlier allows for any element of the page to be updated. You'll notice that the render itself is wrapped in "escape_javascript()". This is done because the majority of partials being rendered will contain html elements that would otherwise escape the double quotes that .append() uses to delineate between start and finish. Kikito over at Stack Overflow does an excellent job explaining this.

1:  locals: {mis: @mis}  

The last crucial element within the .js.erb file is "locals:". The above code takes the variable "@mis" from my controller and makes it available to the partial as "mis". There are two key reasons to use locals, firstly; when rendering a partial in multiple places locals allow you to ensure you're passing it all necessary variables. Secondly; a partial being loaded into an existing view will not have access to the variables that the parent view does.

With filter.js.erb in place, the last necessary element is the partial itself. I want my users to be able to see the partial before selecting options from the <select> boxes, so the partial is actually loaded as an element within the parent view; show.html.erb.

1:  show.html.erb  
2:  <div id="corporationtable">  
3:   <%= render :partial => 'corporation_table', locals: {mis: @mis}%>  
4:  </div>  

As discussed earlier in filter.js.erb, "corporationtable" is the target of the jQuery append() function. This means the new partial is rendered in place of the old one. The partial itself can be seen below.

1:  
2:  <div class="corptable">  
3:   <table class="table table-bordered">  
4:    <thead>  
5:     <tr>  
6:      <h2>Corporation Item Summaries</h2>  
7:     </tr>  
8:    </thead>  
9:    <tr>  
10:     <table class="table table-condensed" style="border-collapse:collapse;"> <!-- Accordioned Table -->  
11:      <tbody id="corp_tbody">  
12:       <% mis.each do |n|%>  
13:       <% if n.entity == 1 %>  
14:         <tr data-toggle="collapse" data-target=".demo<%=mis.index(n)%>" data-target="#data4" class="accordion-toggle"> <!-- First 2 rows are the mis -->  
15:           <td><b> <%= Item.where("type_id = ?", n.type_id).pluck('name')[0] %> </b></td>  
16:           <td> <%= Station.where("station_id = ?", n.station_id).pluck('name')[0] %> </td>  
17:           <td> <%= Character.where("char_id = ?", n.char_id).pluck('name')[0] %> </td>  
18:           <td class="text-success"> <% char = current_user.characters.where("char_id = ?", n.char_id) %>  
19:            <%= Corporation.where("character_id = ?", char).pluck('name')[0] %> </td>  
20:           <td <% if n.bid == false %>   
21:              class="text-success">Sell  
22:             <% else %>  
23:              class="text-error">Buy  
24:             <% end %> </td>  
25:         </tr>  
26:         <tr data-toggle="collapse" data-target=".demo<%=mis.index(n)%>" data-target="#data4" class="accordion-toggle"> <!-- Second line of the mis -->  
27:           <td> APP <%= n.average_purchase_price %> </td>  
28:           <td> ASP <%= n.average_sale_price %> </td>  
29:           <td> APM <%= n.average_percent_markup %> </td>  
30:           <td> TVE <%= n.total_vol_entered %> </td>  
31:           <td> TVR <%= n.total_vol_remaining %> </td>  
32:         </tr>  
33:         <% mo = MarketOrder.where("market_item_summary_id = ?", n.id) %>  
34:         <% mo.each do |m| %>  
35:          <tr> <!-- Each Row after that with TD's classed as hiddenRow are individual orders -->  
36:           <td class="hiddenRow">  
37:             <div class="accordian-body collapse demo<%=mis.index(n)%>"> <%= Item.where("type_id = ?", m.type_id).pluck('name')[0] %></div>  
38:           </td>  
39:           <td class="hiddenRow">  
40:             <div class="accordian-body collapse demo<%=mis.index(n)%>"> <%= m.price %></div>  
41:           </td>  
42:           <td class="hiddenRow">  
43:             <div class="accordian-body collapse demo<%=mis.index(n)%>">%markup</div>  
44:           </td>  
45:           <td class="hiddenRow">  
46:             <div class="accordian-body collapse demo<%=mis.index(n)%>"> <%= m.vol_entered %></div>  
47:           </td>  
48:           <td class="hiddenRow">  
49:             <div class="accordian-body collapse demo<%=mis.index(n)%>"> <%= m.vol_remaining %></div>  
50:           </td>  
51:          </tr>  
52:         <% end %>  
53:        <% end %>  
54:       <% end %>  
55:      </tbody>  
56:     </table>  
57:    </tr>  
58:   </table>  
59:  </div>  

While the partial itself is nothing special, there is one element I would like to draw attention to. Line 11 contains a table body element with the id "corp_tbody". If you look over the code, you'll realize that if there are no valid mis values, corp_tbody will be entirely blank. The architecture of my app does not allow this entire partial to be easily removed. However it is possible to use jQuery and the .ajax() function's success method to hide the entire partial any time corp_tbody is empty.

1:  $(document).ready(function(){  
2:      //On change, trigger this function  
3:      $("#station_select, #listing_character, #owner, #type").change(function(){  
4:          //Load all select's values into variables  
5:          var ss = $("#station_select").val();  
6:          var ls = $("#listing_character").val();  
7:          var ow = $("#owner").val();  
8:          var ty = $("#type").val();  
9:          //Fire an AJAX call to marketsummaries/filter  
10:        $.ajax({  
11:            url: "marketsummaries/filter", type: "GET",  
12:            //Pass in each variable as a parameter.  
13:            data: { station_id: ss,   
14:                listing_character_id: ls,   
15:                owner_id: ow,   
16:                type: ty },  
17:            success: hider  
18:        });  
19:      });  
20:      function hider() {  
21:          //Hide or Show table divs based on whether the table's tbody has content.  
22:          if ($.trim($("#corp_tbody").html())=='') {  
23:              $("#corporationtable").hide()  
24:          }  
25:          if ($.trim($("#corp_tbody").html())!='') {  
26:              $("#corporationtable").show()  
27:          }  
28:          if ($.trim($("#char_tbody").html())=='') {  
29:              $("#charactertable").hide()  
30:          }  
31:          if ($.trim($("#char_tbody").html())!='') {  
32:              $("#charactertable").show()  
33:          }  
34:      }  
35:  });  

At line 20 you can see I've built a JavaScript function and taken advantage of jQuery's easy interoperability with JavaScript. The hider() function calls a series of JavaScript if branches (jQuery actually lacks an if/else mechanism entirely), within the first branch at line 22 I check for corp_tbody being empty. While jQuery does have couple variants on an empty method, according to Serge Shultz over at Stack Overflow, Chrome and Firefox don't play nice with those.

Up at line 17 you can see a very simple addition to the AJAX args list. "success: hider" calls the hider function when the AJAX request completes successfully. That is when the server returns an HTTP 200 to the client. Should the client not receive a 200 code, hider() will never fire. It is important to note that there are no parenthesis in "success: hider", I don't know why that is, but I do know that the function fails to perform reliably with parenthesis.

TL;DR
Asynchronously rendering a partial with jQuery/AJAX/Rails 3.2.14 requires a jQuery AJAX call, supporting routes, a controller with configured method, a properly formatted .js.erb file, a partial, and a target div on the parent view.

The AJAX call must be triggered, the URL it hits must be a valid route, the targeted controller method must be configured to respond with a JavaScript response, this will return a .js.erb file named <method>.js.erb which must contain a target div id and a properly escaped render command. The target div id should be present on the view that triggered the AJAX call, if it is not the partial might not reliably generate.

You can view the full code for my app's jQuery/AJAX/Rails interactions at the following links
routes.rb
https://github.com/islador/market_monitor/blob/viewsMarketSummaries/config/routes.rb
Market Item Summaries Controller
https://github.com/islador/market_monitor/blob/viewsMarketSummaries/app/controllers/market_item_summaries_controller.rb
Market Item Summaries JavaScript
https://github.com/islador/market_monitor/blob/viewsMarketSummaries/app/assets/javascripts/market_item_summaries.js
Parent View
https://github.com/islador/market_monitor/blob/viewsMarketSummaries/app/views/market_item_summaries/show_msi.html.erb
Corporation Table Partial
https://github.com/islador/market_monitor/blob/viewsMarketSummaries/app/views/market_item_summaries/_corporation_table.html.erb
Character Table Partial
https://github.com/islador/market_monitor/blob/viewsMarketSummaries/app/views/market_item_summaries/_character_table.html.erb
filter.js.erb
https://github.com/islador/market_monitor/blob/viewsMarketSummaries/app/views/market_item_summaries/filter.js.erb