• This is jqMVC# – CNBLOGS Google Tracer Sample


    In previous post - This is jqMVC# - Definition & Summary, I briefly introduced what is jqMVC#. In this post, I’ll show you a “CNBLOGS Google Tracer” sample application which is applying the jqMVC# architecture.

    Function

    image

    “Google Tracer” is a HTML & JavaScript application, tracing the blog you are reading with Google Web Search. You should be able to see it running in the left navigation panel in my blog. It is visible only when you are reading any of my blog posts. Technically, it is just googling the title of a post as the search keyword through the Google AJAX Search API.

    The function of this application is pretty simple, I believe any of you could implement a similar feature in even half an hour. The points I really want to demonstrate are the benefits from applying the jqMVC# architecture.

    MVC Pattern In Script# based JavaScript

    Please realize we are writing C# code which is to be compiled info JavaScript by Script# compiler. Like in other server-side MVC pattern implementation, we should have Model, View and Controller.

    Models here are value objects (in Script#, they are called Records) representing the search response data. They are strong typed and just matching the JSON response of the Google AJAX Search API.

    Google Tracer Records
    [Record]
    public sealed class GoogleSearchResponse
    {
        
    public GoogleSearchResponseData ResponseData;
        
    public string ResponseDetails;
        
    public int ResponseStatus;
    }

    [Record]
    public sealed class GoogleSearchResponse
    {
        
    public GoogleSearchResponseData ResponseData;
        
    public string ResponseDetails;
        
    public int ResponseStatus;
    }

    [Record]
    public sealed class GoogleSearchResponseDataResult
    {
        [PreserveCase]
        
    public string GsearchResultClass;
        
    public string UnescapedUrl;
        
    public string Url;
        
    public string VisibleUrl;
        
    public string CacheUrl;
        
    public string Title;
        
    public string TitleNoFormatting;
        
    public string Content;
    }

    … 

    A view logically wraps the data and events of a UI clip implementation. A view is a bridge clearly separating and connecting the pure UI presentation and the controller. Here we define the view interface first:

    IGoogleTracerView
    public interface IGoogleTracerView
    {
        
    int SearchStart { getset; }
        
    string GetSearchKeyword();
        
    void RenderSearchResult(GoogleSearchResponse response);
        
    event DOMEventHandler ShowMoreResults;

     The view interface could have different implementation, representing different but share the same data and event contracts.

    Below is our demo view implementation.

    CnblogsGoogleSearchTracerView
    public class CnblogsGoogleSearchTracerView : IGoogleTracerView
    {
        
    private DOMEventHandler _showMoreResults;
        
    private int _searchStart = 0;

        
    #region IGoogleTracerView Members

        
    public int SearchStart
        {
            
    get
            {
                
    return _searchStart;
            }
            
    set
            {
                _searchStart 
    = value;
            }
        }

        
    public string GetSearchKeyword()
        {
            
    string keyword = (string)(object)JQueryFactory.JQuery(JQuerySelectors.SEARCH_KEYWORD).Text();
            
    return keyword;
        }

        
    public void RenderSearchResult(GoogleSearchResponse response)
        {
            ((JTemplatePlugin)JQueryFactory.JQuery(JQuerySelectors.SEARCH_RESULTS_PANEL))
                .SetTemplateElement(JTemplateElements.GOOGLE_TRACER)
                .ProcessTemplate(response);

            JQueryFactory.JQuery(JQuerySelectors.SHOW_MORE_RESULTS_BUTTON).Click(_showMoreResults);
        }

        
    public event DOMEventHandler ShowMoreResults
        {
            add
            {
                _showMoreResults 
    = (DOMEventHandler)Delegate.Combine(_showMoreResults, value);
            }
            remove
            {
                _showMoreResults 
    = (DOMEventHandler)Delegate.Remove(_showMoreResults, value);
            }
        }

        
    #endregion

    A controller is responsible for querying data, binding Model and event handlers to View.

    GoogleTracerController
    public class GoogleTracerController
    {
        
    private IGoogleTracerView _view;

        
    #region Properties

        
    public IGoogleTracerView View
        {
            
    get
            {
                
    if (_view == null)
                    _view 
    = (IGoogleTracerView)Container.GetInstance(typeof(IGoogleTracerView));
                
    return _view;
            }
        }

        
    #endregion

        
    #region Public Methods

        
    public void Execute()
        {
            View.ShowMoreResults 
    += new DOMEventHandler(ShowMoreResults);

            LoadSearchResults();
        }

        
    public void ShowMoreResults()
        {
            View.SearchStart 
    = View.SearchStart + 4;
            LoadSearchResults();
        }

        
    public static void GoogleWebSearchCallback(object data)
        {
            ((Dictionary)(
    object)Window.Self)["_googlewebsearchresults"= data;
        }

        
    #endregion

        
    #region Private Methods

        
    private void LoadSearchResults()
        {
            
    if (string.IsNullOrEmpty(View.GetSearchKeyword().Trim()))
                
    return;

            jQuery.GetScript(
                
    string.Format(
                    SearchUrls.WEB_SEARCH_URL,
                    View.GetSearchKeyword().Replace(
    "'""").Replace(" ""+").Replace("&""").Replace("?"""),
                    View.SearchStart,
                    
    "NIntegrate.Scripts.Test.Demo.GoogleTracer.Controllers.GoogleTracerController.googleWebSearchCallback"
                ),
                (Function)(
    object)new DOMEventHandler(delegate
                    {
                        View.RenderSearchResult((GoogleSearchResponse)((Dictionary)(
    object)Window.Self)["_googlewebsearchresults"]);
                    })
            );
        }

        
    #endregion

    I use a simple Container which decouples the dependency from the controller to the concrete view implementation in the readonly View property of the controller class.

    Container
    public static class Container
    {
        
    private static Dictionary _cache = new Dictionary();

        
    public static void RegisterInstance(Type type, object instance)
        {
            _cache[type.FullName] 
    = instance;
        }

        
    public static object GetInstance(Type type)
        {
            
    return _cache[type.FullName];
        }

    OK, this is all the implementation. Benefited from the MVC pattern, it is well separated and easy to understand, right? But wait, where is the testing?

    Test Driven Development (TDD)

    To do integration testing, I created a demo HTML page after all the implementation.

    GoogleTracer Demo HTML
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
    <html>
        
    <head>
            
    <title>GoogleTracer Demo</title>
            
    <script type="text/javascript" language="javascript" src="http://www.cnblogs.com/_scripts/jquery-1.3.2.js"></script>
            
    <script type="text/javascript" language="javascript" src="http://www.cnblogs.com/_scripts/jquery-jtemplates.js"></script>
            
    <script type="text/javascript" language="javascript" src="http://www.cnblogs.com/_scripts/sscompat.debug.js"></script>
            
    <script type="text/javascript" language="javascript" src="http://www.cnblogs.com/_scripts/sscorlib.js"></script>
            
    <script type="text/javascript" language="javascript" src="http://www.cnblogs.com/_scripts/ssfx.Core.js"></script>
            
    <script type="text/javascript" language="javascript" src="http://www.cnblogs.com/_scripts/JQuerySharp.debug.js"></script>
            
    <script type="text/javascript" language="javascript" src="http://www.cnblogs.com/_scripts/NIntegrate.Scripts.debug.js"></script>
            
    <script type="text/javascript" language="javascript" src="http://www.cnblogs.com/_scripts/NIntegrate.Scripts.Test.debug.js"></script>
        
    </head>
        
    <body>
            
    <div class="post">
                Search keyword: 
    <span class="postTitle">teddyma wcf</span>
            
    </div>
            
    <hr />
            
    <textarea id="jtGoogleTracer" style="display: none">
                {#if $T.responseStatus == 200}
                    {#foreach $T.responseData.results as result}
                    
    <href="{$T.result.url}" title="{$T.result.content.replace('"', '&quot;')}">{$T.result.titleNoFormatting}</a><br />
                    {#/for}
                    
    <b><href="javascript:void(0)" id="btnShowMoreResults">More&gt;&gt;</a></b>
                {#else}
                    Network error, please try again later!
                {#/if}
            
    </textarea>
            
            
    <div id="divSearchResults"></div>
            
            
    <script type="text/javascript" language="javascript">
                NIntegrate.Scripts.Test.Demo.GoogleTracer.Container.registerInstance(
                    NIntegrate.Scripts.Test.Demo.GoogleTracer.Views.IGoogleTracerView,
                    
    new NIntegrate.Scripts.Test.Demo.GoogleTracer.Views.CnblogsGoogleSearchTracerView()
                );

                
    new NIntegrate.Scripts.Test.Demo.GoogleTracer.Controllers.GoogleTracerController().execute();
            
    </script>
        
    </body>
    </html>

    This demo page runs smoothly without any error even the first time. How it achieves? I do TDD.

    CnblogsGoogleSearchTracerViewTest
        public class CnblogsGoogleSearchTracerViewTest : TestCase
        {
            
    public override void Execute()
            {
                
    base.Execute();

                CnblogsGoogleSearchTracerView view 
    = new CnblogsGoogleSearchTracerView();

                QUnit.Test(
    "Test SearchStart"delegate
                {
                    QUnit.Equals(
    0, view.SearchStart);
                    view.SearchStart 
    = 1;
                    QUnit.Equals(
    1, view.SearchStart);
                    view.SearchStart 
    = 2;
                    QUnit.Equals(
    2, view.SearchStart);
                });

                QUnit.Test(
    "Test GetSearchKeyword()"delegate
                {
                    JQueryFactory.JQuery(JQuerySelectors.SEARCH_KEYWORD).Html(
    "keyword1");
                    QUnit.Equals(
    "keyword1", view.GetSearchKeyword());
                    JQueryFactory.JQuery(JQuerySelectors.SEARCH_KEYWORD).Html(
    "keyword2");
                    QUnit.Equals(
    "keyword2", view.GetSearchKeyword());
                });

                QUnit.Test(
    "Test RenderSearchResult()"delegate
                {
                    jQuery pnlSearchResults 
    = JQueryFactory.JQuery(JQuerySelectors.SEARCH_RESULTS_PANEL);
                    jQuery btnShowMoreResults 
    = JQueryFactory.JQuery(JQuerySelectors.SHOW_MORE_RESULTS_BUTTON);

                    Mock mockJQuery 
    = new Mock(Window.Self, "jQuery");
                    mockJQuery.Modify().Args(JQuerySelectors.SEARCH_RESULTS_PANEL).ReturnValue(pnlSearchResults);
                    mockJQuery.Modify().Args(JQuerySelectors.SHOW_MORE_RESULTS_BUTTON).ReturnValue(btnShowMoreResults);
                    Mock mockSetTemplateElement 
    = new Mock(pnlSearchResults, "setTemplateElement");
                    mockSetTemplateElement.Modify().Args(JTemplateElements.GOOGLE_TRACER).ReturnValue(pnlSearchResults);
                    Mock mockProcessTemplate 
    = new Mock(pnlSearchResults, "processTemplate");
                    mockProcessTemplate.Modify().Args(Is.Anything).ReturnValue();
                    Mock mockShowMoreResultsBindClick 
    = new Mock(btnShowMoreResults, "click");
                    mockShowMoreResultsBindClick.Modify().Args(Is.Anything).ReturnValue();

                    view.RenderSearchResult(
    new GoogleSearchResponse());

                    mockJQuery.Verify();
                    mockJQuery.Restore();
                    mockSetTemplateElement.Verify();
                    mockSetTemplateElement.Restore();
                    mockProcessTemplate.Verify();
                    mockProcessTemplate.Restore();
                    mockShowMoreResultsBindClick.Verify();
                    mockShowMoreResultsBindClick.Restore();
                });

                QUnit.Test(
    "Test ShowMoreResults Event"delegate
                {
                    QUnit.Equals(
    false, _showMoreResultsClicked);

                    view.ShowMoreResults 
    += new System.DHTML.DOMEventHandler(view_ShowMoreResults);
                    view.RenderSearchResult(
    new GoogleSearchResponse());
                    JQueryFactory.JQuery(JQuerySelectors.SHOW_MORE_RESULTS_BUTTON).Click();

                    QUnit.Equals(
    true, _showMoreResultsClicked);
                });
            }

            
    private bool _showMoreResultsClicked = false;

            
    void view_ShowMoreResults()
            {
                _showMoreResultsClicked 
    = true;
            }
        }
    GoogleTracerControllerTest
        public class MockGoogleTracerView : IGoogleTracerView
        {
            
    private int _searchStart = 0;

            
    #region IGoogleTracerView Members

            
    public int SearchStart
            {
                
    get
                {
                    
    return _searchStart;
                }
                
    set
                {
                    _searchStart 
    = value;
                }
            }

            
    public string GetSearchKeyword()
            {
                
    return "keyword";
            }

            
    public void RenderSearchResult(NIntegrate.Scripts.Test.Demo.GoogleTracer.Records.GoogleSearchResponse response)
            {
                
    return;
            }

            
    public event System.DHTML.DOMEventHandler ShowMoreResults;

            
    #endregion
        }

        
    public class GoogleTracerControllerTest : TestCase
        {
            
    public override void Execute()
            {
                
    base.Execute();

                MockGoogleTracerView mockView 
    = new MockGoogleTracerView();
                Container.RegisterInstance(
    typeof(IGoogleTracerView), mockView);

                GoogleTracerController controller 
    = new GoogleTracerController();

                QUnit.Test(
    "Test get View"delegate
                {
                    QUnit.Equals(mockView, controller.View);
                });

                QUnit.Test(
    "Test Execute() & ShowMoreResults()"delegate
                {
                    GoogleSearchResponse data 
    = new GoogleSearchResponse();

                    Mock mockAddShowMoreResults 
    = new Mock(mockView, "add_showMoreResults");
                    mockAddShowMoreResults.Modify().Args(Is.Anything).ReturnValue();
                    Mock mockRenderSearchResult 
    = new Mock(mockView, "renderSearchResult");
                    mockRenderSearchResult.Modify().Args(data).ReturnValue();
                    mockRenderSearchResult.Modify().Args(data).ReturnValue();
                    Mock mockGetScript 
    = new Mock(Script.Eval("jQuery"), "getScript");
                    mockGetScript.Modify().Args(Is.Anything, Is.Anything).Callback(
    1null).ReturnValue();
                    mockGetScript.Modify().Args(Is.Anything, Is.Anything).Callback(
    1null).ReturnValue();

                    QUnit.Equals(
    0, mockView.SearchStart);
                    ((Dictionary)(
    object)Window.Self)["_googlewebsearchresults"= data;
                    controller.Execute();
                    ((Dictionary)(
    object)Window.Self)["_googlewebsearchresults"= data;
                    controller.ShowMoreResults();
                    QUnit.Equals(
    4, mockView.SearchStart);

                    mockAddShowMoreResults.Verify();
                    mockAddShowMoreResults.Restore();
                    mockRenderSearchResult.Verify();
                    mockRenderSearchResult.Restore();
                    mockGetScript.Verify();
                    mockGetScript.Restore();
                });
            }
        }

     The testing results of QUnit:

    Source Code

    You could download the latest source code of this demo from SVN: http://nintegrate.googlecode.com/svn/trunk/jqMVCSharp/

    or download this zip file: jqMVCSharpDemo.zip

    To open the project files in Visual Studio 2008, you should install Script# 0.5.6 for VS 2008 first.

  • 相关阅读:
    python报错Enable tracemalloc to get the object allocation traceback
    解决pycharm每次新建项目都要重新安装一些第三方库的问题
    创建一个CA证书
    [转载]oracle 12C 《服务器、客户端安装》
    [转载]Windows Server 2016中添加AD域控制器
    [转载]Windows Server 2016中部署AD
    虚拟机 VMware Workstation Pro 15.5.0 及永久激活密钥
    Oracle给查询结果增加序列号
    创建自定义ssl证书用于https
    js:getAttribute
  • 原文地址:https://www.cnblogs.com/teddyma/p/1769561.html
Copyright © 2020-2023  润新知