I want to execute the following Objective-C code in my Rails application:
CFMutableStringRef inputString = CFStringCreateMutableCopy(kCFAllocatorDefault, 32, CFSTR("общей"));
CFLocaleRef locale = CFLocaleCreate(kCFAllocatorDefault, CFSTR("ru"));
CFStringTransform(inputString, NULL, kCFStringTransformStripDiacritics, false);
CFStringLowercase(inputString, locale);
NSLog(#"%#", (NSString *)inputString);
CFRelease(locale);
CFRelease(inputString);
It basically outputs a lowercase, diacritics-free version of the input string. I am running on a Snow Leopard server.
How can I do this (without using MacRuby, which seems to be an overkill here)? I've heard of Ruby extensions but can't find any resources in my case.
You can probably do it using ffi, but it'll require entering a bunch of function specs. Tiny test case for calling a Core Foundation function:
require 'ffi'
module CF
extend FFI::Library
ffi_lib '/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation'
attach_function :CFAbsoluteTimeGetCurrent, [], :double
end
puts CF.CFAbsoluteTimeGetCurrent
Instead of creating an app you need to be spawning on and off in a single Rails instance you can write your app using other ways for communication, like named pipes:
http://hints.macworld.com/article.php?story=20041025103920992
http://www.pauldix.net/2009/07/using-named-pipes-in-ruby-for-interprocess-communication.html
Or going the Ruby C extensions way:
http://ruby-doc.org/docs/ProgrammingRuby/html/ext_ruby.html
http://people.apache.org/~rooneg/talks/ruby-extensions/ruby-extensions.html
Related
I'm writing a ZeroBrane Studio plugin for our Solarus Game Engine and It works like a charm. Autocompletion included.
I'm wondering now if it's do-able to register lua APIs for one file only.
I need this to offer autocompletion/documentation on global symbols that may vary per-script but are deducible from annex files from the engine.
To summary : Is it possible to register an api for a single file? For example in the onEditorLoad() event.
Thanks.
Greg
EDIT:
I tried the following without sucess:
local function switch_editor(editor)
if current_editor == editor then
ide:Print("same editor")
return
end
current_editor = editor
if not editor then
ide:Print("null ed")
return
end
lua_file_path = ide:GetDocument(editor).filePath
if lua_file_path:match('/data/maps/') then
ide:Print("map file!",type(editor))
local map_api = make_map_api(lua_file_path)
current_api = map_api
ide:AddAPI('lua','solarus_map',map_api)
else
ide:Print('other file')
if current_api then
ide:RemoveAPI('lua','solarus_map')
current_api = nil
end
end
end
api = {"baselib", "solarus", "solarus_map"}, --in interpreter table
... -- in the plugin table :
onEditorFocusSet = function(self,editor)
switch_editor(editor)
end,
Completion with the solarus api works fine but the on-fly registration of the solarus_map api seem not to be taken in account.
EDIT2:
Silly my, I must have done a typo, because after checking and rewriting some things pretty much as in the code pasted above... it works! Awesome!
The only small gotcha is that when switching to a file where I don't want the solarus_map API... ide:RemoveAPI isn't sufficient. Instead I must do ide:AddAPI('lua','solarus_map',{}) to replace the API with an empty one. Which I can live with.
To summary, to achieve a custom api which change from file to file:
Add the api name to the interpreter
In the onEditorFocusSet event, update the API with ide:AddAPI(...), eventually setting it to {} if it needs to be empty/disabled.
Code sample in the editions of my Question.
I wrote a runner (saved in lib folder). When starting the runner with: rails runner lib/test.rb
def aaa
puts "aaa"
end
aaa
it dumps:
in `eval': wrong number of arguments (0 for 2..3) (ArgumentError)
why?
rails runner is intended to run code from your app codebase as in
(from the guide)
rails runner "Model.long_running_method" # parses the string and executes
# the (hypothetical) method [long_running_method]
# from (hypothetical) model [app/models/model.rb]
the error raises from the fact that in your call you don't provide a string to evaluate
anyway to make it work this way (with a function from lib) you should
enclose your method in some class and
make the class available requiring it someway during application boot
! pay attention: if you call rails runner 'MyClass.my_method' you're calling a class method which has to be defined properly
def self.my_method
# your code
end
if you want to call an instance method you need to do rails runner 'MyClass.new.my_method'
All that said, rails runner boots all the rails app.
If that is not required, may I suggest to investigate whether a rake task could be suited for your needs ?
background
I am trying to migrate an old Rails 2 (Ruby 1.8.7) app to Rails 3.0.9 (Ruby 1.9.3) — yes it's a stepping stone to get it to Rails 4 and Ruby 2.2 — and I've hit the following problem.
The original app makes extensive use of an old Active Form gem which we've hacked slightly to support Ruby 1.9.
It mostly works, but there appears to be some issue with how it interacts with the ActionView::Helpers::AssetTagHelper that's part of ActionPack 3.0.9.
In my specific case I have an ActiveForm::DateCalendarSection (built dynamically) which subclasses ActiveForm::Element::Section, which, according to self.class.ancestors, is a subclass of ActionView::Helpers::AssetTagHelper. Looking at the ActiveForm source however there is no mention of AssetTagHelper or asset_tag_helper so how they are actually connected remains a mystery to me.
Problem
Calls to the method image_path result in an error
undefined local variable or method 'config'
The call to image_path is simply a wrapper around a call to compute_public_path in ActionView::Helpers::AssetTagHelper
# File actionpack/lib/action_view/helpers/asset_tag_helper.rb, line 741
def compute_public_path(source, dir, ext = nil, include_host = true)
return source if is_uri?(source)
source += ".#{ext}" if rewrite_extension?(source, dir, ext)
source = "/#{dir}/#{source}" unless source[0] == //
source = rewrite_asset_path(source, config.asset_path)
has_request = controller.respond_to?(:request)
if has_request && include_host && source !~ %{^#{controller.config.relative_url_root}/}
source = "#{controller.config.relative_url_root}#{source}"
end
source = rewrite_host_and_protocol(source, has_request) if include_host
source
end
Diving into that with binding.pry it's evident that config is indeed not defined. Likewise controller is also not defined.
Question
What would have changed between Rails 2 and Rails 3, such that methods from ActionView::Helpers::AssetTagHelper can no longer access Rails' config or the current controller?
It could be you don't have enough of the Rails support loaded. Maybe pull in everything inside your gem with
require "active_support/all"
Alternatively, have a look at how modern Rails hooks things up via a proxy to the config:
https://github.com/rails/rails/pull/12622/files
I want to define a function available_translations which lists the translations I have made for my application into the I18n module.
I tried putting the following into the file lib/i18n.rb, but it doesn't work when I try to use it from the rails console:
module I18n
# Return the translations available for this application.
def self.available_translations
languages = []
Dir.glob(Rails.root.to_s + '/config/locales/*.yml') do |filename|
if md = filename.match #^.+/(\w+).yml$#
languages << md[1]
end
end
languages
end
end
Console:
ruby-1.9.2-p290 :003 > require Rails.root.to_s + '/lib/i18n.rb'
=> false
ruby-1.9.2-p290 :004 > I18n.available_translations
NoMethodError: undefined method `available_translations' for I18n:Module
...
Besides solving my concrete problem, I would be very pleased to learn how this whole module thing in Ruby on Rails works because it still confuses me, so I would appreciate links to the docs or source code very much.
Either of these will solve your problem:
move the code to config/initializers/i18n.rb, or
require your file from config/application.rb, or
name your class otherwise (to trigger autoload)
The code in lib/i18n.rb wil not be loaded by autoload since I18n name will be already loaded, so either you load it yourself or change the class name (and file name) so the new name will trigger autoload behavior.
BTW, the I18n.available_locales() method is presented in rails.
How to change the environment variable of rails in testing
You could do
Rails.stub(env: ActiveSupport::StringInquirer.new("production"))
Then Rails.env, Rails.development? etc will work as expected.
With RSpec 3 or later you may want to use the new "zero monkeypatching" syntax (as mentioned by #AnkitG in another answer) to avoid deprecation warnings:
allow(Rails).to receive(:env).and_return(ActiveSupport::StringInquirer.new("production"))
I usually define a stub_env method in a spec helper so I don't have to put all that stuff inline in my tests.
An option to consider (as suggested in a comment here) is to instead rely on some more targeted configuration that you can set in your environment files and change in tests.
Rspec 3 onwards you can do
it "should do something specific for production" do
allow(Rails).to receive(:env).and_return(ActiveSupport::StringInquirer.new("production"))
#other assertions
end
Sometimes returning a different environment variable can be a headache (required production environment variables, warning messages, etc).
Depending on your case, as an alternate you may be able to simply return the value you need for your test to think it's in another environment. Such as if you wanted Rails to believe it is in production for code that checks Rails.env.production? you could do something like this:
it "does something specific when in production" do
allow(Rails.env).to receive(:production?).and_return(true)
##other assertions
end
You could do the same for other environments, such as :development?, :staging?, etc. If you don't need the full capacity of returning a full environment, this could be another option.
As a simpler variation on several answers above, this is working for me:
allow(Rails).to receive(:env).and_return('production')
Or, for as I'm doing in shared_examples, pass that in a variable:
allow(Rails).to receive(:env).and_return(target_env)
I suspect this falls short of the ...StringInquirer... solution as your app uses additional methods to inspect the environment (e.g. env.production?, but if you code just asks for Rails.env, this is a lot more readable. YMMV.
If you're using something like rspec, you can stub Rails.env to return a different value for the specific test example you're running:
it "should log something in production" do
Rails.stub(:env).and_return('production')
Rails.logger.should_receive(:warning).with("message")
run_your_code
end