I am trying to get metadata from an sqlite database. The main purpose for the moment was to get a list of tables.
Code below (from here: link):
(defn db-get-tables
"Demonstrate getting table info"
[]
(sql/with-connection db
(into []
(resultset-seq
(-> (sql/connection)
(.getMetaData)
(.getTables nil nil nil (into-array ["TABLE" "VIEW"])))))))
This gives me a list of maps with metadata regarding the tables in the database. However, if I try to iterate this list (using 'for' or 'first') it gives me:
"Don't know how to create ISeq from proj00.operations.database$tables-list"
I believe that there must be an easy way to do this. But I just cannot find the right information on the web. Also, I cannot understand where that error is coming from.
It might be because the connection with the database is only open within the scope of "sql/with connection db". If I iterate through the collection like this with Microsoft SQL, I get an error that the connection is closed.
If you wrap the resultset-seq in a doall, this should be fixed. This breaks lazyness though in favor of getting all results into memory and being able to close the connection. If you want to keep lazyness, you should put the iteration within the "with-connection" scope, but you'll keep the connection open until you're done.
Also, you can generalize this function into supporting all metadata methods by making a macro of this (thanks to Verneri Ã…berg's answer to a question of mine):
(defmacro get-sql-metadata [db method & args]
`(with-connection
~db
(doall
(resultset-seq
(~method
(.getMetaData (connection))
~#args)))))
So now you can call the metadata with the metadata method and its own parameters like so:
(get-sql-metadata db .getTables nil nil nil (into-array ["TABLE" "VIEW"]))
or
(get-sql-metadata db .getColumns nil nil nil nil)
Followup:
Created a testdatabase, connected, everything should work like this.
Leiningen
(defproject sqlitetest "1.0.0-SNAPSHOT"
:description "FIXME: write description"
:dependencies [[org.clojure/clojure "1.3.0"]
[org.xerial/sqlite-jdbc "3.6.16"]
[org.clojure/java.jdbc "0.1.0"]])
Program
(ns sqlitetest
(:use [clojure.java.jdbc]))
(def db { :classname "org.sqlite.JDBC"
:subprotocol "sqlite"
:subname "E:/temp/chinook.db"})
(defmacro get-sql-metadata [db method & args]
`(with-connection
~db
(doall
(resultset-seq
(~method
(.getMetaData (connection))
~#args)))))
(def tables-list
(get-sql-metadata db .getTables nil nil nil (into-array ["TABLE" "VIEW"])))
REPL
sqlitetest=>(map :table_name tables-list)
("SQLITE_SEQUENCE" "ALBUM" "ARTIST" "CUSTOMER" "EMPLOYEE" "GENRE" "INVOICE" "INVOICELINE" "MEDIATYPE" "PLAYLIST" "PLAYLISTTRACK" "TRACK")
sqlitetest=>(first tables-list)
{:self_referencing_col_name nil, :table_name "SQLITE_SEQUENCE", :type_schem nil, :ref_generation nil, :table_type "TABLE", :table_schem nil, :table_cat nil, :type_cat nil, :type_name nil, :remarks nil}
Remark on your comment and answer to the question
The error is caused by doing defn instead of def over the table-list as in your comment. I run into the same error if I use defn.
Related
In my ActionMailer::TestCase test, I'm expecting:
#expected.to = BuyadsproMailer.group_to(campaign.agency.users)
#expected.subject = "You submitted #{offer_log.total} worth of offers for #{offer_log.campaign.name} "
#expected.from = "BuyAds Pro <feedback#buyads.com>"
#expected.body = read_fixture('deliver_to_agency')
#expected.content_type = "multipart/mixed;\r\n boundary=\"something\""
#expected.attachments["#{offer_log.aws_key}.pdf"] = {
:mime_type => 'application/pdf',
:content => fake_pdf.body
}
and stub my mailer to get fake_pdf instead of a real PDF normally fetched from S3 so that I'm sure the bodies of the PDFs match.
However, I get this long error telling me that one email was expected but got a slightly different email:
<...Mime-Version: 1.0\r\nContent-Type: multipart/mixed\r\nContent-Transfer-Encoding: 7bit...> expected but was
<...Mime-Version: 1.0\r\nContent-Type: multipart/mixed;\r\n boundary=\"--==_mimepart_50f06fa9c06e1_118dd3fd552035ae03352b\";\r\n charset=UTF-8\r\nContent-Transfer-Encoding: 7bit...>
I'm not matching the charset or part-boundary of the generated email.
How do I define or stub this aspect of my expected emails?
Here's an example that I copied from my rspec test of a specific attachment, hope that it helps (mail can be creating by calling your mailer method or peeking at the deliveries array after calling .deliver):
mail.attachments.should have(1).attachment
attachment = mail.attachments[0]
attachment.should be_a_kind_of(Mail::Part)
attachment.content_type.should be_start_with('application/ics;')
attachment.filename.should == 'event.ics'
I had something similar where I wanted to check an attached csv's content. I needed something like this because it looks like \r got inserted for newlines:
expect(mail.attachments.first.body.encoded.gsub(/\r/, '')).to(
eq(
<<~CSV
"Foo","Bar"
"1","2"
CSV
)
)
I'm trying to step through a list of records that are retrieved with find_each.
I patterned my controller code on the answer in this stack overflow post, but I'm still getting a "No Block Given (Yield)" error.
I'm just getting started in Ruby and Rails and I haven't yet found a full fledged explanation (lots of basic examples though) of blocks and yield that gives me what I need.
My code looks like this:
def select_save
#class = params[:class]
#student_id = params[:id]
#class.each do |id|
old_subject = Subject.find(id)
new_subject = old_subject.dup
new_subject.student_id = #student_id
new_subject.save
Assignment.find_each.where(:subject_id => id) do |assignments|
assignments.each do |a|
new_assignment = a.dup
new_assignment.subject_id = new_subject.id
new_assignment.save
end
end
end
respond_to do |format|
format.html { redirect_to #student, :notice => 'Subject and assignments created.' }
end
end
and the error points to the line with find_each.
I know I need a block to yield to, but how exactly that would look in this particular case escapes me.
Thanks for any suggestions.
You're passing a block to where and no block to find_each. You can't do that. You need to reverse find_each and where on this line, the order is important as the block is passed to the last method invoked:
Assignment.find_each.where(:subject_id => id) do |assignments|
It should read:
Assignment.where(:subject_id => id).find_each do |assignments|
Your next problem is you're trying to iterate over assignments, which is a single Assignment. find_each is already doing the iteration for you, passing one assignment into the block at a time. The block should read:
Assignment.where(:subject_id => id).find_each do |assignment|
new_assignment = assignment.dup
new_assignment.subject_id = new_subject.id
new_assignment.save
end
I'm going to make the assumption that your Subject has many Assignments, since you have a subject_id inside your Assignment class. If this is the case, the final and most correct way to write your loop would be:
old_subject.assignments.each do |assignment|
new_assignment = assignment.dup
new_assignment.subject_id = new_subject.id
new_assignment.save
end
I searched, and searched.
I went to IRC
Hope the question is not silly. If it was, the right string to search at google would still be much appreciated
Answering such questions with the refactoring engine is quite easy. The following code finds all occurrences of / in the system:
allCodeWithSlash := RBBrowserEnvironment new matches: '/'
From there you can further scope the search, e.g. to inside a class:
allCodeWithSlashInClass := allCodeWithSlash forClasses: (Array with: DosFileDirectory)
Or inside a package:
allCodeWithSlashInPackage := allCodeWithSlash forPackageNames: (Array with: 'Files')
If you have an UI loaded, you can open a browser on any of these search results:
allCodeWithSlashInPackage open
If you use OmniBrowser, you can also build and navigate these scopes without typing any code through the Refactoring scope menu.
Here's an example that shows you all methods in DosFileDirectory containing the string '\'.
aString := '\\'.
class := DosFileDirectory.
methodsContainingString := class methodDictionary values select: [:method |
method hasLiteralSuchThat: [:lit |
(lit isString and: [lit isSymbol not]) and:
[lit = aString]]].
messageList := methodsContainingString collect: [ :e | MethodReference new setStandardClass: class methodSymbol: e selector ].
SystemNavigation new
browseMessageList: messageList
name: 'methods containing string'.
To search a whole package, wrap the search part in:
package := PackageOrganizer default packageNamed: packageName ifAbsent: [ self error: 'package doesn't exist' ].
package classesAndMetaClasses do: [ :class | ... ]
I can't figure out why in the following line of code
res = https.get(url, nil)
The application stops and give me the following exception:
undefined method `keys' for nil:NilClass
The strange things is that this error happens only in my development enviroment, when I put the application online (heroku), everyhthing work like a charm.
That above line of code, use this inclusion:
require 'net/https'
Is anybody able to explain me why ?
Tnx
If you don't want to pass any headers just use:
res = https.get(url, {})
in the documentation for Net::HTTP this method definition is
def get(path, initheader = {}, dest = nil, &block) # :yield: +body_segment+
res = nil
if HAVE_ZLIB
unless initheader.keys.any?{|k| k.downcase == "accept-encoding"}
initheader["accept-encoding"] = "gzip;q=1.0,deflate;q=0.6,identity;q=0.3"
#compression = true
....
in your code initheader is nil so error happens on line four initheader.keys It may work fine on heroku because there is if HAVE_ZLIB which can be faulse so code is skipped.
I'm working on a program that converts the USDA nutrition database files from ASCII into an sqlite database. Information about those files can be found at http://www.ars.usda.gov/Services/docs.htm?docid=8964 and amounts to something really awesome.
However, in the code below, I have my sql connection "disappearing" as soon as I enter a for construct in the function that does conversions.
(defn sanitize-field [field]
(cond
(= field "~~") ""
(= field "") 0
(and (.startsWith field "~") (.endsWith field "~"))
(.substring field 1 (- (.length field) 1))
:default (try
(Double. field)
(catch NumberFormatException n 0.0))))
(defn field-separator [line]
(.split line "\\^"))
(defn parse-file [file]
(map (fn [l] (map sanitize-field (field-separator l)))
(.split (slurp (.getCanonicalPath file)) "\r\n")))
(defn convert-food-description [source-dir]
(println (sql/connection))
(for [entry (parse-file (File. source-dir "FOOD_DES.txt"))]
(do
(println entry)
(println (sql/connection))
(sql/insert-rows "food_des" entry))))
(Class/forName "org.sqlite.JDBC")
(def db {:classname "com.sqlite.JDBC" :subprotocol "sqlite" :subname "nutrition.sqlite"})
(defn convert []
(sql/with-connection db
(convert-food-description (File. "/home/savanni/Downloads/nutrient_database"))))
When I run the convert operation, here is what I get:
user=> (convert)
(convert)
#<Conn org.sqlite.Conn#180e426>
((01001 0100 Butter, salted BUTTER,WITH SALT Y 0.0 6.38 4.27 8.79 3.87)
java.lang.Exception: no current database connection
So, my only println commands are in convert-food-description. It looks like the first one right after the function begins executes fine, printing out the connection that I created with the with-connection statement. But then the for loop begins, I print out the first line of data that I'm going to insert into the database, and then it throws an exception when trying to print the SQL connection again.
What could be causing the connection to disappear there? Is something else entirely happening?
UPDATE: the sql/ functions here all come from clojure.contrib.sql
for is lazy. Your connection handle is being opened and closed by with-connection before for in convert-food-description evaluates.
You have to force evaluation while the connection handle is still alive. Try changing for to doseq, for example.
user> (require '(clojure.contrib [sql :as sql]))
nil
user> (import 'org.sqlite.JDBC)
org.sqlite.JDBC
user> (def db {:classname "com.sqlite.JDBC"
:subprotocol "sqlite"
:subname "foo.sqlite"})
#'user/db
user> (sql/with-connection db (sql/create-table "foo" ["val" "int"]))
(0)
;; Bad
user> (defn foo []
(sql/with-connection db
(for [x (range 10)]
(sql/insert-records "foo" {:val x}))))
#'user/foo
user> (foo)
; Evaluation aborted.
;;java.lang.Exception: no current database connection
;; Good
user> (defn foo []
(sql/with-connection db
(doseq [x (range 10)]
(sql/insert-rows "foo" [x]))))
#'user/foo
user> (foo)
nil
First of all, your (println (sql/connection)) is not printing your current connection. Instead it creates new one and prints it.
Second, you have defined db as (def db {:classname "com.sqlite.JDBC" :subprotocol "sqlite" :subname "nutrition.sqlite"})
This doesn't open connection, this is just a connection description.
Third, you should pass db as a parameter to convert-food-description method, and use that for actual queries.