NuGet: Possibility of adding a line of code via Powershell? - powershell

I have a few NuGet packages that I've put together, with one of them being a common project referenced by all the others.
This common project inserts a configuration class into the App_Start folder, and a method on this class is then invoked by WebActivator.
For one of the other packages I wish to add one more line of code to this method but I'm struggling to find a way to do this.
I know that I could very simply add an additional class which contains just this one line of code but I'd prefer, if possible, to use an Install.ps1 powershell script to add the line of code to the existing configuration class.
Using a pro-processed *.cs.pp file will overwrite the existing file (or add a new one), and *.cs.transform doesn't work on such a file.
I know where the class is and what it's called, and I know what the method is called, so does Powershell offer a means of adding a line to the end of said method?

This is possible but not trivial to do. You also have to be very careful since deleting someone's code from a NuGet package is not going to make them very happy.
When installing a NuGet package you have access to the Visual Studio object model (EnvDTE). The $project variable can be used to access the specific project item you need. From that you can use the FileCodeModel which represents the code in the file. You would then need to find the class and its method. Then create an edit point and insert the text.
The following will find a class called Class1 and insert a line of code into its Foo method. Note that the line of code inserted will not be correctly indented, you would need to find that out by looking at the document.
$project.ProjectItems.Item("Class1.cs")
$namespace = $item.FileCodeModel.CodeElements | ? {$_.Kind -eq 5}
$namespace.Members.Item("Class1")
$method = $class.Members.Item("Foo")
$endPoint = $method.GetEndpoint([EnvDTE.vsCMPart]::vsCMPartBody)
$editPoint = $endpoint.CreateEditPoint()
$editPoint.Insert("int a = 0;")
Also the above code does not do any error handling.

Related

Why I cannot call a module's function after required or used successfully?

I have a Perl module file named genbank.pm, it has a subroutine new; this Perl module didn't use Exporter and didn't define #EXPORT or #EXPORT_OK. In the same directory I have a Perl file named test.pl, the code of test.pl is:
require 'genbank.pm';
use strict;
our $result = genbank::new();
When I run it with Komodo , it reports:
Undefined subroutine &genbank::new called at /home/mrs/scripts/test.pl line 3;
After I changed the code from
our $result = genbank::new();
to
our $result = genbank->new();
it said:
Can't locate object method "new" via package "genbank"(perhaps you forgot to load "genbank"?) at /home/mrs/scripts/test.pl line 3.
You haven't provided enough information to be entirely certain of the problem, but the most likely cause is that the new sub is in a package other than genbank.
When you attempt to call genbank::new (or genbank->new), Perl looks for a sub named new in the package genbank, not in the file genbank.pm. By convention, the file genbank.pm will contain a package named genbank, but this is purely a matter of convention and convenience. It is not enforced by the language and there is absolutely no automatic connection between file names and package names.
To find out what package your subs are being placed in, look in genbank.pm for any lines saying package xxxx;. The last one to appear before the sub new line will tell you the package that new belongs to. If there isn't a package statement before its definition, it will be in the package main by default.
(If you are responsible for the genbank.pm source code, I would also suggest either adding package genbank; to the file or renaming the file to match the package it actually contains, so as to avoid this sort of confusion in the future.)
Edit in response to OP's comment:
Since the module currently starts with package MRS::Scripts::genbank, then that's the package the new sub is created in. You have two options for calling it:
Call it as MRS::Scripts::genbank::new (or, if it actually is an object constructor, MRS::Scripts::genbank->new). If you want to call it using a package name, then you have to use the correct package name.
Export it from the MRS::Scripts::genbank package and modify your calling script to import symbols from that package (either by moving genbank.pm to MRS/Scripts/genbank.pm and changing your require to use MRS::Scripts::genbank; or by adding MRS::Scripts::genbank->import; after the require), then call it as just new(). While this would work within the limited scope of your question, I advise strongly against it because, as soon as you repeat this with another module containing a sub new, it will all fall apart, since there can't be two news at the same time in the same place.
The cleanest way to resolve this would be to move genbank.pm to MRS/Scripts/genbank.pm, change your require to require MRS::Scripts::genbank.pm (or use; if you don't export anything, they're equivalent), and then call the sub as MRS::Scripts::genbank::new or MRS::Scripts::genbank->new. This approach will bring the file name and package name into sync with each other, which will make life easier for whoever has to maintain the code in the future (which will probably be you...).

Matlab gui files in a package?

I am trying to put some .fig and their corresponding .m files in package.
I have:
+ui/mainWindow.fig
+ui/mainWindow.m
But when I try to run mainWindow.fig Matlab prints an error from GUIDE:
Error using feval
Undefined function or variable mainWindow
The funny thing is that if i call with its fully qualified name:
ui.mainWindow
the window appears normally (but all callbacks don't work anyway).
I have tried to import ui.* before running it.
Please note that I want to do this as sort of namespace. I don't want to have my entire application in the global scope of Matlab.
Explanation
The fundamental problem is that MATLAB GUIDE is unaware of packages. Normally, it manages the callback names automatically, keeping the .m and .fig files synchronized, and everyone is happy. When the figure is within a package, it fails to properly update the callbacks in the .fig properties - these still point to the unqualified name mainWindow rather than the correct ui.mainWindow. Subsequently, all callbacks fail.
Workarounds
Two ways around this one:
Export your figure: Guide -> File -> Export. Place this file within your +ui folder. Open the file, and do a find-replace replacing all instances of #(hObject,eventdata)mainWindow with #(hObject,eventdata)ui.mainWindow.
Alternatively, you can manually update the references directly within GUIDE itself, without exporting. For each button and element, Right Click -> Property Inspector then edit the 'Callback' field, replacing mainWindow with ui.mainWindow.
Personally, I prefer the first solution because you can replace all occurrences with a single find-replace command.

Using OpenWrap with Scope

I may not fully understand the wiki article on scoping, so forgive me if this sounds dumb.
Intro:
I have a solution (ABC.sln) with over 40 projects and am trying to implement OpenWrap for package management.
So I did the following in the solution's root folder:
o init-wrap -all
That worked fine: I now have a file called SLN.wrapdesc in the solution's root folder. All of the .csproj files in the subfolders contain the OpenWrap targets line.
I then proceded to add the different wraps to the solution with:
o add-wrap -Name xxx
Again, this worked fine: I have some wraps in the wraps folder, and the build doesn't break after removing the old references from the projects.
Problem:
All of the contents of the wraps are going to all of the projects, even for those that don't need it. I would like to be able to specify which wraps go where, eg AjaxControlToolkit only goes into web projects.
What I tried
First, I removed the AjaxControlToolkit from the wrapdesc:
o remove-wrap AjaxControlToolkit
This causes the build to break (as expected). Then I tried the following:
1. Try to add the wrap back with a scope:
o add-wrap -Name AjaxControlToolkit -scope webproject
This simply puts the wrap back in the wraps folder. I then added <OpenWrap-Scope>customscope</OpenWrap-Scope> to the project file, but the build still broke.
2. Try and manually add a file called ABC.webproject.wrapdesc to the root folder. This causes the following error when I try to open the solution:
The "exists" function only accepts a scalar value, but its argument "#(_WrapFile->'%(FullPath)')" evaluates to "D:\Projects\ABC.webproject.wrapdesc;D:\Projects\ABC.wrapdesc" which is not a scalar value.
I guess it doesn't like 2 wrapdesc files. That is strange because the wiki says "...you can add a second descriptor alongside your default descriptor..."
So now I'm stuck. Anyone have any ideas?
The per-msbuild file is really not a recommended approach to managing dependencies. Doing it per project is not quite the design philosophy behind OpenWrap, so the system is not quite optimized for those scenarios.
If you don't need something from those assemblies then the easiest way to solve it is to not use the references by not using any code from those packages. This solves the problem very easily as nothing will get loaded (or even need to be on disk) if no code has been added to it.
That said, add-wrap -scope newscope will create an additional .wrapdesc file that will add the new dependency to the new scope, by creating a myProject.newscope.wrapdesc file independently of the original myProject.wrapdesc.
If you do want to do this per-project, have you tried using the convention-based scoping? Something like:
directory-structure: src\*{scope: Web=WebProjects}*
Would take any project in a folder child of src containing Web in the name and assign those to the WebProjects scope.
I know that one has worked fine for my projects so far, although you do have to restart VS as it aggressively caches certain files and will not see the change.
Customizing the msbuild file itself is not fully tested (and the wiki entry was very much a design spec rather than final documentation, not all of it has been built that way) so it may or may not work. Happy to take a look if you can open a bug ticket on http://github.com/openrasta/openwrap/issues

Add Models and Controllers to Orchard

Does anybody know how to create his own models and controllers in Orchard-based projects? I have an empty project and a pack of screenshots for pages, but I don't know with what to begin. If it is possible, please show an example.
Thanks.
You should start off at the documentation page. There is an 'Extending Orchard' section which walks you through how to create a module, with data access, content parts, and content fields.
Use the command line to generate the module using the code generation module
Documentation here
Then install the Code Generation Extensions from Piotr and follow the instructions on his blog. http://www.szmyd.com.pl/blog/generating-orchard-content-parts-via-command-line
Module adds an Orchard command-line command “codegen part”. It’s
syntax is as follows:
codegen part [/Properties:]
For example:
codegen part Modules.Shop ProductPart /Properties: Name:string,
Price:int
Properties is an optional parameter, so if you’d like to create an
empty part you can just write
codegen part Modules.Shop ProductPart
The command creates a handler, driver, model, record, display and
editor shapes and updates the Placement.info file with default
Content:before placement for your part shape. If you provide
/Properties parameter, the model, record and editor shapes will be
filled with appropriate code accordingly.

How to convert a directory into a package?

I have a directory with some helper functions that should be put into a package. Step one is obviously naming the directory something like +mypackage\ so I can call functions with mypackage.somefunction. The problem is, some functions depend on one another, and apparently MATLAB requires package functions to call functions in the very same package still by explicitly stating the package name, so I'd have to rewrite all function calls. Even worse, should I decide to rename the package, all function calls would have to be rewritten as well. These functions don't even work correctly anymore when I cd into the directory as soon as its name starts with a +.
Is there an easier solution than rewriting a lot? Or at least something self-referential like import this.* to facilitate future package renaming?
edit I noticed the same goes for classes and static methods, which is why I put the self-referential part into this separate question.
In truth, I don't know that you should really be renaming your packages often. It seems to me that the whole idea behind a package in MATLAB is to organize a set of related functions and classes into a single collection that you could easily use or distribute as a "toolbox" without having to worry about name collisions.
As such, placing functions and classes into packages is like a final step that you perform to make a nice polished collection of tools, so you really shouldn't have much reason to rename your packages. Furthermore, you should only have to go through once prepending the package name to package function calls.
... (pausing to think if what I'm about to suggest is a good idea ;) ) ...
However, if you really want to avoid having to go through your package and prepend your function calls with a new package name, one approach would be to use the function mfilename to get the full file path for the currently running package function, parse the path string to find the parent package directories (which start with "+"), then pass the result to the import function to import the parent packages. You could even place these steps in a separate function packagename (requiring that you also use the function evalin):
function name = packagename
% Get full path of calling function:
callerPath = evalin('caller', 'mfilename(''fullpath'')');
% Parse the path string to get package directories:
name = regexp(callerPath, '\+(\w)+', 'tokens');
% Format the output:
name = strcat([name{:}], [repmat({'.'}, 1, numel(name)-1) {''}]);
name = [name{:}];
end
And you could then place this at the very beginning of your package functions to automatically have them include their parent package namespace:
import([packagename '.*']);
Is this a good idea? Well, I'm not sure what the computational impacts will be if you're doing this every time you call a package function. Also, if you have packages nested within packages you will get output from packagename that looks like this:
'mainpack.subpack.subsubpack'
And the call to import will only include the immediate parent package subsubpack. If you also want to include the other parent packages, you would have to sequentially remove the last package from the above string and import the remainder of the string.
In short, this isn't a very clean solution, but it is possible to make your package a little easier to rename in this way. However, I would still suggest that it's better to view the creation of a package as a final step in the process of creating a core set of tools, in which case renaming should be an unlikely scenario and prepending package function calls with the package name would only have to be done once.
I have been exploring answers to the same question and I have found that combining package with private folders can allow most or all of the code to be used without modification.
Say you have
+mypackage\intfc1.m
+mypackage\intfc2.m
+mypackage\private\foo1.m
+mypackage\private\foo2.m
+mypackage\private\foo3.m
Then from intfc1, foo1, foo2, and foo3 are all reachable without any package qualifiers or import statements, and foo1, foo2, and foo3 can also call each other without any package qualifiers or import statements. If foo1, foo2, or foo3 needs to call intfc1 or intfc2, then that needs qualification as mypackage.intfc1 or an import statement.
In the case that you have a large set of mutually interdependent functions and a small number of entry points, this reduces the burden of adding qualifiers or import statements.
To go even further, you could create new wrapper functions at the package level with the same name as private functions
+mypackage\foo1.m <--- new interface layer wraps private foo1
+mypackage\private\foo1.m <--- original function
where for example +mypackage\foo1.m might be:
function answer = foo1(some_parameter)
answer = foo1(some_parameter); % calls private function, not itself
end
This way, unlike the intfc1 example above, all the private code can run without modification. In particular there is no need for package qualifiers when calling any other function, regardless of whether it is exposed by a wrapper at the package level.
With this configuration, all functions including the package-level wrappers are oblivious of the package name, so renaming the package is nothing more than renaming the folder.