The definitive guide to grails second edition - phần 7 docx

58 351 0
The definitive guide to grails second edition - phần 7 docx

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

CHAPTER 13 ■ PLUGINS 391 can then provide additional named URLs of SVN repositories used for discovery and distribu- tion. Listing 13-32 presents an example of configuring an additional plugin repository. Listing 13-32. Configuring Additional Plugin Repositories grails.plugin.repos.discovery.myRepository="http://foo.bar.com" grails.plugin.repos.distrubtion.myRepository="https://foo.bar.com" Notice in Listing 13-28 how Grails groups repositories under discovery and distribution. The URLs under discovery are used by the list-plugins, install-plugin, and plug-info commands discussed in the section on “Plugin Installation” to produce the plugin list that is presented to the user. The URLs under distribution are used by the release-plugin com- mand, as discussed in the previous section. By default, the release-plugin command will always try to publish to the Grails central repository. To tell the release-plugin command to publish to one of the repositories config- ured as in Listing 13-32, you need to add the name of the repository as an argument to the release-plugin command. For example: $ grails release-plugin -repository=myRepository And with that, we’ve reached the end of this tour of the plugin system. As you can imagine, you can take advantage of the plugin system in many different ways. In this section, we’ve touched on some ideas for plugins such as the simple-cache plugin and the Quartz plugin, but we think the plugin system is such a critical part of the Grails ecosystem that the lessons learned in this chapter should be put to further use. In the next section, you’ll be applying what you’ve learned so far to create two new plugins for the gTunes application. Along the way, you’ll discover how Grails’ plugins can be used as both a way to extend the functionality of an existing application and as a way to effectively modularize your codebase. Plugins in Action So, you’ve learned what plugins are and the basics of creating plugins. It is now time to put that knowledge to work by developing a couple of plugins for the gTunes application. The first one you’re going to create is a plugin that makes the album art service and tag library you devel- oped in Chapter 8 into a reusable plugin. This is a perfect example of developing a plugin to add functionality and enhance behavior. Plugins to Add Behavior To start with, run the create-plugin command to create the basis of an album-art plugin: $ grails create-plugin album-art 392 CHAPTER 13 ■ PLUGINS The next step is to move the AlbumArtService.groovy file and the AlbumArtTagLib.groovy file into the newly created plugin project. Once this is done, your plugin should be structured like Figure 13-3. Figure 13-3. The structure of the album-art plugin Of course, the AlbumArtService relies heavily on the Amazon web services library, so you should move those from the application into the plugin too. Figure 13-4 shows the lib direc- tory with the necessary JAR files in place. Also, don’t forget to move the two tests that provide coverage for the AlbumArtService and AlbumArtTagLib from the application into the plugin. As mentioned previously, the great thing about plugins is that they can be developed and tested separately, which makes them useful for larger projects with multiple developers. With the AlbumArtServiceTests and AlbumArtTagLibTests test cases included in the album-art plugin, you can now immediately test whether your plugin is working by running the test-app command: $ grails test-app CHAPTER 13 ■ PLUGINS 393 Figure 13-4. The album-art plugin’s dependencies With the tests passing, you can add the plugin metadata to the plugin descriptor that describes what this plugin is all about. Listing 13-33 shows the updated plugin descriptor with the metadata provided. Listing 13-33. Providing Metadata to the album-art Plugin class AlbumArtGrailsPlugin { def version = 0.1 def author = "Graeme Rocher" def authorEmail = "graeme@g2one.com" def title = "Album art look-up plugin" def description = 'A plug-in that provides facilities to look-up album art' } One thing to consider is that when you developed the AlbumArtService in Chapter 8, it was designed to work in conjunction with an albumArtCache that used Ehcache provided by the application’s grails-app/conf/spring/resources.groovy file. One solution to this would be to update the doWithSpring of the AlbumArtGrailsPlugin descriptor, as shown in Listing 13-34. 394 CHAPTER 13 ■ PLUGINS Listing 13-34. Providing the albumArtCache with doWithSpring class AlbumArtGrailsPlugin { def version = 0.1 def doWithSpring = { albumArtCache(org.springframework.cache.ehcache.EhCacheFactoryBean) { timeToLive = 300 } } } However, since you previously developed a simple-cache plugin earlier in the chapter, it makes a lot more sense to take advantage of it. To do so, let’s modify the dependsOn property on the album-art plugin descriptor, as shown in Listing 13-35. Listing 13-35. Using dependsOn to Depend on the simple-cache Plugin class AlbumArtGrailsPlugin { def dependsOn = [simpleCache:'0.1 > *'] } ■Tip When specifying dependencies, you need to use bean conventions instead of the hyphen-separated, lowercase name simple-cache. The reason for this Grails design decision is that a hyphen isn’t valid in a variable name or map key in Groovy unless you put quotes around it. To enable the ability to continue to test the album-art plugin in isolation, you can install the simple-cache plugin into the album-art plugin using the install-plugin command from the root of the album-art plugin directory: $ grails install-plugin /path/to/simple-cache/grails-simple-cache-0.1.zip When you package the album-art plugin, Grails will not include the simple-cache plugin within the album-art zip. It is your responsibility to ensure that when you install the album-art plugin into the target application, you install the simple-cache plugin first. If you don’t, you will get an error because Grails will be unable to resolve the album-art plugins’ dependency on the simple-cache plugin, unless the simple-cache plugin is available in one of the configured repositories. Moving on, you now need to update the album-art plugin to use the CacheService provided by the simple-cache plugin. Listing 13-36 shows the changes made to the AlbumArtService high- lighted in bold. CHAPTER 13 ■ PLUGINS 395 Listing 13-36. Updating the AlbumArtService to Use the simple-cache Plugin class AlbumArtService { def cacheService String getAlbumArt(String artist, String album) { def key = new AlbumArtKey(album:album, artist:artist) return cacheService.cacheOrReturn(key) { try { def request = new ItemSearchRequest() def response = client.itemSearch(request) // get the URL to the amazon image (if one was returned). return response.items[0].item[0].largeImage.URL } catch(Exception e) { log.error "Problem calling Amazon: ${e.message}", e return DEFAULT_ALBUM_ART_IMAGE } } } } The changes in Listing 13-36 will cause the tests for the AlbumArtService to fail with a NullPointerException because the cacheService is null within the context of the test. Instead of using a real implementation in the unit test, you can use duck typing to specify a mock implementation using Groovy’s Map literal syntax, as shown in Listing 13-37. Listing 13-37. Mocking the cacheService albumArtService.cacheService = [cacheOrReturn:{key, callable-> callable() }] Groovy allows maps, where the value of a given key is a closure, to act as if they are callable methods. In the example in Listing 13-37, by providing a cacheOrReturn key, you are able to mock the methods of the CacheService. To spice things up even further, you’re going to do a bit of metaprogramming, first by add- ing a getAlbumArt method to all controllers and second by allowing instances of the Album class from the gTunes application to retrieve their art simply by calling a getArt() method. The first case, in Listing 13-38, shows the necessary code, which just gets the AlbumArtService instance and adds a method to all controllers that delegates to the AlbumArtService. 396 CHAPTER 13 ■ PLUGINS Listing 13-38. Adding a getAlbumArt Method to All Controllers class AlbumArtGrailsPlugin { def doWithDynamicMethods = { ctx -> def albumArtService = ctx.getBean("albumArtService") application.controllerClasses *.metaClass *.getAlbumArt = { String artist, String album -> return albumArtService.getAlbumArt(artist, album) } } } Adding a getArt() method to the Album class is a little trickier, because the plugin doesn’t know anything about the Album class. So to implement this enhancement, you’ll search the GrailsApplication instance for a domain class called Album and, if it exists, add the getArt() method to it. Listing 13-39 shows the modifications to the doWithDynamicMethods plugin hook. Listing 13-39. Adding a getAlbumArt Method to All Controllers class AlbumArtGrailsPlugin { def doWithDynamicMethods = { ctx -> def albumClass = application.domainClasses.find { it.shortName == 'Album' } if(albumClass) { albumClass.metaClass.getArt ={-> albumArtService.getAlbumArt( delegate.artist?.name, delegate.title) } } } } Notice how within the body of the new getArt method you can use the closure delegate to obtain the artist and title. The delegate property of a closure, when used in this context, is equivalent to referring to this in a regular method. With the code in Listing 13-39 in place, you can now obtain the URL to an Album instance’s album art with the code shown in Listing 13-40. Listing 13-40. Using the getArt() Method to Obtain Album Art def album = Album.get(10) println "The art for this album is at ${album.art}" Note that, in Groovy, methods that follow bean conventions are accessible via the prop- erty access notation, so the expression album.art is equivalent to album.getArt(). And with that, you have completed the album-art plugin that can now be installed into any application CHAPTER 13 ■ PLUGINS 397 that has a requirement to look up album art. The gTunes application is one such application. However, before you can install the album-art plugin, you need to install the simple-cache plu- gin that the album-art plugin is dependent on into the gTunes application: $ grails install-plugin /simple-cache/grails-simple-cache-0.1.zip With that done, install the album-art plugin next: $ grails install-plugin /simple-cache/grails-album-art-0.1.zip Now you can start up the gTunes application, and it will behave exactly as before, except it is utilizing the album-art plugin’s functionality instead! One thing to note about the album-art plugin is that although it provides new functionality in the form of services, tag libraries, and new methods, it does not comprise an entire self-contained application. We’ll be looking at how you can achieve this in the next section. Plugins for Application Modularity As well as making it possible to extend the available APIs within a Grails application, plugins can also provide entire modules of application functionality. Many newcomers dismiss plugins as purely for plugin developers who are willing to jump into the core Grails APIs, but in fact, plugins are an extremely effective way to modularize your application. In this section, we’ll explain how you can create an entire application as a plugin that can be installed into the gTunes application. To keep things simple, you’ll tackle a very commonly demonstrated application in screen- casts and presentations around Grails: the blog. Yes, as with any self-respecting modern Web 2.0 application, the gTunes application needs a blog where the proprietors of the gTunes store can make big announcements about new music, events, and so on. Luckily, a simple blog takes about five minutes to implement in Grails, so it shouldn’t be too complicated. The first step is to run the create-plugin command to create the blog plugin: $ grails create-plugin blog This will create the blog plugin and associated BlogGrailsPlugin descriptor. You can populate the descriptor with some plugin metadata; Listing 13-41 shows a sample blog plugin descriptor. Listing 13-41. Adding Metadata to the blog Plugin class BlogGrailsPlugin { def version = 0.1 def author = "Graeme Rocher" def authorEmail = "graeme@g2one.com" def title = "A blogging plugin" def description = 'A plugin that provides a blog facility' } Now it’s time to create a domain class that models a blog post: $ grails create-domain-class com.g2one.blog.Post 398 CHAPTER 13 ■ PLUGINS After these two commands are complete, you should have a directory structure similar to that pictured in Figure 13-5. Figure 13-5. The Post domain class Thinking about the Post domain class for a moment, it’s going to have the obvious things like a title and a body, as well as a date posted. Putting this into practice, Listing 13-42 shows the Post domain class containing the necessary properties. Listing 13-42. The Post Domain Class package com.g2one.blog class Post { String title String body Date dateCreated Date lastUpdated static constraints = { title blank:false body type:"text", blank:false } } CHAPTER 13 ■ PLUGINS 399 Note that the Post domain class is using the property names dateCreated and lastUpdated to take advantage of Grails’ auto time stamping capabilities that were first discussed in Chapter 10. With an appropriate domain class in place, to help you get started, you can use scaffolding to quickly generate a controller and views for the Post domain class: $ grails generate-all com.g2one.blog.Post For this first revision of the blog plugin, you’re going to support the creation of new entries only; hence, you can remove the generated edit, update, and delete actions. In addition, you need to show only the first five posts; therefore, you can use the max parameter to the static list method of the Post class to specify that. Listing 13-43 shows the full code for the PostController. Listing 13-43. The PostController for the blog Plugin package com.g2one.blog class PostController { def index = { redirect(action:list,params:params) } def allowedMethods = [save:'POST'] def list = { [ postList: Post.list( max:5) ] } def create = { [post: new Post(params) ] } def save = { def post = new Post(params) if(!post.hasErrors() && post.save()) { flash.message = "Post ${post.id} created" redirect(action:list) } else { render(view:'create',model:[post:post]) } } } Now let’s move onto the views. In the case of the blog plugin, the list.gsp view is the most important because it will be responsible for showing each blog entry. However, Grails’ default scaffolding displays the list view as a table, which is not very useful in this case. You can correct that by modifying the list.gsp view to render a _post.gsp template instead. Listing 13-44 shows the updated list.gsp code. 400 CHAPTER 13 ■ PLUGINS Listing 13-44. The blog Plugin’s list.gsp View <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta name="layout" content="${params.layout ?: 'main'}" /> <title>Post List</title> </head> <body> <div class="nav"> <span class="menuButton"> <g:link class="create" action="create">New Post</g:link> </span> </div> <div class="blog"> <h1>${grailsApplication.config.blog.title ?: 'No Title'}</h1> <g:render plugin="blog" template="post" var="post" collection="${postList?.reverse()}" /> </div> </body> </html> There are a few key things to mention about the list.gsp view in Listing 13-44. First, note that when using the <g:render> tag to render a template in a plugin view, you must specify the plugin that this template belongs to; otherwise, Grails will attempt to resolve the template within the application it is installed into. Second, take note of the usage of the grailsApplication variable to specify the blog title: <h1>${grailsApplication.config.blog.title ?: 'No Title'}</h1> Here the implicit grailsApplication object is used to read a configuration setting from the grails-app/conf/Config.groovy file. If the setting called blog.title is specified in Config.groovy, then the view will use that. Hence, users of this plugin are able to configure the blog to their needs. An alternative approach to doing this would be to use the <g:message> tag, in which case the plugin user has to specify the message in the grails-app/i18n/ messages.properties file. The choice is up to you. Finally, take note of the HTML <meta> tag that dictates what layout the list.gsp uses: <meta name="layout" content="${params.layout ?: 'main'}" /> [...]... the blog plugin, run the following command: $ grails install-plugin fckeditor In addition to this, you need to update the BlogGrailsPlugin descriptor and add a dependsOn setting to ensure that when others install the blog plugin, FCKeditor is resolved too Listing 1 3-4 7 shows the dependsOn set appropriately Listing 1 3-4 7 Making the blog Plugin Depend on the fckeditor Plugin class BlogGrailsPlugin { def... since the FCKeditor plugin exists in the Grails central repository, the install-plugin command will automatically resolve the dependency Now it would be useful to configure the blog’s title using the grails- app/conf/Config.groovy file Remember, the blog.title setting allows you to customize the blog title; simply adding the following setting to Config.groovy will do the trick: // configuration for the. .. implement to facilitate authentication and authorization To get started with JSecurity, you have to install the plugin by running the install-plugin command, as shown in Listing 1 4-1 6 Listing 1 4-1 6 Running the install-plugin command $ grails install-plugin jsecurity Plugin jsecurity-0.2.1 installed Plug-in provides the following new scripts: -grails create-auth-controller grails create-db-realm... the blog plugin into to provide useful style information via CSS Speaking of installing the blog plugin into an application, it is time to do exactly that! First package up the blog plugin by running the package-plugin command: $ grails package-plugin Then navigate to the gTunes application, and use install-plugin to install the blog plugin: $ grails install-plugin /blog /grails- blog-0.1.zip Note how,... validate the user’s authentication token If the token is not valid, an IncorrectCredentialsException is thrown If all is well, the final thing to do is to return the user’s principal: return username And with that, you’ve completed the implementation of the authenticate method Listing 1 4-2 4 shows the full code listing from the authenticate method Listing 1 4-2 4 The authenticate Method def authenticate(authToken)... command will create one that uses GORM to store user information to the database • create-ldap-realm: This creates a realm that authenticates users against a configured LDAP server • quick-start: This combines the create-db-realm and create-auth-controller commands to set up JSecurity in a single command Authentication Realms Both the create-db-realm and create-ldap-realm classes set up a realm class... if there is a layout parameter within the params object, it will use that for the layout; otherwise, use the main layout The main layout will, of course, resolve to grails- app/views/layouts/main.gsp, but why the decision to allow customization via a parameter? The idea here is that the user of the plugin can very easily customize the layout of the blog through URL mappings For example, consider the. .. [fckeditor:'0.8 > *'] } With that done, let’s enable FCKeditor in create-gsp by using the tag provided by the fckeditor plugin Listing 1 3-4 8 shows the updated create.gsp file with the usage of the tag highlighted in bold You will notice the logical name printed when you ran the blog plugin with grails run-app Grails prints out a message such as this: Loading with installed plug-ins:... deleted too—you can always add editing later! Moving on to the create.gsp view, it too could use a little cleaning up Also, it would be nice to provide a rich text–editing capability for authoring the post One of the plugins available for Grails is the fckeditor plugin, which adds support for FCKeditor (http://www.fckeditor net/), a rich text–editing component To install the fckeditor plugin into the blog... name 3 For the “First Name” field, enter the text alert ('hello') 4 Click the “Register” button Figure 1 4-1 shows the form populated with the data from these steps Figure 1 4-1 Entering malicious data into the registration form When you click the “Register” button, you’ll see an alert box pop up with the message “hello.” The JavaScript you entered into the “First . always try to publish to the Grails central repository. To tell the release-plugin command to publish to one of the repositories config- ured as in Listing 1 3-3 2, you need to add the name of the repository. root of the album-art plugin directory: $ grails install-plugin /path /to/ simple-cache /grails- simple-cache-0.1.zip When you package the album-art plugin, Grails will not include the simple-cache. it. To enable the ability to continue to test the album-art plugin in isolation, you can install the simple-cache plugin into the album-art plugin using the install-plugin command from the

Ngày đăng: 13/08/2014, 08:21

Từ khóa liên quan

Tài liệu cùng người dùng

Tài liệu liên quan