プログラマ38の日記

主にプログラムメモです。

【Salesforce】Apex開発で気をつけていること

自分がApexでの開発で気をつけていることのメモです。

  1. Salesforce IDの項目はID型で定義する。
  2. トリガでの他オブジェクトの作成/更新処理を作り過ぎない
  3. 項目自動更新とトリガを混在させない
  4. そこでしか使わない処理は無名ブロックで囲み変数のスコープを限定する
  5. insert upsert update delete を try catch で制御するならロールバックは忘れずに行う

1. Salesforce IDの項目はID型で定義する。

ID型とString型は互換性があり意識しなくても問題はないのですが、ID型で定義することで、後でプログラムを読む際にSalesforce IDが入る項目ということは明確になります。(特に関数の引数をID型で定義するとわかりやすくなります)
Stringで定義すると、Salesforce IDなのかそれとも別のユニークキーなのか明確にならないので、複数人数で開発する際に認識が違ってしまうこともあります。

 

ID型で定義すると、文字列がIDとして正しくないとエラーになってしまうので制御は必要になりますが、後でエラーになるよりは最初でエラーとなった方が苦労しなくてすむと思います。


2. トリガでの他オブジェクトの作成/更新処理を作り過ぎない

トリガで、ある条件によって他のオブジェクトの作成/更新を行いすぎると、後々ガバナ制限の問題がでたり、データパッチを当てるときに注意が必要になったりします。
(気をつけて作成/更新をする分にはいいのですが、知らなくて勝手に他のオブジェクトが作成されたり、更新されたりすることで障害になったりするのが問題です)
利用者の理解が得られるなら、項目の作成/更新時に自動で動くのではなく、項目を更新後にボタンをクリックしてもらいボタンの処理として実装するのが安全だと考えています。
※もちろん時と場合によるので、その時その時で判断は必要ですが。


3. 項目自動更新とトリガを混在させない

トリガの作成が見えているオブジェクトならば、項目自動更新は設定せずに、トリガの中にロジックを全て持っていくのが良いと考えています。
というのは、新規作成時でも、項目自動更新で updateも走るので、トランザクションの中の処理量が増え、ガバナ制限や、性能への影響が出てくる可能性があります。
また、項目自動更新の実行される順番が制御できないので、トリガの方が実装が簡単な場合があります。

 

4. そこでしか使わない処理は無名ブロックで囲み変数のスコープを限定する

一時的な変数を使って制御をしたい時があります。例えば次のようなコードがあります。

    List<Case> caselist = Trigger.New;
    Map<Id, Contact> contactMap = new Map<Id, Contact>();
    Set<Id> contactset = new Set<Id>();
    for(Case c : caselist){
      if( c.ContactId != null ){
        contactset.add(c.ContactId);
      }
    }
    contactMap = new MapMap<Id, Contact>([Select Id,Name,Email From  Contact where Id =:contactset]);

上記の「contactset」は「contactMap」を作成するためだけの一時的なSetで、後続では使わないものとします。
その場合、次のように無名ブロックで囲むのはどうでしょうか。

    List<Case> caselist = Trigger.New;
    Map<Id, Contact> contactMap = new Map<Id, Contact>();
    {
        Set<Id> contactset = new Set<Id>();
        for(Case c : caselist){
          if( c.ContactId != null ){
            contactset.add(c.ContactId);
          }
        }
        contactMap = new MapMap<Id, Contact>([Select Id,Name,Email From  Contact where Id =:contactset]);
    }

こうすることで、処理で使いたい変数と、一時的な変数のインデントが分かれるのと、ブロック内で変数のスコープが
限定されるので、後続の処理でうっかり「contactset」を使ってしまうことがなくなります。


5. insert upsert update delete を try catch で制御するならロールバックは忘れずに行う

処理の中で、2つ以上のオブジェクトに対してレコードを作成/更新することはよくありますが、その処理を
try catchで囲ってしまうと、最初の作成/更新は正常で、次の作成/更新でエラーの場合最初の作成/更新は
実行されてしまいます。そのため、きちんとcatchの中でロールバックする処理を忘れないようにします。
(結構よく忘れてしまうため備忘としてアウトプットしておこうと思います)

【Salesforce】Force.com移行ツール(Force.com Migration Tool)で指定するコンポーネント名をselect可能なSオブジェクト

件名がやたら長くなってしまいました。

オブジェクトのメタデータは、オブジェクトの情報をまとめて取得する方法と、カスタム項目、リストビュー、レコードタイプ、入力規則、カスタムボタンまたはカスタムリンクを個別で取得する方法があります。
個別で取得する際にコンポーネント名を指定しますが、コンポーネント名は対応するSオブジェクトから取得できるものがあります。

 

まず Force.com移行ツール(Force.com Migration Tool)での指定の方法は以下の通りです。

オブジェクトの情報を全て取得する

Force.com移行ツール(Force.com Migration Tool)で、オブジェクトのメタデータを取得する際にはpackage.xmlに次のように指定します。

    <types>
        <members>Sample__c</members>
        <name>CustomObject</name>
    </types>

 上記で指定すると、オブジェクトの情報が全て取得できます。

 

カスタム項目、リストビュー、レコードタイプ、入力規則、カスタムボタンまたはカスタムリンクを個別に取得する

個別に取得する際にはpackage.xmlにそれぞれ次のように指定します。

 

カスタム項目

    <types>
        <members>Sample__c.SampleField__c</members>
        <name>CustomField</name>
    </types>

リストビュー

    <types>
        <members>Sample__c.sampleview1</members>
        <name>ListView</name>
    </types>

レコードタイプ

    <types>
        <members>Sample__c.rectype1</members>
        <name>RecordType</name>
    </types>

入力規則

    <types>
        <members>Sample__c.rule1</members>
        <name>ValidationRule</name>
    </types>

カスタムボタンまたはカスタムリンク

    <types>
        <members>Sample__c.button1</members>
        <name>WebLink</name>
    </types>

上記のように個別に指定することにより必要なものだけ取得することができます。

 

個別に指定する際のコンポーネント名をSオブジェクトから取得する

Force.com移行ツール(Force.com Migration Tool)のpackage.xmlに記載するコンポーネント名は次のSオブジェクトから取得できます。一部取得できないコンポーネント名もあります。

ラベル 対応するSObject コメント
カスタム項目 項目定義(FieldDefinition) △次のselectで取得できますが、項目レベルセキュリティが外れていると結果に含まれません。

Select EntityDefinition.QualifiedApiName
 ,QualifiedApiName
From
  FieldDefinition
where EntityDefinition.QualifiedApiName = 'オブジェクトAPI'
リストビュー リストビュー(ListView) ○次のselectで取得できます。(ビューで「自分にのみ表示」にすると結果に含まれませんが、そもそも「自分にのみ表示」のビューはメタデータが取得できないので関係ありません)
Select 
 SobjectType
 ,DeveloperName
From
  ListView
レコードタイプ レコードタイプ(RecordType) ○書くまでもないと思います。
Select 
  SobjectType
 ,DeveloperName
From
  RecordType
入力規則 ×調べたのですがありませんでした。画面からコピーするしかないのかなと思います。
ボタンまたはリンク カスタムボタンまたはカスタムリンク(WebLink) ○次のselectで取得できます。(今回は関係ありませんが、WebLinkのURL項目にはJavaScriptも書いてあるので、ロジックを調査する時は便利です)
Select
  PageOrSobjectType
 ,Name
From
  WebLink

※SオブジェクトはAPIバージョン40でのものになります。古いデータローダでは取得できない可能性があります。 

最後に

環境間の移送は変更セットが便利なのですが、標準項目が選択できないので、標準項目とレコードタイプの組み合わせや、標準項目とプロファイルの組み合わせなどで移送したい時はForce.com移行ツール(Force.com Migration Tool)を使うと便利です。

 

【Salesforce/VBA/ツール】直接カスタム項目を作成するExcelマクロを作成しました

前回、ロールを作成するExcelマクロを作成しましたが、同様にカスタム項目を作成するExcelマクロを作成しました。メタデータAPIを使用している点など仕組みは前回と同じになります。

 

ダウンロードはこちらから。

 

Salesforceの項目作成は10項目程度であれば画面から作成しても大変ではないのですが、100項目や200項目となってくると画面から作成するのはとても大変です。

そんな時に使えるマクロになります。

※全てのデータ型を作成できません。あと、全ての設定はできません。 

 

作成できるデータ型は

  • テキスト(Text)
  • テキストエリア(TextArea)
  • 数値(Number)
  • 通貨(Currency)
  • チェックボックス(Checkbox)
  • 日付(Date)
  • 日付/時間(DateTime)
  • 電話番号(Phone)
  • メール(Email)
  • URL(Url)

になります。

 

テキスト項目や数値項目をたくさん作成したいときに使えると思います。 

 

使い方は次の通りです。

Salesforceの接続情報を指定
②通信設定
③カスタム項目情報を設定
④登録ボタンをクリック

f:id:crmprogrammer38:20170920083735p:plain

Salesforceの接続情報を指定、②通信設定は、前回のロール作成マクロの時と同様の設定です。

 

③カスタム項目情報を設定

オブジェクトAPI カスタム項目を作成するオブジェクトのAPI名を指定します。標準オブジェクトであれば、Account、Case、Activityなどとなり、カスタムオブジェクトであれば、Sample__cなどとなります。
※活動のカスタム項目はプログラムで使用する時はTaskとEventの2つですが、カスタム項目を作成する時はActivityとなります。
表示ラベル 必須です。カスタム項目のラベルを指定します。
カスタム項目API 必須です。カスタム項目のAPI名を指定します。
作成するのはカスタム項目なので末尾は必ず"__c"となります。
データ型 必須です。テキスト : Text、テキストエリア : TextArea、数値 : Number、通貨 : Currency、チェックボックス : Checkbox、日付 : Date、日付/時間 : DateTime、電話番号 : Phone、メール : Email、URL : Urlとして対応します。
文字数 / 桁数 データ型が、テキスト、数値、通貨の場合必須です。テキストの場合は桁数、数値、通貨の場合は精度となります。精度での指定なので、整数部15桁、小数部3桁であれば、精度は18桁となります。
小数点の位置  データ型が、数値、通貨の場合指定します。
処理結果 正常に登録できた場合、作成/更新を表示します。エラーの場合、エラーメッセージを表示します。

 

 最後に

APIバージョンを29.0にしているのですが、理由は項目レベルセキュリティをデフォルトである程度付与したいからです。(ポータルプロファイルなどには付与されません)

APIバージョン30.0以上で項目を作成すると、項目レベルセキュリティはシステム管理者にも付与されないので多少使いづらいなと感じてバージョンを下げています。

 

選択リスト項目や、参照関係・主従関係項目、数式項目は設定が複雑なのでマクロでは厳しいなと感じています。厳しいと感じるところは具体的には、設定項目が増えていくため画面からの作成と手間がさほど変わらない点と、soap envelopeの中身が複雑になっていくのでマクロのプログラムも複雑になってしまう点です。

 

もちろんピンポイントでこの設定もできると使いやすいのにという事もあると思いますが、その際にはマクロを使いやすいように変更して使ってもらえるとうれしいです。

【Salesforce/VBA/ツール】直接ロールを作成するExcelマクロを作りました

Salesforceの環境構築をする際に、ロールの設定を行いますがロールの作成や更新を簡単にするためのExcelマクロを作成しました。

 

ダウンロードはこちらからになります。

マクロでは、メタデータAPIを使用しますので実行するユーザは該当するシステム権限が必要になります。

 

使い方は次の通りです。

Salesforceとの接続情報を設定する
②通信設定する
③ロール内容を登録する
④登録ボタンをクリックする

f:id:crmprogrammer38:20170919092640p:plain

①の入力項目は次です。

sfdc.endpoint login.salesforce/test.salesforceを選択
sfdc.username ログインユーザ
sfdc.password ログインパスワード

②の入力項目は次です。

proxy.host httpプロキシのホスト
proxy.port httpプロキシのポート番号
proxy.username httpプロキシのユーザ
proxy.password httpプロキシのパスワード

③の入力項目は次です。

表示ラベル ロールの名前
ロール名(API名) ロールのAPI
このロールの上位ロール名 上位ロールのAPI
レポートに表示するロール名  左記の通り
処理結果 正常に作成/更新されたか、エラーの場合はエラー内容を表示

 

最後に

このマクロを作成しようと思ったきっかけはデータローダで作成しようとして煩わしさを感じたからになります。
データローダで「ロール(UserRole)」オブジェクトに登録することでロールを作成することができますが、データローダでParentRoleIdは、一度ロールを作成した後にSalesforce IDで更新する必要があります。(対して手間ではないのですが。。)

階層が深かったり、多くのロールを作成する際にはストレスが減るかなーと思い作ってみました。
(でも結局、組織の設定に依存するので、取引先所有者のケースアクセスレベル(CaseAccessForAccountOwner)、取引先所有者の取引先責任者アクセスレベル(ContactAccessForAccountOwner)、取引先所有者の商談アクセスレベル(OpportunityAccessForAccountOwner)など後で更新することになります。
)

動作は、Windows10とWindows7で行っています。(Windows7ではTLS1.1以上にするパッチを当てています。パッチはこちらを参考)

 

ロールは手でも充分作成できるのでマクロの必要性はあまり感じないのですが、このマクロはメタデータAPIのupsertMetadataを使っているところがポイントとなります。

UpsertMetadataでは、メタデータAPIで提供されているMetadata全てが作成、更新可能です。

今回はロールですが、カスタム表示ラベル、カスタム項目、リストビューなど数が多いのにデータローダでは作成できないものを作成する際に応用できます。

【Salesforce】テストクラスの作成で思うこと

SalesforceのApex開発を行う場合、本番環境にリリースするためにテストクラスを作成する必要があります。

 

設定とちょっとのトリガなどの開発であれば、テストクラスの作成はさほど大変ではないのですが、サイトやコミュニティで多くの画面を作りこむ場合、テストクラスの作成に多くの労力を必要とします。

さらに、モジュールのリリースの際のテストクラス実行中の待ち時間も意外とかかるのでテストクラスの作成の仕方は工夫が必要だと感じています。

 

個人的にテストクラスの作成で思うことは次になります。

  1. テストクラスでは最小メソッド単位の呼び出しをメインとする
  2. テストデータ登録時のトリガは極力スキップする仕組みを作る

1. テストクラスでは最小メソッド単位の呼び出しをメインとする。

テストクラスは大体以下のようになると思います。

  • カスタムWebServiceの場合メソッドを実行
  • トリガの場合、あらかじめ必要なマスタを用意した後、対象のオブジェクトへレコードを作成、更新、削除を実行
  • コントローラの場合、Visualforceページで必要なパラメータを指定後、コントローラのメソッドを実行

上記のテストメソッドは必要ですが、全てのテストメソッドを上記の書き方で書く必要はないと考えています。(労力もかかりますし、テストメソッドの実行時間も長くなります)

通常、上記のメソッドの中はさらに細かいメソッドで構成されています。テストクラスでは、その細かいメソッド単位で実行するようにします。

細かいメソッドは、そのクラス内でしかアクセスしないためprivate や protected としていると思いますが、Salesforceではテストクラスからのテスト用に テストビジブルアノテーション(@TestVisible)が用意されているので、これを使っていきます。

 

テストクラスでカバレージを上げるポイントとして、通常クラスの細かいメソッド内で業務ロジックを記載する場合、SOQLを発行しないようにしておきます。(SOQLを発行するメソッドはSOQLの発行だけの機能とします)

 

コントローラクラスの作成例は次の通りです。

public class SampleController {

    public Case caseObj {get; set;}
    String caseId;
    
    public APC_CUS_200_ShinchokuDetailCtrl(){
      caseId =  ApexPages.currentPage().getParameters().get('caseId');
    }
    
    //vf action
    public void doinit(){
      caseObj = [Select Id  ,Type ,Origin ,Subject ,Priority ,ParentId ,AccountId
                 From Case where id = :caseId ]; 
    }

    public PageReference doSomething() {
      Account acc        = getAccount();
      Case    parentcase = getParentCase();
      
      doCheck(acc, parentcase);
    }
    
    private Account getAccount(){
      if( caseObj.AccountId == nul ){
        return null;
      } else {
          return [Select  Id ,Name ,Industry 
From Account where ID =:caseObj.AccountId][0];
      }
    }

    private Case getParentCase(){
      if( caseObj.ParentId == nul ){
        return null;
      } else {
          return [Select  Id  ,Type ,Origin ,Subject ,Priority 
From Case where ID =:caseObj.ParentId][0];
      }
    }
    
    @TestVisible
    private void doCheck(Account acc,Case parentcase){
    
      if( caseObj.Origin != parentcase.Origin ){
        ・・・・・
      }
    
      if( acc.Industry == 'Education' && parentcase.Origin == 'Phone' ){
        ・・・・・
      }
    }
}

そして上記のdoCheckのメソッドのテストクラスは次のようにします。

@isTest
private class SampleControllerTest {

  static testMethod void testMethod01() {
    SampleController ctrl = new SampleController();
    
    Case caseobj    = new Case();
    Case parentcase = new Case();
    Account acc     = new Account();
//必要なマスタの値のセットをする。
    
    ctrl.caseObj = caseobj;
    ctrl.doCheck(acc, parentcase);
  }
}

こうすることで、特にマスタを事前に登録しておくことも不要となり、doCheckの業務ロジックを確実にテストすることができ、カバレージも確保できます。

 

カスタムWebサービスや、トリガでも同様に、カスタムWebサービスのクラスやトリガハンドラークラスの細かいメソッドを呼び出すことで対応します。

2. テストデータ登録時のトリガは極力スキップする仕組みを作る

上記1ではデータを作成せずにテストをすることで、効率をあげるということを書いていますが、テストクラスでテストデータの作成は避けて通ることができません。

ですが、テストクラス用のデータを作成するときにそのオブジェクトのトリガが邪魔になるときがあります。
そのトリガ処理のテストをしたいわけではないのに、トリガのエラーを解決するために時間がかかったりして効率が落ちます。

そこで、データ登録時のトリガをスキップする処理を用意しておくのはどうでしょうか。そこまで大した変更をせずにテストで使いたいデータを登録ができて、本来のテストに集中することができます。(もちろん全てのオブジェクトのトリガにスキップを入れる必要はありません、効果的な箇所だけ対応するのが良いと思います)
トリガのスキップについては以前に書いていますのでそちらを参照ください。

【Salesforce】カスタムボタンのJavaScriptで注意すること

Salesforceでは、標準レイアウトにカスタムボタンを配置してアプリケーションを作成していきます。(Lightning Experience ではなくClassicを対象とした場合です)

 

自分がカスタムボタンの作成時に注意することのメモです。

  1. 環境依存の文字列はカスタム表示ラベルを使う
  2. オブジェクトの値を差し込む際は、改行や「"」や「'」が入る文字列は使わない
  3. カスタムWebServiceの戻り値ではJSON文字列を返却する
  4. 制御が複雑で、ネストが深くなる場合にはtry, catchをうまく使う

1. 環境依存の文字列はカスタム表示ラベルを使う

カスタムボタンでカスタム表示ラベル( {!$Label.Label1} など)は使えるので、環境依存する文字列、例えば、接続先のURLや、項目IDなどはカスタム表示ラベルにしておきます。※項目IDについては、項目定義(FieldDefinition)のデュラブル ID(DurableId)から取得することをお奨めします。

2. オブジェクトの値を差し込む際は、改行や「"」や「'」が入る文字列は使わない

次のようなId、日付項目、数値、チェックボックスは、問題ないのですが、

( {!Opportunity.Id}、{!Opportunity.CloseDate}、{!Opportunity.Amount}、{!Opportunity.IsClosed} )
次のように、値に改行がはいったり、シングルクオテーション、ダブルクオテーションがはいる可能性がある項目は差し込むとJavaScriptがエラーとなる場合があります。
( {!Opportunity.Description} )

APIコール数が増えますが、カスタムWebServiceをコールしてApex内で処理するようにします。

3. カスタムWebServiceの戻り値ではJSON文字列を返却する

カスタムWebServiceでチェックをしたり、処理結果を戻したりする際には、JSON文字列で返却すると便利です。次のようなクラスを用意しておいて

public class ReturnSample {
  public Boolean IsSuccess;
  public Boolean IsWarning;
  public Boolean IsError;
  public String Message;
  public List<String> successIdList;
  public List<String> warningIdList;
  public List<String> errorIdList;
}

カスタムWebServiceで、上記のインスタンスシリアライズして返却します。

ReturnSample retobj = new ReturnSample();
retobj.IsSuccess = true;
retobj.IsWarning = true;
retobj.IsError   = false;
retobj.Message   = '処理は完了しましたが、警告があります。XXXX';
retobj.successIdList = successlist;
retobj.warningIdList = null;
retobj.errorIdList   = warninglist;

return JSON.serialize(retobj);

そして、カスタムボタンではJSON文字列をデシリアライズして使います。

var retjson = sforce.apex.execute('SampleWebService','samplemethod', { arg1 : '{!Case.Id}' } ); 
var retobj  = JSON.parse(retjson);

多少込み入った処理を実装する場合でも、必要な情報は返却できると思います。

4. 制御が複雑で、ネストが深くなる場合にはtry, catchをうまく使う

カスタムボタンである程度ロジックを組まないといけなくなる場合はあると思います。 特にやっかいなのが、確認メッセージを出して"OK"なら次へ、"キャンセル"の場合は中断するという制御です。

この制御をifを使って実装すると、ネストが確認メッセージ分深くなっていきます。そこで、次のようにtry, catchを使ってジャンプさせてしまうのはどうでしょうか。


try{
  var retjson = sforce.apex.execute('SampleWebService','samplemethod', 
{ arg1 : '{!Case.Id}' } ); 
  var retobj  = JSON.parse(retjson);

  if( retobj.IsSuccess == true ) {
    if( retobj.IsWarning == true ){
      if( confirm('次の警告が発生しています。処理を続けますか? ' + retobj.Message) == false){
         throw { quietclose : true };
      }

      var retjson2 = sforce.apex.execute('SampleWebService','samplemethod2', 
{ arg1 : retobj.successIdList ,arg2 : retobj.warningIdList } ); 
      var retobj2  = JSON.parse(retjson2);

      if( retobj2.IsSuccess == true ){
        if( confirm('次の警告が発生しています。処理を続けますか? ' + retobj2.Message) == false){
           throw { quietclose : true };
        }

        var retjson3 = sforce.apex.execute('SampleWebService','samplemethod3',    { arg1 : retobj2.successIdList ,arg2 : retobj2.warningIdList } ); 
        var retobj3  = JSON.parse(retjson2);
        ・・・・・・・・・

      } else {
        throw retobj2.Message;
      }

    }
  } else {
    throw retobj.Message;
  }

} catch( ex ){
  if( ex.quietclose == true ){
    //何もせず終了する
  } else {
    alert( ex );
  }
}

ポイントは、メッセージ表示はcatchにまかせている点、catchにquietcloseプロパティをtrueにして投げると、何も表示せずに終わるようにしている点です。

上記のサンプルでも結構わかりずらいですが、これを全てifのブロックで実現した場合、ネストが相当深くなりメンテナンスが大変になっていきます。

 

最後に

カスタムボタンや、メールテンプレートではカスタム表示ラベルが使えるのに、差込項目として選べないんですよね。
本番にリリースするたびに、文字列を本番用に変更しているプロジェクトがあると大変そうだなと思います。カスタム表示ラベルではなく直に環境依存の文字列を書いていると予想できますが、一度決めた運用なので変更しずらいのだと思います。

 

後、この慣れ親しんだカスタムボタンもLightning Experienceを採用して使わなくなる日も近いのでしょうね。。 ガバナ制限を回避のため、カスタムボタンの中でカスタムWebServiceを細かく呼び出すような設計をすることがありますが、そういった部分がLightning Experienceでどうなるのか把握できていません。なかなかLightning Experienceへ切り替えができないでいます。。

100記事書いてみて思ったこと

今回がちょうど101回目になります。

 

100回書いてみて思ったことと、これからやってみたいことを書いてみようと思います。

 

[思ったこと]

文章を書くのが下手

プログラマとしてドキュメントを書くことは多く、今までそれなりに書いてきましたが、あらためて文章が下手だなーと感じました。

書く内容も細かかったり、場合分けが多かったりもするのですが、今読み返すと文章の展開がおかしく「え?」というものが多いです。

書いているときは、書きたい内容はあるのですが、うまく文章にできていないんだと思います。もっと書けば、経験値が貯まりコツがつかめてくるんじゃないかと期待しています。他にも文章を書くのが上手な方を真似してみようと思います。

 

1つの記事を書くのにそれなりの時間はかかる

ブログなので、ちょっとした合間にできるものなのかなと最初は思っていたのですが、自分には全くそんなことはありませんでした。

プログラムに関することでは、サンプルプログラムを載せることがありますが、サンプルプログラムでもきちんと動くものを載せたいので、ある程度のテストを行ったりしています。
また、最終的に書く文章は下手なんですが、下手なりに色々試行錯誤してみたりして時間がかかります。テストデータとか表で表現しようものならtableタグを書いたりするのに多くの時間がかかりました。(tableタグを書くのがあまりに辛くExcelマクロを作りました(笑))

世の中には、すごい人たちがいてとても込み入った内容でも、わかりやすく表現しているので、素直に尊敬します。

 

楽しい

なんでもそうですが、1つ1つ作り上げていく作業はとても楽しくて好きです。これまでは自分でプログラムを作って、自分だけで楽しんでいたのですが、ブログ上でインターネットに公開していくことができます。今までかいた100記事の中でいくつかプログラムを公開してみたのですが、これからも公開していきたいなーと思いました。

今は100ですが、500、1000と書いていけたらとても素敵なものになると思います。(私の場合何年かかるかわかりませんが・・)

でも、内容はSalesforce関連が多くなってきています。やはりJavaSQLといったプログラム言語ではなくて、アプリケーションになっているので気づくことが多いのだと思います。今は流れにまかせて書きたいなと思ったことを書こうと思います。

 

[これからやってみたいこと]

今まで書いたものを修正する

てにをはがそもそもおかしいものや、伝えたいことが伝わらないような文章になっているもの、説明が不足しているものを直していきたいなーと思っています。

たまにタグがおかしくて、ちょっとフォントのサイズが大きい箇所があったりするのも直そうと思います。
あと、せっかく使っているので、少しずつでもはてなブログの機能を習得していきたいです。プログラムコードとかももっと綺麗に表示できる仕組みに変更しようと思います。
ちょっとずつ覚えて、覚えたものを使ってみるぐらいでやってみようと思います。(自分の性格は、大変不便なのですが、"勉強する" に対してモチベーションが全くあがらなくなります。。)

はてなProに入ったり、他のWebサービスを使ってみる

はてなProに変更してみたいです。ただちょっとお金がかかるんですよね。しばらくはこのまま無料で使えるバージョンでやっていこうと思います。
後、他のWebサービスなども使ってみたいと思います。その道のスペシャリストの方のメルマガなども購読したいなーと思っています。

なかなか、今まで自分と関りがなかったものに手を出してこなかったのですが、情報を発信することの楽しさや難しさがわかり、がぜん興味が湧いています。

 

最後に

このブログを見てくれている方、スターをつけてくれる方、ありがとうございます。

内容は自分でいうのもなんですが相当偏っていると思います。これからも、他の方と極力かぶらない内容で書いていこうと思います。書いた内容で少しでも参考になることがあれば嬉しいです。