RubyMotion >> How to make a POST? - rubymotion

I've been checking out RestKit and the GET works
#client.get("/?amount=5", delegate:self)
Does anyone know how to make a POST and receive the result?
The docs mention that it looks something like this -
#client.post("/ask", params:#paramvar, delegate:self)
What do you encapsulate #paramvar? I have tried it as an array, hash and even nil - however, none of them have yielded any results.

Take a look at the bubble wrap library. It includes some really nice HTTP helpers.
http://bubblewrap.io/

Found an example in the RubyMotion_Cookbook.
https://github.com/IconoclastLabs/rubymotion_cookbook/tree/master/ch_8/06_sendinghttppost
class AppDelegate
def application(application, didFinishLaunchingWithOptions:launchOptions)
#window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)
#window.rootViewController = RootController.new
url_string = "http://rubymotion-cookbook.herokuapp.com/post"
post_body = "bodyParam1=BodyValue1&bodyParam2=BodyValue2"
url = NSURL.URLWithString(url_string)
request = NSMutableURLRequest.requestWithURL(url)
request.setTimeoutInterval(30)
request.setHTTPMethod("POST")
request.setHTTPBody(post_body.to_s.dataUsingEncoding(NSUTF8StringEncoding))
queue = NSOperationQueue.alloc.init
NSURLConnection.sendAsynchronousRequest(request,
queue: queue,
completionHandler: lambda do |response, data, error|
if(data.length > 0 && error.nil?)
html = NSString.alloc.initWithData(data, encoding: NSUTF8StringEncoding)
p "HTML = #{html}"
elsif( data.length == 0 && error.nil? )
p "Nothing was downloaded"
elsif(!error.nil?)
p "Error: #{error}"
end
end
)
#window.makeKeyAndVisible
true
end
end

Source:
https://github.com/rubymotion/BubbleWrap
Insallation:-
in console run 'gem install bubble-wrap' or mention 'gem bubble-wrap' in Gemfile
Line to be added in 'app_delegate.rb'file(by this Bubblewrap api is available through out app):-
require 'bubble-wrap/http'
Sample code for syntax:-
BW::HTTP.get("https://api.github.com/users/mattetti", {credentials: {username: 'matt', password: 'aimonetti'}}) do |response|
p response.body.to_str # prints the response's body
end

Related

Instagram ?__a=1 not working anymore

I've been using Instagram's undocumented API https://www.instagram.com/<user>/?__a=1 to get a public user feed on a website. Since a while now, this is not working anymore, probably because Facebook removed it. Is there an other way to get the data of an instagram account in a easy way?
I built a small server which does that transformation. You'll receive the instagram data as before with ?__a=1 (as JSON ) - have fun 😊
https://www.instapi.io/u/<username>
https://www.instapi.io/u/appwithus
EDIT 12/2020: Unfortunately the service is no longer available
Edit 15/03 NOT WORKING ANYMORE Seems like instagram changed again their API, now it gives a CORS error.
As of 2 february 2021, I have found a solution
Instead of using https://www.instagram.com/username/?__a=1 which it asks for a login.
Justing adding a /channel seems to make it work, like so:
https://www.instagram.com/username/channel/?__a=1
There is a JSON data in https://www.instagram.com/<user>/.
You can use regexp to find what you need.
Sample
// This regexp gets widest possible dict around "profile_pic_url"
// but inside tag <script type="text/javascript">...</script>
let r = new RegExp('<script type="text\/javascript">' +
'([^{]+?({.*profile_pic_url.*})[^}]+?)' +
'<\/script>');
let source = document.documentElement.outerHTML;
let jsonStr = source.match(r)[2];
let data = JSON.parse(jsonStr);
console.log('data', data);
let oldVariantOfData = data['entry_data']['ProfilePage'][0];
console.log('oldVariantOfData', oldVariantOfData);
The same response is attached in the html response of the profile url, I perform this temporal solution (when I can't use the API) in python:
url_recent_media = 'https://www.instagram.com/%s/' % instagram_id
response = urllib2.urlopen(url_recent_media)
insta_html = response.read()
insta_html_split = insta_html.split('"ProfilePage":[')
if len(insta_html_split) > 1:
insta_html_split_2 = insta_html_split[1].split(']},"gatekeepers"')
if len(insta_html_split_2) > 1:
json_dict = json.loads(insta_html_split_2[0])
I hope this help you.
you can try without using instagram API.
import json, urllib2
img_dicts = []
url = 'https://www.instagram.com/{}/'.format(instagram_username)
try:
r = urllib2.urlopen(url, timeout=10.0)
instagram_html = r.read()
instagram_html_data = instagram_html.split('"ProfilePage":[')
if len(instagram_html_data) > 1:
instagram_html_final_data = instagram_html_data[1].split(']},"gatekeepers"')
if len(instagram_html_final_data) > 1:
json_dict = json.loads(instagram_html_final_data[0])
media = json_dict['graphql']['user']['edge_owner_to_timeline_media']['edges']
for obj in media:
img_dicts.append({
'id': obj['node']['id'],
'caption': obj['node']['edge_media_to_caption']['edges'][0]['node']['text'],
'imgurl_standard': obj['node']['display_url'],
'imgurl_lower': obj['node']['thumbnail_resources'][4]['src'],
'imgurl_thumb': obj['node']['thumbnail_resources'][3]['src']
})
img_dicts will give you images in different quality and caption of instagram post.

ProMotion lifecycle methods running out of order

I have been losing my mind over the last couple of weeks due to some very strange behaviour from ProMotion / RedPotion.
I have narrowed down the strange behaviour to a seemingly out of order execution of ProMotion's lifecycle methods and an API call.
I have a Screen that will get info from my API and display an image based on the image url returned from the API. I have oversimplified my project to a Screen, a Model and a Stylesheet as follows:
TestScreen:
class TestScreen < PM::Screen
title "Your title here"
stylesheet TestScreenStylesheet
def on_load
mp "ran on_load"
#face_image = append!(UIImageView, :face_image)
mp "getting data from API"
Face.get(1) do |response, face|
if response.success?
mp "face returned from API:"
mp face.inspect
#face = face
else
#face = [{attributes: {name: "No faces found"}}]
end
end
mp "should have printed data obtained from API"
end
def will_appear
mp "ran on_will_appear"
mp "face in will_appear:"
if #face
rmq(:face_image).attr(remote_image: #face.picture)
else
mp "#face is nil!!!"
end
end
end
Stylesheet:
class TestScreenStylesheet < ApplicationStylesheet
def setup
end
def root_view(st)
st.background_color = color.white
end
def face_image(st)
st.frame = {l: 30, t: 140, w: 250, h: 250}
st.placeholder_image = image.resource("placeholder_image.png")
end
end
Model:
class Face
attr_accessor :id, :name, :description, :local_description, :picture, :bio_picture, :star_ranking, :status, :facetype_id, :answers
def initialize(response)
#id = response[:data][0][:id]
#name = response[:data][0][:attributes][:name]
#description = response[:data][0][:attributes][:description]
#local_description = response[:data][0][:attributes][:local_description]
#picture = response[:data][0][:attributes][:picture]
#bio_picture = response[:data][0][:attributes][:bio_picture]
#star_ranking = response[:data][0][:attributes][:star_ranking]
#status = response[:data][0][:attributes][:status]
#facetype_id = response[:data][0][:attributes][:facetype_id]
#answers = response[:data][0][:attributes][:answers]
end
def self.get(category_id,&callback)
ApiClient.client.get "random_face?mycategory=#{category_id}" do |response|
model = nil
if response.success?
model = self.new(response.object)
end
callback.call(response, model)
end
end
end
I have placed printout commands (mp) so that I can figure out what is being executed when, and as you can see from the results below, everything is out of order:
"ran on_load"
"getting data from API"
"should have printed data obtained from API"
"ran on_will_appear"
"face in will_appear:"
"#face is nil!!!"
"face returned from API:"
"#<Face:0x113c37c90 #id=\"1\" #name=\"Leonel Messi\" #description=\"Leonel Messi es un jugador portuguĂ©s de fĂștbol, 4 veces ganador del BalĂłn de Oro\" #local_description=\"translation missing: en.Leonel Messi_description\" #picture=\"default_url\" #bio_picture=\"default_url\" #star_ranking=1 #status=\"active\" #facetype_id=nil #answers=\"[\\\"Kun Aguero\\\", \\\"Nicolas Shevchenko\\\", \\\"Leonel Messi\\\", \\\"Clarence Seedorf\\\"]\">"
The on_load method fires first, as expected, but the API call, RIGHT IN THE MIDDLE OF THE on_load method, fires last. Therefore the image attrib I am trying to set in the view_did_load method is nil and fails.
I come from a Rails background and I am new to RubyMotion / ProMotion / RedPotion so I may be going about this all wrong, but it certainly seems something is very wrong here.
As andrewhavens pointed out here:
https://github.com/infinitered/redpotion/issues/164
the reason this happens is that my API call is asynchronous, so it will not finish executing and set the instance variable in time for the will_appear method to use it (it will still be nil at the time will_appear runs).
In order to set the attributes properly, they must be set AFTER the API call completes, for example in the callback itself like this:
...
if response.success?
mp "face returned from API:"
mp face.inspect
#face = face
rmq(:face_image).attr(remote_image: #face.picture)
else
...
then it will work just fine.
Hope this saves someone hours of troubleshooting.

Zend Framework : How to handle "No route matched the request" error?

In Bootstrap.php I have some custom routes using Zend_Controller_Router_Route_Regex.
Everything works fine except when user type in address bar a wrong link.
Ex :
http://mysite/en/1-some-article.html => This is a valid link that matches a ROUTE in Bootstrap.
But when user make a typo mistake like:
http://mysite/en/1-some-article.**html'** or http://mysite/en/1-some-article.**hhtml** , they are not match any ROUTE in Bootstrap, then Zend Framework throws an exception like : No route matched the request
Is there anyway to handle it, like redirecting to a custom page for "No matched routes"?
Any help will be appreciated. Thanks for advance!
Configuration (from comment below)
[production]
phpSettings.display_startup_errors = 1
phpSettings.display_errors = 1
phpSettings.date.timezone = "Europe/London"
;includePaths.library = APPLICATION_PATH "/../library"
bootstrap.path = APPLICATION_PATH "/Bootstrap.php"
bootstrap.class = "Bootstrap"
appnamespace = ""
;resources.frontController.controllerDirectory = APPLICATION_PATH "/controllers"
resources.frontController.params.displayExceptions = 0
resources.frontController.moduleDirectory = APPLICATION_PATH "/modules"
resources.modules = ""
autoloaderNamespaces.Fxml = "Mycms_"
autoloaderNamespaces.Fxml = "Fxml_"
To make use of the ErrorController, you need to set the front controller throwExceptions attribute to false. Put this in your [production] configuration section
resources.frontController.params.throwExceptions = 0
See http://framework.zend.com/manual/1.12/en/zend.controller.exceptions.html and http://framework.zend.com/manual/1.12/en/zend.controller.plugins.html#zend.controller.plugins.standard.errorhandler.fourohfour.
I mentioned this in the comments above but you seem to have missed it (you have displayExceptions set to false but not throwExceptions).

Call multiple webservices from play 2

I am a play2.0-Scala-beginner and have to call several Webservices to generate a HTML page.
After reading the The Play WS API page and a very interesting article from Sadek Drobi I am still unsure what's the best way to accomplish this.
The article shows some code snippets which I don't fully understand as a Play beginner.
Figure 2 on page 4:
val response: Either[Response,Response] =
WS.url("http://someservice.com/post/123/comments").focusOnOk
val responseOrUndesired: Either[Result,Response] = response.left.map {
case Status(4,0,4) => NotFound
case Status(4,0,3) => NotAuthorized
case _ => InternalServerError
}
val comments: Either[Result,List[Comment]] =
responseOrUndesired.right.map(r => r.json.as[List[Comment]])
// in the controller
comment.fold(identity, cs => Ok(html.showComments(cs)))
What does the last line with the fold do? Should comment be comments? Haven't I group the last statement in an Async block?
Figure 4 shows how to combine several IO calls with a single for-expression:
for {
profile <- profilePromise
events <- attachedEventsPromise
articles <- topArticlesPromise
} yield Json.obj(
"profile" -> profile,
"events" -> events,
"articles" -> articles )
}
// in the controller
def showInfo(...) = Action { rq =>
Async {
actorInfo(...).map(info => Ok(info))
}
}
How can I use this snippet? (I am a bit confused by the extra-} after the for-expression.)
Should I write something like this?
var actorInfo = for { // Model
profile <- profilePromise
events <- attachedEventsPromise
articles <- topArticlesPromise
} yield Json.obj(
"profile" -> profile,
"events" -> events,
"articles" -> articles )
def showInfo = Action { rq => // Controller
Async {
actorInfo.map(info => Ok(info))
}
}
What's the best way to combine the snippets from figure 2 and 4 (error handling + composition of IO non-blocking calls)? (f.ex. I want to produce a Error 404 status code if any of the called webservice produce an Error 404).
Maybe someone knows a complete example of calling webservices in the play framework (cannot find an example in the play Sample applications or anywhere else).
I have to say that the article is wrong in the example you show in Figure 2. The method focusOnOk does not exist in Play 2.0. I assume the author of the article used a pre-release version of Play 2 then.
Regarding comment, yes it should be comments. The fold in the statement is operating on an Either. It takes 2 functions as parameters. The first is a function to apply if it is a left value. The second is a function to apply if it is a right value. A more detailed explanation can be found here: http://daily-scala.blogspot.com/2009/11/either.html
So what the line does is. If I have a left value (which meant I got an undesired response), apply the built-in identity function which just gives you back the value. If it has a right value (which means I got an OK response), make a new result that shows the comments somehow.
Regarding Async, it's not actually asynchronous. focusOnOk is a blocking function (a remnant from the old Java days of Play 1.x). But remember, that's not valid Play 2 code.
As for Figure 4, the trailing } is actually because it's a partial alternative of what's in Figure 3. Instead of the numerous promise flatMaps. You can do a for comprehension instead. Also, I think it should be userInfo(...).map instead of actorInfo(...).map.
The Play documentation you linked to actually already shows you a full example.
def feedTitle(feedUrl: String) = Action {
Async {
WS.url(feedUrl).get().map { response =>
Ok("Feed title: " + (response.json \ "title").as[String])
}
}
}
will get whatever is at feedUrl, and you map it to do something with the response which has a status field you can check to see if it was a 404 or something else.
To that end, the Figure 3 and 4 of your linked article should give you a starting point. So you'd have something like,
def getInfo(...) : Promise[String] = {
val profilePromise = WS.url(...).get()
val attachedEventsPromise = WS.url(...).get()
val topArticlesPromise = WS.url(...).get()
for {
profile <- profilePromise
events <- attachedEventsPromise
articles <- topArticlesPromise
} yield {
// or return whatever you want
// remember to change String to something else in the return type
profile.name
}
}
def showInfo(...) = Action { rq =>
Async {
getInfo(...).map { info =>
// convert your info to a Result
Ok(info)
}
}
}

ABPersonViewController error with RubyMotion

I am having trouble using the ABPersonViewController with RubyMotion. The error I'm getting is
Objective-C stub for message setDisplayedPerson:' typev#:^v' not precompiled. Make sure you properly link with the framework or library that defines this message.
I suspect this is due to RubyMotion not casting to the type IOS expects. I think ABPersonCreate() is returning a CFType but the displayedPerson setter is expecting it to be cast as a ABRecordRef (that's just a guess from the error messages)
Here's the sample code to see the problem (based on Apple's QuickContacts sample):
#Rakefile
$:.unshift("/Library/RubyMotion/lib")
require 'motion/project'
Motion::Project::App.setup do |app|
# Use `rake config' to see complete project settings.
app.name = 'contacts'
app.frameworks += ['AddressBook', 'AddressBookUI']
end
and
# app/app_delegate.rb
class AppDelegate
def application(application, didFinishLaunchingWithOptions:launchOptions)
window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.applicationFrame)
window.rootViewController = UINavigationController.alloc.init
window.rootViewController.wantsFullScreenLayout = true
window.makeKeyAndVisible
true
# This works
add_person('Alex', 'Rothenberg')
# This fails (is it a type casting problem?)
show_person_view_controller('Rothenberg')
end
def show_person_view_controller(name)
anError = nil
address_book = ABAddressBookCreate();
people = ABAddressBookCopyPeopleWithName(address_book, name);
person = people.first
picker = ABPersonViewController.alloc.init.autorelease
picker.personViewDelegate = self
puts "Should this be an AddressBookRef? #{person.inspect}" # => #<__NSCFType:0x8c3bec0>
picker.displayedPerson = person
# The previous line fails
puts "We never reach this line!"
self.navigationController.pushViewController(picker, animated:true)
end
def add_person(first_name, last_name)
error = nil
contact = ABPersonCreate()
ABRecordSetValue( contact, KABPersonFirstNameProperty, first_name, error )
ABRecordSetValue( contact, KABPersonLastNameProperty, last_name, error )
address_book = ABAddressBookCreate()
ABAddressBookAddRecord( address_book, contact, error )
ABAddressBookSave( address_book, error )
end
end
When you run it we are able to add to the address book in the add_person method but it fails in show_person_view_controller on the line picker.displayedPerson = person
$ rake
Build ./build/iPhoneSimulator-5.1-Development
Simulate ./build/iPhoneSimulator-5.1-Development/contacts.app
Should this be an AddressBookRef? #<__NSCFType:0x8da2900>
Objective-C stub for message `setDisplayedPerson:' type `v#:^v' not precompiled. Make sure you properly link with the framework or library that defines this message.
Any suggestions would be appreciated
This will work properly as of RubyMotion 1.12 (run sudo motion update).
Fixed a bug where performing Objective-C methods that accept
CFType objects
would crash the program (ex. [ABPersonViewController setDisplayedPerson:]).