2012-10-23 15 views
12

मैं सिर्फ एनोर्म और पार्सर संयोजक के साथ शुरू कर रहा हूं। ऐसा लगता है कि बॉयलरप्लेट कोड का एक बहुत भयानक है। उदाहरण के लिए, मेरे पासक्या एनार्म पार्सर संयोजक स्वचालित रूप से उत्पन्न करने के लिए कोई उपकरण है?

case class Model(
    id:Int, 
    field1:String, 
    field2:Int, 
    // a bunch of fields omitted 
) 

val ModelParser:RowParser[RegdataStudentClass] = { 
    int("id") ~ 
    str("field1") ~ 
    int("field2") ~ 
    // a bunch of fields omitted 
    map { 
    case id ~ field1 ~ field2 //more omissions 
     => Model(id, field1, field2, // still more omissions 
      ) 
    } 
} 

प्रत्येक डेटाबेस फ़ील्ड को पूरी चीज परिभाषित करने से पहले चार (!) बार दोहराया जाता है। ऐसा लगता है कि पार्सर को केस क्लास से अर्द्ध स्वचालित रूप से कम करने में सक्षम होना चाहिए। यहां शामिल काम को कम करने के लिए सुझाव देने के लिए कोई उपकरण या अन्य तकनीकें?

किसी भी पॉइंटर्स के लिए धन्यवाद।

+0

मैं anorm का उपयोग करने से बिल्कुल वैसा ही मुद्दा है। मुझे संदेह है कि जवाब बिल्कुल भीड़ का उपयोग नहीं करना है। मैं बॉयलरप्लेट को कम करने के लिए मैक्रोज़ का उपयोग करके, स्लिम (पहले स्कैलाक्वायर) आगे बढ़ता हूं। दुर्भाग्यवश, मैक्रोज़ को स्कैला 2.10 की आवश्यकता होती है। यह भी देखें: http://stackoverflow.com/questions/11379608/play-framework-slick-scalaquery- ट्यूटोरियल –

उत्तर

3

यहां समाधान मैंने अंततः विकसित किया है। मेरे पास वर्तमान में यह मेरे प्ले प्रोजेक्ट में एक वर्ग के रूप में है; यह एक स्टैंड-अलोन टूल में बदल सकता है (चाहिए!)। इसका उपयोग करने के लिए, अपनी तालिका के नाम पर tableName वैल बदलें। फिर कक्षा के नीचे main का उपयोग करके इसे चलाएं। यह केस क्लास और पार्सर संयोजक के कंकाल को प्रिंट करेगा। ज्यादातर समय इन कंकाल को बहुत कम tweaking की आवश्यकता होती है।

बायरन

package tools 

import scala.sys.process._ 
import anorm._ 

/** 
* Generate a parser combinator for a specified table in the database. 
* Right now it's just specified with the val "tableName" a few lines 
* down. 
* 
* 20121024 bwbecker 
*/ 
object ParserGenerator { 

    val tableName = "uwdata.uwdir_person_by_student_id" 


    /** 
    * Convert the sql type to an equivalent Scala type. 
    */ 
    def fieldType(field:MetaDataItem):String = { 
    val t = field.clazz match { 
     case "java.lang.String" => "String" 
     case "java.lang.Boolean" => "Boolean" 
     case "java.lang.Integer" => "Int" 
     case "java.math.BigDecimal" => "BigDecimal" 
     case other => other 
    } 

    if (field.nullable) "Option[%s]" format (t) 
    else t 
    } 

    /** 
    * Drop the schema name from a string (tablename or fieldname) 
    */ 
    def dropSchemaName(str:String):String = 
    str.dropWhile(c => c != '.').drop(1) 

    def formatField(field:MetaDataItem):String = { 
    "\t" + dropSchemaName(field.column) + " : " + fieldType(field) 
    } 

    /** 
    * Derive the class name from the table name: drop the schema, 
    * remove the underscores, and capitalize the leading letter of each word. 
    */ 
    def deriveClassName(tableName:String) = 
    dropSchemaName(tableName).split("_").map(w => w.head.toUpper + w.tail).mkString 

    /** 
    * Query the database to get the metadata for the given table. 
    */ 
    def getFieldList(tableName:String):List[MetaDataItem] = { 
     val sql = SQL("""select * from %s limit 1""" format (tableName)) 

     val results:Stream[SqlRow] = util.Util.DB.withConnection { implicit connection => sql() } 

     results.head.metaData.ms 
    } 

    /** 
    * Generate a case class definition with one data member for each field in 
    * the database table. 
    */ 
    def genClassDef(className:String, fields:List[MetaDataItem]):String = { 
    val fieldList = fields.map(formatField(_)).mkString(",\n") 

    """ case class %s (
    %s 
    ) 
    """ format (className, fieldList) 
    } 

    /** 
    * Generate a parser for the table. 
    */ 
    def genParser(className:String, fields:List[MetaDataItem]):String = { 

    val header:String = "val " + className.take(1).toLowerCase() + className.drop(1) + 
    "Parser:RowParser[" + className + "] = {\n" 

    val getters = fields.map(f => 
     "\tget[" + fieldType(f) + "](\"" + dropSchemaName(f.column) + "\")" 
    ).mkString(" ~ \n") 

    val mapper = " map {\n  case " + fields.map(f => dropSchemaName(f.column)).mkString(" ~ ") + 
     " =>\n\t" + className + "(" + fields.map(f => dropSchemaName(f.column)).mkString(", ") + ")\n\t}\n}" 

    header + getters + mapper 
    } 

    def main(args:Array[String]) = { 

    val className = deriveClassName(tableName) 
    val fields = getFieldList(tableName) 

    println(genClassDef(className, fields)) 

    println(genParser(className, fields)) 
    } 
} 
3

ठीक है, आपको वास्तव में कुछ भी दोहराना नहीं है। आप एक टपल बनाने के लिए flatten उपयोग कर सकते हैं और फिर उस टपल से बाहर अपने मॉडल उदाहरण बनाने के लिए: यदि आप कुछ आगे परिवर्तनों करने की ज़रूरत है,

(int("id") ~ str("field1") ~ int("field2")) 
    .map(flatten) 
    .map { tuple => (Model apply _).tupled(tuple) } 

हालांकि, अगर आप किसी भी तरह टपल को संशोधित करने की आवश्यकता होगी:

(int("id") ~ str("field1") ~ int("field2")) 
    .map(flatten) 
    .map { tuple => (Model apply _).tupled(tuple.copy(_1=..., _2=....) } 
+0

सुझाव के लिए धन्यवाद। मैं एक अलग समाधान के साथ आया (नीचे देखें) क्योंकि इसे अभी भी फ़ील्ड सूचीबद्ध करने के लिए कम से कम दो बार दोहराने की आवश्यकता है। समाधान के साथ, नीचे, मुझे फ़ील्ड लिखने की आवश्यकता नहीं है। मैं एक मौजूदा डेटाबेस के साथ काम कर रहा हूँ। – bwbecker

+0

ओह, "नीचे" और "उपर्युक्त" ने अपना जवाब स्वीकार करने के बाद रिश्तेदार पदों को बदल दिया। – bwbecker

संबंधित मुद्दे

 संबंधित मुद्दे