Chris Zempel

Joined

2,240 Experience
0 Lessons Completed
1 Question Solved

Activity

Posted in How can I grow this search object?

Thus far, this is my best take on it:

class SearchImage

  def initialize(params)
    @params = params
    @tags = params[:tags] unless params.has_key?(:tags) && params[:tags].empty?
    @year = params[:year] unless params.has_key?(:year) && params[:year].empty?

    set_excluded_image_ids if params.has_key?(:collection_id) && !params[:collection_id].empty?
  end

  def search
    if @params[:commit] == "All Uncategorized"
      unfinished
    else
      sort
    end
  end

  def sort
    @images = Image.excluding(@excluded_image_ids).tagged_with(@tags) if @tags
    @images = @images.excluding(@excluded_image_ids).where(year: @year) if @year

    @images ||= default_image_search
  end

  def set_excluded_image_ids
    collection = Collection.find(@params[:collection_id])
    @excluded_image_ids = collection.images.pluck(:id)
  end

  def unfinished
    @images = Image.excluding(@excluded_image_ids).unfinished
  end

  def default_image_search
    Image.excluding(@excluded_image_ids).order(created_at: :desc).first(8)
  end
end

class Image < ActiveRecord::Base
  def self.excluding(ids)
    where.not(id: ids)
  end
end

it works, for now, but I have this feeling I'm not discovering some deeper pattern made available by ruby that I don't see

Posted in How can I grow this search object?

A collection is simply an ordered list of images that will appear wherever its wanted (in a carousel, in a gallery, etc). Selection is basically a join table that sits between a collection & its images, both so I can add a position column and have an image belong to as many different collections as needed.

class Collection < ActiveRecord::Base
  has_many :selections, -> { order(position: :asc) }
  has_many :images, through: :selections
  accepts_nested_attributes_for :selections, allow_destroy: true
end

class Selection < ActiveRecord::Base
  belongs_to :collection
  belongs_to :image
  acts_as_list scope: :collection
end

class Image < ActiveRecord::Base
  attachment :file
  belongs_to :image_category
  has_many :selections
  acts_as_taggable

  scope :unfinished, -> { where( year: nil) }

  def self.unfinished_count
    unfinished.count
  end
end

If I'm on a page where there's a collection being displayed for editing, the params[:collection_id] will be present, so then I can include & treat that like another filtering component.

class SearchImage

  def initialize(params)
    @params = params
    @tags = params[:tags] unless params.has_key?(:tags) && params[:tags].empty?
    @year = params[:year] unless params.has_key?(:year) && params[:year].empty?

    set_collection if params.has_key?[:collection_id] && !params[:collection_id].empty?
  end

  def search
    if @params[:commit] == "All Uncategorized"
      unfinished
    else
      sort
    end
  end

  def sort
    @images = Image.tagged_with(@tags) if @tags
    @images = @images.where(year: @year) if @year

    @images ||= default_image_search
  end

  def set_collection
    @collection = Collection.find(params[:collection_id])
  end

  def unfinished
    @images = Image.unfinished
  end

  def default_image_search
    Image.order(created_at: :desc).first(8)
  end

This honestly seems like I could just be chaining queries in some fashion, meaning I could add in:

Image.where.not(id: @collection.images.pluck(:id))

or something, but where I'm stuck is: how could I get rid of that if so don't need to deal with two branching paths? Just have one path that will do the right thing. Search and sort don't seem easy to change

Posted in How can I grow this search object?

I've got a domain object I use to search images by whatever tags it has/the year the item in the image was made. It's worked flawlessly in a couple places:

class SearchImage

  def initialize(params)
    @params = params
    @tags = params[:tags] unless params.has_key?(:tags) && params[:tags].empty?
    @year = params[:year] unless params.has_key?(:year) && params[:year].empty?
  end

  def search
    if @params[:commit] == "All Uncategorized"
      unfinished
    else
      sort
    end
  end

  def sort
    @images = Image.tagged_with(@tags) if @tags
    @images = @images.where(year: @year) if @year

    @images ||= default_image_search
  end

  def unfinished
    @images = Image.unfinished
  end

  def default_image_search
    Image.order(created_at: :desc).first(8)
  end
end

But now, I need to filter the results to only return images that aren't in a given collection. The controller action currently looks like this.

def choices
  @images = SearchImage.new(params).search
end

and the way I can get the images I don't want to include in the results is:

@collection.images

Since this is just another way of filtering the search, I figure I could just check for the existence of a collection_id in the params and subtract out the currently selected images from the result...but now, I feel everything is becoming tightly coupled to the state of the page and I'll be adding conditionals of some type to check for the existence, and act upon that existence.

Or I could inject a filter that does that at the end, but that doesn't seem right either.

So I'm gonna go take a nap and then get back on this, but in the meantime here's my query: how could I get rid of the if statement in the `search' function and instead start telling it what to do? Or, if thats not really a good question, what's a different way I could think about the objects and associated responsibilities/actual execution in ruby?

Posted in Must Read / Must Watch

This talk is super interesting - what "OOP" is actually about, from the mouth of the guy who coined the term:

https://www.youtube.com/watch?v=oKg1hTOQXoY

Posted in Coffeescript Instantiation

omg...

 contructor: (item) ->

should be

  constructor

:'( :'(

Posted in Coffeescript Instantiation

I've got a bunch of images thumbnails in an admin area. Wherever one appears, I'd like to be able to click on it, open up a modal with a form, and let the attributes of the image be edited from that form.

To start, I'd first just like to be able to pop open the modal when somebody clicks on a thumbnail, but I'm running into an issue where I'm not sure what's wrong.

thumbnail html:

<div class="col-xs-6 col-md-3" data-behavior="image-thumbnail" data-image-id="1">
  <div class="thumbnail">
     <img class="attachment image file" src="blahblah" alt="Screen+shot+2015 05 29+at+10.29.10+pm" /> 
  </div>
</div>

.erb template for an individual image thumbnail

<div class="col-xs-6 col-md-3" data-behavior="image-thumbnail" data-image-id="<%= image.id %>">
  <div class="thumbnail">
     <%= attachment_image_tag(image, :file, :fill, 300, 300) %> 
  </div>
</div>

images.js.coffee

class Thumbnail
  contructor: (item) ->
    console.log "contructor"
    @thumbnail = item
    @setEvents()

  setEvents: ->
    @thumbnail.on "click", @revealModal

  revealModal: =>
    console.log "wow"
    $("[data-behavior='image-thumbnail-modal']").modal('show')

jQuery ->
  console.log "ready"
  new Thumbnail $("[data-behavior='image-thumbnail']")

alternately, I've tried instantiating the coffeescript objects like this:

  $.map $("[data-behavior='image-thumbnail']"), (item, i) ->
    new Thumbnail(item)

The console logs "ready," but I never make it into the constructor. Where am I going wrong here?

Posted in Coffeescript Polling Issues

In coffeescript, when you call

@loadWhenCached()

it actually invokes the function. Because setInterval()'s context is the window, what happens is that it executed once successfully, then switch to the context of the window (where it no longer had access to @preview, or @interval) and started throwing that error.

What I needed to pass to the setInterval() was a reference to what function to invoke, not the actual invocation of the function.

@interval = setInterval(@loadWhenCached, 3000)

Additionally, I then needed to make sure that it was in the correct closure to grab stuff, meaning @loadWhenCached needs to use the fat => to ensure it takes place where it has access to @preview and @interval.

Posted in Coffeescript Polling Issues

One of the first SO results said "don't use setInterval," but that's exactly what I've been looking for.

The images will take anywhere between 15s and 120s to cache so this seems to be the most prudent way.

class Preview
  constructor: (preview) ->
    @preview = preview
    @interval = null
    @loadWhenCachedPoll() unless @preview.data?.footprintsCached?

  loadWhenCachedPoll: ->
    @interval = setInterval(@loadWhenCached(), 3000)

  loadWhenCached: ->
    console.log "polling"
    $.ajax(
      url: "/user_image_caches/cached",
      method: "GET"
      dataType: "JSON"
      success: (data) =>
        if data["cached"] == "true"
          clearInterval(@interval)
          @preview.data.footprintsCached.val("true")
          @preview.attr('src', '/certificate.pdf')
    )

jQuery ->
  new Preview $("[data-behavior='preview']")

It runs successfully the first time, then every time after that I get

Uncaught SyntaxError: Unexpected identifier

signaling that something fell out of scope. I'm thinking @interval or @loadWhenCached() fell out of scope, but I'm not sure why.

Posted in Coffeescript Polling Issues

I'd like to reload an iframe when the images needed to generate the preview it will display are cached by background workers. However, I can't figure out how to get setTimeout() to work correctly (or any other method of polling), and keep making thousands of immediate requests to my local machine which crashes the browser. While this is fun, I'd like it work.


class Preview
  constructor: (preview) ->
    @preview = preview
    @setBehavior()

  setBehavior: ->
    @loadWhenCached(@preview) unless @preview.data.footprintsCached?

  loadWhenCached: (preview) =>
    console.log "polling"
    $.ajax(
      url: "/user_image_caches/cached",
      method: "GET"
      dataType: "JSON"
      success: (data) =>
        if data["cached"] == "true"
          console.log "wat"
          preview.attr('src', '/certificate.pdf')
    )
    unless preview.data?.footprintsCached?
      setTimeout @loadWhenCached(preview), 2000


jQuery ->
  new Preview $("[data-behavior='preview']").first()

Posted in API Design Decisions

Right now we're just providing a really slow response since caching the images is out of the question for the moment.

Posted in API Design Decisions

Well, the pictures are aactually, protected health information, there are a buttload of them, and we won't know what ones will be requested. So our only real way of doing this is just requesting them from the place where they're stored when they're requested.

We're going to return the easy-to-retrieve information about a record by default, and then if they want the dog picture, they can get that through 'fields.' But 'include' is a lot more descriptive

Posted in API Design Decisions

Or perhaps totally changing all of that:

api/{api_token}/{record_id}/dog

api/{api_token}/{record_id}/resource/{resource_id}

My preference is to keep the urls very short and clear, and sweep the complexity behind the ?
But if everything gets set up more like , its easier to rely upon Rails to just handle it

Posted in API Design Decisions

I'm designing & building an API for a company and have arrived at a decision point I can't figure out.

we're providing access to records. right now they want to quickly integrate with one client, but they also will want to do more in the future. there's no updating, no index, just requests about and for parts of individual records

GET /api/records/12451251252/

Since we know they're gonna want to integrate with more than one person, we added in access tokens that we manage and provide to them so we can know who is requesting what.

/api/records/214125215215?access_token={access_token}

Now here's the tricky part: certain areas of these records are large cat pictures, and take time to retrieve from the backend they're stored in. There are also large dog pictures, and large trainer certificates. Beyond these few stable things, all the other data being recorded for a record is different, changing over time, and we can't reliably count on it being there. This includes stuff like more info about the cats, dogs, trainers, and where the training took place.

I want to make the api backwards compatible and avoid introducing any breaking versions as much as I can. Because of the uncertainty with the data that gets stored, my predisposition is to sweep all of the complexity behind the ? and deal with it there.

Thing is, I'm pretty sure the dog, cat and certificate pictures will always be there. Since they're big and take time, we're only gonna send them over when they're specifically requested.

/api/records/125125215?access_token={access_token}&fields=cat,dog

However, some of these things are stable. So maybe it would be better to go completely restful and have the api set up like this:

/api/records/125125215/dog?access_token={access_token}

And then keep the uncertain stuff like the trainer certificate in a 'fields' parameter, and maybe promote it when we can guarantee it.

The fundamental question I have is: what's nicer to work with? Requesting through a fields parameter, or breaking up what would be the fields thing and turning it more RESTful?

Which way would you rather use, and why?

Posted in How should I model this situation?

Or is this a situation when I'd want to leverage ducktyping to the extreme? Define a model that serves as the sort of abstract interface to the variety of concrete instances of all the metrics in the db? (sorry for using java terms)

Companies need to be independent of how its metrics are created, composed, and represented. I also want metrics to behave on behalf of only one company.

class Metric
  attr_accessor :relevance_date, :value, :value_type

  def initialize(company:, metric_name)
    #finds company
    #grabs appropriate metric from aforementioned structure
    #creates metric object
  end
end

class Company << ActiveRecord::Base
  def metric(metric_name)
    Metric.new(self, metric_name)
  end
end

This is making assumptions about the structure of the metrics I'll be storing. Ultimately, the way I'll want to use them most of the time is as a point in an array of time-series data so they can turn into graphs, or plug in calculations. So I think expecting stuff to behave like:

metric.relevance_date 
metric.value 
metric.value_type 

Is reasonable.

Posted in How should I model this situation?

I need to store different metrics about various companies. These will be present for every company:

  • revenue
  • ebitda
  • cash
  • receivables
  • employees
  • debt

each one has attributes such as:

  • classification - is this the actual #, or an estimated one?
  • amount in cents
  • the date this value is relevant to

(and some other areas that might not be related)

  • who it was entered by
  • who it was approved by

However, I also need to allow users to create custom metrics, and capture these other things. For example, someone monitoring this company could want to record monthly users signups of a specific company.

It's safe to assume the majority of this will be time-series data, but not necessarily everything will be.

Here are my questions, starting with most-meta and going more granular:

How do I approach designing the way all this data is stored?

The crux of the question is that this data is sort of fundamentally unstructured, so where should the structure reside?

Are the areas I know I want to store different than the ones I don't, or should I build a way to have anything and then hardcode in the behavior for the metrics I know I'll want to record?

Here's the path my mind leads me down, by table:

class Company.rb < ActiveRecord::Base
  has_many :company_metrics
end

class CompanyMetric < ActiveRecord::Base
  belongs_to :company
  has_many :metric_attributes
end

class Attribute < ActiveRecord::Base
  belongs_to :company_metric
  belong_to :%(value_name_table)
end

Now these attributes can basically be stored one of several ways:
1) integer (or boolean) (for most everything)
2) bigint (for $$)
3) decimal (for %'s)
4) text

and also, a date will need to be stored at some level

Situation: A user enters the name of a venue they want to add. I query an external API and get back a list of possible venues they actually meant, which they'll then confirm. The venue model will be populated in my local DB based upon the information in their selection.

I've implemented this a couple different ways before in hacky ways just to get it done, but now I get the sense that I'm doing too much work and not leveraging rails properly.

I get the results back turn them into an ostructs that looks like:

>> results.first
=> #<OpenStruct address="2 Maryland Plz", category_ids=[348, 342], category_labels=[["Social", "Food and Dining", "Restaurants", "American"], ["Social", "Food and Dining", "Cafes, Coffee and Tea Houses"]], country="us", factual_id="281d5b40-9d41-4eac-8367-4bc223656cb5", hours={"monday"=>[["00:00", "23:30"]], "tuesday"=>[["00:00", "23:30"]], "wednesday"=>[["00:00", "23:30"]], "thursday"=>[["00:00", "23:30"]], "friday"=>[["00:00", "23:30"]], "saturday"=>[["00:00", "23:30"]], "sunday"=>[["00:00", "23:30"]]}, hours_display="Open Daily 12:00 AM-11:30 PM", latitude=38.644621, locality="St. Louis", longitude=-90.261741, name="Coffee Cartel", neighborhood=["Central West End", "Saint Louis"], postcode="63108", region="MO", tel="(314) 454-0000", website="http://www.thecoffeecartel.com/">

so I can simply call them like:
results.first.name #=> "Coffee Cartel"

I'd like to build a form that for each possible venue shows the name, address, and website, and then persists all the other information about each choice in hidden fields. What's not connecting in my brain is how to only post the selected results to the controller action.

How does associating a radio button with a specific subset of fields work in html, and then what's a clean way to do this in rails with all the form helpers? Is this possible?

Posted in Trouble With Form Objects

Correct on both counts. hadn't fully coded through the create action and didn't realize that's where I actually was - thought all the errors were being thrown on new

Posted in Trouble With Form Objects

Ah shoot, I thought I was inside the new controller action. I was actually inside create. Working now:

  def create
    authorize :invitation
    @invitation = Invitation.new invitation_params

    if @invitation.nil?
      redirect_to new_invitation_path, notice: "Invite successfully sent!"
    else
      render "new", notice: "Invite couldn't be sent!"
    end
  end

The correct way to define model_name is:

  def self.model_name
    ActiveModel::Name.new(self, nil, "ModelNameHere")
  end

Posted in Trouble With Form Objects

I decided to experiment with a form object.

class Invitation
  include ActiveModel::Model

  attr_accessor(
    :first_name,
    :last_name,
    :email,
    :firm_id,
    :role
  )

  validates :first_name, presence: true
  validates :last_name, presence: true
  validates :email, presence: true
  validates :firm_id, presence: true
  validates :role, presence: true

  def invite
    if valid?

    end
  end

  def firms_for(user)
    if user.admin?
      Firm.all
    else
      user.firms
    end
  end

  def invitable_roles_for(user)
    current_role = User::ROLES.index("#{user.role}")
    last_role = -1

    User::ROLES.slice(current_role..last_role)
  end

  private

    def create_firm_connection

    end
end
in the controller:

  def new
    @invitation = Invitation.new
  end
        <%= form_for @invitation do |f| %>
          <%= f.label :first_name %>
          <%= f.text_field :first_name %>
          <br />

          <%= f.label :last_name %>
          <%= f.text_field :last_name %>
          <br />

          <%= f.label :email %>
          <%= f.email_field :email %>
          <br />

          <%= f.label :firm_id, "Firm" %>
          <%= f.select :firm_id, options_from_collection_for_select(@invitation.firms_for(current_user), 'to_param', 'short_name') %>
          <br />

          <%= f.label :role %>
          <%= f.select :role, options_for_select(@invitation.invitable_roles_for(current_user)) %>
          <br />

          <%= f.submit %>
        <% end %>

I've run into this error:

undefined method `model_name' for ActionController::Parameters:Class

I can replicate this through calling @invitation.class.model_name, so here are my attempts at fixing it:

  def self.model_name
    "invitation"
  end

  def self.model_name
    ActiveModel::Name.new(self, nil, "Invitation")
  end

However, neither of these work. What's actually going on here?

Screencast tutorials to help you learn Ruby on Rails, Javascript, Hotwire, Turbo, Stimulus.js, PostgreSQL, MySQL, Ubuntu, and more.

© 2024 GoRails, LLC. All rights reserved.