Skip to content

Commit

Permalink
Merge pull request #25 from pbbwfc/params-develop
Browse files Browse the repository at this point in the history
Update handling of parameters
  • Loading branch information
forki committed Jan 19, 2016
2 parents 143e637 + 3d74c88 commit 2aef5fa
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 33 deletions.
20 changes: 20 additions & 0 deletions docs/content/getting-started.fsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,26 @@ You can then create a type for an individual workbook. The simplest option is to
You will then be given typed access to the data held in the first sheet.
The first row of the sheet will be treated as field names and the subsequent rows will be treated as values for these fields.
Parameters
----------
When creating the type you can specify the following parameters:
* `FileName` Location of the Excel file.
* `SheetName` Name of sheet containing data. Defaults to first sheet.
* `Range` Specification using `A1:D3` type addresses of one or more ranges. Defaults to use whole sheet.
* `ForceString` Specifies forcing data to be processed as strings. Defaults to `false`.
All but the first are optional.
The parameters can be specified by position or by using the name - for example the following are equivalent:
*)

type MultipleSheets1 = ExcelFile<"MultipleSheets.xlsx", "B">
type MultipleSheets2 = ExcelFile<"MultipleSheets.xlsx", SheetName="B">


(**
Example
-------
Expand Down
2 changes: 1 addition & 1 deletion docs/content/ranges.fsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ This example demonstrates referencing multiple ranges:
open FSharp.ExcelProvider

// Let the type provider do it's work
type MultipleRegions = ExcelFile<"MultipleRegions.xlsx", "A1:C5,E3:G5", true>
type MultipleRegions = ExcelFile<"MultipleRegions.xlsx", Range="A1:C5,E3:G5", ForceString=true>
let file = new MultipleRegions()
let rows = file.Data |> Seq.toArray

Expand Down
10 changes: 5 additions & 5 deletions src/ExcelProvider/ExcelAddressing.fs
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,12 @@ let getRangeView (workbook : DataSet) range =
{ StartColumn = topLeft.Column; StartRow = topLeft.Row; EndColumn = bottomRight.Column; EndRow = bottomRight.Row; Sheet = sheet }

///Gets a View object which can be used to read data from the given range in the DataSet
let public getView (workbook : DataSet) range =
let public getView (workbook : DataSet) sheetname range =
let worksheets = workbook.Tables

let workSheetName =
if worksheets.Contains range
then range
if worksheets.Contains sheetname
then sheetname
else worksheets.[0].TableName

let ranges =
Expand Down Expand Up @@ -159,7 +159,7 @@ let getCellValue view row column =
else null

///Reads the contents of an excel file into a DataSet
let public openWorkbookView filename range =
let public openWorkbookView filename sheetname range =

let fail action (ex : exn) =
let exceptionTypeName = ex.GetType().Name
Expand Down Expand Up @@ -195,4 +195,4 @@ let public openWorkbookView filename range =
then workbook.Tables.[0].TableName
else range

getView workbook range
getView workbook sheetname range
51 changes: 29 additions & 22 deletions src/ExcelProvider/ExcelProvider.fs
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,10 @@ let internal getColumnDefinitions (data : View) forcestring =
yield (processedColumnName, (columnIndex, cellType, getter))]

// Simple type wrapping Excel data
type ExcelFileInternal(filename, range) =
type ExcelFileInternal(filename, sheetname, range) =

let data =
let view = openWorkbookView filename range
let view = openWorkbookView filename sheetname range
let columns = [for (columnName, (columnIndex, _, _)) in getColumnDefinitions view true -> columnName, columnIndex] |> Map.ofList
let buildRow rowIndex = new Row(rowIndex, getCellValue view, columns)
seq{ 1 .. view.RowCount}
Expand Down Expand Up @@ -113,11 +113,6 @@ let internal typExcel(cfg:TypeProviderConfig) =
// Create the main provided type
let excelFileProvidedType = ProvidedTypeDefinition(executingAssembly, rootNamespace, "ExcelFile", Some(typeof<ExcelFileInternal>))

// Parameterize the type by the file to use as a template
let filename = ProvidedStaticParameter("filename", typeof<string>)
let range = ProvidedStaticParameter("sheetname", typeof<string>, "")
let forcestring = ProvidedStaticParameter("forcestring", typeof<bool>, false)
let staticParams = [ filename; range; forcestring ]
/// Given a function to format names (such as `niceCamelName` or `nicePascalName`)
/// returns a name generator that never returns duplicate name (by appending an
/// index to already used names)
Expand Down Expand Up @@ -149,22 +144,18 @@ let internal typExcel(cfg:TypeProviderConfig) =
set.Add name |> ignore
name



do excelFileProvidedType.DefineStaticParameters(staticParams, fun tyName paramValues ->
let (filename, range, forcestring) =
match paramValues with
| [| :? string as filename; :? string as range; :? bool as forcestring|] -> (filename, range, forcestring)
| [| :? string as filename; :? bool as forcestring |] -> (filename, String.Empty, forcestring)
| [| :? string as filename|] -> (filename, String.Empty, false)
| _ -> ("no file specified to type provider", String.Empty, true)
let buildTypes (typeName:string) (args:obj[]) =
let filename = args.[0] :?> string
let sheetname = args.[1] :?> string
let range = args.[2] :?> string
let forcestring = args.[3] :?> bool

// resolve the filename relative to the resolution folder
let resolvedFilename = Path.Combine(cfg.ResolutionFolder, filename)

let ProvidedTypeDefinitionExcelCall (filename, range, forcestring) =
let ProvidedTypeDefinitionExcelCall (filename, sheetname, range, forcestring) =
let gen = uniqueGenerator id
let data = openWorkbookView resolvedFilename range
let data = openWorkbookView resolvedFilename sheetname range

// define a provided type for each row, erasing to a int -> obj
let providedRowType = ProvidedTypeDefinition("Row", Some(typeof<Row>))
Expand All @@ -179,13 +170,13 @@ let internal typExcel(cfg:TypeProviderConfig) =
providedRowType.AddMember(prop)

// define the provided type, erasing to an seq<int -> obj>
let providedExcelFileType = ProvidedTypeDefinition(executingAssembly, rootNamespace, tyName, Some(typeof<ExcelFileInternal>))
let providedExcelFileType = ProvidedTypeDefinition(executingAssembly, rootNamespace, typeName, Some(typeof<ExcelFileInternal>))

// add a parameterless constructor which loads the file that was used to define the schema
providedExcelFileType.AddMember(ProvidedConstructor([], InvokeCode = emptyListOrFail (fun () -> <@@ ExcelFileInternal(resolvedFilename, range) @@>)))
providedExcelFileType.AddMember(ProvidedConstructor([], InvokeCode = emptyListOrFail (fun () -> <@@ ExcelFileInternal(resolvedFilename, sheetname, range) @@>)))

// add a constructor taking the filename to load
providedExcelFileType.AddMember(ProvidedConstructor([ProvidedParameter("filename", typeof<string>)], InvokeCode = singleItemOrFail (fun filename -> <@@ ExcelFileInternal(%%filename, range) @@>)))
providedExcelFileType.AddMember(ProvidedConstructor([ProvidedParameter("filename", typeof<string>)], InvokeCode = singleItemOrFail (fun filename -> <@@ ExcelFileInternal(%%filename, sheetname, range) @@>)))

// add a new, more strongly typed Data property (which uses the existing property at runtime)
providedExcelFileType.AddMember(ProvidedProperty("Data", typedefof<seq<_>>.MakeGenericType(providedRowType), GetterCode = singleItemOrFail (fun excFile -> <@@ (%%excFile:ExcelFileInternal).Data @@>)))
Expand All @@ -195,7 +186,23 @@ let internal typExcel(cfg:TypeProviderConfig) =

providedExcelFileType

(memoize ProvidedTypeDefinitionExcelCall)(filename, range, forcestring))
(memoize ProvidedTypeDefinitionExcelCall)(filename, sheetname, range, forcestring)

let parameters =
[ ProvidedStaticParameter("FileName", typeof<string>)
ProvidedStaticParameter("SheetName", typeof<string>, parameterDefaultValue = "")
ProvidedStaticParameter("Range", typeof<string>, parameterDefaultValue = "")
ProvidedStaticParameter("ForceString", typeof<bool>, parameterDefaultValue = false) ]

let helpText =
"""<summary>Typed representation of data in an Excel file.</summary>
<param name='FileName'>Location of the Excel file.</param>
<param name='SheetName'>Name of sheet containing data. Defaults to first sheet.</param>
<param name='Range'>Specification using `A1:D3` type addresses of one or more ranges. Defaults to use whole sheet.</param>
<param name='ForceString'>Specifies forcing data to be processed as strings. Defaults to `false`.</param>"""

do excelFileProvidedType.AddXmlDoc helpText
do excelFileProvidedType.DefineStaticParameters(parameters, buildTypes)

// add the type to the namespace
excelFileProvidedType
Expand Down
18 changes: 13 additions & 5 deletions tests/ExcelProvider.Tests/ExcelProvider.Tests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,17 @@ open FsUnit
open System
open System.IO

type BookTest = ExcelFile<"BookTest.xls", "Sheet1", true>
type HeaderTest = ExcelFile<"BookTestWithHeader.xls", "A2", true>
type MultipleRegions = ExcelFile<"MultipleRegions.xlsx", "A1:C5,E3:G5", true>
type BookTest = ExcelFile<"BookTest.xls", "Sheet1", ForceString=true>
type HeaderTest = ExcelFile<"BookTestWithHeader.xls", Range="A2", ForceString=true>
type MultipleRegions = ExcelFile<"MultipleRegions.xlsx", Range="A1:C5,E3:G5", ForceString=true>
type DifferentMainSheet = ExcelFile<"DifferentMainSheet.xlsx">
type DataTypes = ExcelFile<"DataTypes.xlsx">
type CaseInsensitive = ExcelFile<"DataTypes.XLSX">
type MultiLine = ExcelFile<"MultilineHeader.xlsx">

type MultipleSheetsFirst = ExcelFile<"MultipleSheets.xlsx", "A">
type MultipleSheetsSecond = ExcelFile<"MultipleSheets.xlsx", "B">
type MultipleSheetsSecondRange = ExcelFile<"MultipleSheets.xlsx", "B", "A2">

[<Test>]
let ``Read Text as String``() =
Expand Down Expand Up @@ -181,7 +182,7 @@ let ``Can load from multiple ranges``() =
rows.[3].Sixth |> should equal null

[<Test>]
let ``Can load from first multiple sheets - first``() =
let ``Can load from multiple sheets - first``() =
let file = MultipleSheetsFirst()
let rows = file.Data |> Seq.toArray

Expand All @@ -194,7 +195,7 @@ let ``Can load from first multiple sheets - first``() =
rows.[1].Third |> should equal "b"

[<Test>]
let ``Can load from first multiple sheets - second``() =
let ``Can load from multiple sheets - second``() =
let file = MultipleSheetsSecond()
let rows = file.Data |> Seq.toArray

Expand All @@ -204,6 +205,13 @@ let ``Can load from first multiple sheets - second``() =
rows.[1].Fourth |> should equal 3.2
rows.[1].Fifth |> should equal (new DateTime(2013,2,1))

[<Test>]
let ``Can load from multiple sheets with range``() =
let file = MultipleSheetsSecondRange()
let rows = file.Data |> Seq.toArray

rows.[0].``2.2`` |> should equal 3.2

[<Test>]
let ``Can load file with different casing``() =
let file = CaseInsensitive()
Expand Down

0 comments on commit 2aef5fa

Please sign in to comment.