From 623292eb16593a02e11786116ab6efd5a14047fe Mon Sep 17 00:00:00 2001 From: Paul Duncan Date: Tue, 8 Mar 2016 03:27:56 -0500 Subject: lots of fixes --- src/guff/api-methods.cr | 66 +++++++++++++++++---- src/guff/database.cr | 8 +-- src/guff/model.cr | 10 +++- src/guff/post-model.cr | 133 +++++++++++++++++++++++++++++++++++++++--- src/guff/site-model.cr | 8 ++- src/guff/tag-model.cr | 42 +++++++++++++ src/guff/template-database.cr | 14 ++++- src/guff/template.cr | 27 ++++++--- 8 files changed, 271 insertions(+), 37 deletions(-) diff --git a/src/guff/api-methods.cr b/src/guff/api-methods.cr index d0bf415..c29b324 100644 --- a/src/guff/api-methods.cr +++ b/src/guff/api-methods.cr @@ -248,7 +248,6 @@ module Guff }, }, - "test": { "version": { text: "Get version", @@ -335,37 +334,54 @@ module Guff args : Hash(String, String) ) @models.post.get_posts( - site_id: @models.site.to_site(context.request.headers["host"]?), + site_id: get_site(context), q: args["q"]? || "", - tags: (args.has_key?("tags") && args["tags"].size > 0) ? [args["tags"].split(',')] : NO_TAGS, + tags: get_posts_tags(args["tags"]), page: args.has_key?("page") ? args["page"].to_i : 1, ).to_json end - private def get_site_id(host : String?) - # TODO - 0 - end - private def do_post_add_post( context : HTTP::Server::Context, args : Hash(String, String) ) - @models.post.add_post(args).to_json + post_id = @models.post.add_post( + site_id: get_site(context), + slug: args["slug"], + name: args["name"], + body: args["name"], + tags: get_tags(args["tags"]?), + ) + + # return json + { post_id: post_id }.to_json end private def do_post_remove_posts( context : HTTP::Server::Context, args : Hash(String, String) ) - @models.post.remove_posts(args).to_json + @models.post.remove_posts( + site_id: get_site(context), + post_ids: args["post_ids"].split(',').map { |post_id| + post_id.to_s.to_i + }, + ) + + { ok: true }.to_json end private def do_post_set_tags( context : HTTP::Server::Context, args : Hash(String, String) ) - @models.post.set_tags(args).to_json + @models.post.set_tags( + site_id: get_site(context), + post_id: (args["post_id"] as String).to_i, + tags: get_tags(args["tags"]?), + ) + + { ok: true}.to_json end ############### @@ -486,5 +502,33 @@ module Guff ) raise "some random error" end + + ################### + # utility methods # + ################### + + private def get_site(context : HTTP::Server::Context) : Int? + @models.site.to_site(context.request.headers["host"]?) + end + + private def get_tags( + s : String? + ) : Array(String) + if s && s.size > 0 + Array(String).from_json(s) + else + [] of String + end + end + + private def get_posts_tags( + s : String? + ) : Array(Array(String)) + if s && s.size > 0 + Array(Array(String)).from_json(s) + else + [] of Array(String) + end + end end end diff --git a/src/guff/database.cr b/src/guff/database.cr index bab300f..0e41b75 100644 --- a/src/guff/database.cr +++ b/src/guff/database.cr @@ -71,7 +71,7 @@ module Guff def all( sql : String, args = nil : Array(String) | Hash(String, String) | Nil, - &block : Proc(Hash(String, ::SQLite3::Value), Nil) \ + &block : Hash(String, ::SQLite3::Value) -> \ ) # build statement run(sql, args) do |rs| @@ -117,7 +117,7 @@ module Guff def query( sql : String, args = nil : Array(String) | Hash(String, String) | Nil, - &block : Proc(::SQLite3::ResultSet, Nil) \ + &block : ::SQLite3::ResultSet -> \ ) run(sql, args, &block) end @@ -131,7 +131,7 @@ module Guff private def run( sql : String, args : Hash(String, String), - &block : Proc(::SQLite3::ResultSet, Nil) \ + &block : ::SQLite3::ResultSet -> \ ) run(sql, [args], &block) end @@ -139,7 +139,7 @@ module Guff private def run( sql : String, args = nil : Array(String | Hash(String, String))?, - &block : Proc(::SQLite3::ResultSet, Nil) \ + &block : ::SQLite3::ResultSet -> \ ) # build statement puts "sql = %s" % [sql] diff --git a/src/guff/model.cr b/src/guff/model.cr index 515af4e..bd35629 100644 --- a/src/guff/model.cr +++ b/src/guff/model.cr @@ -26,7 +26,7 @@ module Guff key : Symbol, args : Array(String) | Hash(String, String) | Nil, tmpl_args : Hash(String, String) | Nil, - &block : Proc(Hash(String, ::SQLite3::Value), Nil) \ + &block : Hash(String, ::SQLite3::Value) -> \ ) @db.all(key, args, tmpl_args, &block) end @@ -39,11 +39,19 @@ module Guff @db.query(key, args, tmpl_args) end + def transaction(&block) + @db.transaction(&block) + end + def template( key : Symbol, args : Hash(String, String) | Nil ) @db.template(key, args) end + + def last_insert_row_id + @db.last_insert_row_id + end end end diff --git a/src/guff/post-model.cr b/src/guff/post-model.cr index 4788ba3..0a78458 100644 --- a/src/guff/post-model.cr +++ b/src/guff/post-model.cr @@ -42,6 +42,34 @@ module Guff ORDER BY %{sort} %{dir} OFFSET :offset LIMIT :limit ", + + remove_posts: " + UPDATE posts + SET is_active = 0 + WHERE site_id = :site_id + AND post_id IN (%{post_ids}) + ", + + set_tags_delete: " + DELETE FROM post_tags WHERE post_id = ( + SELECT post_id + FROM posts + WHERE site_id = :site_id + AND post_id = :post_id + ) + ", + + set_tags_insert: " + INSERT INTO post_tags(post_id, tag_id) + SELECT a.post_id, + b.tag_id + + FROM posts a + CROSS JOIN tags b + + WHERE a.site_id = :site_id + AND b.name IN (%{tags}) + ", }) def initialize(models : Models) @@ -196,19 +224,106 @@ module Guff }) : NO_POSTS end - def add_post(req) - # TODO: return post id - {ok: true} + #################### + # add_post methods # + #################### + + def add_post( + site_id = nil : Int?, + slug = "" : String, + name = "" : String, + body = "" : String, + tags = [] of String : Array(String), + ) : Int64 + post_id = -1_i64 + + # check slug, name, and body + { "slug": slug, "name": name, "body": body }.each do |name, text| + raise "invalid %s" % [name] unless text.size > 0 + end + + transaction do + query(:add_post, { + "site_id": (site_id || @models.site.get_default).to_s, + "slug": slug, + "name": name, + "raw_body": body, + "body": body, + }, nil) + + # get post id + post_id = last_insert_row_id + + # set post tags + set_tags( + site_id: site_id, + post_id: post_id, + tags: tags, + use_transaction: false, + ) + end + + # return post id + post_id end - def remove_posts(req) - # TODO - {ok: true} + def remove_posts( + site_id = nil : Int?, + post_ids = [] of Int : Array(Int) + ) + query(:remove_posts, { + "site_id": (site_id || @models.site.get_default).to_s, + }, { + "post_ids": post_ids.map { |post_id| + "'" + @db.quote(post_id.to_s) + "'" + }.join(',') + }) + + # no return value + nil end - def set_tags(req) - # TODO - {ok: true} + def set_tags( + site_id = nil : Int?, + post_id = nil : Int?, + tags = [] of String : Array(String), + use_transaction = true : Bool + ) + if use_transaction + transaction do + raw_set_tags(site_id, post_id, tags) + end + else + raw_set_tags(site_id, post_id, tags) + end + + nil + end + + private def raw_set_tags( + site_id = nil : Int?, + post_id = nil : Int?, + tags = [] of String : Array(String), + ) + # build sql args + args = { + "site_id": (site_id || @models.site.get_default).to_s, + "post_id": post_id.to_s, + } + + # delete existing post tags + query(:set_tags_delete, args, nil) + + if tags.size > 0 + @models.tag.add_tags(tags) + + # add new post tags + query(:set_tags_insert, args, { + "tags": tags.map { |tag| + "'" + @db.quote(tag) + "'" + }.join(','), + }) + end end end end diff --git a/src/guff/site-model.cr b/src/guff/site-model.cr index ecde831..9c8899a 100644 --- a/src/guff/site-model.cr +++ b/src/guff/site-model.cr @@ -13,11 +13,13 @@ module Guff super(models, SQL) end - def get_default - one(:get_default, nil, {} of String => String) + def get_default : Int + r = one(:get_default, nil, {} of String => String) + raise "no default site" unless r + r.to_i end - def to_site(host : String?) + def to_site(host : String?) : Int # TODO get_default end diff --git a/src/guff/tag-model.cr b/src/guff/tag-model.cr index 5d758d1..2aa9d89 100644 --- a/src/guff/tag-model.cr +++ b/src/guff/tag-model.cr @@ -1,10 +1,52 @@ module Guff class TagModel < Model SQL = TemplateCache.new({ + add_tags: " + INSERT INTO tags(name) VALUES(%{tags}) + ", + + get_tags: " + SELECT tag_id, + name + + FROM tags + + WHERE name IN (%{tags}) + ", } of Symbol => String) def initialize(models : Models) super(models, SQL) end + + def add_tags(tags : Array(String)) + # get ids of existing tags + ids = get_ids(tags) + + query(:add_tags, nil, { + "tags": tags.reject { |tag| + ids[tag]? + }.map { |tag| + "'" + @db.quote(tag) + "'" + }.join(','), + }) + end + + private def get_ids( + tags = [] of String : Array(String) + ) : Hash(String, Int32) + r = {} of String => Int32 + + all(:get_tags, nil, { + "tags": tags.map { |tag| + "'" + @db.quote(tag) + "'" + }.join(','), + }) do |row| + r[row["name"] as String] = (row["tag_id"] as String).to_i + end + + # return result + r + end end end diff --git a/src/guff/template-database.cr b/src/guff/template-database.cr index 7ff9690..bfe8321 100644 --- a/src/guff/template-database.cr +++ b/src/guff/template-database.cr @@ -23,7 +23,7 @@ module Guff key : Symbol, args : Array(String) | Hash(String, String) | Nil, tmpl_args : Hash(String, String)?, - &block : Proc(Hash(String, ::SQLite3::Value), Nil) \ + &block : Hash(String, ::SQLite3::Value) -> \ ) @db.all(template(key, tmpl_args), args, &block) end @@ -42,5 +42,17 @@ module Guff ) @templates[key].run(args) end + + def last_insert_row_id + @db.last_insert_row_id + end + + def quote(s) : String + @db.quote(s) + end + + def transaction(&block) + @db.transaction(&block) + end end end diff --git a/src/guff/template.cr b/src/guff/template.cr index 4b54fb6..f5a1693 100644 --- a/src/guff/template.cr +++ b/src/guff/template.cr @@ -7,16 +7,19 @@ module Guff @has_keys = @tokens.select { |t| t.type == :key }.size > 0 end - def run(args = nil : Hash(String, String)?) : String + def run(args : Nil) : String + raise_missing if @has_keys + + # return literal string + @string + end + + def run( + args = {} of String => String : Hash(String, String) + ) : String if @has_keys # check template args - if !args || args.size == 0 - raise "missing template args: %s" % [@tokens.select { |t| - t.type == :key - }.map { |t| - t.value - }.sort.join(", ")] - end + raise_missing if args.size == 0 # build result String.build do |r| @@ -30,6 +33,14 @@ module Guff end end + private def raise_missing + raise "missing template args: %s" % [@tokens.select { |t| + t.type == :key + }.map { |t| + t.value + }.sort.join(", ")] + end + SCAN_RE = %r{ # match key (?:%\{(?[^\}]+)\}) -- cgit v1.2.3