How to edit project build settings via CocoaPod podfile? - cocoapods

I basically need to set MARKETING_VERSION in my own project build settings based on a value in a json file in the same directory.
Am hoping I can piggyback on Podfile to edit my project's own configuration.
Couldn't find anything on this, as most of it is about editing the Pods configuration themselves.
Is this possible or it's not meant to do it like this?

Managed to solve it by modifying the xconfigs manually:
require 'json'
post_install do |installer|
# Getting my version from outside.
json_from_file = File.read("../package.json")
hash = JSON.parse(json_from_file, {:symbolize_names => true})
work_dir = Dir.pwd
# Get the files and add on my own build settings.
Dir.glob("Pods/Target Support Files/Pods-MyApp/*.xcconfig") do |xc_config_filename|
full_path_name = "#{work_dir}/#{xc_config_filename}"
File.open(full_path_name, 'a') { |f| f.write('MARKETING_VERSION = ' + hash[:version]) }
end
end
Feel free to edit/post comment or even as a new answer if there is a cleaner way.

Related

Is there a way to include an image asset that will only be available in previews for SwiftUI?

I'm developing a CocoaPod and I'd like to include images in my target that I can use in SwiftUI previews. I don't want them included in the binary or the pod when I ship it. Is that possible?
If I don't include the asset bundle in the podspec it doesn't seem to appear under my development pods, but I don't want it in the podspec because I don't want to ship the images.
I'm thinking there's perhaps a way to do this by running a custom step after pod install that will copy an asset bundle from the example app to the development pod but I haven't quite figured that out yet.
When you create SwiftUI based project it creates the following group, with Preview-only assets catalog
Target build settings:
Following #Asperi's answer, I wrote the following post_install step for my development pod. Remember to change ${TARGET_NAME} for your target:
post_install do |installer_representation|
project = installer_representation.pods_project
subdir = "PreviewAssets"
# First add the PreviewAssets folder to the build settings for the debug configuration
project.targets.each do |target|
if target.name == "${TARGET_NAME}"
target.build_configurations.each do |config|
if config.name == 'Debug'
config.build_settings['DEVELOPMENT_ASSET_PATHS'] ||= ['${PODS_TARGET_SRCROOT}/' + subdir]
end
end
end
end
# Second grab all the xcassets from the PreviewAssets folder and add them to the ${TARGET_NAME} group in
# the Development Pods group
group = project.pod_group("${TARGET_NAME}")
references = []
# Is there a better way to get the root of your project?
directory = installer_representation.sandbox.root.join("../..", subdir)
Dir.chdir(directory)
Dir.glob("*").each do |f|
if File.directory?(f)
full_path = File.expand_path(f)
references << project.add_file_reference(full_path, group)
end
end
# Last add the asset catalogs to the target
project.targets.each do |target|
if target.name == "${TARGET_NAME}"
target.add_file_references(references)
end
end
end

Is the heuristic CocoaPods uses for the "pod try" command documented anywhere?

In CocoaPods v.0.29, the 'pod try' command was added (see http://blog.cocoapods.org/CocoaPods-0.29/). From the documentation (bold emphasis mine):
In other words the command automates the following steps:
Checkout the source of the Pod in a temporary directory.
Search for any project looking like a demo project using some simple heuristics.
Install any CocoaPods dependencies if needed by the located project.
Open the workspace/project in Xcode.
I've searched both Google and StackOverflow and have not been able to find any documentation about the specific heuristics that CocoaPods uses to locate a demo project. Is the process by which CocoaPods locates a demo project, and/or best practices for including a demo project and scheme documented anywhere? I am putting together a library which I hope to turn into a CocoaPod soon, and would like to ensure that my sample project will actually work correctly with CocoaPods.
Thank you for your time.
I was looking for this as well and the only thing I got was the source of the pod-try plugin:
# Picks a project or workspace suitable for the demo purposes in the
# given directory.
#
# To decide the project simple heuristics are used according to the name,
# if no project is found this method raises and `Informative` otherwise
# if more than one project is found the choice is presented to the user.
#
# #param [#to_s] dir
# The path where to look for projects.
#
# #return [String] The path of the project.
#
def pick_demo_project(dir)
projs = projects_in_dir(dir)
if projs.count == 0
raise Informative, 'Unable to find any project in the source files' \
" of the Pod: `#{dir}`"
elsif projs.count == 1
projs.first
elsif (workspaces = projs.grep(/(demo|example).*\.xcworkspace$/i)).count == 1
workspaces.first
elsif (projects = projs.grep(/demo|example/i)).count == 1
projects.first
else
message = 'Which project would you like to open'
selection_array = projs.map do |p|
Pathname.new(p).relative_path_from(dir).to_s
end
index = choose_from_array(selection_array, message)
projs[index]
end
end
I don't know Ruby, but seems that it gets the list of all XCode projects/workspaces (except the Pods project and the projects that have a sister workspace) and picks:
The only item if there's only one item found.
The only workspace that has "demo" or "example" in filename if there's only one such item found.
The only project that has "demo" or "example" in filename if there's only one such item found.
Nothing, but let's you choose from all the items found.
If somebody has corrections to this - they're welcomed, as I'm no Ruby guy.

Moving blog articles location in Middleman

I'm using the Middleman Blog gem for my site, but by default it appears the blog articles need to be located in /source which isn't particularly nice when looking at the tree in vim and trying to locate one of the other files in there (a template for instance).
From looking at the documentation I can't see if there is any way of moving the blog articles so they are stored somewhere else such as a blog_articles folder or similar.
Is this possible?
Put the following in your config.rb file.
activate :blog do |blog|
blog.permalink = ":year-:month-:day-:title.html"
blog.sources = "blog_articles/:title.html"
end
Assuming you have a post 2012-01-01-example-article.html.markdown stored in the folder source/blog_articles.
You should now see the post with this URL: http://localhost:4567/2012-01-01-example-article.html. (You might have to restart middleman when changing the config.rb file.)
Please note that I also had to set blog.permalink, the blog.sources setting alone didn't do the trick.
A bonus tip: I have activate :directory_indexes in my config.rb file. This setting gives you nice looking URLs, without the .html part.
If you want the same for your blog posts you can drop the .html from your blog.permalinksetting. Like so:
activate :blog do |blog|
blog.permalink = ":year-:month-:day-:title"
blog.sources = "blog_articles/:title.html"
end
Now you can see your post with this URL: http://localhost:4567/2012-01-01-example-article.
I messed with the middleman-blog extension, but gave up for its relative opaqueness. In looking at the source, though, it appears the prefix option might do the trick for you? It's somewhat unclear whether the prefix is a URL prefix or a local path prefix:
activate :blog do |blog|
blog.prefix = "/blog_articles"
end
From looking at the code it transpires there is a :sources option which you can use. If you poke around in the source there is an example of this:
https://github.com/middleman/middleman-blog/tree/master/fixtures/article-dirs-app
The solution above worked for me when I made the following changes to the permalink / source config options:
blog.permalink = ":title.html"
blog.sources = "posts/:year-:month-:day-:title.html"
The permalink is the location which it will appear in the web browser url where the source is the locations of the posts.
Using middleman 3.2.1
I made blog folder inside source directory. Then i make posts directory and moved all my posts there. source/blog/posts/...
and then inside config.rb
activate :blog do |blog|
..........
blog.permalink = "blog/:year/:month/:day/:title.html"
blog.sources = "blog/posts/:year-:month-:day-:title.html"
.........
end

Ruby require a code snippet from github

Is there anyway to get Ruby's require statement to download a file from somewhere like github rather than just the local file system?
Update: Sorry I should have made the question clearer. I want to download a file that contains Ruby module and import it into my script rather than just downloading an image or some other arbitrary file within my script.
In other words something like this
require 'http:\\github.com\myrepo\snippet.rb'
puts 'hi'
By default, this is not possible. Also, it's not a good idea for security reasons.
You have a couple of alternatives. If the file you want to include is a Gem and Git repository, then you can use Bundler to download and package the dependency in your project. Then you'll be able to require the file directly in your source code.
This is the best and safest way to include an external dependency.
If you trust the source and you really know what you are doing, you can download the file using Net::HTTP (or any other HTTP library) and eval the body directly in your Ruby code.
You can package everything in a custom require_remote function.
You could download and eval it
require "open-uri"
alias :require_old :require
def require(path)
return false if $".include?(path)
unless path=~ /\Ahttp:\/\/
return require_old(path)
end
eval(open(path).read)
$"<< path
true
end
Be aware, this code has no error checking for network outages nonexisting files, ... . I also believe it is in general not a good idea to require libraries this way, there are security and reliability problems in this approach. But maybe you have a valid usecase for this.
you can include a remote gem from within Gemfiles then it will download when you run bundle install
After reading this question and answers I wanted something a little more bullet proof and verbose that used a paradigm of creating a local file from a repo and then requiring it, only if it didn't already exist locally already. The request for the repo version is explicit via the method repo_require. Used on files you control, this approach improves security IMO.
# try local load
def local_require(filename, relative_path)
relative_flname = File.join(relative_path, filename)
require_relative(relative_flname)
end
# try loading locally first, try repo version on load error
# caution: only use with files you control access to!
def repo_require(raw_repo_prefix, filename, relative_path = '')
local_require(filename, relative_path)
rescue LoadError => e
puts e.message
require 'open-uri'
tempdir = Dir.mktmpdir("repo_require-")
temp_flname = File.join(tempdir, File.basename(filename))
return false if $LOADED_FEATURES.include?(temp_flname)
remote_flname = File.join(raw_repo_prefix, filename)
puts "file not found locally, checking repo: #{remote_flname}"
begin
File.open(temp_flname, 'w') do |f|
f.write(URI.parse(remote_flname).read)
end
rescue OpenURI::HTTPError => e
raise "Error: Can't load #{filename} from repo: #{e.message} - #{remote_flname}"
end
require(temp_flname)
FileUtils.remove_entry(tempdir)
end
Then you could call repo_require like this:
repo_require('https://raw.githubusercontent.com/username/reponame/branch',
'filename', 'relative_path')
The relative_path would the the relative path you would use for the file if the repo was locally installed. For example, you may have something like require_relative '../lib/utils.rb'. In this example filename='lib/utils.rb' and relative_path='..'. This information allows the repo url to be constructed correctly as it does not use the relative path portion.

Broken precompiled assets in Rails 3.1 when deploying to a sub-URI

I'm in the process of updating a Rails 3 app to use Rails 3.1 and as part of that, am making use of the new asset pipeline. So far, I've got everything working apart from one rather annoying problem I can't solve.
The application and all its assets works fine in development, but in production it is deployed to a sub-URI using Passenger (http://the-host/sub-uri/). The problem with this is that the assets are pre-compiled during deployment and one of my CSS (well, it's a .css.scss file) files is making use of the image-url helper from the sass-rails gem. Since during the pre-compilation process, the paths are hard-coded into the precompiled CSS file, the sub-uri is not taken account of:
In my .css.scss file:
body { background-image: image-url("bg.png"); }
The result in the compiled application-<md5-hash-here>.css file:
body { background-image: url(/assets/bg.png); }
What it should be to make it work correctly:
body { background-image: url(/sub-uri/assets/bg.png); }
Is this scenario just asking too much? If so, I'll have to switch back to the old non-asset-pipelined way and just serve my images and CSS from public. However it seems like something which should have been thought about and solved...? Am I missing the solution?
Edit 1: I should note that using the erb solution instead yields the same result, as one would expect.
Edit 2: in response to Benoit Garret's comment
No, the problem isn't related to the config.assets.prefix. I tried setting that (to /sub-uri/assets rather than the default of /assets) but it turned out that was the wrong thing to do - it seems like this setting is already in relation to the root of the Rails app, not the server. Removing that (and thus returning to the default) has fixed all the weird issues that caused (and there were many, all the assets ended up in /sub-uri/sub-uri/assets - it was all very strange). The only problem is that the image-url helper and friends do not pick up the sub-URI when they are pre-compiled. Needless to say, this is logical since when it is pre-compiled, it couldn't possibly know that when it's running under Passenger, it'll be configured in this way. My question is how to inform it of this and thus end up with the correct paths in the precompiled result. If indeed it can be done.
My current workaround is to reference the iamge in the CSS like this: url(../images/bg.png) and place it in the non-pipelined public/images location. Hardly ideal since it doesn't benefit from the fingerprinting and everything which the pipeline provides.
Finally I've worked out a couple of workarounds/solutions.
1) From https://github.com/rails/sass-rails/issues/17 it looks like this could get fixed in sass-rails. I've monkey-patched helpers.rb myself along the lines of the proposed patch in the link above. I simply set the required environment variable in the asset precompile line in deploy.rb.
I do all my monkey patching in a single file config/initializers/gem_patches.rb. In this file I patched this method as:
module Sass
module Rails
module Helpers
protected
def public_path(asset, kind)
path = options[:custom][:resolver].public_path(asset, kind.pluralize)
path = ENV['PRODUCTION_URI'] + path if ENV['PRODUCTION_URI']
path
end
end
end
end
2) Alternatively if you are ok to embed images in the CSS, changing the stylesheet to have a .erb extension, and replacing the image-url("bg.png") with url(<%= asset_data_uri "bg.png" %>) will work without any need to change sass-rails. asset-data-uri doesn't exist as a pure Sass function so you have to use the Rails helper asset_data_uri.
In the latest Rails 3.1.3 you need to monkey patch a different module now, for it to work
This is what I did
module Sprockets
module Helpers
module RailsHelper
def asset_path(source, options = {})
source = source.logical_path if source.respond_to?(:logical_path)
path = asset_paths.compute_public_path(source, asset_prefix, options.merge(:body => true))
path = options[:body] ? "#{path}?body=1" : path
if !asset_paths.send(:has_request?)
path = ENV['RAILS_RELATIVE_URL_ROOT'] + path if ENV['RAILS_RELATIVE_URL_ROOT']
end
path
end
end
end
end
And in my deploy.rb I have:
desc "precompile the assets"
namespace :assets do
task :precompile_assets do
run "cd #{release_path} && rm -rf public/assets/* && RAILS_ENV=production bundle exec rake assets:precompile RAILS_RELATIVE_URL_ROOT='/my_sub_uri'"
end
end
before "deploy:symlink", "assets:precompile_assets"
I'm using Rails 3.1.3 and deploying to a sub-URI successfully.
I have NOT monkey-patched anything.
The key problems with this setup have been better discussed here. As you can see, the solution was applied to Rails 3.2 and never backPorted to 3.1.4.
But, I have came to a solution using Rails 3.1.3 that works for my setup.
Try this: (I'm no expert, just trying to contribute to solve a problem that hassled me for hours...)
environment.rb:
#at top:
ENV['RAILS_RELATIVE_URL_ROOT'] = '/rais'
production.rb:
config.assets.prefix = ENV['RAILS_RELATIVE_URL_ROOT'] ? ENV['RAILS_RELATIVE_URL_ROOT'] + '/assets' : '/assets'
routes.rb:
Rais::Application.routes.draw do
scope ENV['RAILS_RELATIVE_URL_ROOT'] || '/' do #see config/environment.rb
<<resources here>>
end
end
As you can see, I've put assets.prefix inside production.rb, not in application.rb
After that you do:
rake assets:clear
rake assets:precompile
and than, test with the console:
RAILS_ENV=production rails console
Results:
foo = ActionView::Base.new
foo.stylesheet_link_tag 'application'
=> "<link href=\"/rais/assets/layout.css?body=1\" media=\"screen\" rel=\"stylesheet\" type=\"text/css\" />\n<link href=\"/rais/assets/application.css?body=1\" media=\"screen\" rel=\"stylesheet\" type=\"text/css\" />"
foo.image_tag('arrow-up.png')
=> "<img alt=\"Arrow-up\" src=\"/rais/assets/arrow-up-ca314ad9b991768ad2b9dcbeeb8760de.png\" />"
After a bit of digging around, I have found the issue. The issue is in Rails, specifically Sprockets::Helpers::RailsHelper::AssetPaths#compute_public_path. Sprockets::Helpers::RailsHelper::AssetPaths inherits from ActionView::AssetPaths and overrides a number of methods. When compute_public_path is called through the Sass::Rails::Resolver#public_path method is sass-rails, the rails sprocket helper picks up the task of resolving the asset. Sprockets::Helpers::RailsHelper::AssetPaths#compute_public_path defers to super which is ActionView::AssetPaths#compute_public_path. In this method there is a condition of has_request? on rewrite_relative_url_root as seen below:
def compute_public_path(source, dir, ext = nil, include_host = true, protocol = nil)
...
source = rewrite_relative_url_root(source, relative_url_root) if has_request?
...
end
def relative_url_root
config = controller.config if controller.respond_to?(:config)
config ||= config.action_controller if config.action_controller.present?
config ||= config
config.relative_url_root
end
If you look at the internals of rewrite_relative_url_root it relies on a request to be present and the ability to derive it from the controller variable in order to resolve the relative url root. The issue is that when sprockets resolves these assets for sass it does not have a controller present and therefore no request.
The solution above didn't work in development mode for me. Here is the solution that I am using to make it work for now:
module Sass
module Rails
module Helpers
protected
def public_path(asset, kind)
resolver = options[:custom][:resolver]
asset_paths = resolver.context.asset_paths
path = resolver.public_path(asset, kind.pluralize)
if !asset_paths.send(:has_request?) && ENV['RAILS_RELATIVE_URL_ROOT']
path = ENV['RAILS_RELATIVE_URL_ROOT'] + path
end
path
end
end
end
end

Resources