Forráskód Böngészése

完善 config 跟模組doc

0x0001F36D 5 éve
szülő
commit
5f4a4cbab3

+ 1 - 0
Yuuna.sln

@@ -16,6 +16,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
 	ProjectSection(SolutionItems) = preProject
 		.gitattributes = .gitattributes
 		.gitignore = .gitignore
+		doc\plugins.md = doc\plugins.md
 		readme.md = readme.md
 	EndProjectSection
 EndProject

+ 40 - 2
doc/plugins.md

@@ -1,5 +1,43 @@
 # 模組開發
+## 導言
 1. 在專案中引用 ```Yuuna.Contracts.dll```,裡面有大部分開發時你所需要的類別,除非你需要一些特殊的功能(像是序列化之類的)
     ,這時你可以考慮引用 ```Yuuna.Common.dll``` 這個函式庫。
-2. 我在設計模組及其類別介面時,為開發者提供了建構者模式(Builder Pattern),逐一引導開發者建構模組行為,未來有需要我會考慮導入 IoC 及 Config (這部分我會優先處理)。
-3. 可以直接參考 Yuuna.Contracts.Test 這個專案中我對模組的實作方式。
+2. 在設計模組及其類別介面時,為開發者提供了建構者模式(Builder Pattern),逐一引導開發者建構模組行為,未來有需要我會考慮導入 IoC <del>及 Config (這部分我會優先處理)</del>。
+3. 可以直接參考 Yuuna.Contracts.Test 這個專案中模組的實作方式。
+
+##### **Config 的部分已經完成**
+
+## 名詞解釋
+### 同義詞 - Synonym
+由開發者定義的多個相同意思的詞彙字串所組成,語法系統的最小單位。
+### 群組 - Group
+由開發者將多個類型相似或性質相似的同義詞進行分組及規劃所產生。
+### 模式 - Pattern
+由多個群組順序性串接而成,用以比對輸入的意圖是否具順序性的匹配。
+
+## 建構文法規則及模組行為
+1. 使用 ```IGroupManager``` 介面的 ```Define``` 方法定義群組名稱。
+2. 再透過傳回的 ```IGroup``` 物件中的 ```AppendOrCreate``` 或其他相關方法定義同義詞物件。
+3. 透過 ```IPatternBuilder``` 介面的 ```Build``` 方法及多個群組物件用以建構模式物件。
+4. 並透過傳回的 ```IInvokeBuilder``` 物件所提供的方法 ```OnInvoke``` 建構 ```Invoke``` 委派物件。
+
+## 模組處理流程
+1. 當分詞器將輸入字串分詞後,會送入 ```Strategy``` 物件中,將分詞後的字串序列與模式物件進行評估及分析並傳回最佳的匹配結果物件。
+2. ```Invoke``` 委派類型表示匹配結果為最佳時將進行的動作,並由開發者處理並回傳 ```Response``` 結構用以與使用者視圖層的互動。
+
+## 模組的設定檔與自動保存
+1. 在模組中定義你需要做為設定的屬性,並在上面加上 ```[Field]``` 標籤且 **屬性不可以為自動屬性**。
+2. 透過 ```BuildPatterns``` 中的參數 ```config``` 存取你需要的屬性,並會在值不同時自動更新值到設定檔中。
+```csharp
+[Field()]
+public string Example { get; set; }
+[Field("alias")]
+public string ExampleWithAlias { get; set; }
+...
+void BuildPatterns(... dynamic config)
+{
+    ...
+    var example = config.Example;
+    var exampleWithAlias = config.alias;
+}
+```

+ 28 - 31
src/Yuuna.Common/Configuration/ConfigManager.cs → src/Yuuna.Common/Configuration/ConfigProxy.cs

@@ -1,6 +1,7 @@
 
 namespace Yuuna.Common.Configuration
 {
+    using Microsoft.CodeAnalysis.CSharp;
     using Newtonsoft.Json;
     using Newtonsoft.Json.Linq;
     using System;
@@ -8,13 +9,10 @@ namespace Yuuna.Common.Configuration
     using System.ComponentModel;
     using System.Dynamic;
     using System.IO;
+    using System.Linq.Expressions;
     using System.Reflection;
-    using System.Security.Cryptography;
-    using System.Text;
-    using Yuuna.Common.Serialization;
-    
 
-    public sealed class ConfigData<T> : DynamicObject, INotifyPropertyChanged
+    public sealed class ConfigProxy : DynamicObject, INotifyPropertyChanged
     {
         public override IEnumerable<string> GetDynamicMemberNames()
         {
@@ -53,7 +51,7 @@ namespace Yuuna.Common.Configuration
 
         private static string GetName(Type type)
         {
-            return type.Name + ".meta";
+            return type.GUID + ".meta";
         }
 
         private readonly FileInfo _meta;
@@ -89,23 +87,32 @@ namespace Yuuna.Common.Configuration
                 writer.Write(json);
             }
         }
-        
-        public ConfigData(T graph)
+
+        private readonly object _graph;
+
+        public ConfigProxy(object graph)
         {
             var type = graph?.GetType() ?? throw new ArgumentNullException(nameof(graph));
             this._list = new Dictionary<string, Accessor>();
             this._meta = new FileInfo(GetName(type));
+            
 
-
-            foreach (var pinfo in type.GetProperties((BindingFlags)52))
-                if (pinfo.GetCustomAttribute<FieldAttribute>() != null &&
-
+                foreach (var pinfo in type.GetProperties((BindingFlags)52))
+                if (pinfo.GetCustomAttribute<FieldAttribute>() is FieldAttribute f &&
                     pinfo.GetGetMethod(true) is MethodInfo getter &&
                     pinfo.GetSetMethod(true) is MethodInfo setter)
                 {
                     var getDele = Delegate.CreateDelegate(typeof(Func<>).MakeGenericType(getter.ReturnType), graph, getter);
                     var setDele = Delegate.CreateDelegate(typeof(Action<>).MakeGenericType(setter.GetParameters()[0].ParameterType), graph, setter);
-                    this._list.Add(pinfo.Name, new Accessor(pinfo.PropertyType , getDele, setDele));
+                    
+
+                    var identifier = 
+                        f.Alias is string alias 
+                            ? SyntaxFacts.IsValidIdentifier(alias)
+                                ? alias 
+                                : throw new ArgumentException("Invalid member name", alias)
+                            : pinfo.Name;
+                    this._list.Add(identifier, new Accessor(pinfo.PropertyType , getDele, setDele));
                 }
 
             // metadata not found, create and serialize object to file.
@@ -115,8 +122,7 @@ namespace Yuuna.Common.Configuration
             }
 
             this.Load();
-
-            
+            this._graph = graph;
         }
 
         private struct Accessor
@@ -180,23 +186,14 @@ namespace Yuuna.Common.Configuration
             }
             throw new ArgumentOutOfRangeException();
         }
-    }
+        
+        public override bool Equals(object obj) => this._graph.Equals(obj);
+        
+        public override int GetHashCode() => this._graph.GetHashCode();
 
-    public static class ConfigExtension
-    {
-        public static dynamic Bind<T>(this T obj, PropertyChangedEventHandler onPropertyChnaged = null, bool autoSave = true)
-        {
-            var p = new ConfigData<T>(obj);
-            if (onPropertyChnaged != null)
-                p.PropertyChanged += onPropertyChnaged;
-            if (autoSave)
-                p.PropertyChanged += delegate { p.Save(); };
-            return p;
-        }
-    }
+        public override string ToString() => this._graph.ToString();
 
-    [AttributeUsage(AttributeTargets.Property)]
-    public sealed class FieldAttribute : Attribute
-    {
+        public new Type GetType() => this._graph.GetType();
+        
     }
 }

+ 20 - 0
src/Yuuna.Common/Configuration/FieldAttribute.cs

@@ -0,0 +1,20 @@
+
+namespace Yuuna.Common.Configuration
+{
+    using System;
+
+    [AttributeUsage(AttributeTargets.Property)]
+    public sealed class FieldAttribute : Attribute
+    {
+        public FieldAttribute() : this(null)
+        {
+        }
+
+        public FieldAttribute(string alias)
+        {
+            this.Alias = alias;
+        }
+
+        public string Alias { get; }
+    }
+}

+ 1 - 0
src/Yuuna.Common/Yuuna.Common.csproj

@@ -8,6 +8,7 @@
   </PropertyGroup>
 
   <ItemGroup>
+    <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.4.0" />
     <PackageReference Include="Nett" Version="0.13.0" />
     <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
     <PackageReference Include="YamlDotNet" Version="8.0.0" />

+ 16 - 4
src/Yuuna.Contracts/Modules/ModuleBase.cs

@@ -6,6 +6,7 @@ namespace Yuuna.Contracts.Modules
     using System;
     using System.Collections.Immutable;
     using System.Runtime.Loader;
+    using Yuuna.Common.Configuration;
     using Yuuna.Contracts.Optimization;
     using Yuuna.Contracts.Patterns;
     using Yuuna.Contracts.Semantics;
@@ -17,6 +18,8 @@ namespace Yuuna.Contracts.Modules
         /// </summary> 
         public IModuleMetadata Metadata { get; }
 
+        private readonly ConfigProxy _configProxy;
+
         public Guid Id { get; }
 
         public ModuleBase()
@@ -24,7 +27,8 @@ namespace Yuuna.Contracts.Modules
             var t = this.GetType();
             this.Id = t.GUID;// Guid.NewGuid();
             this.Metadata = ModuleMetadataAttribute.GetMetadata(t);
-           // this.ConfigData = Yuuna.Common.Configuration.ConfigManager.Load(this);
+            this._configProxy = new ConfigProxy(this);
+            this._configProxy.PropertyChanged += (sender, e) => (sender as ConfigProxy).Save();
         }
 
         private bool _initialized;
@@ -41,8 +45,9 @@ namespace Yuuna.Contracts.Modules
             {
                 try
                 {
+                    this.BeforeInitialize(this._configProxy);
                     patterns = new PatternFactory(this);
-                    this.BuildPatterns(groupManager, patterns as IPatternBuilder);
+                    this.BuildPatterns(groupManager, patterns as IPatternBuilder, this._configProxy);
                     textSegmenter.Load(groupManager);
                     this.AfterInitialize();
                     this._initialized = true;
@@ -58,8 +63,14 @@ namespace Yuuna.Contracts.Modules
         internal IPatternSet Patterns { get; private set; }
 
         /// <summary>
-        /// 在初始化引發。
+        /// 在初始化引發。
         /// </summary>
+        protected virtual void BeforeInitialize(dynamic config)
+        {
+        }
+        /// <summary>
+         /// 在初始化後引發。
+         /// </summary>
         protected virtual void AfterInitialize()
         {
         }
@@ -69,6 +80,7 @@ namespace Yuuna.Contracts.Modules
         /// </summary>
         /// <param name="g"></param>
         /// <param name="p"></param>
-        protected abstract void BuildPatterns(IGroupManager g, IPatternBuilder p);
+        /// <param name="config"></param>
+        protected abstract void BuildPatterns(IGroupManager g, IPatternBuilder p, dynamic config);
     }
 }

+ 1 - 65
src/Yuuna.Contracts/Modules/ModuleMetadataAttribute.cs

@@ -19,6 +19,7 @@ namespace Yuuna.Contracts.Modules
         public string Url { get; set; }
         public string Description { get; set; }
 
+
         internal static IModuleMetadata GetMetadata(Type type)
         {
             var meta = type.GetCustomAttribute<ModuleMetadataAttribute>();
@@ -29,69 +30,4 @@ namespace Yuuna.Contracts.Modules
             return meta;
         }
     }
-
-    //public abstract class ModuleBase 
-    //{
-    //    private readonly PatternFactory _patternfactory;
-
-    //    /// <summary>
-    //    /// 中繼資料。
-    //    /// </summary> 
-    //    public ModuleMetadataAttribute Metadata { get; }
-
-    //    internal IPatternSet Patterns => this._patternfactory;
-
-    //    public ModuleBase()
-    //    {
-    //        this.Status = ModuleStatus.Uninitialized;
-    //        this._patternfactory = new PatternFactory(this);
-    //        this.Metadata = ModuleMetadataAttribute.GetMetadata(this.GetType());
-    //    }
-
-
-    //    /// <summary>
-    //    /// 表示模組是否已初始化。
-    //    /// </summary>
-    //    public ModuleStatus Status { get; private set; }
-
-    //    /// <summary>
-    //    /// 初始化模組
-    //    /// </summary>
-    //    /// <param name="textSegmenter">分詞器</param>
-    //    /// <param name="groupManager">群組管理</param>
-    //    internal void Initialize(ITextSegmenter textSegmenter, IGroupManager groupManager)
-    //    {
-    //        if (this.Status.Equals(ModuleStatus.Uninitialized))
-    //        {
-    //            try
-    //            {
-    //                this.BuildPatterns(groupManager, this._patternfactory);
-    //                textSegmenter.Load(groupManager);
-    //                this.Status = ModuleStatus.Initialized;
-    //                this.AfterInitialize();
-    //            }
-    //            catch //(Exception e)
-    //            {
-    //                this.Status = ModuleStatus.FailToInitialize;
-    //            }
-    //        }
-    //    }
-
-
-    //    /// <summary>
-    //    /// 在初始化後引發。
-    //    /// </summary>
-    //    protected virtual void AfterInitialize()
-    //    {
-    //    }
-
-    //    /// <summary>
-    //    /// 建立模式規則。
-    //    /// </summary>
-    //    /// <param name="g"></param>
-    //    /// <param name="p"></param>
-    //    protected abstract void BuildPatterns(IGroupManager g, IPatternBuilder p);
-
-
-    //}
 }

+ 2 - 16
src/Yuuna/EntryPoint.cs

@@ -19,29 +19,15 @@ namespace Yuuna
     using Yuuna.Contracts.Interaction;
     using Yuuna.Common.Serialization;
     using Yuuna.Common.Configuration;
+    using System.Security.Cryptography;
+    using System.Linq;
 
     internal class EntryPoint
     {
-        public class MyClass
-        {
-            [Field]
-            public string Value1 { get;internal set; }
-        }
 
         public static async Task Main(string[] args)
         {
-            var v = new MyClass();
-            var proxy = v.Bind((sender, e) => Console.WriteLine("<:" + e.PropertyName));
-
-            Console.WriteLine(v.Value1);
-            Console.ReadKey();
-            proxy.Value1 = "dfgjjlhhljh";
-
-            Console.WriteLine(v.Value1);
 
-            //proxy.Save();
-            Console.ReadKey();
-            return;
 
             //var canResponses = new Response[]
             //{