Morgan

Joined

1,750 Experience
6 Lessons Completed
1 Question Solved

Activity

I have a page editor that shows the user a live preview of their changes as they make them, but I’m having trouble working out how to do this with erb templates.

In my previous non rails version, I simply synced the changes via a redux store which allowed react to instantly render the changes, but I would prefer to keep erb templates for the rails version which means I need to find another solution.

So far I have the page editor rendering a list of page sections that belong to the page in a column on the left and the actual page rendered in an iframe on the right.

I am currently updating the individual elements in the iframes page sections with::

updateSection: (section) => {         
    const iframe = document.getElementById('ifameId')
    const innerDoc = (iframe.contentDocument) ? iframe.contentDocument : iframe.contentWindow.document;          
    const selected_section = innerDoc.getElementById(section.id)
          
    selected_section('[data-page-setting="title"]').innerHTML = block.title
},

Which is ok for the live updating but I would like to re-render the current sections template whenever the changes are persisted to the database (when a user clicks save or they click on another section to edit )  

Something like:

  1. Users saves section.
  2. Section#update updates section
  3. Responds with json with something like: { resp: { data: @section: template: “ERB STRING”} }
  4. Page editor updates the iframes section with the updated resp.template
  5. Page editor updates the form fields with resp.data 

From what I can see render_to_string would allow me to send the erb template as a string but I can't figure out how to get it working. 

Posted in Can RUBY build graphical editors like this?

I think you would want to use Rails to provide the data via an API and then use something like Vue or React on the frontend for the UI.

Chris has an in-depth series on a similar setup using vue:
Rails & Vue.js Trello Clone

Some links on using Rails as an API:
Build an API in your Rails app now! (Rails 5 version)
Building the Perfect Rails 5 API Only App

Otherwise, if you need a highly interactive real-time app I would look at Meteor:
https://www.meteor.com/
https://www.meteor.com/showcase  

Posted in Best way to create a belongs_to object from a has_many

Yes, I think that would have been the next issue I would have ran into, but I think my problem now is that I am trying to build up the associations in the same order as your example with a User first but I'm creating the Listing first. 

I'll need to play with it some more!

Do you have a patreon account or similar setup? I really appreciate all your help!

Posted in Best way to create a belongs_to object from a has_many

Thanks, Jacob, this makes a lot of sense, but how would you handle the freindlyId subdomains on the Site table with this setup?

Before I was just doing  @listing.assign_attributes(listing_body) which would pass the required attributes to build the subdomain.

It looks like I need to somehow pass the listing to user1_site = user1.build_site.save 

Posted in Best way to create a belongs_to object from a has_many

Hi Jacob, I had all this working beautifully until I added a third model and I have been going around in circles since!

This is not valid, but I'm basically trying to achieve this:

A Site is a very small model which stores things like domains, subdomains etc can be used to create differnt type of sites.  i.e User site, Listing site, Company site.

A Listing could have its own Site (but doesn't have to) and must belong to at least one User but possibly more.

A User can have one Site and can have many Listings.


So something like:

User has_one Site
User
has_many Listings

Listing
has_one Site
Listing
has_many Users

A Site has_many Users

Is had a look at has many through and polymorphic associations but kept getting stuck with all the two way relationships so I suspect I'm going about this the wrong way.

Posted in How do I do upserts and/or conditional saves?

Thanks again Jacob!

I originally had the first_or_create as a block with the image code inside it which would save the post objects with only the external_id so I was missing the crucial @new_post.assign_attributes(post_body)


Posted in How do I do upserts and/or conditional saves?

I have an external API that I am parsing and saving to the database via a scheduled job and I am having trouble working out how to:

A: How to only save the parsed object if it has changed.
B. How to only save the related images if the post object has changed.

Luckily every object from the API comes with a mod_time field which is updated whenever something changes.

Below is a simplified version of what I have now which is currently just saving straight to the database.

module SomeApi
  class ImportPosts < Api

    def self.import
      response = client.get

      response.body['posts'].each do |post|
        post_body = {
          external_id: post['external_id'],
          mod_time: post['mod_time'],
          status: post['status'],
          external_post_id: post['post_id'],
          title: post['title'],
          body:  post['body']
        }

        # This works as it should with no dublicates
        @new_post = Post.where(external_id: post_body[:external_id]).first_or_create(post_body)

        # This will always run
        post['objects']['img'].each do |media|
          @new_post.images.build(
            external_id: media['external_id'],
            mod_time: media['mod_time'],
            url: media['url'],
            format: media['format']
          ).save
        end
      end
    end

    def client
      Api.client
    end
  end
end

My models are pretty standard:

Post
class Post < ApplicationRecord
  has_many :images

  accepts_nested_attributes_for :images
end

Image
class Image < ApplicationRecord
  belongs_to :post
end

As you can see, the Post will only save if the external_id does not exist but I think I need to first check if the mod_time is newer but I can only do that if the post object has been saved to compare.


The other issue which I have found surprisingly difficult, is how to only run the associated images if the parent post is ran? I tried wrapping the image code in a first_or_create block but it only saves the posts external_id.


Posted in Best way to create a belongs_to object from a has_many

Haha thanks, Mate, I think we must have posted at the same time.

Thanks for your help Jacob!

Posted in Best way to create a belongs_to object from a has_many

Man, I love Ruby!

I just changed the above to below and it works perfectly!

  def slug_candidates
[
user.first_name,
[user.first_name, user.last_name,],
[user.first_name, user.last_name, :id]
]
end

Posted in Best way to create a belongs_to object from a has_many

Ahh perfect thanks, Jacob!

I was originally generating the :subdomains in the user model, but now that I have the Sites model I think I should move it there but this obviously won't work now as it doesn't know about the users :first_name & :last_name

class Site < ApplicationRecord
extend FriendlyId
friendly_id :slug_candidates, use: :slugged, slug_column: :subdomain
belongs_to :user

def slug_candidates
[
:first_name,
[:first_name, :last_name],
[:first_name, :last_name, :id],
]
end
end

Posted in Best way to create a belongs_to object from a has_many

I think what you suggested is correct and I have updated my models but I'm still stuck on the controllers.

So I have a list of users who each have a link that looks like this:

<%= link_to "Create site", sites_path(user_id: user.id), method: :post %>

But I'm not sure how to handle the params?

class SitesController < ActionController::Base
  def create
    @user = User.find(params[:user_id])
    @site = @user.build_site(subdomain: @user.subdomain)
    @site.save
  end


  def listing_params
    params.fetch(:listing, {}).permit!
  end
end


This is what my Sites schema looks like:

  create_table "sites", force: :cascade do |t|
    t.string "subdomain"
    t.string "main_domain"
    t.string "type"
    t.string "label"
    t.bigint "user_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["user_id"], name: "index_sites_on_user_id"
  end

As you can see, I have a subdomain which is created from the users first_name & last_name by friendly_id

Posted in Best way to create a belongs_to object from a has_many

Hi Jacob!

I started out with that association but the Site holds :subdomain, & :main_domain which is then used to load realted layouts, pages and other relations so I'm not sure how I would do this if a Site belongs_to a User?

Posted in Best way to create a belongs_to object from a has_many

Sorry for the vague title but I cant think of a better explanation! 

Let's say I have a list of Users and an admin could click a button/link that would create a site for them from the existing user's data. What would be the best way to set this up?

This is what I currently have but I feel like I'm on the wrong track:

class Site < ApplicationRecord
  has_one :user
end

class User < ApplicationRecord
  belongs_to :site
end

I then have a link on the list of users:

<%= link_to "Create site", sites_path(user_id: user.id), method: :post %>


And then the Sites controller:

class SitesController < ActionController::Base
  def create
     # What now?
  end

  def site_params
    params.fetch(:site, {}).permit!
  end
end


Posted in Stimulus JS Framework Introduction Discussion

I'm very excited about Stimulus and can't wait to see where it goes!

Posted in How do I save a save a has_many association?

Thanks Jacob!

Posted in How do I save a save a has_many association?

I have setup a service that parses and saves the result of an external API which all works great, however, I can't work out how to save the array of images that need to be saved in a has_many relation.

The service:

module Domain
  class FetchUserPosts

    def initialize(token)
      @token = token
    end

    def get_listings
      resp = client.get("/v1/some/api/posts")

      resp.body.each do |post|
       @user.posts.create(
          headline: post["headline"],
          body: post["body"],
          images: listing["media"].map { |l| l["url"]} ???
        )
      end
    end

    private

    def client
      Faraday.new(url: "https://example.com", headers: { "Authorization" =>  "Bearer #{@token}" }) do |f|
        f.request :json
        f.request :retry
        f.response :logger, Rails.logger
        f.response :raise_error
        f.response :json
        f.adapter Faraday.default_adapter
      end
    end

  end
end

The Post Model that it saves to:

class Post < ApplicationRecord
  belongs_to :user
  has_many :images, as: :imageable
end

And the associated Image model:

class Image < ApplicationRecord
  belongs_to :imageable, polymorphic: true
end

Everything works except I'm unsure how to deal with the images which look like:

[{
   "type" => "image",
   "url" => "https://image-1.jpg"
 }, {
   "type" => "image",
   "url" => "https://image-2.jpg"
}]

Posted in Drag and drop form builder

Posted in HTTP, Faraday, Typhoeus or something else?

I have an external JSON API that I need to parse and save and was wondering if anyone had any suggestions on what to use?

I found tons of info on building a rails API but very little on consuming an external one.

Posted in Looking for advice on how to setup a relationship.

I'm an idiot.

Thanks for all your help Jacob!

Posted in Looking for advice on how to setup a relationship.

Your a legend!

It appeasr to be all working now except I'm unnsure how to set the user_id on Post

Post Model

class Post < ApplicationRecord
  belongs_to :postable, polymorphic: true
  belongs_to :user
end

Article Model

class Article < ApplicationRecord
    belongs_to :user
    has_many :posts, as: :postable
end

Review Model

class Review < ApplicationRecord
    belongs_to :user
    has_many :posts, as: :postable
end

Post Controller

class PostsController < ApplicationController
  def create
    # @post = current_user.posts.build(posts_params) << How do I do this now?
    @parent = parent
    @post = @parent.posts.new(post_params)

    respond_to do |format|
      if @post.save
        format.html { redirect_to posts_path, notice: "Post successfully created!"}
      else
        format.html { render :new }
      end
    end
  end

  private

  def parent
    Article.find(post_params[:article_id]) if post_params[:article_id]
    Review.find(post_params[:review_id]) if post_params[:review_id]
  end
end