Vercue Extensions with C# – Part 2

In this post, we’ll see how to extend the Vercue Rules module and implement custom Actions, Macros and Monitors.

Before reading this post, you should know:

  1. How to implement a UI Vercue extension
  2. What are Vercue Rules and how to use them

Topics covered by this post:

  • Implementing a Repository-Context Rule Action
  • Implementing a Working-Copy-Context Rule Action
  • Exposing User-Properties in a Rule Action
  • Implementing custom Macros
  • Implementing a Monitor


Rule Actions

Vercue can respond to certain events, some at the SVN Repository and some at the local Working-Copy. A user can define a set of rules and actions to execute when such events occur.

After installing the application, a pre-defined set of actions are available for the user. It is often that additional types of actions are required to complete a task. In this post, we will see how easy it is to implement such custom-actions, and we will even implement some quite useful ones.

Sample 1: An Action that updates the Source to the HEAD revision

Let’s create an Action that will be used to automatically update a Source when an update becomes available.

Step 1: Create the Action class

In your existing extension assembly (from the previous post), add a new class.

Derive and implement the abstract class: Vercue.Monitoring.RepositoryRuleAction

public class SvnUpdateRuleAction : RepositoryRuleAction
{
    protected override void Run(RepositoryRuleContext context)
    {

    }
}

Step 2: Implement the Run method

protected override void Run(RepositoryRuleContext context)
{
    // Create a distinct list of all the Working-Copy Sources
    // that the context applies to
    //
    var distinctSources = context.MatchingContextItems.
        Select(item => item.WrappedItem.Source).
        Where(source => source.IsWorkingCopy).
        Distinct().
        ToList();

    // Update all matching Sources
    //
    foreach (var source in distinctSources)
    {
        // Update
        //
        Svn.Update(source.Path);

        // Check for updates, to remove the green arrow
        //
        source.CheckUpdates();
    }
}

Step 3: Decorate the Action

Decorate the class with the Vercue.Monitoring.RuleActionAttribute.

Give your Action a display name and an optional image file path that will be used as the Action’s icon in the application.

[RuleAction("Svn-Update")]
public class SvnUpdateRuleAction : RepositoryRuleAction

Step 4: Deploy and Test the Action

Build and copy your extension (as described in the previous post).

Create a new Rule, add an Action. You should see your new Action in the list of available Actions. If you haven’t provided an image, a default image will be applied:

image

The action is ready to be used. Select it. Save the Rule and run a test scenario:

Create two working-copies of the same repository. Import both into Vercue.

Make some changes in one of them and commit the changes.

Wait until the other Source is checked for updates. When the update is available, it will automatically update to it and you won’t see the green arrow. Amazing, isn’t it???

By the way, Vercue comes with an internal svn-update Action. The internal Action accepts a path to a working-copy (which doesn’t have to be the same folder as the affecting Source) and it can use interactive TortoiseSVN instead of in-process and non-interactive update.

If you give it ${SourcePath} as the folder path, it will work exactly as the Action you have just implemented except for the extra check for updates after the update is finished (to hide the green arrow)

Sample 2: An Action that commits changes

Now let’s do the opposite trick, and again, use it at your own risk: Let’s write an Action that automatically commits any changes to a Source, assuming there aren’t any conflicts.

Update: Since version 2.0.2, this Action is built-in the application.

Step 1: Create the Action class:

Create a new class, derive from WorkingCopyRuleAction:

[RuleAction("Svn-Commit")]
public class SvnCommitRuleAction : WorkingCopyRuleAction
{
    protected override void Run(WorkingCopyRuleContext context)
    {
    }
}

The Action will use a user-defined message as the log-message message.

Step 2: Create a Rule Action Property:

Add a public-get protected-set string property and decorate it with the RuleActionProeprtyAttribute:

[RuleActionProperty]
public string Message { get; protected set; }

Step 3: Implement the commit method

protected override void Run(WorkingCopyRuleContext context)
{
    // Create a distinct list of all the Working-Copy Sources
    // that the context applies to
    //
    var distinctSources = context.MatchingContextItems.
        Select(item => item.WrappedItem.Source).
        Where(source => source.IsWorkingCopy).
        Distinct().
        ToList();

    // Commit all matching Sources
    //
    foreach (var source in distinctSources)
    {
        // Commit
        //
        Svn.Commit(source.Path, Message);

        // Check for updates, to remove the blue arrow
        //
        source.CheckUpdates();
    }
}

Step 4: Deploy and Test the Action

Create a Rule, add the “Svn-Commit” Action. This time, because your Action exposes a property, an editor dialog will be shown to enter values for the properties.

You can override the GetEditor method and supply your own custom editor, but if you don’t – a default will be provided. Enter a value for the “Message” property and save. For now, lets ignore the “context position” option at the bottom. Next post explains this.

image

Now you can make some modifications to a Source and when the next check-cycle ends – your changes will be automatically committed with the “Hey!” message. Again – Amazing, isn’t it???

Custom Macros

Macros are custom calculated properties. For example, the PossibleConflicted macro returns “True” if an item is both blue and green (modified and ready for update).

There are two types of Macros: SvnLogEntryItem macro and SvnSourceStatusEntry macro.

The first is “green” (Repository) and the second is “blue” (Working-Copy).

Let’s create the same macro for both scopes: A macro that returns the content of a file.

Step 1: Write the Macros

A Macro method must accept either SvnLogEntryItem or SvnSourceStatusEntry.

Add the following method to your already-existing class and decorate it with the RuleMacro attribute. The given Example argument is used to show a sample to the user in the Action editor.

[RuleMacro(Example = "foo")]
public static string FileContent(SvnLogEntryItem item)
{
    // If the item does not locally exist, for example,
    // if it was deleted, return an empty string
    //
    if (!item.ExistsLocally)
    {
        return string.Empty;
    }

    // item.FilePath is the item's full local path
    //
    return File.ReadAllText(item.FilePath);
}

[RuleMacro(Example = "foo")]
public static string FileContent(SvnSourceStatusEntry entry)
{
    // entry.Path is the local path of the file
    //
    return File.ReadAllText(entry.Path);
}

Step 2: Build and Copy

Build and copy your extension. Restart the application to load it.

Step 3: Make sure it works

Create or open an existing Rule.

In the Condition editor, right-click and make sure your Macro is at the top of the list. Try this for both a blue and a green rule:

image

image

That’s it!

You can now use this Macro in both a Condition and an Action.

Appendix: Implement a custom Monitor

A Monitor is a pre-defined set of condition and actions that can be easily created simply by right-clicking an item (Source, Log-Entry or Log-Entry-Item) and selecting the monitor from the list.

Let’s implement a custom Monitor that creates a set of:

  • Condition: The Author is the selected author and the commit day-of-week is Saturday.
  • Action 1: Show a notification popup
  • Action 2: Send him an email

Step 1: Create the Monitor class

Create a new class, name it SaturdayMonitor and derive from Vercue.Monitoring.Monitors.SvnLogEntryMonitor:

public class SaturdayMonitor : SvnLogEntryMonitor
{
    protected override IDictionary<Rule, bool> CreateRules(
        SvnLogEntry item)
    {
    }

    public override string Format(SvnLogEntry item)
    {
    }
}

Step 2: Implement the CreateRules method

This method is called when the user clicks the Monitor menu item. It creates a set of rules, each rule with a condition and a set of actions.

protected override IDictionary<Rule, bool> CreateRules(
    SvnLogEntry item)
{
    var rule = new Rule();

    rule.Condition = new RuleCondition
    {
        CaseSensitive = false,
        Expression = string.Format(
            "Author = '{0}' and CommittedOnDayOfWeek = 'Saturday'",
            item.Author),

        // Repository scope means that the rule is applied only
        // to incoming events (when green arrows appear).
        // It actually means that the rule will run when something
        // happens in the repository and not in the working-copy.
        //
        Scope = RuleCondition.ConditionScope.Repository,
    };

    // Creating Actions is possible only using the RuleAction.Create
    // factory method.
    //
    var action1 = RuleAction.Create<LogEntryNotificationBoxRuleAction>(
        new Dictionary<string, string>
        {
            // Arguments can be passed only as key-value pairs.
            // The real properties are being set only during the
            // execution context, at runtime.
            //
            { "Header", string.Format("{0}, rev{1}",
                item.Author, item.Revision) },
            { "Message", "Is working on Saturday!" },
        });

    var action2 = RuleAction.Create<EmailRuleAction>(
        new Dictionary<string, string>
        {
            { "To", item.Author },
            { "Subject", "Working on Saturday, huh?" },
        });

    rule.Actions.AddAction(action1);
    rule.Actions.AddAction(action2);

    // A Monitor can create several Rules, not only one.
    // In this example, a single Rule is created.
    //
    return new Dictionary<Rule, bool>
    {
        // 'true' means that the created Rule remains open for editing.
        // 'false' will just save it
        //
        { rule, true },
    };
}

Step 3: Implement the Format method

This method is called when the user right-clicks an item (Source, Log-Entry or Log-Entry Item) and opens the Monitor sub-menu. It takes the selected item and returns the text that should appear in the Monitor’s command menu.

In this example, if the user right-clicks a Log-Entry committed by “billjobs@gmail.com”, the Monitor command text will be: “Monitor billjobs@gmail.com for working on Saturdays”.

Notice that this Monitor is good enough only for users that use their email address as the user-name. Otherwise, there’s no point sending an email to “Bill Jobs”. This is just for sake of the sample.

public override string Format(SvnLogEntry item)
{
    return string.Format("Monitor {0} for working on Saturdays",
        item.Author);
}

Step 4: Deploy and Test

Build, copy, restart the application.

Right click a Log-Entry, click Monitor and see yours.

Click it and you’ll have a new Rule, already filled with the Condition and the Actions.

Notice that the Rule is not ready yet, the user still needs to fill some SMTP details.

More stuff to explore

  • The condition syntax is based on the ADO.NET DataColumn Expressions syntax and it supports operators such as Like, Not, In Between and more. You can learn about it in MSDN: http://msdn.microsoft.com/en-us/library/system.data.datacolumn.expression.aspx
  • The list of available columns to use in conditions is available from the condition editor, by right-clicking the textbox.
  • If your monitor is ‘Ready-to-go’, meaning, the code does all the work and you don’t need the user to add additional input, you can return “false” when returning the dictionary of rules (in our sample, we return “true” so the user can edit the SMTP details). If you return false – the Monitor is immediately saved and ready to use.
Share, please:
  • Digg
  • del.icio.us
  • Google Bookmarks
  • DotNetKicks
  • DZone
  • StumbleUpon
  • Facebook
  • Tumblr
  • Twitter
This entry was posted in .NET, Extensions, Vercue and tagged , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Spam protection by WP Captcha-Free