README.md in fmrest-0.2.0 vs README.md in fmrest-0.2.1

- old
+ new

@@ -12,11 +12,12 @@ anyway). If you're looking for a Ruby client for the legacy XML/Custom Web Publishing API try the fabulous [ginjo-rfm gem](https://github.com/ginjo/rfm) instead. -fmrest-ruby does not currently implement the full spec of FileMaker Data API. +fmrest-ruby does not currently implement the full spec of FileMaker 17's Data +API. ## Installation Add this line to your Gemfile: @@ -139,11 +140,11 @@ ``` And finally extend your Spyke models with `FmRest::Spyke`: ```ruby -class Kitty < Spyke::Base +class Honeybee < Spyke::Base include FmRest::Spyke end ``` This will make your Spyke model send all its requests in Data API format, with @@ -153,41 +154,39 @@ Alternatively you can inherit directly from the shorthand `FmRest::Spyke::Base`, which is in itself a subclass of `Spyke::Base` with `FmRest::Spyke` already included: ```ruby -class Kitty < FmRest::Spyke::Base +class Honeybee < FmRest::Spyke::Base end ``` In this case you can pass the `fmrest_config` hash as an argument to `Base()`: ```ruby -class Kitty < FmRest::Spyke::Base(host: "...", database: "...", username: "...", password: "...") +class Honeybee < FmRest::Spyke::Base(host: "...", database: "...", username: "...", password: "...") end -Kitty.fmrest_config # => { host: "...", database: "...", username: "...", password: "..." } +Honeybee.fmrest_config # => { host: "...", database: "...", username: "...", password: "..." } ``` All of Spyke's basic ORM operations work: ```ruby -kitty = Kitty.new +bee = Honeybee.new -kitty.name = "Felix" +bee.name = "Hutch" +bee.save # POST request -kitty.save # POST request +bee.name = "ハッチ" +bee.save # PATCH request -kitty.name = "Tom" +bee.reload # GET request -kitty.save # PATCH request +bee.destroy # DELETE request -kitty.reload # GET request - -kitty.destroy # DELETE request - -kitty = Kitty.find(9) # GET request +bee = Honeybee.find(9) # GET request ``` Read Spyke's documentation for more information on these basic features. In addition `FmRest::Spyke` extends `Spyke::Base` subclasses with the following @@ -196,27 +195,27 @@ ### Model.fmrest_config= Usually to tell a Spyke object to use a certain Faraday connection you'd use: ```ruby -class Kitty < Spyke::Base +class Honeybee < Spyke::Base self.connection = Faraday.new(...) end ``` fmrest-ruby simplfies the process of setting up your Spyke model with a Faraday connection by allowing you to just set your Data API connection settings: ```ruby -class Kitty < Spyke::Base +class Honeybee < Spyke::Base include FmRest::Spyke self.fmrest_config = { host: "example.com", - database: "database name", - username: "username", - password: "password" + database: "My Database", + username: "...", + password: "..." } end ``` This will automatically create a proper Faraday connection for those connection @@ -225,33 +224,33 @@ Note that these settings are inheritable, so you could create a base class that does the initial connection setup and then inherit from it in models using that same connection. E.g.: ```ruby -class KittyBase < Spyke::Base +class BeeBase < Spyke::Base include FmRest::Spyke self.fmrest_config = { host: "example.com", database: "My Database", - username: "username", - password: "password" + username: "...", + password: "..." } end -class Kitty < KittyBase - # This model will use the same connection as KittyBase +class Honeybee < BeeBase + # This model will use the same connection as BeeBase end ``` ### Model.layout Use `layout` to set the `:layout` part of API URLs, e.g.: ```ruby -class Kitty < FmRest::Spyke::Base - layout "FluffyKitty" # uri path will be "layouts/FluffyKitty/records(/:id)" +class Honeybee < FmRest::Spyke::Base + layout "Honeybees Web" # uri path will be "layouts/Honeybees%20Web/records(/:id)" end ``` This is much preferred over using Spyke's `uri` to set custom URLs for your Data API models. @@ -266,106 +265,101 @@ since they may sometimes contain spaces and other special characters, so fmrest-ruby extends `attributes`' functionality to allow you to map Ruby-friendly attribute names to FileMaker field names. E.g.: ```ruby -class Kitty < FmRest::Spyke::Base +class Honeybee < FmRest::Spyke::Base attributes first_name: "First Name", last_name: "Last Name" end ``` You can then simply use the pretty attribute names whenever working with your model and they will get mapped to their FileMaker fields: ```ruby -kitty = Kitty.find(1) +bee = Honeybee.find(1) -kitty.first_name # => "Mr." -kitty.last_name # => "Fluffers" +bee.first_name # => "Princess" +bee.last_name # => "Buzz" -kitty.first_name = "Dr." +bee.first_name = "Queen" -kitty.attributes # => { "First Name": "Dr.", "Last Name": "Fluffers" } +bee.attributes # => { "First Name": "Queen", "Last Name": "Buzz" } ``` ### Model.has_portal You can define portal associations on your model as such: ```ruby -class Kitty < FmRest::Spyke::Base - has_portal :wool_yarns +class Honeybee < FmRest::Spyke::Base + has_portal :flowers end -class WoolYarn < FmRest::Spyke::Base - attributes :color, :thickness +class Flower < FmRest::Spyke::Base + attributes :color, :species end ``` In this case fmrest-ruby will expect the portal table name and portal object -name to be both "wool_yarns". E.g., the expected portal JSON portion should be -look like this: +name to be both "flowers", i.e. the expected portal JSON portion should look +like this: ```json ... "portalData": { - "wool_yarns": [ + "flowers": [ { - "wool_yarns::color": "yellow", - "wool_yarns::thickness": "thick", + "flowers::color": "red", + "flowers::species": "rose" } ] } ``` If you need to specify different values for them you can do so with `portal_key` for the portal table name, and `attribute_prefix` for the portal -object name, e.g.: +object name, and `class_name`, e.g.: ```ruby -class Kitty < FmRest::Spyke::Base - has_portal :wool_yarns, portal_key: "Wool Yarn", attribute_prefix: "WoolYarn" +class Honeybee < FmRest::Spyke::Base + has_portal :pollinated_flowers, portal_key: "Bee Flowers", + attribute_prefix: "Flower", + class_name: "Flower" end ``` -The above expects the following portal JSON portion: +The above will use the `Flower` model class and expects the following portal JSON +portion: ```json ... "portalData": { - "Wool Yarn": [ + "Bee Flowers": [ { - "WoolYarn::color": "yellow", - "WoolYarn::thickness": "thick", + "Flower::color": "white", + "Flower::species": "rose" } ] } ``` -You can also specify a different class name with the `class_name` option: - -```ruby -class Kitty < FmRest::Spyke::Base - has_portal :wool_yarns, class_name: "FancyWoolYarn" -end -``` - ### Dirty attributes fmrest-ruby includes support for ActiveModel's Dirty mixin out of the box, providing methods like: ```ruby -kitty = Kitty.new +bee = Honeybee.new -kitty.changed? # => false +bee.changed? # => false -kitty.name = "Mr. Fluffers" +bee.name = "Maya" -kitty.changed? # => true +bee.changed? # => true -kitty.name_changed? # => true +bee.name_changed? # => true ``` fmrest-ruby uses the Dirty functionality to only send changed attributes back to the server on save. @@ -378,155 +372,155 @@ passing arbitrary parameters to the REST backend. fmrest-ruby however is well aware of its backend API, so it extends Spkye models with a bunch of useful querying methods. ```ruby -class Kitty < Spyke::Base +class Honeybee < Spyke::Base include FmRest::Spyke - attributes name: "CatName", age: "CatAge" + attributes name: "Bee Name", age: "Bee Age" - has_portal :toys, portal_key: "CatToys" + has_portal :hives, portal_key: "Bee Hives" end ``` #### .limit `.limit` sets the limit for get and find request: ```ruby -Kitty.limit(10) +Honeybee.limit(10) ``` #### .offset `.offset` sets the offset for get and find requests: ```ruby -Kitty.offset(10) +Honeybee.offset(10) ``` #### .sort `.sort` (or `.order`) sets sorting options for get and find requests: ```ruby -Kitty.sort(:name, :age) -Kitty.order(:name, :age) # alias method +Honeybee.sort(:name, :age) +Honeybee.order(:name, :age) # alias method ``` You can set descending sort order by appending either `!` or `__desc` to a sort attribute (defaults to ascending order): ```ruby -Kitty.sort(:name, :age!) -Kitty.sort(:name, :age__desc) +Honeybee.sort(:name, :age!) +Honeybee.sort(:name, :age__desc) ``` #### .portal `.portal` (or `.includes`) sets the portals to fetch for get and find requests (this recognizes portals defined with `has_portal`): ```ruby -Kitty.portal(:toys) -Kitty.includes(:toys) # alias method +Honeybee.portal(:hives) +Honeybee.includes(:hives) # alias method ``` #### .query `.query` sets query conditions for a find request (and supports attributes as defined with `attributes`): ```ruby -Kitty.query(name: "Mr. Fluffers") -# JSON -> {"query": [{"CatName": "Mr. Fluffers"}]} +Honeybee.query(name: "Hutch") +# JSON -> {"query": [{"Bee Name": "Hutch"}]} ``` Passing multiple attributes to `.query` will group them in the same JSON object: ```ruby -Kitty.query(name: "Mr. Fluffers", age: 4) -# JSON -> {"query": [{"CatName": "Foo", "CatAge": 4}]} +Honeybee.query(name: "Hutch", age: 4) +# JSON -> {"query": [{"Bee Name": "Hutch", "Bee Age": 4}]} ``` Calling `.query` multiple times or passing it multiple hashes creates separate JSON objects (so you can define OR queries): ```ruby -Kitty.query(name: "Mr. Fluffers").query(name: "Coronel Chai Latte") -Kitty.query({ name: "Mr. Fluffers" }, { name: "Coronel Chai Latte" }) -# JSON -> {"query": [{"CatName": "Mr. Fluffers"}, {"CatName": "Coronel Chai Latte"}]} +Honeybee.query(name: "Hutch").query(name: "Maya") +Honeybee.query({ name: "Hutch" }, { name: "Maya" }) +# JSON -> {"query": [{"Bee Name": "Hutch"}, {"Bee Name": "Maya"}]} ``` #### .omit `.omit` works like `.query` but excludes matches: ```ruby -Kitty.omit(name: "Captain Whiskers") -# JSON -> {"query": [{"CatName": "Captain Whiskers", "omit": "true"}]} +Honeybee.omit(name: "Hutch") +# JSON -> {"query": [{"Bee Name": "Hutch", "omit": "true"}]} ``` You can get the same effect by passing `omit: true` to `.query`: ```ruby -Kitty.query(name: "Captain Whiskers", omit: true) -# JSON -> {"query": [{"CatName": "Captain Whiskers", "omit": "true"}]} +Honeybee.query(name: "Hutch", omit: true) +# JSON -> {"query": [{"Bee Name": "Hutch", "omit": "true"}]} ``` #### Other notes on querying You can chain all query methods together: ```ruby -Kitty.limit(10).offset(20).sort(:name, :age!).portal(:toys).query(name: "Mr. Fluffers") +Honeybee.limit(10).offset(20).sort(:name, :age!).portal(:hives).query(name: "Hutch") ``` You can also set default values for limit and sort on the class: ```ruby -Kitty.default_limit = 1000 -Kitty.default_sort = [:name, :age!] +Honeybee.default_limit = 1000 +Honeybee.default_sort = [:name, :age!] ``` Calling any `Enumerable` method on the resulting scope object will trigger a server request, so you can treat the scope as a collection: ```ruby -Kitty.limit(10).sort(:name).each { |kitty| ... } +Honeybee.limit(10).sort(:name).each { |bee| ... } ``` If you want to explicitly run the request instead you can use `.find_some` on the scope object: ```ruby -Kitty.limit(10).sort(:name).find_some # => [<Kitty...>, ...] +Honeybee.limit(10).sort(:name).find_some # => [<Honeybee...>, ...] ``` If you want just a single result you can use `.find_one` instead (this will force `.limit(1)`): ```ruby -Kitty.query(name: "Mr. Fluffers").find_one # => <Kitty...> +Honeybee.query(name: "Hutch").find_one # => <Honeybee...> ``` NOTE: If you know the id of the record you should use `.find(id)` instead of `.query(id: id).find_one` (so that the request is sent as `GET ../:layout/records/:id` instead of `POST ../:layout/_find`). ```ruby -Kitty.find(89) # => <Kitty...> +Honeybee.find(89) # => <Honeybee...> ``` ### Container fields You can define container fields on your model class with `container`: ```ruby -class Kitty < FmRest::Spyke::Base - container :photo, field_name: "Vet Card Photo ID" +class Honeybee < FmRest::Spyke::Base + container :photo, field_name: "Beehive Photo ID" end ``` `:field_name` specifies the original field in the FM layout and is optional, if not given it will default to the name of your attribute (just `:photo` in this @@ -536,17 +530,17 @@ addition to the `container` definition.) This will provide you with the following instance methods: ```ruby -kitty = Kitty.new +bee = Honeybee.new -kitty.photo.url # The URL of the container file on the FileMaker server +bee.photo.url # The URL of the container file on the FileMaker server -kitty.photo.download # Download the contents of the container as an IO object +bee.photo.download # Download the contents of the container as an IO object -kitty.photo.upload(filename_or_io) # Upload a file to the container +bee.photo.upload(filename_or_io) # Upload a file to the container ``` `upload` also accepts an options hash with the following options: * `:repetition` - Sets the field repetition @@ -573,16 +567,16 @@ password: "abc123", log: true } # Or in your model -class LoggyKitty < FmRest::Spyke::Base +class LoggyBee < FmRest::Spyke::Base self.fmrest_config = { host: "example.com", database: "My Database", - username: "z3r0c00l", - password: "abc123", + username: "...", + password: "...", log: true } end ``` @@ -591,10 +585,10 @@ If you need to set up more complex logging for your models can use the `faraday` block inside your class to inject your own logger middleware into the Faraday connection, e.g.: ```ruby -class LoggyKitty < FmRest::Spyke::Base +class LoggyBee < FmRest::Spyke::Base faraday do |conn| conn.response :logger, MyApp.logger, bodies: true end end ```