diff --git a/Classes/article.rb b/Classes/article.rb index 2ccb54a..ece0d68 100644 --- a/Classes/article.rb +++ b/Classes/article.rb @@ -8,13 +8,14 @@ require "constants" require "data_fetcher" +require "cgi" module Instapaper class Article < DataFetcher - attr_accessor :ip_id, :title, :site, :status, :html + attr_accessor :ip_id, :title, :site, :status, :html, :url def initialize(id, title = "") @@ -48,11 +49,12 @@ def fetch log("fetch") @loading = true - init_fetch + connection = init_fetch end def connection(connection, didFailWithError: error) + log("connection:didFailWithError: " + @ip_id.to_s) if super @status = { :error => error.localizedDescription } @html = "" @@ -63,65 +65,79 @@ def connection(connection, didFailWithError: error) def connectionDidFinishLoading(connection) super + log("connection:connectionDidFinishLoading: " + @ip_id.to_s) - process_raw + error = Pointer.new_with_type("@") + doc = NSXMLDocument.alloc.initWithData(@data, options: NSXMLDocumentTidyHTML, error: error) + + if NSString.alloc.initWithData(@data, encoding: NSUTF8StringEncoding).empty? + @html = "" + else + @html = process_raw doc, error + end + notify("article.fetched") end - private + # Wandelt die rohen Daten in HTML um, räumt das HTML auf, erzeugt einige # weitere Attribute (+@site+ etc.). - def process_raw - log("process_raw") - error = Pointer.new_with_type("@") - @doc = NSXMLDocument.alloc.initWithData(@data, options: NSXMLDocumentTidyHTML, error: error) + def process_raw(doc, error) + result = NSXMLDocument.alloc.initWithXMLString("", options: NSXMLDocumentTidyHTML, error: error) - if NSString.alloc.initWithData(@data, encoding: NSUTF8StringEncoding).empty? - @html = "" - return - end - - # Titel etc. extrahieren - @site = get_xpath_value_from_doc("//div[@class='sm']") + body = result.nodesForXPath("//body", error: error).first - # Unnützes Zeug löschen + # Titel etc. extrahieren + @site = get_xpath_value_from_doc(doc, "//*[@id='titlebar']/span/a") + @title = get_xpath_value_from_doc(doc, "//h1") + + # Unnützes Zeug löschen - strip out redundant stuff xpath_selectors = [ "//style", "//link", "//meta", + "//script", "//noscript", - "//@*[local-name()='style']", - "//div[contains(@class, 'top')]", - "//div[contains(@class, 'bar')][1]" + "//img" ].join("|") - @doc.nodesForXPath(xpath_selectors, error: error).each { |n| n.detach } - - adjust_bottom_bar - adjust_blockquotes + doc.nodesForXPath(xpath_selectors, error: error).each { |n| n.detach } + + #adjust_blockquotes doc - @html = NSMutableString.alloc.initWithData(@doc.XMLData, encoding: NSUTF8StringEncoding) - end + if doc.nodesForXPath("//h1", error: error).first.nil? + log('process_raw:unexpected title format for ' + @title + "/" + @url) + else + title = doc.nodesForXPath("//h1", error: error).first.detach + body.addChild(title) + end + + if doc.nodesForXPath("//div[@id='story']", error: error).first.nil? + log('process_raw:unexpected story format for ' + @url) + else + story = doc.nodesForXPath("//div[@id='story']", error: error).first.detach + body.addChild(story) + + add_bottom_bar body + end + NSMutableString.alloc.initWithData(result.XMLData, encoding: NSUTF8StringEncoding) + end - def get_xpath_value_from_doc(xpath_selector) + private + def get_xpath_value_from_doc(doc, xpath_selector) error = Pointer.new_with_type("@") - value = @doc.nodesForXPath(xpath_selector, error: error).first.objectValue rescue "" + value = doc.nodesForXPath(xpath_selector, error: error).first.objectValue rescue "" + value = CGI.unescapeHTML(value) + value = value.gsub('’','\'') normalize_string(value) end - def adjust_bottom_bar - error = Pointer.new_with_type("@") - bb = @doc.nodesForXPath("//div[contains(@class, 'bottom')] | //div[contains(@class, 'bar')]", error: error).first - - return if bb.nil? - - bb.setChildren(nil) - + def add_bottom_bar(doc) tags = [ ["hr"], ["p", "You're reading an Instapaper article which was copied to your reader by Ephemera, the Mac tool for IP enthusiasts. Delete this article on your reading device, and during the next sync it'll be archived on Instapaper.com."], @@ -138,14 +154,14 @@ def adjust_bottom_bar node = NSXMLNode.elementWithName(n) end - bb.insertChild(node, atIndex: i) + doc.addChild(node) end end - def adjust_blockquotes + def adjust_blockquotes doc error = Pointer.new_with_type("@") - @doc.nodesForXPath("//blockquote/p", error: error).each do |node| + doc.nodesForXPath("//blockquote/p", error: error).each do |node| node.setName("div") end end @@ -154,8 +170,6 @@ def adjust_blockquotes def normalize_string(string) string.gsub(/<[^>]*>/, " ").squeeze(" ").strip end - end - end diff --git a/Classes/data_fetcher.rb b/Classes/data_fetcher.rb index 5dbacff..7649dca 100644 --- a/Classes/data_fetcher.rb +++ b/Classes/data_fetcher.rb @@ -6,9 +6,9 @@ # Copyright (c) 2010 Carlo Zottmann. All rights reserved. # +require "bootstrap" require "notifiable" - module Instapaper class DataFetcher @@ -41,7 +41,6 @@ def init_fetch cachePolicy: NSURLRequestReloadIgnoringCacheData, timeoutInterval: @timeout ) - @connection = NSURLConnection.connectionWithRequest(@request, delegate: self) end @@ -53,6 +52,7 @@ def connection(connection, didReceiveResponse: response) def connection(connection, didReceiveData:data) + log("connection:didReceiveData") # NSLog("#{self.class}#didReceiveData") @data.appendData(data) end diff --git a/Classes/unread_articles_list.rb b/Classes/unread_articles_list.rb index 7db324d..701d78c 100644 --- a/Classes/unread_articles_list.rb +++ b/Classes/unread_articles_list.rb @@ -86,7 +86,7 @@ def extract_articles_and_bundles links.each do |l| node = NSXMLDocument.alloc.initWithXMLString( l.XMLString, options: NSXMLDocumentTidyXML, error: error ) - first_subnode = node.nodesForXPath( "//a[@class='actionLink']/@href", error: error ).first + first_subnode = node.nodesForXPath( "//a[@class='actionLink' and @title='Edit']/@href", error: error ).first next if first_subnode.nil? diff --git a/Classes/window_controller.rb b/Classes/window_controller.rb index 20ba314..34a4bca 100644 --- a/Classes/window_controller.rb +++ b/Classes/window_controller.rb @@ -199,7 +199,7 @@ def singlearticlesprocessor_status(notification) article = a[:article] msg = article.site + ": " + article.title holler(msg, "→") - log("singlearticlesprocessor_status", "- #{msg}") + log("singlearticlesprocessor_status", "- " + a[:id].to_s + ": " + msg) end log("singlearticlesprocessor_status", "processing unread articles") diff --git a/Ephemera.xcodeproj/project.pbxproj b/Ephemera.xcodeproj/project.pbxproj index c26fbf0..a045350 100755 --- a/Ephemera.xcodeproj/project.pbxproj +++ b/Ephemera.xcodeproj/project.pbxproj @@ -65,7 +65,6 @@ 2395290011050A170086E993 /* world_domination.rb in Resources */ = {isa = PBXBuildFile; fileRef = 239528FF11050A170086E993 /* world_domination.rb */; }; 239DD73010F38E61007FE8B0 /* log_controller.rb in Resources */ = {isa = PBXBuildFile; fileRef = 239DD72F10F38E61007FE8B0 /* log_controller.rb */; }; 239DD73310F38E89007FE8B0 /* controller.rb in Resources */ = {isa = PBXBuildFile; fileRef = 239DD73210F38E89007FE8B0 /* controller.rb */; }; - 23A2CDD111076FCF00574599 /* gems in Resources */ = {isa = PBXBuildFile; fileRef = 23A2CDAC11076FCF00574599 /* gems */; }; 23A2CEE31108AC2C00574599 /* premade_bundles_processor.rb in Resources */ = {isa = PBXBuildFile; fileRef = 23A2CEE21108AC2C00574599 /* premade_bundles_processor.rb */; }; 23A2CF0E1108B4BC00574599 /* premade_bundle_file.rb in Resources */ = {isa = PBXBuildFile; fileRef = 23A2CF0D1108B4BC00574599 /* premade_bundle_file.rb */; }; 23A840EA1117235600DB2301 /* Sparkle.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 23CBFB5F11171F22002267F5 /* Sparkle.framework */; }; @@ -74,12 +73,38 @@ 23C83F19110ECFBB004800B9 /* EMKeychain.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 23C83F18110ECFBB004800B9 /* EMKeychain.dylib */; }; 23C83F21110ECFFD004800B9 /* EMKeychain.dylib in Resources */ = {isa = PBXBuildFile; fileRef = 23C83F18110ECFBB004800B9 /* EMKeychain.dylib */; }; 23CBFB8311171FB1002267F5 /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 23CBFB5F11171F22002267F5 /* Sparkle.framework */; }; - 23DE1AE21116E4D0008815F8 /* dsa_pub.pem in Resources */ = {isa = PBXBuildFile; fileRef = 23DE1AE11116E4D0008815F8 /* dsa_pub.pem */; }; 4DE339F70D74FCDD00ADB6EE /* rb_main.rb in Resources */ = {isa = PBXBuildFile; fileRef = 4DE339F60D74FCDD00ADB6EE /* rb_main.rb */; }; 4DE3BE140D8651D900ECA448 /* MacRuby.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4DE3BE130D8651D900ECA448 /* MacRuby.framework */; }; 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; }; 8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; }; 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; }; + AFBB7179172BF58F0019ADC7 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AFBB7178172BF58F0019ADC7 /* Cocoa.framework */; }; + AFBB717F172BF58F0019ADC7 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = AFBB717D172BF58F0019ADC7 /* InfoPlist.strings */; }; + AFBB7188172BF8630019ADC7 /* spec_helper.rb in Resources */ = {isa = PBXBuildFile; fileRef = AFBB7187172BF8630019ADC7 /* spec_helper.rb */; }; + AFBB718A172BF89A0019ADC7 /* article_spec.rb in Resources */ = {isa = PBXBuildFile; fileRef = AFBB7189172BF89A0019ADC7 /* article_spec.rb */; }; + AFBB718E172BFB440019ADC7 /* article.rb in Sources */ = {isa = PBXBuildFile; fileRef = 233236BC10FB9904005E6973 /* article.rb */; }; + AFBB71A7172BFDF50019ADC7 /* config.rb in Resources */ = {isa = PBXBuildFile; fileRef = 231549AE1110CCA400473CA3 /* config.rb */; }; + AFBB71B3172C086A0019ADC7 /* data_fetcher.rb in Resources */ = {isa = PBXBuildFile; fileRef = 233236CF10FBC91E005E6973 /* data_fetcher.rb */; }; + AFBB71B4172C08720019ADC7 /* data_fetcher.rb in Sources */ = {isa = PBXBuildFile; fileRef = 233236CF10FBC91E005E6973 /* data_fetcher.rb */; }; + AFBB71B5172C089B0019ADC7 /* bootstrap.rb in Sources */ = {isa = PBXBuildFile; fileRef = 234D33BE110B2601002688E4 /* bootstrap.rb */; }; + AFBB71B6172C089B0019ADC7 /* constants.rb in Sources */ = {isa = PBXBuildFile; fileRef = 239314C810FF590400DD5645 /* constants.rb */; }; + AFBB71B7172C089B0019ADC7 /* article_archiver.rb in Sources */ = {isa = PBXBuildFile; fileRef = 23931C1E11010BFF00DD5645 /* article_archiver.rb */; }; + AFBB71B8172C089B0019ADC7 /* config.rb in Sources */ = {isa = PBXBuildFile; fileRef = 231549AE1110CCA400473CA3 /* config.rb */; }; + AFBB71B9172C089B0019ADC7 /* login.rb in Sources */ = {isa = PBXBuildFile; fileRef = 2332348110FB2D52005E6973 /* login.rb */; }; + AFBB71BA172C089B0019ADC7 /* notifiable.rb in Sources */ = {isa = PBXBuildFile; fileRef = 2393182B1100879E00DD5645 /* notifiable.rb */; }; + AFBB71BB172C089B0019ADC7 /* premade_bundle_file.rb in Sources */ = {isa = PBXBuildFile; fileRef = 23A2CF0D1108B4BC00574599 /* premade_bundle_file.rb */; }; + AFBB71BC172C089B0019ADC7 /* premade_bundles_processor.rb in Sources */ = {isa = PBXBuildFile; fileRef = 23A2CEE21108AC2C00574599 /* premade_bundles_processor.rb */; }; + AFBB71BD172C089B0019ADC7 /* reader.rb in Sources */ = {isa = PBXBuildFile; fileRef = 239318291100874500DD5645 /* reader.rb */; }; + AFBB71BE172C089B0019ADC7 /* single_article_files.rb in Sources */ = {isa = PBXBuildFile; fileRef = 232B0A2B110C68C400BFE239 /* single_article_files.rb */; }; + AFBB71BF172C089B0019ADC7 /* single_articles_processor.rb in Sources */ = {isa = PBXBuildFile; fileRef = 236DF18A110752DA00044020 /* single_articles_processor.rb */; }; + AFBB71C0172C089B0019ADC7 /* unread_articles_list.rb in Sources */ = {isa = PBXBuildFile; fileRef = 236DF1691107521F00044020 /* unread_articles_list.rb */; }; + AFBB71C1172C089B0019ADC7 /* world_domination.rb in Sources */ = {isa = PBXBuildFile; fileRef = 239528FF11050A170086E993 /* world_domination.rb */; }; + AFBB71C2172C089B0019ADC7 /* controller.rb in Sources */ = {isa = PBXBuildFile; fileRef = 239DD73210F38E89007FE8B0 /* controller.rb */; }; + AFBB71C3172C089B0019ADC7 /* about_controller.rb in Sources */ = {isa = PBXBuildFile; fileRef = 23931D501101F52F00DD5645 /* about_controller.rb */; }; + AFBB71C4172C089B0019ADC7 /* log_controller.rb in Sources */ = {isa = PBXBuildFile; fileRef = 239DD72F10F38E61007FE8B0 /* log_controller.rb */; }; + AFBB71C5172C089B0019ADC7 /* prefs_controller.rb in Sources */ = {isa = PBXBuildFile; fileRef = 23691FF710F3EA3B00A49DAC /* prefs_controller.rb */; }; + AFBB71C6172C089B0019ADC7 /* window_controller.rb in Sources */ = {isa = PBXBuildFile; fileRef = 23BBDBAB10F4BE8100AC77B4 /* window_controller.rb */; }; + AFBB71C9172C153A0019ADC7 /* wait_helper.rb in Resources */ = {isa = PBXBuildFile; fileRef = AFBB71C8172C153A0019ADC7 /* wait_helper.rb */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -133,20 +158,26 @@ 239528FF11050A170086E993 /* world_domination.rb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.ruby; name = world_domination.rb; path = ../Ephemera/Classes/world_domination.rb; sourceTree = ""; }; 239DD72F10F38E61007FE8B0 /* log_controller.rb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.ruby; name = log_controller.rb; path = ../Ephemera/Classes/log_controller.rb; sourceTree = SOURCE_ROOT; }; 239DD73210F38E89007FE8B0 /* controller.rb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.ruby; name = controller.rb; path = ../Ephemera/Classes/controller.rb; sourceTree = SOURCE_ROOT; }; - 23A2CDAC11076FCF00574599 /* gems */ = {isa = PBXFileReference; lastKnownFileType = folder; name = gems; path = ../Ephemera/gems; sourceTree = SOURCE_ROOT; }; 23A2CEE21108AC2C00574599 /* premade_bundles_processor.rb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.ruby; name = premade_bundles_processor.rb; path = ../Ephemera/Classes/premade_bundles_processor.rb; sourceTree = ""; }; 23A2CF0D1108B4BC00574599 /* premade_bundle_file.rb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.ruby; name = premade_bundle_file.rb; path = ../Ephemera/Classes/premade_bundle_file.rb; sourceTree = ""; }; 23B98ED8110E4BCE00600227 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = English.lproj/MainMenu.xib; sourceTree = ""; }; 23BBDBAB10F4BE8100AC77B4 /* window_controller.rb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.ruby; name = window_controller.rb; path = ../Ephemera/Classes/window_controller.rb; sourceTree = SOURCE_ROOT; }; 23C83F18110ECFBB004800B9 /* EMKeychain.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = EMKeychain.dylib; sourceTree = ""; }; 23CBFB5F11171F22002267F5 /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Sparkle.framework; sourceTree = ""; }; - 23DE1AE11116E4D0008815F8 /* dsa_pub.pem */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = dsa_pub.pem; sourceTree = ""; }; 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 29B97324FDCFA39411CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; 29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; 4DE339F60D74FCDD00ADB6EE /* rb_main.rb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.ruby; path = rb_main.rb; sourceTree = ""; }; 4DE3BE130D8651D900ECA448 /* MacRuby.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MacRuby.framework; path = /Library/Frameworks/MacRuby.framework; sourceTree = ""; }; 8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + AFBB7177172BF58F0019ADC7 /* Specs.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Specs.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + AFBB7178172BF58F0019ADC7 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; + AFBB717C172BF58F0019ADC7 /* Specs-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Specs-Info.plist"; sourceTree = ""; }; + AFBB717E172BF58F0019ADC7 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + AFBB7180172BF58F0019ADC7 /* Specs-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Specs-Prefix.pch"; sourceTree = ""; }; + AFBB7187172BF8630019ADC7 /* spec_helper.rb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.ruby; path = spec_helper.rb; sourceTree = ""; }; + AFBB7189172BF89A0019ADC7 /* article_spec.rb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.ruby; path = article_spec.rb; sourceTree = ""; }; + AFBB71C8172C153A0019ADC7 /* wait_helper.rb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.ruby; path = wait_helper.rb; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -161,6 +192,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + AFBB7173172BF58F0019ADC7 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + AFBB7179172BF58F0019ADC7 /* Cocoa.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -197,6 +236,7 @@ isa = PBXGroup; children = ( 2369205410F3F44600A49DAC /* Ephemera.app */, + AFBB7177172BF58F0019ADC7 /* Specs.framework */, ); name = Products; sourceTree = ""; @@ -245,13 +285,13 @@ 29B97314FDCFA39411CA2CEA /* Ephemera */ = { isa = PBXGroup; children = ( - 23A2CDAC11076FCF00574599 /* gems */, 239318A51100923100DD5645 /* Util */, 2332347110FB2CCD005E6973 /* Models */, 239DD73110F38E6D007FE8B0 /* Controllers */, 172754AE1075979200D0347B /* Tests */, 29B97315FDCFA39411CA2CEA /* Other Sources */, 29B97317FDCFA39411CA2CEA /* Resources */, + AFBB717A172BF58F0019ADC7 /* Specs */, 29B97323FDCFA39411CA2CEA /* Frameworks */, 19C28FACFE9D520D11CA2CBB /* Products */, ); @@ -271,7 +311,6 @@ 29B97317FDCFA39411CA2CEA /* Resources */ = { isa = PBXGroup; children = ( - 23DE1AE11116E4D0008815F8 /* dsa_pub.pem */, 23271D761105FAFD00B8A53F /* autorun.bak.sh */, 23931D921101F7F900DD5645 /* about.rtf */, 239317FE10FFF37000DD5645 /* de.municode.Ephemera.plist */, @@ -287,13 +326,53 @@ isa = PBXGroup; children = ( 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */, + AFBB7178172BF58F0019ADC7 /* Cocoa.framework */, 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */, ); name = Frameworks; sourceTree = ""; }; + AFBB717A172BF58F0019ADC7 /* Specs */ = { + isa = PBXGroup; + children = ( + AFBB71C7172C153A0019ADC7 /* support */, + AFBB717B172BF58F0019ADC7 /* Supporting Files */, + AFBB7187172BF8630019ADC7 /* spec_helper.rb */, + AFBB7189172BF89A0019ADC7 /* article_spec.rb */, + ); + path = Specs; + sourceTree = ""; + }; + AFBB717B172BF58F0019ADC7 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + AFBB717C172BF58F0019ADC7 /* Specs-Info.plist */, + AFBB717D172BF58F0019ADC7 /* InfoPlist.strings */, + AFBB7180172BF58F0019ADC7 /* Specs-Prefix.pch */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + AFBB71C7172C153A0019ADC7 /* support */ = { + isa = PBXGroup; + children = ( + AFBB71C8172C153A0019ADC7 /* wait_helper.rb */, + ); + path = support; + sourceTree = ""; + }; /* End PBXGroup section */ +/* Begin PBXHeadersBuildPhase section */ + AFBB7174172BF58F0019ADC7 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + /* Begin PBXNativeTarget section */ 8D1107260486CEB800E47090 /* Ephemera */ = { isa = PBXNativeTarget; @@ -315,6 +394,24 @@ productReference = 2369205410F3F44600A49DAC /* Ephemera.app */; productType = "com.apple.product-type.application"; }; + AFBB7176172BF58F0019ADC7 /* Specs */ = { + isa = PBXNativeTarget; + buildConfigurationList = AFBB7186172BF58F0019ADC7 /* Build configuration list for PBXNativeTarget "Specs" */; + buildPhases = ( + AFBB7172172BF58F0019ADC7 /* Sources */, + AFBB7173172BF58F0019ADC7 /* Frameworks */, + AFBB7174172BF58F0019ADC7 /* Headers */, + AFBB7175172BF58F0019ADC7 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Specs; + productName = Specs; + productReference = AFBB7177172BF58F0019ADC7 /* Specs.framework */; + productType = "com.apple.product-type.framework"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -325,7 +422,11 @@ }; buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "Ephemera" */; compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 29B97314FDCFA39411CA2CEA /* Ephemera */; projectDirPath = ""; projectRoot = ""; @@ -334,6 +435,7 @@ 172754BD107597F200D0347B /* Unit Tests */, 233227D91109B43300419259 /* Embed & Compile */, 23DE1AE71116E9F7008815F8 /* Distribution */, + AFBB7176172BF58F0019ADC7 /* Specs */, ); }; /* End PBXProject section */ @@ -365,14 +467,25 @@ 23271D771105FAFD00B8A53F /* autorun.bak.sh in Resources */, 236DF16A1107521F00044020 /* unread_articles_list.rb in Resources */, 236DF18B110752DA00044020 /* single_articles_processor.rb in Resources */, - 23A2CDD111076FCF00574599 /* gems in Resources */, 23A2CEE31108AC2C00574599 /* premade_bundles_processor.rb in Resources */, 23A2CF0E1108B4BC00574599 /* premade_bundle_file.rb in Resources */, 234D33BF110B2601002688E4 /* bootstrap.rb in Resources */, 232B0A2C110C68C400BFE239 /* single_article_files.rb in Resources */, 23B98ED9110E4BCE00600227 /* MainMenu.xib in Resources */, 231549AF1110CCA400473CA3 /* config.rb in Resources */, - 23DE1AE21116E4D0008815F8 /* dsa_pub.pem in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + AFBB7175172BF58F0019ADC7 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + AFBB717F172BF58F0019ADC7 /* InfoPlist.strings in Resources */, + AFBB7188172BF8630019ADC7 /* spec_helper.rb in Resources */, + AFBB718A172BF89A0019ADC7 /* article_spec.rb in Resources */, + AFBB71A7172BFDF50019ADC7 /* config.rb in Resources */, + AFBB71B3172C086A0019ADC7 /* data_fetcher.rb in Resources */, + AFBB71C9172C153A0019ADC7 /* wait_helper.rb in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -445,6 +558,33 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + AFBB7172172BF58F0019ADC7 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + AFBB71B5172C089B0019ADC7 /* bootstrap.rb in Sources */, + AFBB71B6172C089B0019ADC7 /* constants.rb in Sources */, + AFBB71B7172C089B0019ADC7 /* article_archiver.rb in Sources */, + AFBB71B8172C089B0019ADC7 /* config.rb in Sources */, + AFBB71B9172C089B0019ADC7 /* login.rb in Sources */, + AFBB71BA172C089B0019ADC7 /* notifiable.rb in Sources */, + AFBB71BB172C089B0019ADC7 /* premade_bundle_file.rb in Sources */, + AFBB71BC172C089B0019ADC7 /* premade_bundles_processor.rb in Sources */, + AFBB71BD172C089B0019ADC7 /* reader.rb in Sources */, + AFBB71BE172C089B0019ADC7 /* single_article_files.rb in Sources */, + AFBB71BF172C089B0019ADC7 /* single_articles_processor.rb in Sources */, + AFBB71C0172C089B0019ADC7 /* unread_articles_list.rb in Sources */, + AFBB71C1172C089B0019ADC7 /* world_domination.rb in Sources */, + AFBB71C2172C089B0019ADC7 /* controller.rb in Sources */, + AFBB71C3172C089B0019ADC7 /* about_controller.rb in Sources */, + AFBB71C4172C089B0019ADC7 /* log_controller.rb in Sources */, + AFBB71C5172C089B0019ADC7 /* prefs_controller.rb in Sources */, + AFBB71C6172C089B0019ADC7 /* window_controller.rb in Sources */, + AFBB71B4172C08720019ADC7 /* data_fetcher.rb in Sources */, + AFBB718E172BFB440019ADC7 /* article.rb in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ @@ -472,6 +612,14 @@ name = MainMenu.xib; sourceTree = ""; }; + AFBB717D172BF58F0019ADC7 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + AFBB717E172BF58F0019ADC7 /* en */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ @@ -538,6 +686,76 @@ }; name = Release; }; + AFBB7184172BF58F0019ADC7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + FRAMEWORK_VERSION = A; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "Specs/Specs-Prefix.pch"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + INFOPLIST_FILE = "Specs/Specs-Info.plist"; + MACOSX_DEPLOYMENT_TARGET = 10.7; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + WRAPPER_EXTENSION = framework; + }; + name = Debug; + }; + AFBB7185172BF58F0019ADC7 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + FRAMEWORK_VERSION = A; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "Specs/Specs-Prefix.pch"; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + INFOPLIST_FILE = "Specs/Specs-Info.plist"; + MACOSX_DEPLOYMENT_TARGET = 10.7; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + WRAPPER_EXTENSION = framework; + }; + name = Release; + }; C01FCF4B08A954540054247B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -595,13 +813,13 @@ C01FCF4F08A954540054247B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = x86_64; + ARCHS = "$(NATIVE_ARCH_ACTUAL)"; GCC_ENABLE_OBJC_GC = required; GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNUSED_VARIABLE = YES; ONLY_ACTIVE_ARCH = YES; PREBINDING = NO; - SDKROOT = macosx10.5; + SDKROOT = ""; VALID_ARCHS = "i386 x86_64"; }; name = Debug; @@ -609,16 +827,13 @@ C01FCF5008A954540054247B /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = ( - i386, - x86_64, - ); + ARCHS = "$(NATIVE_ARCH_ACTUAL)"; GCC_ENABLE_OBJC_GC = required; GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNUSED_VARIABLE = YES; - ONLY_ACTIVE_ARCH = NO; + ONLY_ACTIVE_ARCH = YES; PREBINDING = NO; - SDKROOT = macosx10.5; + SDKROOT = ""; VALID_ARCHS = "x86_64 i386"; }; name = Release; @@ -653,6 +868,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + AFBB7186172BF58F0019ADC7 /* Build configuration list for PBXNativeTarget "Specs" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + AFBB7184172BF58F0019ADC7 /* Debug */, + AFBB7185172BF58F0019ADC7 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "Ephemera" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/Rakefile b/Rakefile index 002afdf..e028e15 100644 --- a/Rakefile +++ b/Rakefile @@ -1,4 +1,4 @@ -namespace :debug do +namespace :debug do namespace :prefs do diff --git a/Specs/Specs-Info.plist b/Specs/Specs-Info.plist new file mode 100644 index 0000000..fe4f581 --- /dev/null +++ b/Specs/Specs-Info.plist @@ -0,0 +1,30 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIconFile + + CFBundleIdentifier + tony2nite.${PRODUCT_NAME:rfc1034identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + NSHumanReadableCopyright + Copyright © 2013 Carlo Zottmann. All rights reserved. + NSPrincipalClass + + + diff --git a/Specs/Specs-Prefix.pch b/Specs/Specs-Prefix.pch new file mode 100644 index 0000000..acfb945 --- /dev/null +++ b/Specs/Specs-Prefix.pch @@ -0,0 +1,7 @@ +// +// Prefix header for all source files of the 'Specs' target in the 'Specs' project +// + +#ifdef __OBJC__ + #import +#endif diff --git a/Specs/article_spec.rb b/Specs/article_spec.rb new file mode 100644 index 0000000..1491ace --- /dev/null +++ b/Specs/article_spec.rb @@ -0,0 +1,37 @@ +require "#{ENV['SRCROOT']}/Specs/spec_helper" + +require "article" + +module Instapaper + + describe Article do + a = Article.new(12,"Article Title") + a.fetch() + + file = File.join(File.dirname(__FILE__), "fixtures/", "article-data-1.html") + data = File.read(file) + + e = Pointer.new_with_type("@") + doc = NSXMLDocument.alloc.initWithXMLString(data, options: NSXMLDocumentTidyHTML, error: e) + a.html = a.process_raw (doc, e) + + a.title.should == "Why Sleep Deprivation Eases Depression: Scientific American" + a.site.should == "Scientific American" + a.html.should_not == nil + a.html.should_not == "" + + + file = File.join(File.dirname(__FILE__), "fixtures/", "article-data-2.html") + data = File.read(file) + + e = Pointer.new_with_type("@") + doc = NSXMLDocument.alloc.initWithXMLString(data, options: NSXMLDocumentTidyHTML, error: e) + a.html = a.process_raw (doc, e) + + a.title.should == "Reading Other People's Code" + a.site.should == "mahdiyusuf.com" + a.html.should_not == nil + a.html.should_not == "" + + end +end \ No newline at end of file diff --git a/Specs/en.lproj/InfoPlist.strings b/Specs/en.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Specs/en.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Specs/fixtures/article-data-1.html b/Specs/fixtures/article-data-1.html new file mode 100644 index 0000000..eb859dd --- /dev/null +++ b/Specs/fixtures/article-data-1.html @@ -0,0 +1,853 @@ + + + + + + + + + + + + + + Why Sleep Deprivation Eases Depression: Scientific American + + + + +
+

Why Sleep Deprivation Eases Depression: Scientific American

+ Scientific American
+ +
+
+
+

+woman half asleep, sleepy head, woman with coffee cupimage

+Image: SHARON DOMINICK iStockphoto +

+
+

Sleep deprivation is a quick and efficient way to treat depression. It works 60 to 70 percent of the time—far better than existing drugs—but the mood boost usually lasts only until the patient falls asleep. As an ongoing treatment, sleep deprivation is impractical, but researchers have been studying the phenomenon in an effort to uncover the cellular mechanisms behind depression and remission. Now a team at Tufts University has pinpointed glia as the key players.

+

The researchers previously found that astrocytes, a star-shaped type of glial cell, regulate the brain chemicals involved in sleepiness. During our waking hours, astrocytes continuously release the neurotransmitter adenosine, which builds up in the brain and causes “sleep pressure,” the feeling of sleepiness and its related memory and attention impairments. The neurotransmitter causes this pressure by binding to adenosine receptors on the outside of neurons like a key fitting into a lock. As more adenosine builds up, more receptors are triggered, and the urge to sleep gets stronger.

+

In the new study, published online January 15 in the journal Translational Psychiatry, the scientists investigated whether this process is responsible for the antidepressant effects of sleep deprivation. Mice with depressivelike symptoms were administered three doses of a compound that triggers adenosine receptors, thus mimicking sleep deprivation. Although the mice continued to sleep normally, after 12 hours they showed a rapid improvement in mood and behavior, which lasted for 48 hours.

+

The results confirm that the adenosine buildup is responsible for the antidepressant effects of a lack of sleep. This finding points to a promising target for new drug development because it suggests that mimicking sleep deprivation chemically may offer the antidepressant benefits without the unwanted side effects of actually skipping sleep. Such an intervention could offer immediate relief from depression, in stark contrast with traditional antidepressants, which take six to eight weeks to kick in.

+

The study may also have implications beyond depression and sleep regulation, according to Dustin Hines, lead author and a postdoctoral fellow at Tufts. “For many years neuroscientists focused almost exclusively on neurons, whereas the role of glia was neglected,” Hines says. “We now know that glia play an important role in the control of brain function and have the potential to aid in the development of new treatments for many illnesses, including depression and sleep disorders.”

+

This article was originally published with the title A Fast-Acting Antidepressant.



+ +Buy This Issue + +
+If your institution has site license access, enter here. +
+
+ + + + + diff --git a/Specs/fixtures/article-data-2.html b/Specs/fixtures/article-data-2.html new file mode 100644 index 0000000..47d80d7 --- /dev/null +++ b/Specs/fixtures/article-data-2.html @@ -0,0 +1,883 @@ + + + + + + + + + + + + + + Reading Other People’s Code + + + + +
+

Reading Other People’s Code

+ mahdiyusuf.com
+ +
+
+
+

I really enjoy reading other people’s code, it allows you see how others solve common problems, and sometimes even provide parables on what you shouldn’t do with yours. It also teaches the much needed skill of reading other people’s code. Unfortunately there isn’t a convenient and fun way of reading other people’s code.

+

Now, ask yourself two questions:

+
  1. how many hours a week you spend writing code?
  2. +
  3. how many hours do you spend reading code written by someone you don’t know or have access to?
  4. +

The brave might even ask:

+
  1. how much of that code am I confident I understand, and could contribute to?
  2. +

Many people avoid reading code simply because they hate being outside of their comfort zone. Documentation is an effective use of time if it does what it says. Often times documentation is outdated and unreliable and maybe even lies. Only thing you can be sure of is the source.

+

Entire businesses have been built on the idea of sharing and writing code, but not reading.

+

Reading

+

What about reading? I would argue reading code is as integral to writing as listening is to talking. Everyone likes to talk/write its a form of expression; the art behind the craft, but neither can be appreciated without their counterparts listening/reading.

+

If I were to hand you a book and ask you to take me to where you would start reading, you would flip past the acknowledgements, table of contents, and other credits and go to the first page, or chapter one. Fantastic.

+

Now if I were to hand you a stack of code I printed out could you tell me where to start reading it as easily? Could you break the code up into chapters as easily? Should you the reader have to? What if it doesn’t have a main method? :)

+

Before I answer that, I would like to tell you a story.

+

When I first started out, I loved writing things from scratch. The clarity of how everything worked together made me feel powerful. I understood everything because I wrote it, I didn’t need to read it, because I wrote it. NIH (not invented here) is basically a thing because rewriting is actually easier than reading the source in developers minds; developers develop and winners win.

+

Why would anyone rewrite a program so they could add some functionality, when they could just as easily patch the already existing program to do what they want?

+

This involves the following things:

+
  1. Obtaining the source
  2. +
  3. Reading & Understanding the source
  4. +
  5. Contributing/Patching
  6. +
  7. Testing (hopefully)
  8. +
  9. Sharing
  10. +

Most would agree step 2 is probably what they are trying to avoid for the most part which brings us back to our original question.

+

Where is the first chapter or first page in a piece of software? In my opinion its where the writing begins. This shares many analogies with book writing in general. The architecture of a book may not change (beginning, middle, and end) but they share many parallels to software development having an end goal in mind at the start.

+

So to answer the question:

+

Where do you begin reading software? It’s where the writer starting writing it. Thats irregardless of it actually does anything. Does an unfinished story say anything? You can still read it right?

+

Rainman

+

That being said, today I am going to start working on Rainman. A tool that will use git repositories (to start out) to allow the replay of git commits and diffs in a sane manner providing transparency of the software writing process.

+

So you might be thinking that commits are too large to be useful. That might largely be the case, thats why I also intend to provide a recording tool that will allow developers to record their software development process and allow a replay that is more granular and indicative of the development process.

+

This is still in the inception phase and welcome feedback and ideas in the form of issues here.

+ + + + + +

67 notes

+ + +
+ + +

About

+

Blog covering topics ranging from software, philosophy, and typography by Mahdi Yusuf

+

Latest From Twitter

+ + + Follow On Twitter + +

Pierre 1.2 by mikedidthis

+
+ + + + diff --git a/Specs/spec_helper.rb b/Specs/spec_helper.rb new file mode 100644 index 0000000..775af1b --- /dev/null +++ b/Specs/spec_helper.rb @@ -0,0 +1,19 @@ +framework "Cocoa" + +$LOAD_PATH << File.join(File.dirname(__FILE__), "..", "Classes") + + +# Requires supporting ruby files with custom matchers and macros, etc, +# in spec/support/ and its subdirectories. +Dir["#{ENV['SRCROOT']}/Specs/support/**/*.rb"].each {|f| require f} + +RSpec.configure do |config| + # == Mock Framework + # + # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line: + # + # config.mock_with :mocha + # config.mock_with :flexmock + # config.mock_with :rr + config.mock_with :spec +end \ No newline at end of file diff --git a/Specs/support/wait_helper.rb b/Specs/support/wait_helper.rb new file mode 100644 index 0000000..65007ca --- /dev/null +++ b/Specs/support/wait_helper.rb @@ -0,0 +1,13 @@ +def wait(time, increment = 5, elapsed_time = 0, &block) + begin + yield + rescue Exception => e + if elapsed_time >= time + raise e + else + sleep increment + wait(time, increment, elapsed_time + increment, &block) + end + end +end +