问:
Connect to SignalR self host hub from another app in c#
So yet again I have another SignalR question, but this time it's a little more specific.

I am building a CMS and I've really gone all out this time and ideally want SignalR to act as an API. Over the last 2 years I've built many SignalR apps and all work amazingly, But this time I want to avoid connecting to the HUB/Self host via JavaScript.

I'll explain a littler further once you have viewed the architecture of the system (image below)

After reviewing this tut : http://www.asp.net/signalr/overview/deployment/tutorial-signalr-self-host

it explains how to set up Self Host and most importantly how to access the hub IE:

```<code>$.connection.hub.url = "http://localhost:8080/signalr";

// Declare a proxy to reference the hub.
var chat = $.connection.myHub;
</code>```

Basically I want to scrap that and avoid creating the connection in JavaScript. Is it possible to connect to the hub in C# from an external application? Please note all apps will sit under the same IIS instance.

Hope this is not to much to digest and thank you in advance!

Regards,

答:
Yes, you can do this by creating a self-hosted SignalR Hub instead of having SignalR be hosted like it normally is through IIS.  With the self-hosted solution both the Hub and the client can entirely be in C#.

In this explanation my Hub will be a Windows Service but you could also use a WPF desktop, Winforms desktop, or a Console application.

First in Visual Studio create a Windows Service, ensuring you are using the .NET Framework 4.5 or greater:

Then type this in the package manager console:

```<code>PM> Install-Package Microsoft.AspNet.SignalR.SelfHost
PM> Install-Package ServiceProcess.Helpers
PM> Install-Package Microsoft.Owin.Cors
</code>```

Notice that the latter Microsoft.Owin.Cors is required for cross-domain support.  Next add this to your app.config file:

```<code>  <runtime>
    <loadFromRemoteSources enabled="true" />
  </runtime>
</code>```

Then add this to your Program.cs file:

```<code>using ServiceProcess.Helpers;
using System;
using System.Collections.Generic;
using System.Data;
using System.ServiceProcess;

namespace SignalRBroadcastServiceSample
{
    static class Program
    {
        private static readonly List<ServiceBase> _servicesToRun = new List<ServiceBase>();

        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        static void Main()
        {
            _servicesToRun.Add(CurrencyExchangeService.Instance);

            if (Environment.UserInteractive)
            {
                _servicesToRun.ToArray().LoadServices();
            }
            else
            {
                ServiceBase.Run(_servicesToRun.ToArray());
            }
        }
    }
}
</code>```

Next add a class library which will be the domain.  Now add a Startup class containing this to your service project:

```<code>using System;
using System.Threading.Tasks;
using Microsoft.Owin;
using Owin;

[assembly: OwinStartup(typeof(SignalRBroadcastServiceSample.Startup))]

namespace SignalRBroadcastServiceSample
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            // Branch the pipeline here for requests that start with "/signalr"
            app.Map("/signalr", map =>
            {
                // Setup the CORS middleware to run before SignalR.
                // By default this will allow all origins. You can
                // configure the set of origins and/or http verbs by
                // providing a cors options with a different policy.
                map.UseCors(CorsOptions.AllowAll);
                var hubConfiguration = new HubConfiguration
                {
                    // You can enable JSONP by uncommenting line below.
                    // JSONP requests are insecure but some older browsers (and some
                    // versions of IE) require JSONP to work cross domain
                    // EnableJSONP = true
                    EnableDetailedErrors = true,
                    EnableJSONP = true
                };
                // Run the SignalR pipeline. We're not using MapSignalR
                // since this branch already runs under the "/signalr"
                // path.
                map.RunSignalR(hubConfiguration);
            });
        }
    }
}
</code>```

Now add this to your domain project:

```<code>using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SignalrDomain
{
    public class Currency
    {
        private decimal _usdValue;
        public string CurrencySign { get; set; }
        public decimal Open { get; private set; }
        public decimal Low { get; private set; }
        public decimal High { get; private set; }
        public decimal LastChange { get; private set; }

        public decimal RateChange
        {
            get
            {
                return USDValue - Open;
            }
        }

        public double PercentChange
        {
            get
            {
                return (double)Math.Round(RateChange / USDValue, 4);
            }
        }

        public decimal USDValue
        {
            get
            {
                return _usdValue;
            }
            set
            {
                if (_usdValue == value)
                {
                    return;
                }

                LastChange = value - _usdValue;
                _usdValue = value;

                if (Open == 0)
                {
                    Open = _usdValue;
                }
                if (_usdValue < Low || Low == 0)
                {
                    Low = _usdValue;
                }
                if (_usdValue > High)
                {
                    High = _usdValue;
                }
            }
        }
    }
}
</code>```

Finally create your SignalR hub here where the WebApp.Start call is the key part of the SignalR self-hosting:

```<code>using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Hubs;
using Microsoft.Owin;
using SignalrDomain;
using System;
using System.Collections.Generic;
using System.Linq;

namespace SignalRBroadcastServiceSample
{
    public class CurrencyExchangeHub : Hub
    {
        private readonly CurrencyExchangeService _currencyExchangeHub;

        public CurrencyExchangeHub() :
            this(CurrencyExchangeService.Instance)
        {

        }

        public CurrencyExchangeHub(CurrencyExchangeService currencyExchange)
        {
            _currencyExchangeHub = currencyExchange;
        }

        public IEnumerable<Currency> GetAllCurrencies()
        {
            return _currencyExchangeHub.GetAllCurrencies();
        }

        public string GetMarketState()
        {
            return _currencyExchangeHub.MarketState.ToString();
        }

        public bool OpenMarket()
        {
            _currencyExchangeHub.OpenMarket();
            return true;
        }

        public bool CloseMarket()
        {
            _currencyExchangeHub.CloseMarket();
            return true;
        }

        public bool Reset()
        {
            _currencyExchangeHub.Reset();
            return true;
        }
    }
}
</code>```

Also add this to your CurrencyExchangeService.cs file:

```<code>using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Hubs;
using Microsoft.Owin.Hosting;
using SignalrDomain;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ServiceProcess;
using System.Threading;

namespace SignalRBroadcastServiceSample
{
    public partial class CurrencyExchangeService : ServiceBase
    {
        private Thread mainThread;
        private bool isRunning = true;
        private Random random = new Random();

        protected override void OnStart(string[] args)
        {
            WebApp.Start("http://localhost:8083"); // Must be
                //@"http://+:8083" if you want to connect from other computers
            LoadDefaultCurrencies();

            // Start main thread
            mainThread = new Thread(new ParameterizedThreadStart(this.RunService));
            mainThread.Start(DateTime.MaxValue);
        }

        protected override void OnStop()
        {
            mainThread.Join();
        }

        public void RunService(object timeToComplete)
        {
            DateTime dtTimeToComplete = timeToComplete != null ?
                Convert.ToDateTime(timeToComplete) : DateTime.MaxValue;

            while (isRunning && DateTime.UtcNow < dtTimeToComplete)
            {
                Thread.Sleep(15000);
                NotifyAllClients();
            }
        }

        // This line is necessary to perform the broadcasting to all clients
        private void NotifyAllClients()
        {
            Currency currency = new Currency();
            currency.CurrencySign = "CAD";
            currency.USDValue = random.Next();
            BroadcastCurrencyRate(currency);
            Clients.All.NotifyChange(currency);
        }

        #region "SignalR code"

        // Singleton instance
        private readonly static Lazy<CurrencyExchangeService>
            _instance = new Lazy<CurrencyExchangeService>(
            () => new CurrencyExchangeService
            (GlobalHost.ConnectionManager.GetHubContext<CurrencyExchangeHub>().Clients));

        private readonly object _marketStateLock = new object();
        private readonly object _updateCurrencyRatesLock = new object();

        private readonly ConcurrentDictionary<string,
            Currency> _currencies = new ConcurrentDictionary<string, Currency>();

        // Currency can go up or down by a percentage of this factor on each change
        private readonly double _rangePercent = 0.002;

        private readonly TimeSpan _updateInterval = TimeSpan.FromMilliseconds(250);

        public TimeSpan UpdateInterval
        {
            get { return _updateInterval; }
        }

        private readonly Random _updateOrNotRandom = new Random();

        private Timer _timer;
        private volatile bool _updatingCurrencyRates;
        private volatile MarketState _marketState;

        public CurrencyExchangeService(IHubConnectionContext<dynamic> clients)
        {
            InitializeComponent();

            Clients = clients;
        }

        public static CurrencyExchangeService Instance
        {
            get
            {
                return _instance.Value;
            }
        }

        private IHubConnectionContext<dynamic> Clients
        {
            get;
            set;
        }

        public MarketState MarketState
        {
            get { return _marketState; }
            private set { _marketState = value; }
        }

        public IEnumerable<Currency> GetAllCurrencies()
        {
            return _currencies.Values;
        }

        public bool OpenMarket()
        {
            bool returnCode = false;

            lock (_marketStateLock)
            {
                if (MarketState != MarketState.Open)
                {
                    _timer = new Timer(UpdateCurrencyRates, null, _updateInterval, _updateInterval);

                    MarketState = MarketState.Open;

                    BroadcastMarketStateChange(MarketState.Open);
                }
            }
            returnCode = true;

            return returnCode;
        }

        public bool CloseMarket()
        {
            bool returnCode = false;

            lock (_marketStateLock)
            {
                if (MarketState == MarketState.Open)
                {
                    if (_timer != null)
                    {
                        _timer.Dispose();
                    }

                    MarketState = MarketState.Closed;

                    BroadcastMarketStateChange(MarketState.Closed);
                }
            }
            returnCode = true;

            return returnCode;
        }

        public bool Reset()
        {
            bool returnCode = false;

            lock (_marketStateLock)
            {
                if (MarketState != MarketState.Closed)
                {
                    throw new InvalidOperationException
                        ("Market must be closed before it can be reset.");
                }

                LoadDefaultCurrencies();
                BroadcastMarketReset();
            }
            returnCode = true;

            return returnCode;
        }

        private void LoadDefaultCurrencies()
        {
            _currencies.Clear();

            var currencies = new List<Currency>
            {
                new Currency { CurrencySign = "USD", USDValue = 1.00m },
                new Currency { CurrencySign = "CAD", USDValue = 0.85m },
                new Currency { CurrencySign = "EUR", USDValue = 1.25m }
            };

            currencies.ForEach(currency => _currencies.TryAdd(currency.CurrencySign, currency));
        }

        private void UpdateCurrencyRates(object state)
        {
            // This function must be re-entrant as it's running as a timer interval handler
            lock (_updateCurrencyRatesLock)
            {
                if (!_updatingCurrencyRates)
                {
                    _updatingCurrencyRates = true;

                    foreach (var currency in _currencies.Values)
                    {
                        if (TryUpdateCurrencyRate(currency))
                        {
                            BroadcastCurrencyRate(currency);
                        }
                    }

                    _updatingCurrencyRates = false;
                }
            }
        }

        private bool TryUpdateCurrencyRate(Currency currency)
        {
            // Randomly choose whether to update this currency or not
            var r = _updateOrNotRandom.NextDouble();
            if (r > 0.1)
            {
                return false;
            }

            // Update the currency price by a random factor of the range percent
            var random = new Random((int)Math.Floor(currency.USDValue));
            var percentChange = random.NextDouble() * _rangePercent;
            var pos = random.NextDouble() > 0.51;
            var change = Math.Round(currency.USDValue * (decimal)percentChange, 2);
            change = pos ? change : -change;

            currency.USDValue += change;
            return true;
        }

        private void BroadcastMarketStateChange(MarketState marketState)
        {
            switch (marketState)
            {
                case MarketState.Open:
                    Clients.All.marketOpened();
                    break;
                case MarketState.Closed:
                    Clients.All.marketClosed();
                    break;
                default:
                    break;
            }
        }

        private void BroadcastMarketReset()
        {
            Clients.All.marketReset();
        }

        private void BroadcastCurrencyRate(Currency currency)
        {
            Clients.All.updateCurrencyRate(currency);
        }
    }

    public enum MarketState
    {
        Closed,
        Open
    }

    #endregion
}
</code>```

Next in your Client Console application, add this NuGet package:

```<code>PM> Install-Package  Microsoft.AspNet.SignalR.Client
</code>```

Also, add a CommmunicationHandler class:

```<code>using Microsoft.AspNet.SignalR.Client;
using SignalrDomain;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Client
{
    public static class CommunicationHandler
    {
        public static string ExecuteMethod(string method, string args, string serverUri, string hubName)
        {
            var hubConnection = new HubConnection("http://localhost:8083");
            IHubProxy currencyExchangeHubProxy = hubConnection.CreateHubProxy("CurrencyExchangeHub");

            // This line is necessary to subscribe for broadcasting messages
            currencyExchangeHubProxy.On<Currency>("NotifyChange", HandleNotify);

            // Start the connection
            hubConnection.Start().Wait();

            var result = currencyExchangeHubProxy.Invoke<string>(method).Result;

            return result;
        }

        private static void HandleNotify(Currency currency)
        {
            Console.WriteLine("Currency " + currency.CurrencySign + ", Rate = " + currency.USDValue);
        }
    }
}
</code>```

Here is the Program class in the client:

```<code>using System;
using System.Diagnostics;
using System.Net;

namespace Client
{
    class Program
    {
        static void Main(string[] args)
        {
            var state = CommunicationHandler.ExecuteMethod("GetMarketState",
                "", IPAddress.Any.ToString(), "CurrencyExchangeHub");
            Console.WriteLine("Market State is " + state);

            if (state == "Closed")
            {
                var returnCode = CommunicationHandler.ExecuteMethod
                    ("OpenMarket", "", IPAddress.Any.ToString(), "CurrencyExchangeHub");
                Debug.Assert(returnCode == "True");
                Console.WriteLine("Market State is Open");
            }

            Console.ReadLine();
        }
    }
}
</code>```

Now run the service and start it, then when you run the Console client application you should see currency rates being updated periodically.

The original article is here:  https://www.codeproject.com/Articles/892634/Using-SignalR-Broadcasting-and-Notifications-with

You can download the full source code here: http://www.sandbox.ca/~rmoore/export/codeproject/CodeProjectSelfHostedBroadcastServiceSignalRSample.zip

Also, instead of using the old way of hosting and installing a Windows service as shown above you could update the application using the helpful Topshelf NuGet package: https://github.com/Topshelf/Topshelf

Body:
So yet again I have another SignalR question, but this time it's a little more specific.

I am building a CMS and I've really gone all out this time and ideally want SignalR to act as an API. Over the last 2 years I've built many SignalR apps and all work amazingly, But this time I want to avoid connecting to the HUB/Self host via JavaScript.

I'll explain a littler further once you have viewed the architecture of the system (image below)

After reviewing this tut : http://www.asp.net/signalr/overview/deployment/tutorial-signalr-self-host

it explains how to set up Self Host and most importantly how to access the hub IE:

$.connection.hub.url = "http://localhost:8080/signalr";

// Declare a proxy to reference the hub.
var chat = $.connection.myHub;

Basically I want to scrap that and avoid creating the connection in JavaScript. Is it possible to connect to the hub in C# from an external application? Please note all apps will sit under the same IIS instance.

Hope this is not to much to digest and thank you in advance!

Regards,

Body:
Yes, you can do this by creating a self-hosted SignalR Hub instead of having SignalR be hosted like it normally is through IIS.  With the self-hosted solution both the Hub and the client can entirely be in C#.

In this explanation my Hub will be a Windows Service but you could also use a WPF desktop, Winforms desktop, or a Console application.

First in Visual Studio create a Windows Service, ensuring you are using the .NET Framework 4.5 or greater:

Then type this in the package manager console:

PM> Install-Package Microsoft.AspNet.SignalR.SelfHost
PM> Install-Package ServiceProcess.Helpers
PM> Install-Package Microsoft.Owin.Cors

Notice that the latter Microsoft.Owin.Cors is required for cross-domain support.  Next add this to your app.config file:

  <runtime>
    <loadFromRemoteSources enabled="true" />
  </runtime>

Then add this to your Program.cs file:

using ServiceProcess.Helpers;
using System;
using System.Collections.Generic;
using System.Data;
using System.ServiceProcess;

namespace SignalRBroadcastServiceSample
{
    static class Program
    {
        private static readonly List<ServiceBase> _servicesToRun = new List<ServiceBase>();

        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        static void Main()
        {
            _servicesToRun.Add(CurrencyExchangeService.Instance);

            if (Environment.UserInteractive)
            {
                _servicesToRun.ToArray().LoadServices();
            }
            else
            {
                ServiceBase.Run(_servicesToRun.ToArray());
            }
        }
    }
}

Next add a class library which will be the domain.  Now add a Startup class containing this to your service project:

using System;
using System.Threading.Tasks;
using Microsoft.Owin;
using Owin;

[assembly: OwinStartup(typeof(SignalRBroadcastServiceSample.Startup))]

namespace SignalRBroadcastServiceSample
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            // Branch the pipeline here for requests that start with "/signalr"
            app.Map("/signalr", map =>
            {
                // Setup the CORS middleware to run before SignalR.
                // By default this will allow all origins. You can
                // configure the set of origins and/or http verbs by
                // providing a cors options with a different policy.
                map.UseCors(CorsOptions.AllowAll);
                var hubConfiguration = new HubConfiguration
                {
                    // You can enable JSONP by uncommenting line below.
                    // JSONP requests are insecure but some older browsers (and some
                    // versions of IE) require JSONP to work cross domain
                    // EnableJSONP = true
                    EnableDetailedErrors = true,
                    EnableJSONP = true
                };
                // Run the SignalR pipeline. We're not using MapSignalR
                // since this branch already runs under the "/signalr"
                // path.
                map.RunSignalR(hubConfiguration);
            });
        }
    }
}

Now add this to your domain project:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SignalrDomain
{
    public class Currency
    {
        private decimal _usdValue;
        public string CurrencySign { get; set; }
        public decimal Open { get; private set; }
        public decimal Low { get; private set; }
        public decimal High { get; private set; }
        public decimal LastChange { get; private set; }

        public decimal RateChange
        {
            get
            {
                return USDValue - Open;
            }
        }

        public double PercentChange
        {
            get
            {
                return (double)Math.Round(RateChange / USDValue, 4);
            }
        }

        public decimal USDValue
        {
            get
            {
                return _usdValue;
            }
            set
            {
                if (_usdValue == value)
                {
                    return;
                }

                LastChange = value - _usdValue;
                _usdValue = value;

                if (Open == 0)
                {
                    Open = _usdValue;
                }
                if (_usdValue < Low || Low == 0)
                {
                    Low = _usdValue;
                }
                if (_usdValue > High)
                {
                    High = _usdValue;
                }
            }
        }
    }
}

Finally create your SignalR hub here where the WebApp.Start call is the key part of the SignalR self-hosting:

using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Hubs;
using Microsoft.Owin;
using SignalrDomain;
using System;
using System.Collections.Generic;
using System.Linq;

namespace SignalRBroadcastServiceSample
{
    public class CurrencyExchangeHub : Hub
    {
        private readonly CurrencyExchangeService _currencyExchangeHub;

        public CurrencyExchangeHub() :
            this(CurrencyExchangeService.Instance)
        {

        }

        public CurrencyExchangeHub(CurrencyExchangeService currencyExchange)
        {
            _currencyExchangeHub = currencyExchange;
        }

        public IEnumerable<Currency> GetAllCurrencies()
        {
            return _currencyExchangeHub.GetAllCurrencies();
        }

        public string GetMarketState()
        {
            return _currencyExchangeHub.MarketState.ToString();
        }

        public bool OpenMarket()
        {
            _currencyExchangeHub.OpenMarket();
            return true;
        }

        public bool CloseMarket()
        {
            _currencyExchangeHub.CloseMarket();
            return true;
        }

        public bool Reset()
        {
            _currencyExchangeHub.Reset();
            return true;
        }
    }
}

Also add this to your CurrencyExchangeService.cs file:

using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Hubs;
using Microsoft.Owin.Hosting;
using SignalrDomain;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ServiceProcess;
using System.Threading;

namespace SignalRBroadcastServiceSample
{
    public partial class CurrencyExchangeService : ServiceBase
    {
        private Thread mainThread;
        private bool isRunning = true;
        private Random random = new Random();

        protected override void OnStart(string[] args)
        {
            WebApp.Start("http://localhost:8083"); // Must be
                //@"http://+:8083" if you want to connect from other computers
            LoadDefaultCurrencies();

            // Start main thread
            mainThread = new Thread(new ParameterizedThreadStart(this.RunService));
            mainThread.Start(DateTime.MaxValue);
        }

        protected override void OnStop()
        {
            mainThread.Join();
        }

        public void RunService(object timeToComplete)
        {
            DateTime dtTimeToComplete = timeToComplete != null ?
                Convert.ToDateTime(timeToComplete) : DateTime.MaxValue;

            while (isRunning && DateTime.UtcNow < dtTimeToComplete)
            {
                Thread.Sleep(15000);
                NotifyAllClients();
            }
        }

        // This line is necessary to perform the broadcasting to all clients
        private void NotifyAllClients()
        {
            Currency currency = new Currency();
            currency.CurrencySign = "CAD";
            currency.USDValue = random.Next();
            BroadcastCurrencyRate(currency);
            Clients.All.NotifyChange(currency);
        }

        #region "SignalR code"

        // Singleton instance
        private readonly static Lazy<CurrencyExchangeService>
            _instance = new Lazy<CurrencyExchangeService>(
            () => new CurrencyExchangeService
            (GlobalHost.ConnectionManager.GetHubContext<CurrencyExchangeHub>().Clients));

        private readonly object _marketStateLock = new object();
        private readonly object _updateCurrencyRatesLock = new object();

        private readonly ConcurrentDictionary<string,
            Currency> _currencies = new ConcurrentDictionary<string, Currency>();

        // Currency can go up or down by a percentage of this factor on each change
        private readonly double _rangePercent = 0.002;

        private readonly TimeSpan _updateInterval = TimeSpan.FromMilliseconds(250);

        public TimeSpan UpdateInterval
        {
            get { return _updateInterval; }
        }

        private readonly Random _updateOrNotRandom = new Random();

        private Timer _timer;
        private volatile bool _updatingCurrencyRates;
        private volatile MarketState _marketState;

        public CurrencyExchangeService(IHubConnectionContext<dynamic> clients)
        {
            InitializeComponent();

            Clients = clients;
        }

        public static CurrencyExchangeService Instance
        {
            get
            {
                return _instance.Value;
            }
        }

        private IHubConnectionContext<dynamic> Clients
        {
            get;
            set;
        }

        public MarketState MarketState
        {
            get { return _marketState; }
            private set { _marketState = value; }
        }

        public IEnumerable<Currency> GetAllCurrencies()
        {
            return _currencies.Values;
        }

        public bool OpenMarket()
        {
            bool returnCode = false;

            lock (_marketStateLock)
            {
                if (MarketState != MarketState.Open)
                {
                    _timer = new Timer(UpdateCurrencyRates, null, _updateInterval, _updateInterval);

                    MarketState = MarketState.Open;

                    BroadcastMarketStateChange(MarketState.Open);
                }
            }
            returnCode = true;

            return returnCode;
        }

        public bool CloseMarket()
        {
            bool returnCode = false;

            lock (_marketStateLock)
            {
                if (MarketState == MarketState.Open)
                {
                    if (_timer != null)
                    {
                        _timer.Dispose();
                    }

                    MarketState = MarketState.Closed;

                    BroadcastMarketStateChange(MarketState.Closed);
                }
            }
            returnCode = true;

            return returnCode;
        }

        public bool Reset()
        {
            bool returnCode = false;

            lock (_marketStateLock)
            {
                if (MarketState != MarketState.Closed)
                {
                    throw new InvalidOperationException
                        ("Market must be closed before it can be reset.");
                }

                LoadDefaultCurrencies();
                BroadcastMarketReset();
            }
            returnCode = true;

            return returnCode;
        }

        private void LoadDefaultCurrencies()
        {
            _currencies.Clear();

            var currencies = new List<Currency>
            {
                new Currency { CurrencySign = "USD", USDValue = 1.00m },
                new Currency { CurrencySign = "CAD", USDValue = 0.85m },
                new Currency { CurrencySign = "EUR", USDValue = 1.25m }
            };

            currencies.ForEach(currency => _currencies.TryAdd(currency.CurrencySign, currency));
        }

        private void UpdateCurrencyRates(object state)
        {
            // This function must be re-entrant as it's running as a timer interval handler
            lock (_updateCurrencyRatesLock)
            {
                if (!_updatingCurrencyRates)
                {
                    _updatingCurrencyRates = true;

                    foreach (var currency in _currencies.Values)
                    {
                        if (TryUpdateCurrencyRate(currency))
                        {
                            BroadcastCurrencyRate(currency);
                        }
                    }

                    _updatingCurrencyRates = false;
                }
            }
        }

        private bool TryUpdateCurrencyRate(Currency currency)
        {
            // Randomly choose whether to update this currency or not
            var r = _updateOrNotRandom.NextDouble();
            if (r > 0.1)
            {
                return false;
            }

            // Update the currency price by a random factor of the range percent
            var random = new Random((int)Math.Floor(currency.USDValue));
            var percentChange = random.NextDouble() * _rangePercent;
            var pos = random.NextDouble() > 0.51;
            var change = Math.Round(currency.USDValue * (decimal)percentChange, 2);
            change = pos ? change : -change;

            currency.USDValue += change;
            return true;
        }

        private void BroadcastMarketStateChange(MarketState marketState)
        {
            switch (marketState)
            {
                case MarketState.Open:
                    Clients.All.marketOpened();
                    break;
                case MarketState.Closed:
                    Clients.All.marketClosed();
                    break;
                default:
                    break;
            }
        }

        private void BroadcastMarketReset()
        {
            Clients.All.marketReset();
        }

        private void BroadcastCurrencyRate(Currency currency)
        {
            Clients.All.updateCurrencyRate(currency);
        }
    }

    public enum MarketState
    {
        Closed,
        Open
    }

    #endregion
}

Next in your Client Console application, add this NuGet package:

PM> Install-Package  Microsoft.AspNet.SignalR.Client

Also, add a CommmunicationHandler class:

using Microsoft.AspNet.SignalR.Client;
using SignalrDomain;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Client
{
    public static class CommunicationHandler
    {
        public static string ExecuteMethod(string method, string args, string serverUri, string hubName)
        {
            var hubConnection = new HubConnection("http://localhost:8083");
            IHubProxy currencyExchangeHubProxy = hubConnection.CreateHubProxy("CurrencyExchangeHub");

            // This line is necessary to subscribe for broadcasting messages
            currencyExchangeHubProxy.On<Currency>("NotifyChange", HandleNotify);

            // Start the connection
            hubConnection.Start().Wait();

            var result = currencyExchangeHubProxy.Invoke<string>(method).Result;

            return result;
        }

        private static void HandleNotify(Currency currency)
        {
            Console.WriteLine("Currency " + currency.CurrencySign + ", Rate = " + currency.USDValue);
        }
    }
}

Here is the Program class in the client:

using System;
using System.Diagnostics;
using System.Net;

namespace Client
{
    class Program
    {
        static void Main(string[] args)
        {
            var state = CommunicationHandler.ExecuteMethod("GetMarketState",
                "", IPAddress.Any.ToString(), "CurrencyExchangeHub");
            Console.WriteLine("Market State is " + state);

            if (state == "Closed")
            {
                var returnCode = CommunicationHandler.ExecuteMethod
                    ("OpenMarket", "", IPAddress.Any.ToString(), "CurrencyExchangeHub");
                Debug.Assert(returnCode == "True");
                Console.WriteLine("Market State is Open");
            }

            Console.ReadLine();
        }
    }
}

Now run the service and start it, then when you run the Console client application you should see currency rates being updated periodically.

The original article is here:  https://www.codeproject.com/Articles/892634/Using-SignalR-Broadcasting-and-Notifications-with

You can download the full source code here: http://www.sandbox.ca/~rmoore/export/codeproject/CodeProjectSelfHostedBroadcastServiceSignalRSample.zip

Also, instead of using the old way of hosting and installing a Windows service as shown above you could update the application using the helpful Topshelf NuGet package: https://github.com/Topshelf/Topshelf


来源: stackexchange

问:
Compile Sass Files in .NET Core
I am trying to add Sass files to my .NET Core application in Visual Studio 2017. However, I cannot find any way to compile .scss files once I've made them. I sifted through NPM and Bower, but neither offer support for Sass. </p>

<p>The Microsoft documentation for adding Sass (https://learn.microsoft.com/en-us/aspnet/core/client-side/less-sass-fa) is outdated, and assumes that the project has a package.json file. Any guidance on enabling Sass compiling for my project would be much appreciated. </p>

答:
The easiest way to add .scss to your ASP.NET Core apps is by going to the Nuget Package Manager and installing LigerShark.WebOptimizer.Core and LigerShark.WebOptimizer.Sass.

After that, you can just put

```<code><link rel="stylesheet" href="~/css/site.scss" />
</code>```

in the head of the _Layout.cshtml file and the LigerShark.WebOptimizer.Sass package will compile all of your sass files automatically.

ContentLicense: CC BY-SA 3.0

Title: Compile Sass Files in .NET Core

Body:
I am trying to add Sass files to my .NET Core application in Visual Studio 2017. However, I cannot find any way to compile .scss files once I've made them. I sifted through NPM and Bower, but neither offer support for Sass.

The Microsoft documentation for adding Sass (https://learn.microsoft.com/en-us/aspnet/core/client-side/less-sass-fa) is outdated, and assumes that the project has a package.json file. Any guidance on enabling Sass compiling for my project would be much appreciated.

Body:
The easiest way to add .scss to your ASP.NET Core apps is by going to the Nuget Package Manager and installing LigerShark.WebOptimizer.Core and LigerShark.WebOptimizer.Sass.

After that, you can just put

<link rel="stylesheet" href="~/css/site.scss" />

in the head of the _Layout.cshtml file and the LigerShark.WebOptimizer.Sass package will compile all of your sass files automatically.

ContentLicense: CC BY-SA 4.0


问:
Devise Error: NameError (uninitialized constant Unlock)
I recently started getting complaints that locked users cannot reset their accounts from the email I am sending.

I'm getting

NameError (uninitialized constant Unlock):
    config/initializers/quiet_assets.rb:6:in `call_with_quiet_assets'

Nothing has changed on my routesr and nothing has changed in my links... what could be causing this error message and how can I fix it?

quiet_assets.rb:

```<code>Rails.application.assets.logger = Logger.new('log/logger.txt')
Rails::Rack::Logger.class_eval do
  def call_with_quiet_assets(env)
    previous_level = Rails.logger.level
    Rails.logger.level = Logger::ERROR if env['PATH_INFO'].index("/assets/") == 0
    call_without_quiet_assets(env).tap do
      Rails.logger.level = previous_level
    end
  end
  alias_method_chain :call, :quiet_assets
end
</code>```

Stacktrace:

```<code>INFO  Started GET "/users/unlock?unlock_token=CzpxHwV5kL7EyDZb32Ex" for 127.0.0.1 at 2018-11-22 21:08:56 +0200
INFO  Processing by Devise::UnlocksController#show as HTML
INFO  Parameters: {"unlock_token"=>"CzpxHwV5kL7EyDZb32Ex"}
INFO  -- store_location: /users/unlock?unlock_token=CzpxHwV5kL7EyDZb32Ex
INFO  Completed 500 Internal Server Error in 8.1ms
FATAL  NameError (uninitialized constant Unlock):
  config/initializers/quiet_assets.rb:6:in `call_with_quiet_assets'
INFO  Rendered /Users/david/.rvm/gems/ruby-1.9.3-p551/gems/actionpack-3.2.17/lib/action_dispatch/middleware/templates/rescues/_trace.erb (1.7ms)
INFO  Rendered /Users/david/.rvm/gems/ruby-1.9.3-p551/gems/actionpack-3.2.17/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb (0.8ms)
</code>```

Controller

```<code>require 'white_label_utils'
include ERB::Util
#Need to include these helpers or image_path won't work
include Sprockets::Helpers::RailsHelper
include Sprockets::Helpers::IsolatedHelper
include ActionView::Helpers::AssetTagHelper

class CustomMailer < Devise::Mailer
  include Devise::Mailers::Helpers
  include Devise::Controllers::UrlHelpers # Optional. eg. `confirmation_url`
  helper :application # gives access to all helpers defined within `application_helper`.
  default from: 'no-reply@ourcompany.com'
  # Sends a simple email (without attachments and call to action button)
  # to - array of recipients
  # subject - subject title of email
  # message - body of email
  # section - section used for white labeling purposes
  # file_attachments - array of files attachments to be included in email. eg. [{name: 'myFileName.csv', content: fileData}, {name: 'anotherFileName.zip', content: anotherFileData}] Empty by default
  def simple_email(to, subject, message, section = nil, file_attachments = [])
    get_whitelabel_details(section)
    set_email_global_params(to, subject, message)
    # if there are files, attach them
    file_attachments = [] if file_attachments.nil?
    if file_attachments.length > 0
      file_attachments.each do |file|
        attachments["#{file[:name]}"] = file[:content]
      end
    end
    mail(to: to, subject: @subject, from: @whitelabel.email_from).delivery_method.settings.merge!(Dynamic::Application.config.send_grid_smtp_settings)
  end

  # Sends a devise invitation_instructions email located under app/views/user/mailer/invitation_instructions.html
  def invitation_instructions(record, opts = {})
    to = record.email
    begin
      section = record.publisher.sections.first
    rescue => e
      Rails.logger.warn "Could not find any sections associated with user #{record}"
      section = nil
    end
    initialize_from_record(record)
    get_whitelabel_details(section)
    set_email_global_params(to, invitation_email_subject, I18n.t("devise.mailer.invitation_instructions.message", invitee_name: record.first_name.capitalize, inviter_name: @resource.invited_by.is_dy_admin ? @resource.invited_by.last_name.capitalize : @resource.invited_by.full_name, product_name: @whitelabel.product_name))
    uri = URI.parse(edit_invitations_url({invitation_token: @resource.invitation_token}))
    @cta_text = I18n.t("devise.mailer.invitation_instructions.call_to_action_text")
    @cta_link = whitelabel_links(uri)
    super record, opts
  end

  # Sends a devise unlock_instructions email located under app/views/user/mailer/unlock_instructions.html
  def unlock_instructions(record, opts = {})
    to = record.email
    begin
      section = record.publisher.sections.first
    rescue => e
      Rails.logger.warn "Could not find any sections associated with user #{record}"
      section = nil
    end
    initialize_from_record(record)
    get_whitelabel_details(section)
    set_email_global_params(to, I18n.t("devise.mailer.unlock_instructions.subject"), I18n.t("devise.mailer.unlock_instructions.message"))
    uri = URI.parse(unlock_url(@resource, :unlock_token => @resource.unlock_token))
    @cta_text = I18n.t("devise.mailer.unlock_instructions.call_to_action_text")
    @cta_link = whitelabel_links(uri)
    super record, opts
  end

  # Sends a devise reset_password_instruction email located under app/views/user/mailer/reset_password_instructions.html
  def reset_password_instructions(record, token = nil, opts = {})
    to = record.email
    begin
      section = record.publisher.sections.first
    rescue => e
      Rails.logger.warn "Could not find any sections associated with user #{record}"
      section = nil
    end
    initialize_from_record(record)
    get_whitelabel_details(section)
    set_email_global_params(to, I18n.t("devise.mailer.reset_password_instructions.subject"), I18n.t("devise.mailer.reset_password_instructions.message"))
    uri = URI.parse(edit_password_url(@resource, {reset_password_token: @resource.reset_password_token}))
    @cta_text = I18n.t("devise.mailer.reset_password_instructions.call_to_action_text")
    @cta_link = whitelabel_links(uri)
    super record, opts
  end

  private
  def devise_mail(record, action, opts={})
    initialize_from_record(record)
    (mail headers_for(action, opts)).delivery_method.settings.merge!(Dynamic::Application.config.send_grid_smtp_settings)
  end

  # Customize the subject and sender display name by the white-label profile
  def headers_for(action, opts)
    headers = {:from => @email_from,
               :reply_to => @email_from}
    super.merge!(headers)
  end

  # Overrides the default subject line for devise emails (reset_password_instructions, invitation_instructions, etc)
  def subject_for(key)
    return super unless key.to_s == 'invitation_instructions'
    invitation_email_subject
  end

  # Gets the whitelabel details associated with the section
  def get_whitelabel_details(section)
    @section = section.blank? ? err_message('section') : section
    begin
      @whitelabel = WhiteLabelUtils::get_profile(nil, section.site.publisher)
    rescue => e
      Rails.logger.warn "Could not determine WhiteLabel profile when sending email"
      @whitelabel = WhiteLabelUtils::get_profile(nil, nil)
    end
  end

  # Validates the existence of parameters and assigns them to global vars that will be used in the email template itself
  def set_email_global_params(to, subject, message)
    @errors = nil
    @to = to.blank? ? err_message('to') : to
    @subject = subject.blank? ? err_message('subject') : subject.slice(0, 1).capitalize + subject.slice(1..-1).chomp('.') #remove trailing period, we add this in the template so this avoids duplicates
    @message = message.blank? ? err_message('message') : message.slice(0, 1).capitalize + message.slice(1..-1).chomp('.') #remove trailing period, we add this in the template so this avoids duplicates
    @email_from = @whitelabel.email_from
    @reply_to = @whitelabel.reply_to
    @introduction = create_introduction(to)

    unless @errors.blank?
      raise @errors
    end
  end

  def invitation_email_subject
    I18n.t("devise.mailer.invitation_instructions.subject", product_name: @whitelabel.product_name)
  end

  # Receives a generic url and replaces it with whitelabelled domains
  def whitelabel_links uri
    "#{@whitelabel.root_url_info[:protocol]}://#{@whitelabel.root_url_info[:host]}#{uri.path}?#{uri.query}"
    # rendered as https://companydomain.com/users/unlock?unlock_token=TOKENVALUE
  end

  # Searches the system for a user with 'email_address' and returns
  # a personalized introduction with the user's first name otherwise
  # returns a generic introduction with the wording 'Dear User'
  def create_introduction email_address
    user = User.where(email: email_address).first
    "#{I18n.t("dy.common.general.hi")}#{user.nil? ? '' : " #{user.first_name.capitalize}"},"
  end

  def err_message(val)
    @errors = @errors.blank? ? '<' + val + '> field can not be empty' : @errors + '\n<' + val + '> field can not be empty'
  end

end
</code>```

The route being called is https://example.com/users/unlock?unlock_token=USERS_TOKEN
and the controller is from devise unlockscontroller

```<code>class Devise::UnlocksController < DeviseController
  prepend_before_filter :require_no_authentication

  # GET /resource/unlock/new
  def new
    build_resource({})
  end

  # POST /resource/unlock
  def create
    self.resource = resource_class.send_unlock_instructions(resource_params)

    if successfully_sent?(resource)
      respond_with({}, :location => after_sending_unlock_instructions_path_for(resource))
    else
      respond_with(resource)
    end
  end

  # GET /resource/unlock?unlock_token=abcdef
  def show
    self.resource = resource_class.unlock_access_by_token(params[:unlock_token])

    if resource.errors.empty?
      set_flash_message :notice, :unlocked if is_navigational_format?
      respond_with_navigational(resource){ redirect_to after_unlock_path_for(resource) }
    else
      respond_with_navigational(resource.errors, :status => :unprocessable_entity){ render :new }
    end
  end

  protected

    # The path used after sending unlock password instructions
    def after_sending_unlock_instructions_path_for(resource)
      new_session_path(resource) if is_navigational_format?
    end

    # The path used after unlocking the resource
    def after_unlock_path_for(resource)
      new_session_path(resource)  if is_navigational_format?
    end

end
</code>```

答:
This was never properly resolved. I ended overriding the built-in devise callback functions (which is a horrible practice)

Old versions of Rails3 and Ruby 1.9.3 are most probably the culprits - but I don't have the privilege of upgrading - too much legacy code working in production already.

Story of my life :(

Body:
I recently started getting complaints that locked users cannot reset their accounts from the email I am sending.

I'm getting

NameError (uninitialized constant Unlock):
    config/initializers/quiet_assets.rb:6:in `call_with_quiet_assets'

Nothing has changed on my routesr and nothing has changed in my links... what could be causing this error message and how can I fix it?

quiet_assets.rb:

Rails.application.assets.logger = Logger.new('log/logger.txt')
Rails::Rack::Logger.class_eval do
  def call_with_quiet_assets(env)
    previous_level = Rails.logger.level
    Rails.logger.level = Logger::ERROR if env['PATH_INFO'].index("/assets/") == 0
    call_without_quiet_assets(env).tap do
      Rails.logger.level = previous_level
    end
  end
  alias_method_chain :call, :quiet_assets
end

Stacktrace:

INFO  Started GET "/users/unlock?unlock_token=CzpxHwV5kL7EyDZb32Ex" for 127.0.0.1 at 2018-11-22 21:08:56 +0200
INFO  Processing by Devise::UnlocksController#show as HTML
INFO  Parameters: {"unlock_token"=>"CzpxHwV5kL7EyDZb32Ex"}
INFO  -- store_location: /users/unlock?unlock_token=CzpxHwV5kL7EyDZb32Ex
INFO  Completed 500 Internal Server Error in 8.1ms
FATAL  NameError (uninitialized constant Unlock):
  config/initializers/quiet_assets.rb:6:in `call_with_quiet_assets'
INFO  Rendered /Users/david/.rvm/gems/ruby-1.9.3-p551/gems/actionpack-3.2.17/lib/action_dispatch/middleware/templates/rescues/_trace.erb (1.7ms)
INFO  Rendered /Users/david/.rvm/gems/ruby-1.9.3-p551/gems/actionpack-3.2.17/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb (0.8ms)

Controller

require 'white_label_utils'
include ERB::Util
#Need to include these helpers or image_path won't work
include Sprockets::Helpers::RailsHelper
include Sprockets::Helpers::IsolatedHelper
include ActionView::Helpers::AssetTagHelper

class CustomMailer < Devise::Mailer
  include Devise::Mailers::Helpers
  include Devise::Controllers::UrlHelpers # Optional. eg. `confirmation_url`
  helper :application # gives access to all helpers defined within `application_helper`.
  default from: 'no-reply@ourcompany.com'
  # Sends a simple email (without attachments and call to action button)
  # to - array of recipients
  # subject - subject title of email
  # message - body of email
  # section - section used for white labeling purposes
  # file_attachments - array of files attachments to be included in email. eg. [{name: 'myFileName.csv', content: fileData}, {name: 'anotherFileName.zip', content: anotherFileData}] Empty by default
  def simple_email(to, subject, message, section = nil, file_attachments = [])
    get_whitelabel_details(section)
    set_email_global_params(to, subject, message)
    # if there are files, attach them
    file_attachments = [] if file_attachments.nil?
    if file_attachments.length > 0
      file_attachments.each do |file|
        attachments["#{file[:name]}"] = file[:content]
      end
    end
    mail(to: to, subject: @subject, from: @whitelabel.email_from).delivery_method.settings.merge!(Dynamic::Application.config.send_grid_smtp_settings)
  end

  # Sends a devise invitation_instructions email located under app/views/user/mailer/invitation_instructions.html
  def invitation_instructions(record, opts = {})
    to = record.email
    begin
      section = record.publisher.sections.first
    rescue => e
      Rails.logger.warn "Could not find any sections associated with user #{record}"
      section = nil
    end
    initialize_from_record(record)
    get_whitelabel_details(section)
    set_email_global_params(to, invitation_email_subject, I18n.t("devise.mailer.invitation_instructions.message", invitee_name: record.first_name.capitalize, inviter_name: @resource.invited_by.is_dy_admin ? @resource.invited_by.last_name.capitalize : @resource.invited_by.full_name, product_name: @whitelabel.product_name))
    uri = URI.parse(edit_invitations_url({invitation_token: @resource.invitation_token}))
    @cta_text = I18n.t("devise.mailer.invitation_instructions.call_to_action_text")
    @cta_link = whitelabel_links(uri)
    super record, opts
  end

  # Sends a devise unlock_instructions email located under app/views/user/mailer/unlock_instructions.html
  def unlock_instructions(record, opts = {})
    to = record.email
    begin
      section = record.publisher.sections.first
    rescue => e
      Rails.logger.warn "Could not find any sections associated with user #{record}"
      section = nil
    end
    initialize_from_record(record)
    get_whitelabel_details(section)
    set_email_global_params(to, I18n.t("devise.mailer.unlock_instructions.subject"), I18n.t("devise.mailer.unlock_instructions.message"))
    uri = URI.parse(unlock_url(@resource, :unlock_token => @resource.unlock_token))
    @cta_text = I18n.t("devise.mailer.unlock_instructions.call_to_action_text")
    @cta_link = whitelabel_links(uri)
    super record, opts
  end

  # Sends a devise reset_password_instruction email located under app/views/user/mailer/reset_password_instructions.html
  def reset_password_instructions(record, token = nil, opts = {})
    to = record.email
    begin
      section = record.publisher.sections.first
    rescue => e
      Rails.logger.warn "Could not find any sections associated with user #{record}"
      section = nil
    end
    initialize_from_record(record)
    get_whitelabel_details(section)
    set_email_global_params(to, I18n.t("devise.mailer.reset_password_instructions.subject"), I18n.t("devise.mailer.reset_password_instructions.message"))
    uri = URI.parse(edit_password_url(@resource, {reset_password_token: @resource.reset_password_token}))
    @cta_text = I18n.t("devise.mailer.reset_password_instructions.call_to_action_text")
    @cta_link = whitelabel_links(uri)
    super record, opts
  end

  private
  def devise_mail(record, action, opts={})
    initialize_from_record(record)
    (mail headers_for(action, opts)).delivery_method.settings.merge!(Dynamic::Application.config.send_grid_smtp_settings)
  end

  # Customize the subject and sender display name by the white-label profile
  def headers_for(action, opts)
    headers = {:from => @email_from,
               :reply_to => @email_from}
    super.merge!(headers)
  end

  # Overrides the default subject line for devise emails (reset_password_instructions, invitation_instructions, etc)
  def subject_for(key)
    return super unless key.to_s == 'invitation_instructions'
    invitation_email_subject
  end

  # Gets the whitelabel details associated with the section
  def get_whitelabel_details(section)
    @section = section.blank? ? err_message('section') : section
    begin
      @whitelabel = WhiteLabelUtils::get_profile(nil, section.site.publisher)
    rescue => e
      Rails.logger.warn "Could not determine WhiteLabel profile when sending email"
      @whitelabel = WhiteLabelUtils::get_profile(nil, nil)
    end
  end

  # Validates the existence of parameters and assigns them to global vars that will be used in the email template itself
  def set_email_global_params(to, subject, message)
    @errors = nil
    @to = to.blank? ? err_message('to') : to
    @subject = subject.blank? ? err_message('subject') : subject.slice(0, 1).capitalize + subject.slice(1..-1).chomp('.') #remove trailing period, we add this in the template so this avoids duplicates
    @message = message.blank? ? err_message('message') : message.slice(0, 1).capitalize + message.slice(1..-1).chomp('.') #remove trailing period, we add this in the template so this avoids duplicates
    @email_from = @whitelabel.email_from
    @reply_to = @whitelabel.reply_to
    @introduction = create_introduction(to)

    unless @errors.blank?
      raise @errors
    end
  end

  def invitation_email_subject
    I18n.t("devise.mailer.invitation_instructions.subject", product_name: @whitelabel.product_name)
  end

  # Receives a generic url and replaces it with whitelabelled domains
  def whitelabel_links uri
    "#{@whitelabel.root_url_info[:protocol]}://#{@whitelabel.root_url_info[:host]}#{uri.path}?#{uri.query}"
    # rendered as https://companydomain.com/users/unlock?unlock_token=TOKENVALUE
  end

  # Searches the system for a user with 'email_address' and returns
  # a personalized introduction with the user's first name otherwise
  # returns a generic introduction with the wording 'Dear User'
  def create_introduction email_address
    user = User.where(email: email_address).first
    "#{I18n.t("dy.common.general.hi")}#{user.nil? ? '' : " #{user.first_name.capitalize}"},"
  end

  def err_message(val)
    @errors = @errors.blank? ? '<' + val + '> field can not be empty' : @errors + '\n<' + val + '> field can not be empty'
  end

end

The route being called is https://example.com/users/unlock?unlock_token=USERS_TOKEN
and the controller is from devise unlockscontroller

class Devise::UnlocksController < DeviseController
  prepend_before_filter :require_no_authentication

  # GET /resource/unlock/new
  def new
    build_resource({})
  end

  # POST /resource/unlock
  def create
    self.resource = resource_class.send_unlock_instructions(resource_params)

    if successfully_sent?(resource)
      respond_with({}, :location => after_sending_unlock_instructions_path_for(resource))
    else
      respond_with(resource)
    end
  end

  # GET /resource/unlock?unlock_token=abcdef
  def show
    self.resource = resource_class.unlock_access_by_token(params[:unlock_token])

    if resource.errors.empty?
      set_flash_message :notice, :unlocked if is_navigational_format?
      respond_with_navigational(resource){ redirect_to after_unlock_path_for(resource) }
    else
      respond_with_navigational(resource.errors, :status => :unprocessable_entity){ render :new }
    end
  end

  protected

    # The path used after sending unlock password instructions
    def after_sending_unlock_instructions_path_for(resource)
      new_session_path(resource) if is_navigational_format?
    end

    # The path used after unlocking the resource
    def after_unlock_path_for(resource)
      new_session_path(resource)  if is_navigational_format?
    end

end


问:
Why Activity getIntent().getExtras() will return null sometimes?
I have an Activity, where under rare situation, its getIntent().getExtras() will return null.

```<code>public class NewNoteChecklistLauncherFragmentActivity extends AppCompatActivity {
    private static final String NEW_NOTE_CHECKLIST_LAUNCHER_FRAGMENT = "NEW_NOTE_CHECKLIST_LAUNCHER_FRAGMENT";

    private int mAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        this.mAppWidgetId = getIntent().getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);

        FragmentManager fm = getSupportFragmentManager();
        NewNoteChecklistLauncherFragment newNoteChecklistLauncherFragment = (NewNoteChecklistLauncherFragment) fm.findFragmentByTag(NEW_NOTE_CHECKLIST_LAUNCHER_FRAGMENT);

        if (newNoteChecklistLauncherFragment == null) {
            Bundle bundle = this.getIntent().getExtras();
            if (bundle == null) {
                // WHY?
                throw new java.lang.RuntimeException();
            }
</code>```

I'm not really sure how that happen. As, every-time, I'm launching the Activity with call to putExtra

```<code>Intent i = new Intent(context, NewNoteChecklistLauncherFragmentActivity.class);
Note note = new Note();
i.putExtra(NewNoteChecklistLauncherFragment.INTENT_EXTRA_NOTE, note);
</code>```

At the same time, it also act as intent filter for "share" action.

```<code><activity android:name="com.yocto.wenote.note.NewNoteChecklistLauncherFragmentActivity"
    android:theme="@style/Theme.Transparent"
    android:windowSoftInputMode="stateAlwaysHidden" >
    <intent-filter>
        <action android:name="android.intent.action.SEND" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:mimeType="text/plain" />
    </intent-filter>

    <intent-filter>
        <action android:name="android.intent.action.SEND" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:mimeType="image/*" />
    </intent-filter>

    <intent-filter>
        <action android:name="android.intent.action.SEND_MULTIPLE" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:mimeType="image/*" />
    </intent-filter>
</activity>
</code>```

I'm not able to produce such problem. But, once a while, I can see such thing happens in production.

Any idea, in what situation, getIntent().getExtras() will return null for the above case?

答:
This could be a buggy ACTION_SEND implementation that failed to attach any extras. This could be some automated script — or script kiddie — manually invoking your activity without any extras.

Since the activity is exported, I recommend some "defensive programming". Do not assume the existence of an extras Bundle. Instead, "gracefully degrade" as best you can in that case.

Body:
I have an Activity, where under rare situation, its getIntent().getExtras() will return null.

public class NewNoteChecklistLauncherFragmentActivity extends AppCompatActivity {
    private static final String NEW_NOTE_CHECKLIST_LAUNCHER_FRAGMENT = "NEW_NOTE_CHECKLIST_LAUNCHER_FRAGMENT";

    private int mAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        this.mAppWidgetId = getIntent().getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);

        FragmentManager fm = getSupportFragmentManager();
        NewNoteChecklistLauncherFragment newNoteChecklistLauncherFragment = (NewNoteChecklistLauncherFragment) fm.findFragmentByTag(NEW_NOTE_CHECKLIST_LAUNCHER_FRAGMENT);

        if (newNoteChecklistLauncherFragment == null) {
            Bundle bundle = this.getIntent().getExtras();
            if (bundle == null) {
                // WHY?
                throw new java.lang.RuntimeException();
            }

I'm not really sure how that happen. As, every-time, I'm launching the Activity with call to putExtra

Intent i = new Intent(context, NewNoteChecklistLauncherFragmentActivity.class);
Note note = new Note();
i.putExtra(NewNoteChecklistLauncherFragment.INTENT_EXTRA_NOTE, note);

At the same time, it also act as intent filter for "share" action.

<activity android:name="com.yocto.wenote.note.NewNoteChecklistLauncherFragmentActivity"
    android:theme="@style/Theme.Transparent"
    android:windowSoftInputMode="stateAlwaysHidden" >
    <intent-filter>
        <action android:name="android.intent.action.SEND" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:mimeType="text/plain" />
    </intent-filter>

    <intent-filter>
        <action android:name="android.intent.action.SEND" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:mimeType="image/*" />
    </intent-filter>

    <intent-filter>
        <action android:name="android.intent.action.SEND_MULTIPLE" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:mimeType="image/*" />
    </intent-filter>
</activity>

I'm not able to produce such problem. But, once a while, I can see such thing happens in production.

Any idea, in what situation, getIntent().getExtras() will return null for the above case?


问:
roles.forEach not a function
So basically what is happening, is this code is running when a command is run. If the user's string (UserID) fits into the array, it responds as true and sets the access level to 6. If that is false, it checks for roles and sets the access level if they have certain roles by the role names. However, the role names part is not working. For example, a user with the Moderator role (normally access level 3 / perms.three), is showing up as access level 0 and I cannot figure out why.

The way this is supposed to work is that it checks the ID of a user. If that ID is in a pre-defined array located in a .json file, it overrides all other access levels by giving an access level of 6. If the array doesn't exist it moves onto the roles function and searches to see if the role exists in the persons list of roles.

This is what I use to check the roles/userID.

```<code>if (command.accessLevel > perms.getAccess(message.member.roles) || command.accessLevel > perms.getAccess(message.member.id)){
      return message.channel.send("``Error: Access Denied.``");
    }
</code>```

This is the permissions.js file:

```<code>const bot = require('./bot').skynet;
const config = require('./config.json');
const perms = require('./permissions.json').access;

function arrayHas(arr, val){
    var result = false;

    for (var item in arr){
        if (arr[item] == val){
            result = true;
            break;
        }
    }

    return result;
}

function getAccess(user){
    var access = 0;

    if (typeof(user) == 'string'){
        if (arrayHas(perms.six, user)){
            return 6;
        }
    }

    var roles = user;
    roles.forEach(function(role){
        if (arrayHas(perms.five, role.name)){
            access = 5;
            return;
        }
        else if (arrayHas(perms.four, role.name)){
            access = 4;
            return;
        }
        else if (arrayHas(perms.three, role.name)){
            access = 3;
            return;
        }
        else if (arrayHas(perms.two, role.name)){
            access = 2;
            return;
        }
        else if (arrayHas(perms.one, role.name)){
            access = 1;
            return;
        }
    });

    return access;
}

bot.on('message', message => {
    console.log(getAccess(message.member.id));
    console.log(getAccess(message.member.roles));
})

module.exports = {
    arrayHas: arrayHas,
    getAccess: getAccess,
};
</code>```

答:
In the first if statement, you should return a value after the inside if statement.  Otherwise, you'll continue to the following code roles.forEach for a string which will fail (roles.forEach is not a function)

Or place the non-string logic inside an else statement:

```<code>function getAccess(user){
    var access = 0;

    if (typeof(user) == 'string'){
        if (arrayHas(perms.six, user)){
            return 6;
        }
    } else {

        var roles = user;

        roles.forEach(function(role){
            if (arrayHas(perms.five, role.name)){
                access = 5;
                return;
            }
            else if (arrayHas(perms.four, role.name)){
                access = 4;
                return;
            }
            else if (arrayHas(perms.three, role.name)){
                access = 3;
                return;
            }
            else if (arrayHas(perms.two, role.name)){
                access = 2;
                return;
            }
            else if (arrayHas(perms.one, role.name)){
                access = 1;
                return;
            }
        });
    }

    return access;
}
</code>```

Title: roles.forEach not a function

Body:
So basically what is happening, is this code is running when a command is run. If the user's string (UserID) fits into the array, it responds as true and sets the access level to 6. If that is false, it checks for roles and sets the access level if they have certain roles by the role names. However, the role names part is not working. For example, a user with the Moderator role (normally access level 3 / perms.three), is showing up as access level 0 and I cannot figure out why.

The way this is supposed to work is that it checks the ID of a user. If that ID is in a pre-defined array located in a .json file, it overrides all other access levels by giving an access level of 6. If the array doesn't exist it moves onto the roles function and searches to see if the role exists in the persons list of roles.

This is what I use to check the roles/userID.

if (command.accessLevel > perms.getAccess(message.member.roles) || command.accessLevel > perms.getAccess(message.member.id)){
      return message.channel.send("``Error: Access Denied.``");
    }

This is the permissions.js file:

const bot = require('./bot').skynet;
const config = require('./config.json');
const perms = require('./permissions.json').access;

function arrayHas(arr, val){
    var result = false;

    for (var item in arr){
        if (arr[item] == val){
            result = true;
            break;
        }
    }

    return result;
}

function getAccess(user){
    var access = 0;

    if (typeof(user) == 'string'){
        if (arrayHas(perms.six, user)){
            return 6;
        }
    }

    var roles = user;
    roles.forEach(function(role){
        if (arrayHas(perms.five, role.name)){
            access = 5;
            return;
        }
        else if (arrayHas(perms.four, role.name)){
            access = 4;
            return;
        }
        else if (arrayHas(perms.three, role.name)){
            access = 3;
            return;
        }
        else if (arrayHas(perms.two, role.name)){
            access = 2;
            return;
        }
        else if (arrayHas(perms.one, role.name)){
            access = 1;
            return;
        }
    });

    return access;
}

bot.on('message', message => {
    console.log(getAccess(message.member.id));
    console.log(getAccess(message.member.roles));
})

module.exports = {
    arrayHas: arrayHas,
    getAccess: getAccess,
};

Body:
In the first if statement, you should return a value after the inside if statement.  Otherwise, you'll continue to the following code roles.forEach for a string which will fail (roles.forEach is not a function)

Or place the non-string logic inside an else statement:

function getAccess(user){
    var access = 0;

    if (typeof(user) == 'string'){
        if (arrayHas(perms.six, user)){
            return 6;
        }
    } else {

        var roles = user;

        roles.forEach(function(role){
            if (arrayHas(perms.five, role.name)){
                access = 5;
                return;
            }
            else if (arrayHas(perms.four, role.name)){
                access = 4;
                return;
            }
            else if (arrayHas(perms.three, role.name)){
                access = 3;
                return;
            }
            else if (arrayHas(perms.two, role.name)){
                access = 2;
                return;
            }
            else if (arrayHas(perms.one, role.name)){
                access = 1;
                return;
            }
        });
    }

    return access;
}


问:
How to style a table in a vue.js component
I am trying to style the table in this vue component, my understanding is that I need to create the css from the data function and then bind it to the appropriate element in the template.

What I am trying to do is apply the following css to my table

css:

```<code> .table-striped>tr:nth-of-type(odd) {
   background-color: #f9f9f9
 }
</code>```

vue component:

```<code>Vue.component('overview', {
    delimiters: [ '[[', ']]' ],
    props: ['job_execs'],
    template: `
      <div overview>
      <h3>Overview</h3>
      <table class="table table-striped table-bordered">
        <tr>
          <td>Start Time</td>
          <td>[[ job_execs[0].time_start ]]</td>
        </tr>
        <tr>
          <td>End Time</td>
          <td>[[ job_execs[0].time_end ]]</td>
        </tr>
        <tr>
          <td>Job</td>
          <td>http://placeholder</td>
        </tr>
      </table>
      </div>
    `,
    data: function() {
        return {
            divExample: {
                color: 'red',
                fontSize: '13px'
                }
        }
    },
});
</code>```

Im not sure how to 1.  create this CSS from the data function and 2.  Bind it within the template.

I am currently working on a project where I need to take an existing web app and convert the front end to vue.js and it seems like its going to be a real headache extracting the css I need from the already bloated css that exists and then converting it to vue js functions that inject that css per component.

答:
You can bind css properties to a tag in Vue using the following way:</p>

```<code><tr>
    <td>Start Time</td>
    <td>[[ job_execs[0].time_start ]]</td>
</tr>
<tr v-bind:style="strippedTr">
    <td>End Time</td>
    <td>[[ job_execs[0].time_end ]]</td>
</tr>
<tr>
    <td>Job</td>
    <td>http://placeholder</td>
</tr>

...

data: function() {
    return {
        strippedTr: {
            'background-color': '#f9f9f9'
        }
    }
},
</code>```

Body:
I am trying to style the table in this vue component, my understanding is that I need to create the css from the data function and then bind it to the appropriate element in the template.

What I am trying to do is apply the following css to my table

css:

 .table-striped>tr:nth-of-type(odd) {
   background-color: #f9f9f9
 }

vue component:

Vue.component('overview', {
    delimiters: [ '[[', ']]' ],
    props: ['job_execs'],
    template: `

Overview

          Start Time
          [[ job_execs[0].time_start ]]

          End Time
          [[ job_execs[0].time_end ]]

          Job
          http://placeholder

    `,
    data: function() {
        return {
            divExample: {
                color: 'red',
                fontSize: '13px'
                }
        }
    },
});

Im not sure how to 1.  create this CSS from the data function and 2.  Bind it within the template.

I am currently working on a project where I need to take an existing web app and convert the front end to vue.js and it seems like its going to be a real headache extracting the css I need from the already bloated css that exists and then converting it to vue js functions that inject that css per component.

Body:
You can bind css properties to a tag in Vue using the following way:

<tr>
    <td>Start Time</td>
    <td>[[ job_execs[0].time_start ]]</td>
</tr>
<tr v-bind:style="strippedTr">
    <td>End Time</td>
    <td>[[ job_execs[0].time_end ]]</td>
</tr>
<tr>
    <td>Job</td>
    <td>http://placeholder</td>
</tr>

...

data: function() {
    return {
        strippedTr: {
            'background-color': '#f9f9f9'
        }
    }
},


问:
Bash Scripting: How to auto start VSC and run a few commands inside integrated terminal?
I work on a large project and I only use ubuntu to run that project.

It's kind of cumbersome for me to open ubuntu, then open VSC, then run vagrant up and then run vagrant ssh and then run redis-server and then run ./run-dev.py, would it be possible to automate this process?

Thank you for your time :).

答: You can make a bash script with the commands that you want to be executed after vagrant machine is up and configure Vagrant Provisioner in your case is "shell" inside the Vagrantfile. This way the provisioner executes all the commands that you want once the machine is up and running without needed to ssh in the machine and type them manually. Take a look at Vagrant Provisioner related to shell : https://www.vagrantup.com/docs/provisioning/shell.html</p>


问:
accordion with expandable sections
I would like to create an accordion box with Javascipt and CSS. The expanded section should include a link that, when clicked, would enable this section already expanded to expand even more without any vertical scroll bar. Is it possible? Thanks

答:
Something like this, perhaps?

http://jsfiddle.net/Jaybles/2PSME/

You'll essentially want to take your existing accordion, and place 2 divs in the content area you want expanded. One will hold additional information (initially hidden) and the other will say "Click here for more". When you click that div, it disappears and causes the more info div to show. If you do this with a sliding animation, you will get the desired effect.

Title: accordion with expandable sections


问:
Why do parentheses slow down my program in R
I caught some redundant parentheses in my friend's code and it really slowed down the execution time. If there any explanation for this. Please check out this example code

Python is also a (quesi) interpreted language and doesn't suffer from this program.

```<code># 0.370 seconds
x <- 0
while (x < 100000) {
    10
    x = x + 1
}
</code>```

```<code># 0.743 seconds
x <- 0
while (x < 100000) {
    ((((((((((10))))))))))
    x = x + 1
}
</code>```

答:
While akrun's comment talks about R v2.11.1, things haven't really changed.

The help for ?paren (Parentheses and Braces), says that unlike some other languages parentheses and braces are primitive functions in R. Which means that when ever you write a <- 1, the 1 is just a 1. However, if you write a <- (1), the 1 is inside a function.

Therefore, if you'll run even simple evaluations, parentheses will take more time (as you evaluate a function and a number).

```<code>library(microbenchmark)

microbenchmark("simple" = {a <- 1},
                      "parentheses" = {
                        a <- (1)})

Unit: nanoseconds
        expr min  lq mean median  uq   max neval cld
      simple   0 100   89    100 100   700   100   a
 parentheses 100 100  310    200 200 16000   100   a

sessionInfo()

R version 3.5.3 (2019-03-11)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows >= 8 x64 (build 9200)
</code>```

Body:
I caught some redundant parentheses in my friend's code and it really slowed down the execution time. If there any explanation for this. Please check out this example code

Python is also a (quesi) interpreted language and doesn't suffer from this program.

# 0.370 seconds
x <- 0
while (x < 100000) {
    10
    x = x + 1
}

# 0.743 seconds
x <- 0
while (x < 100000) {
    ((((((((((10))))))))))
    x = x + 1
}

Body:
While akrun's comment talks about R v2.11.1, things haven't really changed.

The help for ?paren (Parentheses and Braces), says that unlike some other languages parentheses and braces are primitive functions in R. Which means that when ever you write a <- 1, the 1 is just a 1. However, if you write a <- (1), the 1 is inside a function.

Therefore, if you'll run even simple evaluations, parentheses will take more time (as you evaluate a function and a number).

library(microbenchmark)

microbenchmark("simple" = {a <- 1},
                      "parentheses" = {
                        a <- (1)})

Unit: nanoseconds
        expr min  lq mean median  uq   max neval cld
      simple   0 100   89    100 100   700   100   a
 parentheses 100 100  310    200 200 16000   100   a

sessionInfo()

R version 3.5.3 (2019-03-11)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows >= 8 x64 (build 9200)


问:
Correct way to declare and name a partial version of an interface
What would be the "right" way to declare the Book Interface in the following scenario, keeping in mind that we know the exact structure of the book object in both cases.
Let's say there is a Book interface:

```<code>export interface Book {
  id: string;
  title: string;
  year: number;
  authorId: string;
  pages: number;
  language: string;
}
</code>```

And an Author interface, that has a books property, array of "partial" book objects:

```<code>export interface Author {
  id: string;
  firstName: string;
  lastName: string;
  // How this book object should be declared?
  books: Array<{ id: string; title: string, year: number }>;
}
</code>```

答:
If by "partial" you're referring to making any of the properties on Book optional you could use typescript's exported helper Partial:

```<code>export interface Author {
  ...
  books: Partial<Book>[]
}
</code>```

if by "partial" you mean that it only includes some properties of Book and those properties should maintain their original type then you can use typescript's exported helper type Pick

```<code>export interface Author {
  ...
  books: Pick<Book, "id" | "title" | "year">[]
}
</code>```

Body:
What would be the "right" way to declare the Book Interface in the following scenario, keeping in mind that we know the exact structure of the book object in both cases.
Let's say there is a Book interface:

export interface Book {
  id: string;
  title: string;
  year: number;
  authorId: string;
  pages: number;
  language: string;
}

And an Author interface, that has a books property, array of "partial" book objects:

export interface Author {
  id: string;
  firstName: string;
  lastName: string;
  // How this book object should be declared?
  books: Array<{ id: string; title: string, year: number }>;
}

Body:
If by "partial" you're referring to making any of the properties on Book optional you could use typescript's exported helper Partial:

export interface Author {
  ...
  books: Partial<Book>[]
}

if by "partial" you mean that it only includes some properties of Book and those properties should maintain their original type then you can use typescript's exported helper type Pick

export interface Author {
  ...
  books: Pick<Book, "id" | "title" | "year">[]
}


问:
The default Kafka partitioner create hash key collision
I have a topic with 10 partitions, and I have generate events with A,B,C,D,E,F,G,H,I 9 different keys.

I've observed messages doing this:

```<code>Partition 0- (Message1, Key E), (Message2, Key I)
Partition 1- (Message3, Key F)
.
.
Partition7-(Message4, Key A), (Message5, Key A)
Partition8- Empty
Partition9- Empty
</code>```

There are 2 messages with different keys in the same partition and there are empty partitions as well.

Is the default partitioner of Kafka creating collisions?

I am producing from one stream which is balanced to two default rest producers.

This is what I was expecting:

```<code> Partition 0- (Message1, Key E)
 Partition 1- (Message3, Key F)
 .
 .
 Partition7-(Message4, Key A), (Message5, Key A)
 Partition8-(Message2, Key I)
 Partition9- Empty
</code>```

答:
Kafka's DefaultPartitioner uses a murmur hash algorithm at the producer client side to assign a partition to each message. There is no guarantee that for 10 partitions and single digit number of keys, they will be uniformly distributed. Calculation of partition for each message is independent of each other and the probability of collision is a mathematical interest.

EDIT:

It is very unlikely that murmur hash algorithm results in a collision. Partitions in Kafka topic is fixed - it cannot grow unlike bucket size in java HashMap implementation. So partition algorithm uses a formula which calculates modulo of number of partitions. Exact formula is Utils.toPositive(Utils.murmur2(keyBytes)) % numPartitions;

Now you can see that two different keys can indeed result in same partition number if hash mod number of partitions results in same value.

For a large number of random key set, keys will be uniformly distributed across all partitions.

If you want ordering, then you must use a partition key..in which case your worries surrounding collisions and empty partitions have little practical consequences (well, for a large set of random keys, they will be ok). If you assumed that Kafka would centrally make sure that empty partitions are filled first before a key is routed to an already filled partition, that is not how things work

Body:
I have a topic with 10 partitions, and I have generate events with A,B,C,D,E,F,G,H,I 9 different keys.

I've observed messages doing this:

Partition 0- (Message1, Key E), (Message2, Key I)
Partition 1- (Message3, Key F)
.
.
Partition7-(Message4, Key A), (Message5, Key A)
Partition8- Empty
Partition9- Empty

There are 2 messages with different keys in the same partition and there are empty partitions as well.

Is the default partitioner of Kafka creating collisions?

I am producing from one stream which is balanced to two default rest producers.

This is what I was expecting:

 Partition 0- (Message1, Key E)
 Partition 1- (Message3, Key F)
 .
 .
 Partition7-(Message4, Key A), (Message5, Key A)
 Partition8-(Message2, Key I)
 Partition9- Empty


问:
Event-Sourcing: How to handle Projections?
I have three aggregates, a Task, a User, and a User Group. The aggregates are event-sourced. I also have two projections, a collection of Tasks and a collection of User Groups. These are built from the events. My users want to filter the Tasks based on the User Groups they are in. What approach should I take?

答:
At some point, there will be the event of assigning a certain task (or a list of tasks) to a certain user group.
The projection of that event then writes some information about that relationship into your read store. Depending on your use case and database technology, this could be a simple property added to each projected task / task list, or you could build pre-filtered lists for each user group.

In Event Sourcing, the event stream represents the source of truth. Thus, the projected data can be tailored much more towards certain representation needs than it is the case with traditional, relational data.


问:
Parsing dict with nested dict of lists
I am trying to parse the following dict

```<code>{'IsTruncated': False,
    'MaxItems': '100',
    'ResourceRecordSets': [{'Name': 'test.com.',
        {'Name': '1.test.com.',
        'ResourceRecords': [{'Value': '10.0.0.1'}],
        {'Name': '2.test.com.',
        'ResourceRecords': [{'Value': '10.0.0.2'}],
}
</code>```

The output I am looking for is:

```<code>1.test.com    10.0.0.1
2.test.com    10.0.0.2
</code>```

I have tried:

```<code>for resource in response['ResourceRecordSets']:
    print("{}  {}".format(resource['Name'], resource['ResourceRecords'] ))
</code>```

and

```<code>for resource in response['ResourceRecordSets']:
    print("{}  {}".format(resource['Name'], resource['ResourceRecords'][0] ))
</code>```

Is there a simple way to access this dict key/values within the nested list?

答:
Assuming that your dictionary should look like this:

```<code>{'IsTruncated': False,
'MaxItems': '100',
'ResourceRecordSets': [
    {'Name': 'test.com.',
    'ResourceRecords' : [{'Value' : '<mising ip addr>'}]},
    {'Name': '1.test.com.',
    'ResourceRecords': [{'Value': '10.0.0.1'}]},
    {'Name': '2.test.com.',
    'ResourceRecords': [{'Value': '10.0.0.2'}]},
    ]
</code>```

Try this:

```<code>for resource in response['ResourceRecordSets']:
    print("{}  {}".format(resource['Name'], resource['ResourceRecords'][0]['Value']))
</code>```

Title: Parsing dict with nested dict of lists

Body:
I am trying to parse the following dict

{'IsTruncated': False,
    'MaxItems': '100',
    'ResourceRecordSets': [{'Name': 'test.com.',
        {'Name': '1.test.com.',
        'ResourceRecords': [{'Value': '10.0.0.1'}],
        {'Name': '2.test.com.',
        'ResourceRecords': [{'Value': '10.0.0.2'}],
}

The output I am looking for is:

1.test.com    10.0.0.1
2.test.com    10.0.0.2

I have tried:

for resource in response['ResourceRecordSets']:
    print("{}  {}".format(resource['Name'], resource['ResourceRecords'] ))

and

for resource in response['ResourceRecordSets']:
    print("{}  {}".format(resource['Name'], resource['ResourceRecords'][0] ))

Is there a simple way to access this dict key/values within the nested list?

Body:
Assuming that your dictionary should look like this:

{'IsTruncated': False,
'MaxItems': '100',
'ResourceRecordSets': [
    {'Name': 'test.com.',
    'ResourceRecords' : [{'Value' : '<mising ip addr>'}]},
    {'Name': '1.test.com.',
    'ResourceRecords': [{'Value': '10.0.0.1'}]},
    {'Name': '2.test.com.',
    'ResourceRecords': [{'Value': '10.0.0.2'}]},
    ]

Try this:

for resource in response['ResourceRecordSets']:
    print("{}  {}".format(resource['Name'], resource['ResourceRecords'][0]['Value']))


问:
Is there a way to simulate a click on an alert in JavaScript?
I have a page with an iframe whose source page is in a separate domain. From time to time, the source page generates an alert. When it does so, it stops what it is doing until the user clicks OK to the alert.

What I would like to do is programmatically click OK on this alert so the source page can get back to being useful. Is this possible?

答: The best bet to solve your problems with annoying JavaScript would  be to override it with a client side scripting tool like Greasemonkey.</p>

OwnerDisplayName: moffdub

ContentLicense: CC BY-SA 2.5


问:
Report to find objects of a segw project and put them on a request
I'm new in SAP and during my practicing I came up with a 'problem': when I was transporting my project to another system I had to manually include some objects that were in other requests.
So now I'm trying to make a report to join all the objects related to a segw project in a single request. My idea is passing the Project ID or name to my report find the objects, create a request and put all of them into it.

I've already found something. When creating a segw project and generate it, the request has:
- Class (ABAP objects) with the DPC and MPC
- SAP Gateway Business Suite Enablement - Model
- SAP Gateway BSE - Service Builder Project
- SAP Gateway Business Suite Enablement - Service

I've found two tables that help me to get DPC and MPC objects:
TMDIR, VSEOCLASS.

Am I in the right path? Is there a way to find all related objects to the project or I'll need to find them separately like the DPC and MPC I've already found?

Thanks!

答:
Assuming all your SEGW objects reside in a single package which is usually the case when you create SEGW project from scratch:

```<code>DATA: l_trkorr TYPE trkorr,
      l_package TYPE devclass VALUE 'ZSEGW_P'.

cl_pak_package_queries=>get_all_subpackages( EXPORTING im_package     = l_package
                                             IMPORTING et_subpackages = DATA(lt_descendant) ).

INSERT VALUE cl_pak_package_queries=>ty_subpackage_info( package = l_package ) INTO TABLE lt_descendant.

SELECT pgmid, object, obj_name FROM tadir
  INTO TABLE @DATA(lt_segw_objects)
   FOR ALL ENTRIES IN @lt_descendant
 WHERE devclass = @lt_descendant-package.

DATA(instance) = cl_adt_cts_management=>create_instance( ).

LOOP AT lt_segw_objects ASSIGNING FIELD-SYMBOL(<fs_obj>).
  TRY.
      instance->insert_objects_in_wb_request( EXPORTING pgmid        = <fs_obj>-pgmid
                                                        object       = <fs_obj>-object
                                                        obj_name     = CONV trobj_name( <fs_obj>-obj_name )
                                              IMPORTING result       = DATA(result)
                                                        request      = DATA(request)
                                              CHANGING  trkorr       = l_trkorr ).
    CATCH cx_adt_cts_insert_error.
  ENDTRY.
ENDLOOP.
</code>```

This snippet creates transport request with number l_trkorr and as soon as this var is not changed put all remainign objects in the same request.

WARNING: this will not work if the objects are locked (in another request) and will give you cx_adt_cts_insert_error exception. There is no way to unlock objects programmatically, only via SE03 tool.

Body:
Assuming all your SEGW objects reside in a single package which is usually the case when you create SEGW project from scratch:

DATA: l_trkorr TYPE trkorr,
      l_package TYPE devclass VALUE 'ZSEGW_P'.

cl_pak_package_queries=>get_all_subpackages( EXPORTING im_package     = l_package
                                             IMPORTING et_subpackages = DATA(lt_descendant) ).

INSERT VALUE cl_pak_package_queries=>ty_subpackage_info( package = l_package ) INTO TABLE lt_descendant.

SELECT pgmid, object, obj_name FROM tadir
  INTO TABLE @DATA(lt_segw_objects)
   FOR ALL ENTRIES IN @lt_descendant
 WHERE devclass = @lt_descendant-package.

DATA(instance) = cl_adt_cts_management=>create_instance( ).

LOOP AT lt_segw_objects ASSIGNING FIELD-SYMBOL(<fs_obj>).
  TRY.
      instance->insert_objects_in_wb_request( EXPORTING pgmid        = <fs_obj>-pgmid
                                                        object       = <fs_obj>-object
                                                        obj_name     = CONV trobj_name( <fs_obj>-obj_name )
                                              IMPORTING result       = DATA(result)
                                                        request      = DATA(request)
                                              CHANGING  trkorr       = l_trkorr ).
    CATCH cx_adt_cts_insert_error.
  ENDTRY.
ENDLOOP.

This snippet creates transport request with number l_trkorr and as soon as this var is not changed put all remainign objects in the same request.

WARNING: this will not work if the objects are locked (in another request) and will give you cx_adt_cts_insert_error exception. There is no way to unlock objects programmatically, only via SE03 tool.


问:
Access token for google classroom API says it authorized classroom api but rest api returns request is missing credentials
I have a react-native app that signs users in by using the firebase web SDK. The app successfully signs users in, and when signing in, users are prompted to authorize the google classroom api using these scopes:
['profile', 'email', "https://www.googleapis.com/auth/classroom.courses", "https://www.googleapis.com/auth/classroom.courses.readonly"]

After successfully authenticating, users are redirected to a dashboard. In this dashboard, I want to display their google classrooms that they are enrolled in. I store the access token with firebase, but when I go to fetch from the google classroom rest api, I get an error saying that the request is missing credentials.

I have tried including the access token in both the header and the url directly.

How I sign my users in:

(onSignIn manually logs in the user to firebase using the firebase manual code that they provided)

```<code>googleAuth = async () => {
    try {
      const result = await Expo.Google.logInAsync({
        iosClientId : "MY IOS CLIENT ID IS ACTUALLY SET IN MY CODE",
        scopes : ['profile', 'email', "https://www.googleapis.com/auth/classroom.courses", "https://www.googleapis.com/auth/classroom.courses.readonly"],
        behavior : 'web'
      })

      if(result.type === 'success') {
        this.onSignIn(result)
        console.log(result)
      }
    }
    catch(e) {
      console.log("ERROR", e)
    }
  }

</code>```

Once routed to the dashboard:

```<code>listCourses = async (auth) => {
    if(firebase.auth().currentUser) {

      const token = await firebase.auth().currentUser.getIdToken()
      .then(tkn => {
        return tkn
      })
      .catch(err => {
        console.log(err)
        return undefined;
      })

      if(token) {
        fetch("https://classroom.googleapis.com/v1/courses?accessToken=" + token, {
          method : "GET",
        })
        .catch(err => {
          console.log(err)
        })
        .then(res => {
          console.log(res)
        })
      }
    }
  }
</code>```

What I expected was to get a list of classes, similar to what happened when I authenticated using the Oauth2.0 client on the google classroom rest API (I received a 200 request with the courses in an array)
What actually happened is I received this error:

```<code>401 error
\"message\": \"Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.\",
"url": "https://classroom.googleapis.com/v1/courses?accessToken=MY ACTUAL ACCESS TOKEN",
</code>```

答:
An ID token and an access token are different things. The ID token contains user information in JWT format, while an access token is an opaque string you send along with future request. More information about Firebase Login with Google is available here:

https://firebase.google.com/docs/auth/web/google-signin

Body:
I have a react-native app that signs users in by using the firebase web SDK. The app successfully signs users in, and when signing in, users are prompted to authorize the google classroom api using these scopes:
['profile', 'email', "https://www.googleapis.com/auth/classroom.courses", "https://www.googleapis.com/auth/classroom.courses.readonly"]

After successfully authenticating, users are redirected to a dashboard. In this dashboard, I want to display their google classrooms that they are enrolled in. I store the access token with firebase, but when I go to fetch from the google classroom rest api, I get an error saying that the request is missing credentials.

I have tried including the access token in both the header and the url directly.

How I sign my users in:

(onSignIn manually logs in the user to firebase using the firebase manual code that they provided)

googleAuth = async () => {
    try {
      const result = await Expo.Google.logInAsync({
        iosClientId : "MY IOS CLIENT ID IS ACTUALLY SET IN MY CODE",
        scopes : ['profile', 'email', "https://www.googleapis.com/auth/classroom.courses", "https://www.googleapis.com/auth/classroom.courses.readonly"],
        behavior : 'web'
      })

      if(result.type === 'success') {
        this.onSignIn(result)
        console.log(result)
      }
    }
    catch(e) {
      console.log("ERROR", e)
    }
  }

Once routed to the dashboard:

listCourses = async (auth) => {
    if(firebase.auth().currentUser) {

      const token = await firebase.auth().currentUser.getIdToken()
      .then(tkn => {
        return tkn
      })
      .catch(err => {
        console.log(err)
        return undefined;
      })

      if(token) {
        fetch("https://classroom.googleapis.com/v1/courses?accessToken=" + token, {
          method : "GET",
        })
        .catch(err => {
          console.log(err)
        })
        .then(res => {
          console.log(res)
        })
      }
    }
  }

What I expected was to get a list of classes, similar to what happened when I authenticated using the Oauth2.0 client on the google classroom rest API (I received a 200 request with the courses in an array)
What actually happened is I received this error:

401 error
\"message\": \"Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.\",
"url": "https://classroom.googleapis.com/v1/courses?accessToken=MY ACTUAL ACCESS TOKEN",


答:
It really depends on what you want to accomplish.

You can use the Partial keyword, books: Partial<Book>[];, or create another interface to contain { id: string; title: string, year: number } of the Book interface.

Title: Correct way to declare and name a partial version of an interface