Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Events
Videos
Audiobooks
Packt Hub
Free Learning
Arrow right icon
timer SALE ENDS IN
0 Days
:
00 Hours
:
00 Minutes
:
00 Seconds

How-To Tutorials

7019 Articles
article-image-creating-mobile-friendly-themes
Packt
15 Mar 2013
3 min read
Save for later

Creating mobile friendly themes

Packt
15 Mar 2013
3 min read
(For more resources related to this topic, see here.) Getting ready Before we start working on the code, we'll need to copy the basic gallery example code from the previous example and rename the folder and files to be pre fixed with mobile. After this is done, our folder and file structure should look like the following screenshot: How to do it... We'll start off with our galleria.mobile.js theme JavaScript file: (function($) { Galleria.addTheme({ name: 'mobile', author: 'Galleria How-to', css: 'galleria.mobile.css', defaults: { transition: 'fade', swipe: true, responsive: true }, init: function(options) { } }); }(jQuery)); The only difference here from our basic theme example is that we've enabled the swipe parameter for navigating images and the responsive parameter so we can use different styles for different device sizes. Then, we'll provide the additional CSS file using media queries to match only smartphones. Add the following rules in the already present code in the galleria. mobile.css file: /* for smartphones */ @media only screen and (max-width : 480px){ .galleria-stage, .galleria-thumbnails-container, .galleria-thumbnails-container, .galleria-image-nav, .galleria-info { width: 320px; } #galleria{ height: 300px; } .galleria-stage { max-height: 410px; } } Here, we're targeting any device size with a width less than 480 pixels. This should match smartphones in landscape and portrait mode. These styles will override the default styles when the width of the browser is less than 480 pixels. Then, we wire it up just like the previous theme example. Modify the galleria. mobile-example.html file to include the following code snippet for bootstrapping the gallery: <script> $(document).ready(function(){ Galleria.loadTheme('galleria.mobile.js'); Galleria.run('#galleria'); }); </script> We should now have a gallery that scales well for smaller device sizes, as shown in the following screenshot: How to do it... The responsive option tells Galleria to actively detect screen size changes and to redraw the gallery when they are detected. Then, our responsive CSS rules style the gallery differently for different device sizes. You can also provide additional rules so the gallery is styled differently when in portrait versus landscape mode. Galleria will detect when the device screen orientation has changed and apply the new styles accordingly. Media queries A very good list of media queries that can be used to target different devices for responsive web design is available at http://css-tricks.com/snippets/css/media-queriesfor-standard-devices/. Testing for mobile The easiest way to test the mobile theme is to simply resize the browser window to the size of the mobile device. This simulates the mobile device screen size and allows the use of standard web development tools that modern browsers provide. Integrating with existing sites In order for this to work effectively with existing websites, the existing styles will also have to play nicely with mobile devices. This means that an existing site, if trying to integrate a mobile gallery, will also need to have its own styles scale correctly to mobile devices. If this is not the case and the mobile devices switch to zoom-like navigation mode for the site, the mobile gallery styles won't ever kick in. Summary In this article we have seen how to create mobile friendly themes using responsive web design. Resources for Article : Further resources on this subject: jQuery Mobile: Organizing Information with List Views [Article] An Introduction to Rhomobile [Article] Creating and configuring a basic mobile application [Article]
Read more
  • 0
  • 0
  • 6933

article-image-creating-basic-javascript-plugin
Packt
17 Jan 2014
9 min read
Save for later

Creating a basic JavaScript plugin

Packt
17 Jan 2014
9 min read
(For more resources related to this topic, see here.) Getting started with an empty plugin To get started, create three files called manifest.xml, MyCompany.WebAccess.Plugin.debug.js, and MyCompany.WebAccess.Plugin.min.js. In the manifest.xml file, place the following XML: <WebAccess version="12.0"> <plugin name="MyCompany Plugin - Web Access" vendor="Gordon Beeming" moreinfo="http://31og.com" version="1.0"> <modules> <module namespace="MyCompany.WebAccess.Plugin" loadAfter="TFS.Agile.TaskBoard.View"/> <module namespace="MyCompany.WebAccess.Plugin" loadAfter="TFS.Agile.Boards.Controls"/> </modules> </plugin> </WebAccess> In the preceding code, once the plugin node has the attributes name, vendor, moreinfo, and version, we will be able to easily identify our plugin in the TFS Web Access admin area. Under the modules node, you will see that we have added two child module nodes. This informs TFS that we want to load our MyCompany.WebAccess.Plugin namespace after the TFS.Agile.TaskBoard.View and TFS.Agile.Boards.Controls namespaces, which are namespaces loaded on the task board and portfolio boards. You can get the base of this plugin from the sample code in the MyCompany.WebAccess.Plugin - Base.js file. If you have used the RequireJs module loader, you will notice that this syntax is very familiar. In the base code, you will see a bit of code like the following: TfsWebAccessPlugin.prototype.initialize = function () { // place code here to get started alert('MyCompany.WebAccess.Plugin is running'); }; This initialize method is where you start gaining control of what is happening in Web Access. Take all the code in the base code and place it in the debug.js file. Importing a plugin into TFS Web Access The first part of importing a plugin into TFS is to make sure that you have placed a minified version of your *.debug.js contents into your *.min.js file. Update the version of your plugin in the manifest.xml file, if required; for now, we will leave it at 1.0. Zip the three files we created; the name of this ZIP file doesn't make a difference to the usage of the plugin. Browse to the server's home page and then click on the Administer Server button in the top-right corner as shown in the following screenshot: The Administer Server Button Click on the Extensions tab and then click on Install . In the model window, click on browse to browse for the ZIP file you created with the contents of the plugin and then click on OK . You will now see that the plugin is visible in the extensions screen but is currently not enabled. Click on Enable and then on OK to enable it, as shown in the following screenshot: Web access extension when disabled When you navigate to any of the boards, you will see the alert that we placed in the initialize function. Setting up the debug mode We have just imported our plugin into TFS, and this was quite a long process. Although it is fine if we upload our plugin into an environment, when we have finished creating our plugin, it becomes very time consuming when we need to make changes to the plugin. You have to go through this whole process to see the changes. So, we will use some tricks that will help us debug our extension. Enabling the Script Debug Mode Navigate to the TFS URL with _diagnostics appended at the end, that is, http://gordon-pc:8080/tfs/_diagnostics. On this page, we will click on the Script Debug Mode link, which should currently be disabled. This should also switch Client Trace Point Collector to Enabled , as shown in the following screenshot: TFS diagnostics settings This will now make TFS use the debug.js file instead of the min.js file. You will also see more requests for JavaScript files as each file is now streamed separately instead of being bundled together for better load performance. For this reason, it is probably very clear that this should not be enabled on a production environment. Configuring a Fiddler AutoResponder rule The next part is to configure Fiddler to automatically respond to any requests for your plugin from the server with your local debug.js file. You can download Fiddler from http://fiddler2.com/. We are going to use Fiddler to intercept the request for our plugins' JavaScript file from TFS and use our local version of the plugin. The first step would be to start up Fiddler and make sure you can see the request for the MyCompany.WebAccess.Plugin.js file, which should have a URL similar to http://gordon-pc:8080/tfs/_static/tfs/12/_scripts/TFS/debug//tfs/_plugins/1957/MyCompany.WebAccess.Plugin.js. In Fiddler, switch to the AutoResponder tab and check Enable automatic responses and Unmatched requests passthrough . Now click on Add Rule and in the Rule editor menu, use the regex:http://gordon-pc:8080/tfs/_static/tfs/12/_scripts/TFS/.+/MyCompany.WebAccess.Plugin.js rule; this will put a wildcard on the mode and plugin ID that is being used currently. In the second textbox, write down the full location of the debug.js file for this plugin and then click on Save . Add a second rule in the same pattern, but this time in the second textbox, use header:CachControl=no-cache and click on Save . You should see something similar to the following screenshot in Fiddler: Fiddler AutoResponder rule added This will now make Web Access use your local debug.js file for all requests for the plugin in TFS. To try this out, go to the debug.js file, change the alert to we have added debugging , and save the file. Refresh the board, and you will see that without any additional effort, the alert changed. Adding information to display work items We will be going through some of the snippets that make a difference and are crucial to our plugin working correctly. The easiest way to make use of these types of plugins is to change the HTML based on the information available in the HTML; this is useful for small changes, such as displaying the ID of work items on the work item cards on the boards. For this, you would, on initialization of your plugin, use the setInterval function in JavaScript and call the following function every 500 milliseconds: function TaskBoardFunctions() { //replace IDs for tasks $("#taskboard-table .tbTile").each(function () { var id = $(this).attr("id"); id = id.split('-')[1]; $(this).find(".tbTileContent .witTitle").html("<span style='font-weight:bold;'>" + id + "</span> - " + $(this).find(".witTitle").html()); }); //replace IDs for tasks $("#taskboard-table .taskboard-row .taskboard-parent").each(function () { var id = $(this).attr("id"); if (id != undefined) { id = id.split('_')[1]; id = id.substring(1); $(this).find(".witTitle").html("<span style='font-weight:bold;'>" + id + "</span> - " + $(this).find(".witTitle").html()); } }); } This function just looks for all work items on the page using the IDs that are specified in the attributes in the HTML elements to add the IDs to the UI. A better way to do this would be to make use of the events in the API, and only make modifications to the displayed information when necessary. You would still use something similar to the preceding code for your initial loading to go through the board, and set all the information you would want to display; however, you would reply on the events to do any further updates. So, in this case, we would use the preceding code to scan for all the IDs on the page and then pass that through to a method, such as the following one, which will query the work item store. TFS has a configurable value that tells us the number of results that can be returned per query through the JavaScript API, and for this reason, we query 100 work items at a time; however, you can change this if it's not applicable to your plugin. Core.prototype.loadWorkItemsWork = function (idsToFetch, onComplete, that) { var takeAmount = 100; if (takeAmount >= idsToFetch.length) { takeAmount = idsToFetch.length; } if (takeAmount > 0) { that.WorkItemManager.store.beginPageWorkItems(idsToFetch.splice(0,takeAmount), [ "System.Id", "System.State" ], function (payload) { that.loadWorkItemsWork(idsToFetch, onComplete, that); $.each(payload.rows, function (index, row) { onComplete(index, row, that); }); }, function (err) { that.loadWorkItemsWork(idsToFetch, onComplete, that); alert(err); }); } }; As you can see, we are querying the work item store for the ID and the state of each work item on the page. We are then passing this off to an onComplete function that is using jQuery to find the elements by ID. We then alter the displayed information to show the ID, and on the task board to show the state of the requirement. If you use all the sample code and upload it into TFS, you will see a portfolio board like the one shown in the following screenshot: IDs on the portfolio board And on the task board, you will see the following screenshot: IDs and State on task board You can see that the tasks have IDs on them, which are the same as the portfolio boards, and the requirements listed on the left have IDs and their current states. Summary In this article, we covered customizing the TFS dashboard to display information that helps us find out a team's current status by pinning queries, build status, and recent changes to the source code. We then made some changes to the columns displayed in the portfolio backlog and the quick add panel. We finished off by going through what is required to create a TFS Web Access plugin. Resources for Article : Further resources on this subject: Ensuring Quality for Unit Testing with Microsoft Visual Studio 2010 [Article] Team Foundation Server 2012 [Article] The Command Line [Article]
Read more
  • 0
  • 0
  • 6919

Packt
25 Mar 2015
35 min read
Save for later

Fun with Sprites – Sky Defense

Packt
25 Mar 2015
35 min read
This article is written by Roger Engelbert, the author of Cocos2d-x by Example: Beginner's Guide - Second Edition. Time to build our second game! This time, you will get acquainted with the power of actions in Cocos2d-x. I'll show you how an entire game could be built just by running the various action commands contained in Cocos2d-x to make your sprites move, rotate, scale, fade, blink, and so on. And you can also use actions to animate your sprites using multiple images, like in a movie. So let's get started. In this article, you will learn: How to optimize the development of your game with sprite sheets How to use bitmap fonts in your game How easy it is to implement and run actions How to scale, rotate, swing, move, and fade out a sprite How to load multiple .png files and use them to animate a sprite How to create a universal game with Cocos2d-x (For more resources related to this topic, see here.) The game – sky defense Meet our stressed-out city of...your name of choice here. It's a beautiful day when suddenly the sky begins to fall. There are meteors rushing toward the city and it is your job to keep it safe. The player in this game can tap the screen to start growing a bomb. When the bomb is big enough to be activated, the player taps the screen again to detonate it. Any nearby meteor will explode into a million pieces. The bigger the bomb, the bigger the detonation, and the more meteors can be taken out by it. But the bigger the bomb, the longer it takes to grow it. But it's not just bad news coming down. There are also health packs dropping from the sky and if you allow them to reach the ground, you'll recover some of your energy. The game settings This is a universal game. It is designed for the iPad retina screen and it will be scaled down to fit all the other screens. The game will be played in landscape mode, and it will not need to support multitouch. The start project The command line I used was: cocos new SkyDefense -p com.rengelbert.SkyDefense -l cpp -d /Users/rengelbert/Desktop/SkyDefense In Xcode you must set the Devices field in Deployment Info to Universal, and the Device Family field is set to Universal. And in RootViewController.mm, the supported interface orientation is set to Landscape. The game we are going to build requires only one class, GameLayer.cpp, and you will find that the interface for this class already contains all the information it needs. Also, some of the more trivial or old-news logic is already in place in the implementation file as well. But I'll go over this as we work on the game. Adding screen support for a universal app Now things get a bit more complicated as we add support for smaller screens in our universal game, as well as some of the most common Android screen sizes. So open AppDelegate.cpp. Inside the applicationDidFinishLaunching method, we now have the following code: auto screenSize = glview->getFrameSize(); auto designSize = Size(2048, 1536); glview->setDesignResolutionSize(designSize.width, designSize.height, ResolutionPolicy::EXACT_FIT); std::vector<std::string> searchPaths; if (screenSize.height > 768) {    searchPaths.push_back("ipadhd");    director->setContentScaleFactor(1536/designSize.height); } else if (screenSize.height > 320) {    searchPaths.push_back("ipad");    director->setContentScaleFactor(768/designSize.height); } else {    searchPaths.push_back("iphone");    director->setContentScaleFactor(380/designSize.height); } auto fileUtils = FileUtils::getInstance(); fileUtils->setSearchPaths(searchPaths); Once again, we tell our GLView object (our OpenGL view) that we designed the game for a certain screen size (the iPad retina screen) and once again, we want our game screen to resize to match the screen on the device (ResolutionPolicy::EXACT_FIT). Then we determine where to load our images from, based on the device's screen size. We have art for iPad retina, then for regular iPad which is shared by iPhone retina, and for the regular iPhone. We end by setting the scale factor based on the designed target. Adding background music Still inside AppDelegate.cpp, we load the sound files we'll use in the game, including a background.mp3 (courtesy of Kevin MacLeod from incompetech.com), which we load through the command: auto audioEngine = SimpleAudioEngine::getInstance(); audioEngine->preloadBackgroundMusic(fileUtils->fullPathForFilename("background.mp3").c_str()); We end by setting the effects' volume down a tad: //lower playback volume for effects audioEngine->setEffectsVolume(0.4f); For background music volume, you must use setBackgroundMusicVolume. If you create some sort of volume control in your game, these are the calls you would make to adjust the volume based on the user's preference. Initializing the game Now back to GameLayer.cpp. If you take a look inside our init method, you will see that the game initializes by calling three methods: createGameScreen, createPools, and createActions. We'll create all our screen elements inside the first method, and then create object pools so we don't instantiate any sprite inside the main loop; and we'll create all the main actions used in our game inside the createActions method. And as soon as the game initializes, we start playing the background music, with its should loop parameter set to true: SimpleAudioEngine::getInstance()-   >playBackgroundMusic("background.mp3", true); We once again store the screen size for future reference, and we'll use a _running Boolean for game states. If you run the game now, you should only see the background image: Using sprite sheets in Cocos2d-x A sprite sheet is a way to group multiple images together in one image file. In order to texture a sprite with one of these images, you must have the information of where in the sprite sheet that particular image is found (its rectangle). Sprite sheets are often organized in two files: the image one and a data file that describes where in the image you can find the individual textures. I used TexturePacker to create these files for the game. You can find them inside the ipad, ipadhd, and iphone folders inside Resources. There is a sprite_sheet.png file for the image and a sprite_sheet.plist file that describes the individual frames inside the image. This is what the sprite_sheet.png file looks like: Batch drawing sprites In Cocos2d-x, sprite sheets can be used in conjunction with a specialized node, called SpriteBatchNode. This node can be used whenever you wish to use multiple sprites that share the same source image inside the same node. So you could have multiple instances of a Sprite class that uses a bullet.png texture for instance. And if the source image is a sprite sheet, you can have multiple instances of sprites displaying as many different textures as you could pack inside your sprite sheet. With SpriteBatchNode, you can substantially reduce the number of calls during the rendering stage of your game, which will help when targeting less powerful systems, though not noticeably in more modern devices. Let me show you how to create a SpriteBatchNode. Time for action – creating SpriteBatchNode Let's begin implementing the createGameScreen method in GameLayer.cpp. Just below the lines that add the bg sprite, we instantiate our batch node: void GameLayer::createGameScreen() {   //add bg auto bg = Sprite::create("bg.png"); ...   SpriteFrameCache::getInstance()-> addSpriteFramesWithFile("sprite_sheet.plist"); _gameBatchNode = SpriteBatchNode::create("sprite_sheet.png"); this->addChild(_gameBatchNode); In order to create the batch node from a sprite sheet, we first load all the frame information described by the sprite_sheet.plist file into SpriteFrameCache. And then we create the batch node with the sprite_sheet.png file, which is the source texture shared by all sprites added to this batch node. (The background image is not part of the sprite sheet, so it's added separately before we add _gameBatchNode to GameLayer.) Now we can start putting stuff inside _gameBatchNode. First, the city: for (int i = 0; i < 2; i++) { auto sprite = Sprite::createWithSpriteFrameName   ("city_dark.png");    sprite->setAnchorPoint(Vec2(0.5,0)); sprite->setPosition(_screenSize.width * (0.25f + i * 0.5f),0)); _gameBatchNode->addChild(sprite, kMiddleground); sprite = Sprite::createWithSpriteFrameName ("city_light.png"); sprite->setAnchorPoint(Vec2(0.5,0)); sprite->setPosition(Vec2(_screenSize.width * (0.25f + i * 0.5f), _screenSize.height * 0.1f)); _gameBatchNode->addChild(sprite, kBackground); } Then the trees: //add trees for (int i = 0; i < 3; i++) { auto sprite = Sprite::createWithSpriteFrameName("trees.png"); sprite->setAnchorPoint(Vec2(0.5f, 0.0f)); sprite->setPosition(Vec2(_screenSize.width * (0.2f + i * 0.3f),0)); _gameBatchNode->addChild(sprite, kForeground);   } Notice that here we create sprites by passing it a sprite frame name. The IDs for these frame names were loaded into SpriteFrameCache through our sprite_sheet.plist file. The screen so far is made up of two instances of city_dark.png tiling at the bottom of the screen, and two instances of city_light.png also tiling. One needs to appear on top of the other and for that we use the enumerated values declared in GameLayer.h: enum { kBackground, kMiddleground, kForeground }; We use the addChild( Node, zOrder) method to layer our sprites on top of each other, using different values for their z order. So for example, when we later add three sprites showing the trees.png sprite frame, we add them on top of all previous sprites using the highest value for z that we find in the enumerated list, which is kForeground. Why go through the trouble of tiling the images and not using one large image instead, or combining some of them with the background image? Because I wanted to include the greatest number of images possible inside the one sprite sheet, and have that sprite sheet to be as small as possible, to illustrate all the clever ways you can use and optimize sprite sheets. This is not necessary in this particular game. What just happened? We began creating the initial screen for our game. We are using a SpriteBatchNode to contain all the sprites that use images from our sprite sheet. So SpriteBatchNode behaves as any node does—as a container. And we can layer individual sprites inside the batch node by manipulating their z order. Bitmap fonts in Cocos2d-x The Cocos2d-x Label class has a static create method that uses bitmap images for the characters. The bitmap image we are using here was created with the program GlyphDesigner, and in essence, it works just as a sprite sheet does. As a matter of fact, Label extends SpriteBatchNode, so it behaves just like a batch node. You have images for all individual characters you'll need packed inside a PNG file (font.png), and then a data file (font.fnt) describing where each character is. The following screenshot shows how the font sprite sheet looks like for our game: The difference between Label and a regular SpriteBatchNode class is that the data file also feeds the Label object information on how to write with this font. In other words, how to space out the characters and lines correctly. The Label objects we are using in the game are instantiated with the name of the data file and their initial string value: _scoreDisplay = Label::createWithBMFont("font.fnt", "0"); And the value for the label is changed through the setString method: _scoreDisplay->setString("1000"); Just as with every other image in the game, we also have different versions of font.fnt and font.png in our Resources folders, one for each screen definition. FileUtils will once again do the heavy lifting of finding the correct file for the correct screen. So now let's create the labels for our game. Time for action – creating bitmap font labels Creating a bitmap font is somewhat similar to creating a batch node. Continuing with our createGameScreen method, add the following lines to the score label: _scoreDisplay = Label::createWithBMFont("font.fnt", "0"); _scoreDisplay->setAnchorPoint(Vec2(1,0.5)); _scoreDisplay->setPosition(Vec2   (_screenSize.width * 0.8f, _screenSize.height * 0.94f)); this->addChild(_scoreDisplay); And then add a label to display the energy level, and set its horizontal alignment to Right: _energyDisplay = Label::createWithBMFont("font.fnt", "100%", TextHAlignment::RIGHT); _energyDisplay->setPosition(Vec2   (_screenSize.width * 0.3f, _screenSize.height * 0.94f)); this->addChild(_energyDisplay); Add the following line for an icon that appears next to the _energyDisplay label: auto icon = Sprite::createWithSpriteFrameName ("health_icon.png"); icon->setPosition( Vec2(_screenSize.   width * 0.15f, _screenSize.height * 0.94f) ); _gameBatchNode->addChild(icon, kBackground); What just happened? We just created our first bitmap font object in Cocos2d-x. Now let's finish creating our game's sprites. Time for action – adding the final screen sprites The last sprites we need to create are the clouds, the bomb and shockwave, and our game state messages. Back to the createGameScreen method, add the clouds to the screen: for (int i = 0; i < 4; i++) { float cloud_y = i % 2 == 0 ? _screenSize.height * 0.4f : _screenSize.height * 0.5f; auto cloud = Sprite::createWithSpriteFrameName("cloud.png"); cloud->setPosition(Vec2 (_screenSize.width * 0.1f + i * _screenSize.width * 0.3f, cloud_y)); _gameBatchNode->addChild(cloud, kBackground); _clouds.pushBack(cloud); } Create the _bomb sprite; players will grow when tapping the screen: _bomb = Sprite::createWithSpriteFrameName("bomb.png"); _bomb->getTexture()->generateMipmap(); _bomb->setVisible(false);   auto size = _bomb->getContentSize();   //add sparkle inside bomb sprite auto sparkle = Sprite::createWithSpriteFrameName("sparkle.png"); sparkle->setPosition(Vec2(size.width * 0.72f, size.height *   0.72f)); _bomb->addChild(sparkle, kMiddleground, kSpriteSparkle);   //add halo inside bomb sprite auto halo = Sprite::createWithSpriteFrameName   ("halo.png"); halo->setPosition(Vec2(size.width * 0.4f, size.height *   0.4f)); _bomb->addChild(halo, kMiddleground, kSpriteHalo); _gameBatchNode->addChild(_bomb, kForeground); Then create the _shockwave sprite that appears after the _bomb goes off: _shockWave = Sprite::createWithSpriteFrameName ("shockwave.png"); _shockWave->getTexture()->generateMipmap(); _shockWave->setVisible(false); _gameBatchNode->addChild(_shockWave); Finally, add the two messages that appear on the screen, one for our intro state and one for our gameover state: _introMessage = Sprite::createWithSpriteFrameName ("logo.png"); _introMessage->setPosition(Vec2   (_screenSize.width * 0.5f, _screenSize.height * 0.6f)); _introMessage->setVisible(true); this->addChild(_introMessage, kForeground);   _gameOverMessage = Sprite::createWithSpriteFrameName   ("gameover.png"); _gameOverMessage->setPosition(Vec2   (_screenSize.width * 0.5f, _screenSize.height * 0.65f)); _gameOverMessage->setVisible(false); this->addChild(_gameOverMessage, kForeground); What just happened? There is a lot of new information regarding sprites in the previous code. So let's go over it carefully: We started by adding the clouds. We put the sprites inside a vector so we can move the clouds later. Notice that they are also part of our batch node. Next comes the bomb sprite and our first new call: _bomb->getTexture()->generateMipmap(); With this we are telling the framework to create antialiased copies of this texture in diminishing sizes (mipmaps), since we are going to scale it down later. This is optional of course; sprites can be resized without first generating mipmaps, but if you notice loss of quality in your scaled sprites, you can fix that by creating mipmaps for their texture. The texture must have size values in so-called POT (power of 2: 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, and so on). Textures in OpenGL must always be sized this way; when they are not, Cocos2d-x will do one of two things: it will either resize the texture in memory, adding transparent pixels until the image reaches a POT size, or stop the execution on an assert. With textures used for mipmaps, the framework will stop execution for non-POT textures. I add the sparkle and the halo sprites as children to the _bomb sprite. This will use the container characteristic of nodes to our advantage. When I grow the bomb, all its children will grow with it. Notice too that I use a third parameter to addChild for halo and sparkle: bomb->addChild(halo, kMiddleground, kSpriteHalo); This third parameter is an integer tag from yet another enumerated list declared in GameLayer.h. I can use this tag to retrieve a particular child from a sprite as follows: auto halo = (Sprite *)   bomb->getChildByTag(kSpriteHalo); We now have our game screen in place: Next come object pools. Time for action – creating our object pools The pools are just vectors of objects. And here are the steps to create them: Inside the createPools method, we first create a pool for meteors: void GameLayer::createPools() { int i; _meteorPoolIndex = 0; for (i = 0; i < 50; i++) { auto sprite = Sprite::createWithSpriteFrameName("meteor.png"); sprite->setVisible(false); _gameBatchNode->addChild(sprite, kMiddleground, kSpriteMeteor); _meteorPool.pushBack(sprite); } Then we create an object pool for health packs: _healthPoolIndex = 0; for (i = 0; i < 20; i++) { auto sprite = Sprite::createWithSpriteFrameName("health.png"); sprite->setVisible(false); sprite->setAnchorPoint(Vec2(0.5f, 0.8f)); _gameBatchNode->addChild(sprite, kMiddleground, kSpriteHealth); _healthPool.pushBack(sprite); } We'll use the corresponding pool index to retrieve objects from the vectors as the game progresses. What just happened? We now have a vector of invisible meteor sprites and a vector of invisible health sprites. We'll use their respective pool indices to retrieve these from the vector as needed as you'll see in a moment. But first we need to take care of actions and animations. With object pools, we reduce the number of instantiations during the main loop, and it allows us to never destroy anything that can be reused. But if you need to remove a child from a node, use ->removeChild or ->removeChildByTag if a tag is present. Actions in a nutshell If you remember, a node will store information about position, scale, rotation, visibility, and opacity of a node. And in Cocos2d-x, there is an Action class to change each one of these values over time, in effect animating these transformations. Actions are usually created with a static method create. The majority of these actions are time-based, so usually the first parameter you need to pass an action is the time length for the action. So for instance: auto fadeout = FadeOut::create(1.0f); This creates a fadeout action that will take one second to complete. You can run it on a sprite, or node, as follows: mySprite->runAction(fadeout); Cocos2d-x has an incredibly flexible system that allows us to create any combination of actions and transformations to achieve any effect we desire. You may, for instance, choose to create an action sequence (Sequence) that contains more than one action; or you can apply easing effects (EaseIn, EaseOut, and so on) to your actions. You can choose to repeat an action a certain number of times (Repeat) or forever (RepeatForever); and you can add callbacks to functions you want called once an action is completed (usually inside a Sequence action). Time for action – creating actions with Cocos2d-x Creating actions with Cocos2d-x is a very simple process: Inside our createActions method, we will instantiate the actions we can use repeatedly in our game. Let's create our first actions: void GameLayer::createActions() { //swing action for health drops auto easeSwing = Sequence::create( EaseInOut::create(RotateTo::create(1.2f, -10), 2), EaseInOut::create(RotateTo::create(1.2f, 10), 2), nullptr);//mark the end of a sequence with a nullptr _swingHealth = RepeatForever::create( (ActionInterval *) easeSwing ); _swingHealth->retain(); Actions can be combined in many different forms. Here, the retained _swingHealth action is a RepeatForever action of Sequence that will rotate the health sprite first one way, then the other, with EaseInOut wrapping the RotateTo action. RotateTo takes 1.2 seconds to rotate the sprite first to -10 degrees and then to 10. And the easing has a value of 2, which I suggest you experiment with to get a sense of what it means visually. Next we add three more actions: //action sequence for shockwave: fade out, callback when //done _shockwaveSequence = Sequence::create( FadeOut::create(1.0f), CallFunc::create(std::bind(&GameLayer::shockwaveDone, this)), nullptr); _shockwaveSequence->retain();   //action to grow bomb _growBomb = ScaleTo::create(6.0f, 1.0); _growBomb->retain();   //action to rotate sprites auto rotate = RotateBy::create(0.5f , -90); _rotateSprite = RepeatForever::create( rotate ); _rotateSprite->retain(); First, another Sequence. This will fade out the sprite and call the shockwaveDone function, which is already implemented in the class and turns the _shockwave sprite invisible when called. The last one is a RepeatForever action of a RotateBy action. In half a second, the sprite running this action will rotate -90 degrees and will do that again and again. What just happened? You just got your first glimpse of how to create actions in Cocos2d-x and how the framework allows for all sorts of combinations to accomplish any effect. It may be hard at first to read through a Sequence action and understand what's happening, but the logic is easy to follow once you break it down into its individual parts. But we are not done with the createActions method yet. Next come sprite animations. Animating a sprite in Cocos2d-x The key thing to remember is that an animation is just another type of action, one that changes the texture used by a sprite over a period of time. In order to create an animation action, you need to first create an Animation object. This object will store all the information regarding the different sprite frames you wish to use in the animation, the length of the animation in seconds, and whether it loops or not. With this Animation object, you then create a Animate action. Let's take a look. Time for action – creating animations Animations are a specialized type of action that require a few extra steps: Inside the same createActions method, add the lines for the two animations we have in the game. First, we start with the animation that shows an explosion when a meteor reaches the city. We begin by loading the frames into an Animation object: auto animation = Animation::create(); int i; for(i = 1; i <= 10; i++) { auto name = String::createWithFormat("boom%i.png", i); auto frame = SpriteFrameCache::getInstance()->getSpriteFrameByName(name->getCString()); animation->addSpriteFrame(frame); } Then we use the Animation object inside a Animate action: animation->setDelayPerUnit(1 / 10.0f); animation->setRestoreOriginalFrame(true); _groundHit = Sequence::create(    MoveBy::create(0, Vec2(0,_screenSize.height * 0.12f)),    Animate::create(animation),    CallFuncN::create(CC_CALLBACK_1(GameLayer::animationDone, this)), nullptr); _groundHit->retain(); The same steps are repeated to create the other explosion animation used when the player hits a meteor or a health pack. animation = Animation::create(); for(int i = 1; i <= 7; i++) { auto name = String::createWithFormat("explosion_small%i.png", i); auto frame = SpriteFrameCache::getInstance()->getSpriteFrameByName(name->getCString()); animation->addSpriteFrame(frame); }   animation->setDelayPerUnit(0.5 / 7.0f); animation->setRestoreOriginalFrame(true); _explosion = Sequence::create(      Animate::create(animation),    CallFuncN::create(CC_CALLBACK_1(GameLayer::animationDone, this)), nullptr); _explosion->retain(); What just happened? We created two instances of a very special kind of action in Cocos2d-x: Animate. Here is what we did: First, we created an Animation object. This object holds the references to all the textures used in the animation. The frames were named in such a way that they could easily be concatenated inside a loop (boom1, boom2, boom3, and so on). There are 10 frames for the first animation and seven for the second. The textures (or frames) are SpriteFrame objects we grab from SpriteFrameCache, which as you remember, contains all the information from the sprite_sheet.plist data file. So the frames are in our sprite sheet. Then when all frames are in place, we determine the delay of each frame by dividing the total amount of seconds we want the animation to last by the total number of frames. The setRestoreOriginalFrame method is important here. If we set setRestoreOriginalFrame to true, then the sprite will revert to its original appearance once the animation is over. For example, if I have an explosion animation that will run on a meteor sprite, then by the end of the explosion animation, the sprite will revert to displaying the meteor texture. Time for the actual action. Animate receives the Animation object as its parameter. (In the first animation, we shift the position of the sprite just before the explosion appears, so there is an extra MoveBy method.) And in both instances, I make a call to an animationDone callback already implemented in the class. It makes the calling sprite invisible: void GameLayer::animationDone (Node* pSender) { pSender->setVisible(false); } We could have used the same method for both callbacks (animationDone and shockwaveDone) as they accomplish the same thing. But I wanted to show you a callback that receives as an argument, the node that made the call and one that did not. Respectively, these are CallFuncN and CallFunc, and were used inside the action sequences we just created. Time to make our game tick! Okay, we have our main elements in place and are ready to add the final bit of logic to run the game. But how will everything work? We will use a system of countdowns to add new meteors and new health packs, as well as a countdown that will incrementally make the game harder to play. On touch, the player will start the game if the game is not running, and also add bombs and explode them during gameplay. An explosion creates a shockwave. On update, we will check against collision between our _shockwave sprite (if visible) and all our falling objects. And that's it. Cocos2d-x will take care of all the rest through our created actions and callbacks! So let's implement our touch events first. Time for action – handling touches Time to bring the player to our party: Time to implement our onTouchBegan method. We'll begin by handling the two game states, intro and game over: bool GameLayer::onTouchBegan (Touch * touch, Event * event){   //if game not running, we are seeing either intro or //gameover if (!_running) {    //if intro, hide intro message    if (_introMessage->isVisible()) {      _introMessage->setVisible(false);        //if game over, hide game over message    } else if (_gameOverMessage->isVisible()) {      SimpleAudioEngine::getInstance()->stopAllEffects();      _gameOverMessage->setVisible(false);         }       this->resetGame();    return true; } Here we check to see if the game is not running. If not, we check to see if any of our messages are visible. If _introMessage is visible, we hide it. If _gameOverMessage is visible, we stop all current sound effects and hide the message as well. Then we call a method called resetGame, which will reset all the game data (energy, score, and countdowns) to their initial values, and set _running to true. Next we handle the touches. But we only need to handle one each time so we use ->anyObject() on Set: auto touch = (Touch *)pTouches->anyObject();   if (touch) { //if bomb already growing... if (_bomb->isVisible()) {    //stop all actions on bomb, halo and sparkle    _bomb->stopAllActions();    auto child = (Sprite *) _bomb->getChildByTag(kSpriteHalo);    child->stopAllActions();    child = (Sprite *) _bomb->getChildByTag(kSpriteSparkle);    child->stopAllActions();       //if bomb is the right size, then create shockwave    if (_bomb->getScale() > 0.3f) {      _shockWave->setScale(0.1f);      _shockWave->setPosition(_bomb->getPosition());      _shockWave->setVisible(true);      _shockWave->runAction(ScaleTo::create(0.5f, _bomb->getScale() * 2.0f));      _shockWave->runAction(_shockwaveSequence->clone());      SimpleAudioEngine::getInstance()->playEffect("bombRelease.wav");      } else {      SimpleAudioEngine::getInstance()->playEffect("bombFail.wav");    }    _bomb->setVisible(false);    //reset hits with shockwave, so we can count combo hits    _shockwaveHits = 0; //if no bomb currently on screen, create one } else {    Point tap = touch->getLocation();    _bomb->stopAllActions();    _bomb->setScale(0.1f);    _bomb->setPosition(tap);    _bomb->setVisible(true);    _bomb->setOpacity(50);    _bomb->runAction(_growBomb->clone());         auto child = (Sprite *) _bomb->getChildByTag(kSpriteHalo);      child->runAction(_rotateSprite->clone());      child = (Sprite *) _bomb->getChildByTag(kSpriteSparkle);      child->runAction(_rotateSprite->clone()); } } If _bomb is visible, it means it's already growing on the screen. So on touch, we use the stopAllActions() method on the bomb and we use the stopAllActions() method on its children that we retrieve through our tags: child = (Sprite *) _bomb->getChildByTag(kSpriteHalo); child->stopAllActions(); child = (Sprite *) _bomb->getChildByTag(kSpriteSparkle); child->stopAllActions(); If _bomb is the right size, we start our _shockwave. If it isn't, we play a bomb failure sound effect; there is no explosion and _shockwave is not made visible. If we have an explosion, then the _shockwave sprite is set to 10 percent of the scale. It's placed at the same spot as the bomb, and we run a couple of actions on it: we grow the _shockwave sprite to twice the scale the bomb was when it went off and we run a copy of _shockwaveSequence that we created earlier. Finally, if no _bomb is currently visible on screen, we create one. And we run clones of previously created actions on the _bomb sprite and its children. When _bomb grows, its children grow. But when the children rotate, the bomb does not: a parent changes its children, but the children do not change their parent. What just happened? We just added part of the core logic of the game. It is with touches that the player creates and explodes bombs to stop meteors from reaching the city. Now we need to create our falling objects. But first, let's set up our countdowns and our game data. Time for action – starting and restarting the game Let's add the logic to start and restart the game. Let's write the implementation for resetGame: void GameLayer::resetGame(void) {    _score = 0;    _energy = 100;       //reset timers and "speeds"    _meteorInterval = 2.5;    _meteorTimer = _meteorInterval * 0.99f;    _meteorSpeed = 10;//in seconds to reach ground    _healthInterval = 20;    _healthTimer = 0;    _healthSpeed = 15;//in seconds to reach ground       _difficultyInterval = 60;    _difficultyTimer = 0;       _running = true;       //reset labels    _energyDisplay->setString(std::to_string((int) _energy) + "%");    _scoreDisplay->setString(std::to_string((int) _score)); } Next, add the implementation of stopGame: void GameLayer::stopGame() {       _running = false;       //stop all actions currently running    int i;    int count = (int) _fallingObjects.size();       for (i = count-1; i >= 0; i--) {        auto sprite = _fallingObjects.at(i);        sprite->stopAllActions();        sprite->setVisible(false);        _fallingObjects.erase(i);    }    if (_bomb->isVisible()) {        _bomb->stopAllActions();        _bomb->setVisible(false);        auto child = _bomb->getChildByTag(kSpriteHalo);        child->stopAllActions();        child = _bomb->getChildByTag(kSpriteSparkle);        child->stopAllActions();    }    if (_shockWave->isVisible()) {        _shockWave->stopAllActions();        _shockWave->setVisible(false);    }    if (_ufo->isVisible()) {        _ufo->stopAllActions();        _ufo->setVisible(false);        auto ray = _ufo->getChildByTag(kSpriteRay);       ray->stopAllActions();        ray->setVisible(false);    } } What just happened? With these methods we control gameplay. We start the game with default values through resetGame(), and we stop all actions with stopGame(). Already implemented in the class is the method that makes the game more difficult as time progresses. If you take a look at the method (increaseDifficulty) you will see that it reduces the interval between meteors and reduces the time it takes for meteors to reach the ground. All we need now is the update method to run the countdowns and check for collisions. Time for action – updating the game We already have the code that updates the countdowns inside the update. If it's time to add a meteor or a health pack we do it. If it's time to make the game more difficult to play, we do that too. It is possible to use an action for these timers: a Sequence action with a Delay action object and a callback. But there are advantages to using these countdowns. It's easier to reset them and to change them, and we can take them right into our main loop. So it's time to add our main loop: What we need to do is check for collisions. So add the following code: if (_shockWave->isVisible()) { count = (int) _fallingObjects.size(); for (i = count-1; i >= 0; i--) {    auto sprite = _fallingObjects.at(i);    diffx = _shockWave->getPositionX() - sprite->getPositionX();    diffy = _shockWave->getPositionY() - sprite->getPositionY();    if (pow(diffx, 2) + pow(diffy, 2) <= pow(_shockWave->getBoundingBox().size.width * 0.5f, 2)) {    sprite->stopAllActions();    sprite->runAction( _explosion->clone());    SimpleAudioEngine::getInstance()->playEffect("boom.wav");    if (sprite->getTag() == kSpriteMeteor) {      _shockwaveHits++;      _score += _shockwaveHits * 13 + _shockwaveHits * 2;    }    //play sound    _fallingObjects.erase(i); } } _scoreDisplay->setString(std::to_string(_score)); } If _shockwave is visible, we check the distance between it and each sprite in _fallingObjects vector. If we hit any meteors, we increase the value of the _shockwaveHits property so we can award the player for multiple hits. Next we move the clouds: //move clouds for (auto sprite : _clouds) { sprite->setPositionX(sprite->getPositionX() + dt * 20); if (sprite->getPositionX() > _screenSize.width + sprite->getBoundingBox().size.width * 0.5f)    sprite->setPositionX(-sprite->getBoundingBox().size.width * 0.5f); } I chose not to use a MoveTo action for the clouds to show you the amount of code that can be replaced by a simple action. If not for Cocos2d-x actions, we would have to implement logic to move, rotate, swing, scale, and explode all our sprites! And finally: if (_bomb->isVisible()) {    if (_bomb->getScale() > 0.3f) {      if (_bomb->getOpacity() != 255)        _bomb->setOpacity(255);    } } We give the player an extra visual cue to when a bomb is ready to explode by changing its opacity. What just happened? The main loop is pretty straightforward when you don't have to worry about updating individual sprites, as our actions take care of that for us. We pretty much only need to run collision checks between our sprites, and to determine when it's time to throw something new at the player. So now the only thing left to do is grab the meteors and health packs from the pools when their timers are up. So let's get right to it. Time for action – retrieving objects from the pool We just need to use the correct index to retrieve the objects from their respective vector: To retrieve meteor sprites, we'll use the resetMeteor method: void GameLayer::resetMeteor(void) {    //if too many objects on screen, return    if (_fallingObjects.size() > 30) return;       auto meteor = _meteorPool.at(_meteorPoolIndex);      _meteorPoolIndex++;    if (_meteorPoolIndex == _meteorPool.size())      _meteorPoolIndex = 0;      int meteor_x = rand() % (int) (_screenSize.width * 0.8f) + _screenSize.width * 0.1f;    int meteor_target_x = rand() % (int) (_screenSize.width * 0.8f) + _screenSize.width * 0.1f;       meteor->stopAllActions();    meteor->setPosition(Vec2(meteor_x, _screenSize.height + meteor->getBoundingBox().size.height * 0.5));    //create action    auto rotate = RotateBy::create(0.5f , -90);    auto repeatRotate = RepeatForever::create( rotate );    auto sequence = Sequence::create (                MoveTo::create(_meteorSpeed, Vec2(meteor_target_x, _screenSize.height * 0.15f)),                CallFunc::create(std::bind(&GameLayer::fallingObjectDone, this, meteor) ), nullptr);   meteor->setVisible ( true ); meteor->runAction(repeatRotate); meteor->runAction(sequence); _fallingObjects.pushBack(meteor); } We grab the next available meteor from the pool, then we pick a random start and end x value for its MoveTo action. The meteor starts at the top of the screen and will move to the bottom towards the city, but the x value is randomly picked each time. We rotate the meteor inside a RepeatForever action, and we use Sequence to move the sprite to its target position and then call back fallingObjectDone when the meteor has reached its target. We finish by adding the new meteor we retrieved from the pool to the _fallingObjects vector so we can check collisions with it. The method to retrieve the health (resetHealth) sprites is pretty much the same, except that swingHealth action is used instead of rotate. You'll find that method already implemented in GameLayer.cpp. What just happened? So in resetGame we set the timers, and we update them in the update method. We use these timers to add meteors and health packs to the screen by grabbing the next available one from their respective pool, and then we proceed to run collisions between an exploding bomb and these falling objects. Notice that in both resetMeteor and resetHealth we don't add new sprites if too many are on screen already: if (_fallingObjects->size() > 30) return; This way the game does not get ridiculously hard, and we never run out of unused objects in our pools. And the very last bit of logic in our game is our fallingObjectDone callback, called when either a meteor or a health pack has reached the ground, at which point it awards or punishes the player for letting sprites through. When you take a look at that method inside GameLayer.cpp, you will notice how we use ->getTag() to quickly ascertain which type of sprite we are dealing with (the one calling the method): if (pSender->getTag() == kSpriteMeteor) { If it's a meteor, we decrease energy from the player, play a sound effect, and run the explosion animation; an autorelease copy of the _groundHit action we retained earlier, so we don't need to repeat all that logic every time we need to run this action. If the item is a health pack, we increase the energy or give the player some points, play a nice sound effect, and hide the sprite. Play the game! We've been coding like mad, and it's finally time to run the game. But first, don't forget to release all the items we retained. In GameLayer.cpp, add our destructor method: GameLayer::~GameLayer () {       //release all retained actions    CC_SAFE_RELEASE(_growBomb);    CC_SAFE_RELEASE(_rotateSprite);    CC_SAFE_RELEASE(_shockwaveSequence);    CC_SAFE_RELEASE(_swingHealth);    CC_SAFE_RELEASE(_groundHit);    CC_SAFE_RELEASE(_explosion);    CC_SAFE_RELEASE(_ufoAnimation);    CC_SAFE_RELEASE(_blinkRay);       _clouds.clear();    _meteorPool.clear();    _healthPool.clear();    _fallingObjects.clear(); } The actual game screen will now look something like this: Now, let's take this to Android. Time for action – running the game in Android Follow these steps to deploy the game to Android: This time, there is no need to alter the manifest because the default settings are the ones we want. So, navigate to proj.android and then to the jni folder and open the Android.mk file in a text editor. Edit the lines in LOCAL_SRC_FILES to read as follows: LOCAL_SRC_FILES := hellocpp/main.cpp \                    ../../Classes/AppDelegate.cpp \                    ../../Classes/GameLayer.cpp Follow the instructions from the HelloWorld and AirHockey examples to import the game into Eclipse. Save it and run your application. This time, you can try out different size screens if you have the devices. What just happened? You just ran a universal app in Android. And nothing could have been simpler. Summary In my opinion, after nodes and all their derived objects, actions are the second best thing about Cocos2d-x. They are time savers and can quickly spice things up in any project with professional-looking animations. And I hope with the examples found in this article, you will be able to create any action you need with Cocos2d-x. Resources for Article: Further resources on this subject: Animations in Cocos2d-x [article] Moving the Space Pod Using Touch [article] Cocos2d-x: Installation [article]
Read more
  • 0
  • 0
  • 6889

article-image-creating-your-first-module-drupal-7-module-development
Packt
14 Dec 2010
15 min read
Save for later

Using Drupal 7 for Module Development

Packt
14 Dec 2010
15 min read
  Drupal 7 Module Development Create your own Drupal 7 modules from scratch Specifically written for Drupal 7 development Write your own Drupal modules, themes, and libraries Discover the powerful new tools introduced in Drupal 7 Learn the programming secrets of six experienced Drupal developers Get practical with this book's project-based format         Read more about this book       The focus of this article by Matt Butcher, author of Drupal 7 Module Development, is module creation. We are going to begin coding in this article. Here are some of the important topics that we will cover in this article: Starting a new module Creating .info files to provide Drupal with module information Creating .module files to store Drupal code Adding new blocks using the Block Subsystem Using common Drupal functions Formatting code according to the Drupal coding standards (For more resources on this subject, see here.) Our goal: a module with a block In this article we are going to build a simple module. The module will use the Block Subsystem to add a new custom block. The block that we add will simply display a list of all of the currently enabled modules on our Drupal installation. We are going to divide this task of building a new module into the three parts: Create a new module folder and module files Work with the Block Subsystem Write automated tests using the SimpleTest framework included in Drupal We are going to proceed in that order for the sake of simplicity. One might object that, following agile development processes, we ought to begin by writing our tests. This approach is called Test-driven Development (TDD), and is a justly popular methodology. Agile software development is a particular methodology designed to help teams of developers effectively and efficiently build software. While Drupal itself has not been developed using an agile process, it does facilitate many of the agile practices. To learn more about agile, visit http://agilemanifesto.org/ However, our goal here is not to exemplify a particular methodology, but to discover how to write modules. It is easier to learn module development by first writing the module, and then learn how to write unit tests. It is easier for two reasons: SimpleTest (in spite of its name) is the least simple part of this article. It will have double the code-weight of our actual module. We will need to become acquainted with the APIs we are going to use in development before we attempt to write tests that assume knowledge of those APIs. In regular module development, though, you may certainly choose to follow the TDD approach of writing tests first, and then writing the module. Let's now move on to the first step of creating a new module. Creating a new module Creating Drupal modules is easy. How easy? Easy enough that over 5,000 modules have been developed, and many Drupal developers are even PHP novices! In fact, the code in this article is an illustration of how easy module coding can be. We are going to create our first module with only one directory and two small files. Module names It goes without saying that building a new module requires naming the module. However, there is one minor ambiguity that ought to be cleared up at the outset, a Drupal module has two names: A human-readable name: This name is designed to be read by humans, and should be one or a couple of words long. The words should be capitalized and separated by spaces. For example, one of the most popular Drupal modules has the human-readable name Views. A less-popular (but perhaps more creatively named) Drupal 6 module has the human-readable name Eldorado Superfly. A machine-readable name: This name is used internally by Drupal. It can be composed of lower-case and upper-case letters, digits, and the underscore character (using upper-case letters in machine names is frowned upon, though). No other characters are allowed. The machine names of the above two modules are views and eldorado_superfly, respectively. By convention, the two names ought to be as similar as possible. Spaces should be replaced by underscores. Upper-case letters should generally be changed to lower-case. Because of the convention of similar naming, the two names can usually be used interchangeably, and most of the time it is not necessary to specifically declare which of the two names we are referring to. In cases where the difference needs to be made (as in the next section), the authors will be careful to make it. Where does our module go? One of the less intuitive aspects of Drupal development is the filesystem layout. Where do we put a new module? The obvious answer would be to put it in the /modules directory alongside all of the core modules. As obvious as this may seem, the /modules folder is not the right place for your modules. In fact, you should never change anything in that directory. It is reserved for core Drupal modules only, and will be overwritten during upgrades. The second, far less obvious place to put modules is in /sites/all/modules. This is the location where all unmodified add-on modules ought to go, and tools like Drush ( a Drupal command line tool) will download modules to this directory. In some sense, it is okay to put modules here. They will not be automatically overwritten during core upgrades. However, as of this writing, /sites/all/modules is not the recommended place to put custom modules unless you are running a multi-site configuration and the custom module needs to be accessible on all sites. The current recommendation is to put custom modules in the /sites/default/modules directory, which does not exist by default. This has a few advantages. One is that standard add-on modules are stored elsewhere, and this separation makes it easier for us to find our own code without sorting through clutter. There are other benefits (such as the loading order of module directories), but none will have a direct impact on us. We will always be putting our custom modules in /sites/default/modules. This follows Drupal best practices, and also makes it easy to find our modules as opposed to all of the other add-on modules. The one disadvantage of storing all custom modules in /sites/default/modules appears only under a specific set of circumstances. If you have Drupal configured to serve multiple sites off of one single instance, then the /sites/default folder is only used for the default site. What this means, in practice, is that modules stored there will not be loaded at all for other sites. In such cases, it is generally advised to move your custom modules into /sites/all/modules/custom. Other module directories Drupal does look in a few other places for modules. However, those places are reserved for special purposes. Creating the module directory Now that we know that our modules should go in /sites/default/modules, we can create a new module there. Modules can be organized in a variety of ways, but the best practice is to create a module directory in /sites/default/modules, and then place at least two files inside the directory: a .info (pronounced "dot-info") file and a .module ("dot-module") file. The directory should be named with the machine-readable name of the module. Similarly, both the .info and .module files should use the machine-readable name. We are going to name our first module with the machine-readable name first, since it is our first module. Thus, we will create a new directory, /sites/default/modules/first, and then create a first.info file and a first.module file: Those are the only files we will need for our module. For permissions, make sure that your webserver can read both the .info and .module files. It should not be able to write to either file, though. In some sense, the only file absolutely necessary for a module is the .info file located at a proper place in the system. However, since the .info file simply provides information about the module, no interesting module can be built with just this file. Next, we will write the contents of the .info file. Writing the .info file The purpose of the .info file is to provide Drupal with information about a module—information such as the human-readable name, what other modules this module requires, and what code files this module provides. A .info file is a plain text file in a format similar to the standard INI configuration file. A directive in the .info file is composed of a name, and equal sign, and a value: name = value By Drupal's coding conventions, there should always be one space on each side of the equals sign. Some directives use an array-like syntax to declare that one name has multiple values. The array-like format looks like this: name[] = value1 name[] = value2 Note that there is no blank space between the opening square bracket and the closing square bracket. If a value spans more than one line, it should be enclosed in quotation marks. Any line that begins with a ; (semi-colon) is treated as a comment, and is ignored by the Drupal INI parser. Drupal does not support INI-style section headers such as those found in the php.ini file. To begin, let's take a look at a complete first.info file for our first module: ;$Id$ name = First description = A first module. package = Drupal 7 Development core = 7.x files[] = first.module ;dependencies[] = autoload ;php = 5.2 This ten-line file is about as complex as a module's .info file ever gets. The first line is a standard. Every .info file should begin with ;$Id$. What is this? It is the placeholder for the version control system to store information about the file. When the file is checked into Drupal's CVS repository, the line will be automatically expanded to something like this: ;$Id: first.info,v 1.1 2009/03/18 20:27:12 mbutcher Exp $ This information indicates when the file was last checked into CVS, and who checked it in. CVS is going away, and so is $Id$. While Drupal has been developed in CVS from the early days through Drupal 7, it is now being migrated to a Git repository. Git does not use $Id$, so it is likely that between the release of Drupal 7 and the release of Drupal 8, $Id$ tags will be removed. You will see all PHP and .info files beginning with the $Id$ marker. Once Drupal uses Git, those tags may go away. The next couple of lines of interest in first.info are these: name = First description = A first module. package = Drupal 7 Development The first two are required in every .info file. The name directive is used to declare what the module's human-readable name is. The description provides a one or two-sentence description of what this module provides or is used for. Among other places, this information is displayed on the module configuration section of the administration interface in Modules. In the screenshot, the values of the name and description fields are displayed in their respective columns. The third item, package, identifies which family (package) of modules this module is related to. Core modules, for example, all have the package Core. In the screenshot above, you can see the grouping package Core in the upper-left corner. Our module will be grouped under the package Drupal 7 Development to represent its relationship. As you may notice, package names are written as human-readable values. When choosing a human-readable module name, remember to adhere to the specifications mentioned earlier in this section. The next directive is the core directive: core = 7.x. This simply declares which main-line version of Drupal is required by the module. All Drupal 7 modules will have the line core = 7.x. Along with the core version, a .info file can also specify what version of PHP it requires. By default, Drupal 7 requires Drupal 5.1 or newer. However, if one were to use, say, closures (a feature introduced in PHP 5.3), then the following line would need to be added: php = 5.3 Next, every .info file must declare which files in the module contain PHP functions, classes, or interfaces. This is done using the files[] directive. Our small initial module will only have one file, first.module. So we need only one files[] directive. files[] = first.module More complex files will often have several files[] directives, each declaring a separate PHP source code file. JavaScript, CSS, image files, and PHP files (like templates) that do not contain functions that the module needs to know about needn't be included in files[] directives. The point of the directive is simply to indicate to Drupal that these files should be examined by Drupal. One directive that we will not use for this module, but which plays a very important role is the dependencies[] directive. This is used to list the other modules that must be installed and active for this module to function correctly. Drupal will not allow a module to be enabled unless its dependencies have been satisfied. Drupal does not contain a directive to indicate that another module is recommended or is optional. It is the task of the developer to appropriately document this fact and make it known. There is currently no recommended best practice to provide such information. Now we have created our first.info file. As soon as Drupal reads this file, the module will appear on our Modules page. In the screenshot, notice that the module appears in the DRUPAL 7 DEVELOPMENT package, and has the NAME and DESCRIPTION as assigned in the .info file. With our .info file completed, we can now move on and code our .module file. Modules checked into Drupal's version control system will automatically have a version directive added to the .info file. This should typically not be altered. Creating a module file The .module file is a PHP file that conventionally contains all of the major hook implementations for a module. We will gain some practical knowledge of them. A hook implementation is a function that follows a certain naming pattern in order to indicate to Drupal that it should be used as a callback for a particular event in the Drupal system. For Object-oriented programmers, it may be helpful to think of a hook as similar to the Observer design pattern. When Drupal encounters an event for which there is a hook (and there are hundreds of such events), Drupal will look through all of the modules for matching hook implementations. It will then execute each hook implementation, one after another. Once all hook implementations have been executed, Drupal will continue its processing. In the past, all Drupal hook implementations had to reside in the .module file. Drupal 7's requirements are more lenient, but in most moderately sized modules, it is still preferable to store most hook implementations in the .module file. There are cases where hook implementations belong in other files. In such cases, the reasons for organizing the module in such a way will be explained. To begin, we will create a simple .module file that contains a single hook implementation – one that provides help information. <?php // $Id$ /** * @file * A module exemplifying Drupal coding practices and APIs. * * This module provides a block that lists all of the * installed modules. It illustrates coding standards, * practices, and API use for Drupal 7. */ /** * Implements hook_help(). */ function first_help($path, $arg) { if ($path == 'admin/help#first') { return t('A demonstration module.'); } } Before we get to the code itself, we will talk about a few stylistic items. To begin, notice that this file, like the .info file, contains an $Id$ marker that CVS will replace when the file is checked in. All PHP files should have this marker following a double-slash-style comment: // $Id$. Next, the preceding code illustrates a few of the important coding standards for Drupal. Source code standards Drupal has a thorough and strictly enforced set of coding standards. All core code adheres to these standards. Most add-on modules do, too. (Those that don't generally receive bug reports for not conforming.) Before you begin coding, it is a good idea to familiarize yourself with the standards as documented here: http://drupal.org/coding-standards. The Coder module can evaluate your code and alert you to any infringement upon the coding standards. We will adhere to the Drupal coding standards. In many cases, we will explain the standards as we go along. Still, the definitive source for standards is the URL listed above, not our code here. We will not re-iterate the coding standards. The details can be found online. However, several prominent standards deserve immediate mention. I will just mention them here, and we will see examples in action as we work through the code. Indenting: All PHP and JavaScript files use two spaces to indent. Tabs are never used for code formatting. The <?php ?> processor instruction: Files that are completely PHP should begin with <?php, but should omit the closing ?>. This is done for several reasons, most notably to prevent the inclusion of whitespace from breaking HTTP headers. Comments: Drupal uses Doxygen-style (/** */) doc-blocks to comment functions, classes, interfaces, constants, files, and globals. All other comments should use the double-slash (//) comment. The pound sign (#) should not be used for commenting. Spaces around operators: Most operators should have a whitespace character on each side. Spacing in control structures: Control structures should have spaces after the name and before the curly brace. The bodies of all control structures should be surrounded by curly braces, and even that of if statements with one-line bodies. Functions: Functions should be named in lowercase letters using underscores to separate words. Later we will see how class method names differ from this. Variables: Variable names should be in all lowercase letters using underscores to separate words. Member variables in objects are named differently. As we work through examples, we will see these and other standards in action. As we work through examples, we will see these and other standards in action.
Read more
  • 0
  • 0
  • 6880

article-image-coldfusion-ajax-programming
Packt
27 Oct 2009
9 min read
Save for later

ColdFusion AJAX Programming

Packt
27 Oct 2009
9 min read
Binding When it comes to programming, the two most commonly used features are CFAJAXProxy and binding. The binding feature allows us to bind or tie things together by using a simpler technique than we would otherwise have needed to create. Binding acts as a double-ended connector in some scenarios. You can set the bind to pull data from another ColdFusion tag on the form. These must be AJAX tags with binding abilities. There are four forms of bindings, on page, CFC, JavaScript, and URL. Let's work through each style so that we will understand them well. We will start with on page binding. Remember that the tag has to support the binding. This is not a general ColdFusion feature, but we can use it wherever we desire. On Page Binding We are going to bind 'cfdiv' to pull its content to show on page binding. We will set the value of a text input to the div. Refer to the following code. ColdFusion AJAX elements work in a manner different from how AJAX is written traditionally. It is more customary to name our browser-side HTML elements with id attributes. This is not the case with the binding features. As we can see in our code example, we have used the name attribute. We should remember to be case sensitive, since this is managed by JavaScript. When we run the code, we will notice that we must leave the input field before the browser registers that there has been a change in the value of the field. This is how the event model for the browser DOM works. <cfform id="myForm" format="html"> This is my edit box.<br /> <cfinput type="text" name="myText"></cfform><hr />And this is the bound div container.<br /><cfdiv bind="{myText}"></cfdiv> Notice how we use curly brackets to bind the value of the 'myText' input box. This inserts the contents into 'div' when the text box loses focus. This is an example of binding to in-page elements. If the binding we use is tied to a hidden window or tab, then the contents may not be updated. CFC Binding Now, we are going to bind our div to a CFC method. We will take the data that was being posted directly to the object, and then we will pass it out to the CFC. The CFC is going to repackage it, and send it back to the browser. The binding will enable the modified version of the content to be sent to the div. Refer to the following CFC code: <cfcomponent output="false"> <cffunction name="getDivContent" returntype="string" access="remote"> <cfargument name="edit"> <cfreturn "This is the content returned from the CFC for the div, the calling page variable is '<strong>#arguments.edit#</strong>'."> </cffunction></cfcomponent> From the previous code, we can see that the CFC only accepts the argument and passes it back. This could have even returned an image HTML segment with something like a user picture. The following code shows the new page code modifications. <cfform id="myForm" format="html"> This is my edit box.<br /> <cfinput type="text" name="myText"></cfform><hr />And this is the bound div container.<br /><cfdiv bind="cfc:bindsource.getDivContent({myText})"></cfdiv> The only change lies in how we bind the cfdiv element tag. Here, you can see that it starts with CFC. Next, it calls bindsource, which is the name of a local CFC. This tells ColdFusion to wire up the browser page, so it will connect to the CFC and things will work as we want. You can observe that inside the method, we are passing the bound variable to the method. When the input field changes by losing focus, the browser sends a new request to the CFC and updates the div. We need to have the same number of parameters going to the CFC as the number of arguments in our CFC method. We should also make sure that the method has its access method set to remote. Here we can see an example results page. It is valid to pass the name of the CFC method argument with the data value. This can prevent exceptions caused by not pairing the data in the same order as the method arguments. The last line of the previous code can be modified as follows: <cfdiv bind="cfc:bindsource.getDivContent(edit:{myText})"></cfdiv> JavaScript Binding Now, we will see how simple power can be managed on the browser. We will create a standard JavaScript function and pass the same bound data field through the function. Whenever we update the text box and it looses focus, the contents of the div will be updated from the function on the page. It is suggested that we include all JavaScript rather than put it directly on the page. Refer to the following code: <cfform id="myForm" format="html"> This is my edit box.<br /> <cfinput type="text" name="myText"></cfform><hr />And this is the bound div container.<br /><cfdiv bind="javascript:updateDiv({myText})"></cfdiv><script> updateDiv = function(myEdit){ return 'This is the result that came from the JavaScript function with the edit box sending "<strong>'+myEdit+'</strong>"'; } </script> Here is the result of placing the same text into our JavaScript example. URL Binding We can achieve the same results by calling a web address. We can actually call a static HTML page. Now, we will call a .cfm page to see the results of changing the text box reflected back, as for CFC and JavaScript. Here is the code for our main page with the URL binding. <cfform id="myForm" format="html"> This is my edit box.<br /> <cfinput type="text" name="myText"></cfform><hr />And this is the bound div container.<br /><cfdiv bind="url:bindsource.cfm?myEdit={myText}"></cfdiv> In the above code, we can see that the binding type is set to URL. Earlier, we used the CFC method bound to a file named bindsource.cfc. Now, we will bind through the URL to a .cfm file. The bound myText data will work in a manner similar to the other cases. It will be sent to the target; in this case, it is a regular server-side page. We require only one line. In this example, our variables are URL variables. Here is the handler page code: <cfoutput> 'This is the result that came from the server page with the edit box sending "<strong>#url.myEdit#</strong>"'</cfoutput> This tells us that if there is no prefix to the browse request on the bind attribute of the <cfdiv> tag, then it will only work with on-page elements. If we prefix it, then we can pass the data through a CFC, a URL, or through a JavaScript function present on the same page. If we bind to a variable present on the same page, then whenever the bound element updates, the binding will be executed. Bind with Event One of the features of binding that we might overlook its binding based on an event. In the previous examples, we mentioned that the normal event trigger for binding took place when the bound field lost its focus. The following example shows a bind that occurs when the key is released. <cfform id="myForm" format="html"> This is my edit box.<br /> <cfinput type="text" name="myText"></cfform><hr />And this is the bound div container.<br /><cfdiv bind="{myText@keyup}"></cfdiv> This is similar to our first example, with the only difference being that the contents of the div are updated as each key is pressed. This works in a manner similar to CFC, JavaScript, and URL bindings. We might also consider binding other elements on a click event, such as a radio button. The following example shows another feature. We can pass any DOM attribute by putting that as an item after the element id. It must be placed before the @ symbol, if you are using a particular event. In this code, we change the input in order to have a class in which we can pass the value of the class attribute and change the binding attribute of the cfdiv element. <cfform id="myForm" format="html"> This is my edit box.<br /> <cfinput type="text" name="myText" class="test"> </cfform><hr />And this is the bound div container.<br /><cfdiv bind="{myText.class@keyup}.{myText}"></cfdiv> Here is a list of the events that we can bind. @click @keyup @mousedown @none The @none event is used for grids and trees, so that changes don't trigger bind events. Extra Binding Notes If you have an ID on your CFForm element, then you can refer to the form element based on the container form. The following example helps us to understand this better. Bind = "url:bindsource.cfm?myEdit={myForm:myText}" The ColdFusion 8 documents give the following guides in order to specify the binding expressions. cfc: componentPath.functionName (parameters) The component path cannot use a mapping. The componentPath value must be a dot-delimited path from the web root or the directory that contains the page. javascript: functionName (parameters) url: URL?parameters ULR?parameters A string containing one or more instances of {bind parmeter}, such as {firstname}.{lastname}@{domain} The following table represents the supported formats based on attributes and tags: Attribute Tags Supported Formats Autosuggest cfinput type="text" 1,2,3 Bind cfdiv, cfinput, cftextarea 1,2,3,5 Bind cfajaxproxy, cfgrid, cfselect cfsprydataset, cftreeitem 1,2,3 onChange cfgrid 1,2,3 Source cflayoutarea, cfpod, cfwindow 4
Read more
  • 0
  • 0
  • 6873

article-image-integrating-microsoft-dynamics-gp-business-application-fundamentals
Packt
01 Mar 2013
5 min read
Save for later

Integrating Microsoft Dynamics GP Business Application fundamentals

Packt
01 Mar 2013
5 min read
(For more resources related to this topic, see here.) Defining the project Before you start installing software and designing windows, you need a plan. It's almost time to search for your flowcharting template, but first you need to answer this query: Just what are you trying to accomplish? Let's begin to define the project by responding to some fundamental questions: Do you want to change the way a window looks or behaves? Do you want to change or extend current Dynamics GP functionality? Do you need to create brand-new functionality? Do you need to exchange data between dissimilar systems? Are you just trying to store some additional static data? Changing a window's look or behavior Maybe the field prompts are wrong or the tabbing order is unacceptable. Perhaps there are not enough, or even too many, fields on the window. The window may need additional navigation options such as menus or buttons. You might want a field to be created only if certain criteria are met. For instance, if your customer were a reseller, the Tax Schedule ID field would not be required; but if your customer were not a reseller, the Tax Schedule ID field would be required. Maybe more visual cues should be present on the window, such as a red/green light indicator on the Customer Maintenance window to represent their payment pattern similar to the following screenshot: You may want a more obvious cue if a record note exists, like a bigger icon or an icon that flashes! You might want to see the quantity of a stock item available in the Sales Order Processing lookup window. This list could go on forever. Changing current functionality Many times Dynamics GP is just missing a little something with the way it processes certain transaction types. For example, perhaps you would like to be warned if you are entering a Payables Transaction for a vendor with an outstanding purchase order, or you need a receivables document to move to a history table automatically when you pay it, instead of having to run a monthly routine. This list does go on forever Creating new functionality This category is filled with things that Dynamics GP doesn't do at all. Dynamics GP constructed the foundation, and developers like you make its functionality boundless. Many of our vertical solutions are present here; applications for running mining operations, restaurants, and retail stores have been developed for Dynamics GP. The unique needs of a myriad of industries have been satisfied by third-party applications. Exchanging data between systems Very often, you accumulate detailed information in a different system of record and you need to import it into Dynamics GP. Sometimes you need to export information out of Dynamics GP in order to update another system. For instance, Point of Sale (POS) systems update the general ledger with daily sales, and payroll services send weekly payroll details to upload into the general ledger. Vendors send new price sheets that cause adjustments in the list price. These list price changes need to update the website as well as the accounting system. A constant stream of data flows back and forth every day and it needs to update other systems, or be updated itself. The goal is to take the information from the point of original entry, and electronically place it wherever it needs to go. We only want to touch the data once; dual entry needs to be eliminated. Our aim is to have only one version of the truth. Storing additional data Quite often the fields available for user customization, so called user-defined fields, are far too few. This problem is nearly universal when it comes to the inventory. Take, for example, a company that trades in high-end audio equipment. For a preamp they may need to know the distortion percentage, the number and types of inputs, outputs available, and so on. For speakers, they need a completely different set of information, such as sizes and types of drivers. Yes, there is much more information that needs to be at a salesperson's fingertips than the part number and the price. All of that additional information needs a place where you can enter it, and a table to call home. Types of integrations At the end of the day, there are generally two types of integrations: Database-level integrations include tasks such as the following: Importing data into Dynamics GP Exporting data out of Dynamics GP Storing additional data in new tables Synchronizing data between Dynamics GP and peripheral systems User-interface-level integrations include tasks such as the following: Adding entirely new windows Adding fields or controls to an existing window Adding navigation items to the home page Adding new menus Changing field locations on a window Changing a window's tab order A single customization often involves both types of integrations. OK, so now that the interrogation is complete, it's time to find that template and start flowcharting!
Read more
  • 0
  • 0
  • 6872
Unlock access to the largest independent learning library in Tech for FREE!
Get unlimited access to 7500+ expert-authored eBooks and video courses covering every tech area you can think of.
Renews at ₹800/month. Cancel anytime
article-image-mine-popular-trends-github-python-part-2
Amey Varangaonkar
27 Dec 2017
1 min read
Save for later

How to Mine Popular Trends on GitHub using Python - Part 2

Amey Varangaonkar
27 Dec 2017
1 min read
[box type="note" align="" class="" width=""]This article is an excerpt taken from the book Python Social Media Analytics, written by Siddhartha Chatterjee and Michal Krystyanczuk. In this book, you will find widely used social media mining techniques for extracting useful insights to drive your business.[/box] In Part 1 of this series, we gathered the GitHub data for analysis. Here, we will analyze that data as per our requirements, to get interesting insights on the highest trending and popular tools and languages on GitHub. We have seen so far that the GitHub API provides interesting sets of information about the code repositories and metadata around the activity of its users around these repositories. In the following sections, we will analyze this data to find out which are the most popular repositories through the analysis of its descriptions and then drilling down to the watchers, forks, and issues submitted on the emerging technologies. Since, technology is evolving so rapidly, this approach could help us to stay on top of the latest trending technologies. In order to find out what are the trending technologies, we will perform the analysis in a few steps: Identifying top technologies First of all, we will use text analytics techniques to identify what are the most popular phrases related to technologies in repositories from 2017. Our analysis will be focused on the most frequent bigrams. We import a nltk.collocation module which implements n-gram search tools: import nltk from nltk.collocations import * Then, we convert the clean description column into a list of tokens: list_documents = df['clean'].apply(lambda x: x.split()).tolist() As we perform an analysis on documents, we will use the method from_documents instead of a default one from_words. The difference between these two methods lies in then input data format. The one used in our case takes as argument a list of tokens and searches for n-grams document-wise instead of corpus-wise. It protects against detecting bi-grams composed of the last word of one document and the first one of another one: bigram_measures = nltk.collocations.BigramAssocMeasures() bigram_finder = BigramCollocationFinder.from_documents(list_documents) We take into account only bi-grams which appear at least three times in our document set: bigram_finder.apply_freq_filter(3) We can use different association measures to find the best bi-grams, such as raw frequency, pmi, student t, or chi sq. We will mostly be interested in the raw frequency measure, which is the simplest and most convenient indicator in our case. We get top 20 bigrams according to raw_freq measure: bigrams = bigram_finder.nbest(bigram_measures.raw_freq,20) We can also obtain their scores by applying the score_ngrams method: scores = bigram_finder.score_ngrams(bigram_measures.raw_freq) All the other measures are implemented as methods of BigramCollocationFinder. To try them, you can replace raw_freq by, respectively, pmi, student_t, and chi_sq. However, to create a visualization we will need the actual number of occurrences instead of scores. We create a list by using the ngram_fd.items() method and we sort it in descending order. ngram = list(bigram_finder.ngram_fd.items()) ngram.sort(key=lambda item: item[-1], reverse=True) It returns a dictionary of tuples which contain an embedded tuple and its frequency. We transform it into a simple list of tuples where we join bigram tokens: frequency = [(" ".join(k), v) for k,v in ngram] For simplicity reasons we put the frequency list into a dataframe: df=pd.DataFrame(frequency) And then, we plot the top 20 technologies in a bar chart: import matplotlib.pyplot as plt plt.style.use('ggplot') df.set_index([0], inplace = True) df.sort_values(by = [1], ascending = False).head(20).plot(kind = 'barh') plt.title('Trending Technologies') plt.ylabel('Technology') plt.xlabel('Popularity') plt.legend().set_visible(False) plt.axvline(x=14, color='b', label='Average', linestyle='--', linewidth=3) for custom in [0, 10, 14]: plt.text(14.2, custom, "Neural Networks", fontsize = 12, va = 'center', bbox = dict(boxstyle='square', fc='white', ec='none')) plt.show() We've added an additional line which helps us to aggregate all technologies related to neural networks. It is done manually by selecting elements by indices, (0,10,14) in this case. This operation might be useful for interpretation. The preceding analysis provides us with an interesting set of the most popular technologies on GitHub. It includes topics for software engineering, programming languages, and artificial intelligence. An important thing to be noted is that technology around neural networks emerges more than once, notably, deep learning, TensorFlow, and other specific projects. This is not surprising, since neural networks, which are an important component in the field of artificial intelligence, have been spoken about and practiced heavily in the last few years. So, if you're an aspiring programmer interested in AI and machine learning, this is a field to dive into! Programming languages  The next step in our analysis is the comparison of popularity between different programming languages. It will be based on samples of the top 1,000 most popular repositories by year. Firstly, we get the data for last three years: queries = ["created:>2017-01-01", "created:2015-01-01..2015-12-31", "created:2016-01-01..2016-12-31"] We reuse the search_repo_paging function to collect the data from the GitHub API and we concatenate the results to a new dataframe. df = pd.DataFrame() for query in queries: data = search_repo_paging(query) data = pd.io.json.json_normalize(data) df = pd.concat([df, data]) We convert the dataframe to a time series based on the create_at column df['created_at'] = df['created_at'].apply(pd.to_datetime) df = df.set_index(['created_at']) Then, we use aggregation method groupby which restructures the data by language and year, and we count the number of occurrences by language: dx = pd.DataFrame(df.groupby(['language', df.index.year])['language'].count()) We represent the results on a bar chart: fig, ax = plt.subplots() dx.unstack().plot(kind='bar', title = 'Programming Languages per Year', ax= ax) ax.legend(['2015', '2016', '2017'], title = 'Year') plt.show() The preceding graph shows a multitude of programming languages from assembly, C, C#, Java, web, and mobile languages, to modern ones like Python, Ruby, and Scala. Comparing over the three years, we see some interesting trends. We notice HTML, which is the bedrock of all web development, has remained very stable over the last three years. This is not something that will not be replaced in a hurry. Once very popular, Ruby now has a decrease in popularity. The popularity of Python, also our language of choice for this book, is going up. Finally, the cross-device programming language, Swift, initially created by Apple but now open source, is getting extremely popular over time. It could be interesting to see in the next few years, if these trends change or hold true for long. Programming languages used in top technologies Now we know what are the top programming languages and technologies quoted in repositories description. In this section we will try to combine this information and find out what are the main programming languages for each technology. We select four technologies from previous section and print corresponding programming languages. We look up the column containing cleaned repository description and create a set of the languages related to the technology. Using a set will assure that we have unique Values. technologies_list = ['software engineering', 'deep learning', 'open source', 'exercise practice'] for tech in technologies_list: print(tech) print(set(df[df['clean'].str.contains(tech)]['language'])) software engineering {'HTML', 'Java'} deep learning {'Jupyter Notebook', None, 'Python'} open source {None, 'PHP', 'Java', 'TypeScript', 'Go', 'JavaScript', 'Ruby', 'C++'} exercise practice {'CSS', 'JavaScript', 'HTML'} Following the text analysis of the descriptions of the top technologies and then extracting the programming languages for them we notice the following: You can do a lot more analysis with this GitHub data such as: Want to know how? You can check out our book Python Social Media Analytics to get a detailed walkthrough of these topics.
Read more
  • 0
  • 0
  • 6861

article-image-aws-mobile-developers-getting-started-mobile-hub
Raka Mahesa
11 Aug 2016
5 min read
Save for later

AWS for Mobile Developers - Getting Started with Mobile Hub

Raka Mahesa
11 Aug 2016
5 min read
Amazon Web Services, also known as AWS, is a go-to destination for developers to host their server-side apps. AWS isn’t exactly beginner-friendly, however. So if you're a mobile developer who doesn't have much knowledge in the backend field, AWS, with its countless weirdly-named services, may look like a complex beast. Well, Amazon has decided to rectify that. In late 2015, they rolled out the AWS Mobile Hub, a new dashboard for managing Amazon services on a mobile platform. The most important part about it is that it’s easy to use. AWS Mobile Hub features the following services: User authentication via Amazon Cognito Data, file, and resource storage using Amazon S3 and Cloudfront Backend code with Amazon Lambda Push notification using Amazon SNS Analytics using Amazon Analytics After seeing this list of services, you may still think it's complicated and that you only need one or two services from that list. The good news is that the hub allows you to cherry-pick the service you want instead of forcing you to use all of them. So, if your mobile app only needs to access some files on the Internet, then you can choose to only use the resource storage functionality and skip the other features. So, is AWS Mobile Hub basically a more focused version of the AWS dashboard? Well, it's more than just that. The hub is able to configure the Amazon services that you're going to use, so they're automatically tailored to your app. The hub will then generate Android and iOS codes for connecting to the services you just set up, so that you can quickly copy them to your app and use the services right away. Do note that most of the stuff that was done automatically by the hub can also be achieved by integrating the AWS SDK and configuring each service manually. Fortunately, you can easily add Amazon services that aren’t included in the hub. So, if you want to also use the Amazon DynamoDB service on your app, all you have to do is call the relevant DynamoDB functions from the AWS SDK and you're good to go. All right, enough talking. Let's give the AWS Mobile Hub a whirl! We're going to use the hub for the Android platform, so make sure you satisfy the following requirements: Android Studio v1.2 or above Android 4.4 (API 19) SDK Android SDK Build-tools 23.0.1 Let's start by opening the AWS Mobile Hub to create a new mobile project (you will be asked to log in to your Amazon account if you haven't done so). After entering the project name, you are presented with the hub dashboard, where you can choose the service you want to add to your app. Let's start by adding the User Sign-in functionality. There are a couple of steps that must be completed to configure the authentication service. First you need to figure out whether your app can be used without logging in or not (for example, users can use Imgur without logging in, but they have to log in to use Facebook). If a user doesn't need to be logged in, choose "Sign-in is optional"; otherwise, choose that sign-in is required. The next step is to add the actual authentication method. You can use your own authentication method, but that requires setting up a server, so let's go with a 3rd party authentication instead. When choosing a 3rd party authentication, create a corresponding app on the 3rd party website and then copy the required information to the Mobile Hub dashboard. When that's done, save the changes you made and return to the service picker. Except for the Cloud Logic service, the configurations for the other services are quite straightforward, so let's add User Data Storage and App Content Delivery services. If you want to integrate Cloud Logic, you will be directed to the Amazon Lambda dashboard, where you will need to write a function with JavaScript that will be run on the server. So let's leave it to another time for now. All right, you should be all set up now, so let's proceed with building the base Android app. Click on the build button on the menu to the left and then choose Android. The Hub will then configure all the services you chose earlier and provide you with an Android project that has been integrated with all of the necessary SDK, including the SDK for the 3rd party authentication. It's pretty nice, isn't it? Download the Android project and unzip it, and make note of the "MySampleApp" folder inside it. Fire up Android Studio and import (File > New > Import Project...) that folder. Wait for the project to finish syncing, and once it's done, try running it in your Android device to see if AWS was integrated successfully or not. And that's it. All of the code needed to connect to the Amazon services you have set up earlier can be found in the MySampleApp project. Now you can simply copy that to your actual project or use the project as a base to build the app you want. Check out the build section of the dashboard for a more detailed explanation of the generated codes.  About the author Raka Mahesa is a game developer at Chocoarts (http://chocoarts.com/) who is interested in digital technology in general. Outside of work hours, he likes to work on his own projects, with Corridoom VR (https://play.google.com/store/apps/details?id=com.rakamahesa.corridoom) being his latest released game. Raka also regularly tweets as @legacy99.
Read more
  • 0
  • 0
  • 6856

article-image-working-neo4j-embedded-database
Packt
09 May 2014
6 min read
Save for later

Working with a Neo4j Embedded Database

Packt
09 May 2014
6 min read
(For more resources related to this topic, see here.) Neo4j is a graph database, which means that it does not use tables and rows to represent data logically; instead, it uses nodes and relationships. Both nodes and relationships can have a number of properties. While relationships must have one direction and one type, nodes can have a number of labels. For example, the following diagram shows three nodes and their relationships, where every node has a label (language or graph database), while relationships have a type (QUERY_LANGUAGE_OF and WRITTEN_IN). The properties used in the graph shown in the following diagram are: name, type, and from. Note that every relation must have exactly one type and one direction, whereas labels for nodes are optional and can be multiple. Neo4j running modes Neo4j can be used in two modes: An embedded database in a Java application; A standalone server via REST In any case, this choice does not affect the way you query and work with the database. It's only an architectural choice driven by the nature of the application (whether a standalone server or a client-server), performance, monitoring, and safety of data. An embedded database An embedded Neo4j database is the best choice for performance. It runs in the same process of the client application that hosts it and stores data in the given path. Thus, an embedded database must be created programmatically. We choose an embedded database for the following reasons: When we use Java as the programming language for our project When our application is standalone Preparing the development environment The fastest way to prepare the IDE for Neo4j is using Maven. Maven is a dependency management and automated building tool. In the following procedure, we will use NetBeans 7.4, but it works in a very similar way with the other IDEs (for Eclipse, you would need the m2eclipse plugin). The procedure is described as follows: Create a new Maven project as shown in the following screenshot: In the next page of the wizard, name the project, set a valid project location, and then click on Finish. After NetBeans has created the project, expand Project Files in the project tree and open the pom.xml file. In the <dependencies> tag, insert the following XML code: <dependencies> <dependency> <groupId>org.neo4j</groupId> <artifactId>neo4j</artifactId> <version>2.0.1</version> </dependency> </dependencies> <repositories> <repository> <id>neo4j</id> <url>http://m2.neo4j.org/content/repositories/releases/</url> <releases> <enabled>true</enabled> </releases> </repository> </repositories>   This code instructs Maven the dependency we are using on our project, that is, Neo4j. The version we have used here is 2.0.1. Of course, you can specify the latest available version. Once saved, the Maven file resolves the dependency, downloads the JAR files needed, and updates the Java build path. Now, the project is ready to use Neo4j and Cypher. Creating an embedded database Creating an embedded database is straightforward. First of all, to create a database, we need a GraphDatabaseFactory class, which can be done with the following code: GraphDatabaseFactory graphDbFactory = new GraphDatabaseFactory();   Then, we can invoke the newEmbeddedDatabase method with the following code: GraphDatabaseService graphDb = graphDbFactory .newEmbeddedDatabase("data/dbName");   Now, with the GraphDatabaseService class, we can fully interact with the database, create nodes, create relationships, set properties and indexes. Invoking Cypher from Java To execute Cypher queries on a Neo4j database, you need an instance of ExecutionEngine; this class is responsible for parsing and running Cypher queries, returning results in a ExecutionResult instance: import org.neo4j.cypher.javacompat.ExecutionEngine; import org.neo4j.cypher.javacompat.ExecutionResult; // ... ExecutionEngine engine = new ExecutionEngine(graphDb); ExecutionResult result = engine.execute("MATCH (e:Employee) RETURN e");   Note that we use the org.neo4j.cypher.javacompat package and not the org.neo4j.cypher package even though they are almost the same. The reason is that Cypher is written in Scala, and Cypher authors provide us with the former package for better Java compatibility. Now with the results, we can do one of the following options: Dumping to a string value Converting to a single column iterator Iterating over the full row Dumping to a string is useful for testing purposes: String dumped = result.dumpToString();   If we print the dumped string to the standard output stream, we will get the following result: Here, we have a single column (e) that contains the nodes. Each node is dumped with all its properties. The numbers between the square brackets are the node IDs, which are the long and unique values assigned by Neo4j on the creation of the node. When the result is single column, or we need only one column of our result, we can get an iterator over one column with the following code: import org.neo4j.graphdb.ResourceIterator; // ... ResourceIterator<Node> nodes = result.columnAs("e");   Then, we can iterate that column in the usual way, as shown in the following code: while(nodes.hasNext()) { Node node = nodes.next(); // do something with node }   However, Neo4j provides a syntax-sugar utility to shorten the code that is to be iterated: import org.neo4j.helpers.collection.IteratorUtil; // ... for (Node node : IteratorUtil.asIterable(nodes)) { // do something with node }   If we need to iterate over a multiple-column result, we would write this code in the following way: ResourceIterator<Map<String, Object>> rows = result.iterator(); for(Map<String,Object> row : IteratorUtil.asIterable(rows)) { Node n = (Node) row.get("e"); try(Transaction t = n.getGraphDatabase().beginTx()) { // do something with node } }   The iterator function returns an iterator of maps, where keys are the names of the columns. Note that when we have to work with nodes, even if they are returned by a Cypher query, we have to work in transaction. In fact, Neo4j requires that every time we work with the database, either reading or writing to the database, we must be in a transaction. The only exception is when we launch a Cypher query. If we launch the query within an existing transaction, Cypher will work as any other operation. No change will be persisted on the database until we commit the transaction, but if we run the query outside any transaction, Cypher will open a transaction for us and will commit changes at the end of the query. Summary We have now completed the setting up of a Neo4j database. We also learned about Cypher pattern matching. Resources for Article: Further resources on this subject: OpenSceneGraph: Advanced Scene Graph Components [Article] Creating Network Graphs with Gephi [Article] Building a bar graph cityscape [Article]
Read more
  • 0
  • 0
  • 6847

article-image-gathering-and-analyzing-stock-market-data-r-part-1-2
Erik Kappelman
07 Dec 2016
6 min read
Save for later

Gathering and analyzing stock market data with R Part 1 of 2

Erik Kappelman
07 Dec 2016
6 min read
This two-part blogseries walks through a set of R scripts used to collect and analyze data from the New York Stock Exchange. Collecting data in realtime from the stock market can be valuable in at least two ways. First, historical intraday trading data is valuable. There are many companies you can find around the Web that sell historical intraday trading data. This data can be used to make quick investment decisions. Investment strategies like day trading and short selling rely on being able to ride waves in the stock market that might only last a few hours or minutes. So, if a person could collect daily trading data for a time long enough, this data would eventually become valuable and could be sold. While almost any programming language can be used to collect data from the Internet, using R to collect stock market data is somewhat more convenient if R will be used to analyze and make predictions with the data. Additionally, I find R to be an intuitive scripting language that can be used for a wide range of solutions. I will first discuss how to create a script that can collect intraday trading data. I will then discuss using R to collect historical daily trading data. I will also discuss analyzing this data and making predictions from it. There is a lot of ground to cover, so this post is split into two parts. All of the code and accompanying files can be found in this repository. So, let’s get started. If you don’t have the R binaries installed, go ahead and get them as they are going to be a must for following along. Additionally, I would highly recommend using RStudio in development projects centered around R. Although there are absolutely flaws with RStudio, in my opinion, it is the best choice. library(httr) library(jsonlite) source('DataFetch.R') The above three lines source the file containing the functions that actually collect the data and load the required packages to execute their commands. Libraries are a common feature in R. Before you try to do something too complex, make sure that you check whether there is an existing library that already performs the operation. The R community is extensive and thriving, which makes using R for development that much better. Sys.sleep(55*60) frame.list<-list() ticker<-function(rest.time){ ptm<-proc.time() df<-data.frame(get.data(),Date= date()) timer.time<-proc.time()-ptm Sys.sleep(as.numeric(rest.time-timer.time[3])) return(list(df)) } The next lines of code stop the system until it is time for the stock market to open. I start this script before I go to work in the morning. So, 55*60 is about how many seconds pass between when I leave for work and the market opens. We then initialize an empty list using the next line of code. If you are new to R, you will notice the use of an arrow instead of an equals sign. Although the equals sign does work, many people, including me, use the arrow. This list is going to hold the dataframes containing the stock data that is created throughout the day. We then initialize the ticker function, which is used to repeatedly call the set of functions that retrieve the data and then return the data in the form of a dataframe. for(i in1:80){ frame.list<-c(suppressWarnings(ticker(5*30)),frame.list) } save(frame.list,file="RealTimeData.rda") The ticker function takes the number of seconds to wait between queries to the market as its only argument. This number is modified based on the length of time the query takes. This ensures that the timing of the data points is consistent. The ticker function is called eighty times in five minute intervals. The results are appended onto the list of dataframes. After the for-loop is completed. The data is saved in the R format. Now let’s look into the functions that fetch the data located in DataFetch.R. R code can become pretty verbose, so it is good to get in the habit of segmenting your code into multiple files. The functions used to fetch data are displayed below. We will start by discussing the parse.data function because it is the work horse, and the get.data function is more of a controller. parse.data<-function(symbols,range){ base.URL <-"http://finance.google.com/finance/info?client=ig&q=" start= min(range) end= max(range) symbol.string<-paste0("NYSE:",symbols[start],",") for(i in(start+1):end){ temp<- paste0("NYSE:",symbols[i],",") symbol.string<-paste(symbol.string,temp,sep="") } URL <-paste(base.URL,symbol.string,sep="") data<- GET(URL) now<- date() bin<- content(data,"raw") writeBin(bin,"data.txt") conn<- file("data.txt",open="r") linn<-readLines(conn) jstring<-"[" for(i in3:length(linn)){ jstring<- paste0(jstring,linn[i]) } close(conn) file.remove("data.txt") obj<-fromJSON(jstring) return(data.frame(Symbol=obj$t,Price=as.numeric(obj$l))) } The first function takes a list of stock symbols and the list indices of the symbols that are to be queried. The function then builds a string in the proper format to be used to query Google Finance for the latest price information on the chosen symbols. The query is performed using the ‘httr’ R package, a package used to perform HTTP tasks. The response from the web request is shuttled through a few formats in order to get the data into an easy-to-use format. The function then returns a dataframe containing the symbols and prices. get.data<-function(){ syms<- read.csv("NYSE.txt",header=2,sep="t") sb<-grep("[A-Z]{4}|[A-Z]{3}",syms$Symbol,perl= F, value = T) result<- c() in.list<-list() list.seq<-seq(1,2901,100) for(i in1:(length(list.seq)-1)){ range<-list.seq[i]:list.seq[i+1] result<-rbind(result,parse.data(sb,range)) } return(droplevels.data.frame(na.omit(result))) } The get.data function above is called by the ticker function. It serves as a controller on the parse.data function by calling for the prices in chunks so that the queries are small enough. It also reads the symbol list in from the "NYSE.txt" file, which is a simple list of stocks in the New York Stock Exchange and their symbols. The symbols are then put through a RegEx routine that eliminates symbols that do not follow the right format for Google Finance. Gathering intraday data from the stock market using R, or any language, is obviously somewhat of a pain; however, if properly executed, the results could be quite useful and valuable. I hope you read part two of this blog series where we use R to gather and analyze historical stock market data. About the author Erik Kappelman is a transportation modeler for the Montana Department of Transportation. He is also the CEO of Duplovici, a technology consulting and web design company.
Read more
  • 0
  • 0
  • 6845
article-image-advanced-effects-using-blender-particle-system
Packt
18 Aug 2010
6 min read
Save for later

Advanced Effects using Blender Particle System

Packt
18 Aug 2010
6 min read
(For more resources on Blender, see here.) The list above might be a bit daunting to some users but don't worry, I will discuss as much as I could (and bear with me when I ramble a lot) and hopefully I'll succeed in imbibing as much information as possible so when you're done reading this, you're proud to say: “I know Particle System!”, just like how Neo said in the Matrix: “I know Kung-Fu”. Unlike the previous articles that I've written before where I solely used one version of Blender through the entirety of the process, this time we might switch between the legacy Blender 2.4* and the recently-developed Blender 2.5*. The reason for this is that some Particle System features that we have been happily using in Blender 2.4* isn't merged yet in Blender 2.5*, making it unusable for the moment. I guess that is reasonable enough since Blender 2.5* is still undergoing heavy development and is still in beta stage. But who knows, maybe during this time of writing, it is already being developed or already is. So in line with that, here are the basic requirements for you to get going: Blender 2.49b (http://www.blender.org/download/get-blender/) Blender 2.53 (http://www.blender.org/development/release-logs/blender-250/) Basic Blender Particle System Knowledge (refer to http://www.packtpub.com/article/getting-started-with-blender-particle-system-1 for some info) lots and lots of patience! And just a bonus, we decided to provide you with the .blend files for all of our examples illustrated here. So hop on! Disintegration Effect The disintegration effect has been a common and very popular visual effect seen in feature movies, advertisements, and simply an eye candy. Often, it starts by having an object in its original and full form then after a while it will dissipate and disappear as though it was now made of dust. You can see this effect in one of the tests I did before here: http://vimeo.com/6763010. Much of the inspiration came from Daniel (aka NionsChannel in Youtube) who has really some nice effects on his list. The basic requirements for achieving this kind of effect are: a suitable particle system, highly subdivided mesh, and a force field. With that said, let's go ahead and start tinkering, shall we? Fire up Blender 2.49b and delete the default Cube (if any). (Move the mouse over the image to enlarge it.) (Deleting the Default Cube) Next, add or model the object of your choice. For purposes of this tutorial, let's add a simple UV Sphere with 256 Segments and 256 Rings, however, if your machine couldn't handle the high subdivision levels, you can lower it down to your liking. NOTE: The higher number of subdivisions you set, the finer and the more seamless the “shards” will be. Additionally, you can always go to Edit mode and press W > Subdivide to subdivide your mesh accordingly or adding a Subsurf modifier and applying it afterwards. The higher number of subdivisions you set, the finer and the more seamless the “shards” will be. Additionally, you can always go to Edit mode and press W > Subdivide to subdivide your mesh accordingly or adding a Subsurf modifier and applying it afterwards. (Adding a UV Sphere) (Highly Subdivided UV Sphere) After the UV Sphere has been added, proceed to Edit Mode and check over at the header the amount of faces it has. We'll use this as a base for the amount of particles that we'll be adding later on for the actual simulation. (UV Sphere Face Count) While in Edit Mode and all the vertices selected, press W then choose Set Smooth to smooth out the geometry shading. Now go back to Object Mode and proceed to Object (F7) in the Buttons Window then on the Particle Buttons, then click on Add New under Particle System tab to add a new particle system. (Adding a New Particle System) Rename the just-added particle system to something more relevant like “disintegration”. Then on the Amount input, we'll be changing the default 1000 to the number of faces our UV Sphere currently has (that's the reason we checked a while back in edit mode). So in this case, type in 65536. This will then correspond to one particle is equal to one face of our uv sphere. Next, change the End value to something shorter than 100 which is default. Let's try 40 for this example, which means all of the 65536 particles will be emitted within 40 frames. Basing from the default 25 frames per second rate, this would mean all those particles will be emitted in less than 2 seconds, which is what we want for this. Next is the Life value which we should be set to something longer as compared to the default 50 which is a little bit too early for our simulation. Let's set Life to 150; this will make our particles stay in our simulation area longer and not disappear earlier than expected. Under “Emit From:” panel, enable Random and Even then leave the other defaults as they are. Then finally, alter the values in the Physics tab and see which ones you are satisfied with. Check the screenshot below for some reference. (Particle System Settings) The next part is the icing on the cake, where we'll be adding a force field to generate the particle system's motion as though it was affected by real world effects like wind, turbulence, etc. With your cursor centered on your UV Sphere, add an Empty, name it “force”, and make sure the object rotation is cleared (ALT R) such that the local z-axis is oriented on the world z-axis. The UV Sphere and the Empty (“force”) should be in the same layer for the following effect to work. (Empty “force” Added) After adding our Empty object, we need to tell Blender how this object will affect our particle system. We'll do this by adding force values to this object. Forces in Blender act as external effectors for physics systems, which includes our particle system. You'll see what I mean in a while. Let's select the UV Sphere object and add a new Material Datablock to the object. (Adding a New Material to the Sphere) After adding a new material datablock, you can go ahead and tweak the material and shader settings the way you want to. Just like how I did mine (see screenshot): (Adding Material to the Sphere) With the Sphere still selected, head over to the Texture buttons under Shading (F5) and add a new texture slot. (Adding a New Texture) Next, choose Clouds as the Texture Type, increase the Noise Size and Noise Depth accordingly and just leave the Noise Basis to the default Blender Original, this will ensure a better distinction for the form that our particle system will exhibit later on. And the last but not the least, increase the Contrast of the texture, which will exaggerate the shape of our particle form later on. (Cloud Texture Settings)
Read more
  • 0
  • 0
  • 6845

article-image-linux-shell-script-logging-tasks
Packt
28 Jan 2011
7 min read
Save for later

Linux Shell Script: Logging Tasks

Packt
28 Jan 2011
7 min read
Linux Shell Scripting Cookbook Collecting information about the operating environment, logged in users, the time for which the computer has been powered on, and any boot failures are very helpful. This recipe will go through a few commands used to gather information about a live machine. Getting ready This recipe will introduce the commands who, w, users, uptime, last, and lastb. How to do it... To obtain information about users currently logged in to the machine use: $ who slynux   pts/0   2010-09-29 05:24 (slynuxs-macbook-pro.local) slynux   tty7    2010-09-29 07:08 (:0) Or: $ w 07:09:05 up  1:45,  2 users,  load average: 0.12, 0.06, 0.02 USER     TTY     FROM    LOGIN@   IDLE  JCPU PCPU WHAT slynux   pts/0   slynuxs 05:24  0.00s  0.65s 0.11s sshd: slynux slynux   tty7    :0      07:08  1:45m  3.28s 0.26s gnome-session It will provide information about logged in users, the pseudo TTY used by the users, the command that is currently executing from the pseudo terminal, and the IP address from which the users have logged in. If it is localhost, it will show the hostname. who and w format outputs with slight difference. The w command provides more detail than who. TTY is the device file associated with a text terminal. When a terminal is newly spawned by the user, a corresponding device is created in /dev/ (for example, /dev/pts/3). The device path for the current terminal can be found out by typing and executing the command tty. In order to list the users currently logged in to the machine, use: $ users Slynux slynux slynux hacker If a user has opened multiple pseudo terminals, it will show that many entries for the same user. In the above output, the user slynux has opened three pseudo terminals. The easiest way to print unique users is to use sort and uniq to filter as follows: $ users | tr ' ' 'n' | sort | uniq slynux hacker We have used tr to replace ' ' with 'n'. Then combination of sort and uniq will produce unique entries for each user. In order to see how long the system has been powered on, use: $ uptime 21:44:33 up  3:17,  8 users,  load average: 0.09, 0.14, 0.09 The time that follows the word up indicates the time for which the system has been powered on. We can write a simple one-liner to extract the uptime only. Load average in uptime's output is a parameter that indicates system load. In order to get information about previous boot and user logged sessions, use: $ last slynux tty7         :0              Tue Sep 28 18:27   still logged in reboot system boot 2.6.32-21-generi Tue Sep 28 18:10 - 21:46 (03:35) slynux pts/0      :0.0          Tue Sep 28 05:31 - crash (12:39) The last command will provide information about logged in sessions. It is actually a log of system logins that consists of information such as tty from which it has logged in, login time, status, and so on. The last command uses the log file /var/log/wtmp for input log data. It is also possible to explicitly specify the log file for the last command using the –f option. For example: $ last -f /var/log/wtmp In order to obtain info about login sessions for a single user, use: $ last USER Get information about reboot sessions as follows: $ last reboot reboot system boot 2.6.32-21-generi Tue Sep 28 18:10 - 21:48 (03:37) reboot system boot 2.6.32-21-generi Tue Sep 28 05:14 - 21:48 (16:33) In order to get information about failed user login sessions use: # lastb test     tty8    :0          Wed Dec 15 03:56 - 03:56  (00:00) slynux tty8    :0          Wed Dec 15 03:55 - 03:55  (00:00) You should run lastb as the root user. Logging access to files and directories Logging of file and directory access is very helpful to keep track of changes that are happening to files and folders. This recipe will describe how to log user accesses. Getting ready The inotifywait command can be used to gather information about file accesses. It doesn't come by default with every Linux distro. You have to install the inotify-tools package by using a package manager. It also requires the Linux kernel to be compiled with inotify support. Most of the new GNU/Linux distributions come with inotify enabled in the kernel. How to do it... Let's walk through the shell script to monitor the directory access: #/bin/bash #Filename: watchdir.sh #Description: Watch directory access path=$1 #Provide path of directory or file as argument to script inotifywait -m -r -e create,move,delete $path -q A sample output is as follows: $ ./watchdir.sh . ./ CREATE new ./ MOVED_FROM new ./ MOVED_TO news ./ DELETE news How it works... The previous script will log events create, move, and delete files and folders from the given path. The -m option is given for monitoring the changes continuously rather than going to exit after an event happens. -r is given for enabling a recursive watch the directories. -e specifies the list of events to be watched. -q is to reduce the verbose messages and print only required ones. This output can be redirected to a log file. We can add or remove the event list. Important events available are as follows: Logfile management with logrotate Logfiles are essential components of a Linux system's maintenance. Logfiles help to keep track of events happening on different services on the system. This helps the sysadmin to debug issues and also provides statistics on events happening on the live machine. Management of logfiles is required because as time passes the size of a logfile gets bigger and bigger. Therefore, we use a technique called rotation to limit the size of the logfile and if the logfile reaches a size beyond the limit, it will strip the logfile and store the older entries from the logfile in an archive. Hence older logs can be stored and kept for future reference. Let's see how to rotate logs and store them. Getting ready logrotate is a command every Linux system admin should know. It helps to restrict the size of logfile to the given SIZE. In a logfile, the logger appends information to the log file. Hence the recent information appears at the bottom of the log file. logrotate will scan specific logfiles according to the configuration file. It will keep the last 100 kilobytes (for example, specified SIZE = 100k) from the logfile and move rest of the data (older log data) to a new file logfile_name.1 with older entries. When more entries occur in the logfile (logfile_name.1) and it exceeds the SIZE, it updates the logfile with recent entries and creates logfile_name.2 with older logs. This process can easily be configured with logrotate.logrotate can also compress the older logs as logfile_name.1.gz, logfile_name2.gz, and so on. The option for whether older log files are to be compressed or not is available with the logrotate configuration. How to do it... logrotate has the configuration directory at /etc/logrotate.d. If you look at this directory by listing contents, many other logfile configurations can be found. We can write our custom configuration for our logfile (say /var/log/program.log) as follows: $ cat /etc/logrotate.d/program /var/log/program.log { missingok notifempty size 30k compress weekly rotate 5 create 0600 root root } Now the configuration is complete. /var/log/program.log in the configuration specifies the logfile path. It will archive old logs in the same directory path. Let's see what each of these parameters are: The options specified in the table are optional; we can specify the required options only in the logrotate configuration file. There are numerous options available with logrotate. Please refer to the man pages (http://linux.die.net/man/8/logrotate) for more information on logrotate.  
Read more
  • 0
  • 0
  • 6841

article-image-structural-equation-modeling-and-confirmatory-factor-analysis
Packt
06 Feb 2015
30 min read
Save for later

Structural Equation Modeling and Confirmatory Factor Analysis

Packt
06 Feb 2015
30 min read
In this article by Paul Gerrard and Radia M. Johnson, the authors of Mastering Scientific Computation with R, we'll discuss the fundamental ideas underlying structural equation modeling, which are often overlooked in other books discussing structural equation modeling (SEM) in R, and then delve into how SEM is done in R. We will then discuss two R packages, OpenMx and lavaan. We can directly apply our discussion of the linear algebra underlying SEM using OpenMx. Because of this, we will go over OpenMx first. We will then discuss lavaan, which is probably more user friendly because it sweeps the matrices and linear algebra representations under the rug so that they are invisible unless the user really goes looking for them. Both packages continue to be developed and there will always be some features better supported in one of these packages than in the other. (For more resources related to this topic, see here.) SEM model fitting and estimation methods To ultimately find a good solution, software has to use trial and error to come up with an implied covariance matrix that matches the observed covariance matrix as well as possible. The question is what does "as well as possible" mean? The answer to this is that the software must try to minimize some particular criterion, usually some sort of discrepancy function. Just what that criterion is depends on the estimation method used. The most commonly used estimation methods in SEM include: Ordinary least squares (OLS) also called unweighted least squares Generalized least squares (GLS) Maximum likelihood (ML) There are a number of other estimation methods as well, some of which can be done in R, but here we will stick with describing the most common ones. In general, OLS is the simplest and computationally cheapest estimation method. GLS is computationally more demanding, and ML is computationally more intensive. We will see why this is, as we discuss the details of these estimation methods. Any SEM estimation method seeks to estimate model parameters that recreate the observed covariance matrix as well as possible. To evaluate how closely an implied covariance matrix matches an observed covariance matrix, we need a discrepancy function. If we assume multivariate normality of the observed variables, the following function can be used to assess discrepancy: In the preceding figure, R is the observed covariance matrix, C is the implied covariance matrix, and V is a weight matrix. The tr function refers to the trace function, which sums the elements of the main diagonal. The choice of V varies based on the SEM estimation method: For OLS, V = I For GLS, V = R-1 In the case of an ML estimation, we seek to minimize one of a number of similar criteria to describe ML, as follows: In the preceding figure, n is the number of variables. There are a couple of points worth noting here. GLS estimation inverts the observed correlation matrix, something computationally demanding with large matrices, but something that must only be done once. Alternatively, ML requires inversion of the implied covariance matrix, which changes with each iteration. Thus, each iteration requires the computationally demanding step of matrix inversion. With modern fast computers, this difference may not be noticeable, but with large SEM models, this might start to be quite time-consuming. Assessing SEM model fit The final question in an SEM model is how well the model explains the data. This is answered with the use of SEM measures of fit. Most of these measures are based on a chi-squared distribution. The fit criteria for GLS and ML (as well as a number of other estimation procedures such as asymptotic distribution-free methods) multiplied by N-1 is approximately chi-square distributed. Here, the capital N represents the number of observations in the dataset, as opposed to lower case n, which gives the number of variables. We compute degrees of freedom as the difference between the number of estimated parameters and the number of known covariances (that is, the total number of values in one triangle of an observed covariance matrix). This gives way to the first test statistic for SEM models, a chi-squared significance level comparing our chi-square value to some minimum chi-square threshold to achieve statistical significance. As with conventional chi-square testing, a chi-square value that is higher than some minimal threshold will reject the null hypothesis. Most experimental science features such as rejection supports the hypothesis of the experiment. This is not the case in SEM, where the null hypothesis is that the model fits the data. Thus, a non-significant chi-square is an indicator of model fit, whereas a significant chi-square rejects model fit. A notable limitation of this is that a greater sample size, greater N, will increase the chi-square value and will therefore increase the power to reject model fit. Thus, using conventional chi-squared testing will tend to support models developed in small samples and reject models developed in large samples. The choice an interpretation of fit measures is a contentious one in SEM literature. However, as can be seen, chi-square has limitations. As such, other model fit criteria were developed that do not penalize models that fit in large samples (some may penalize models fit to small samples though). There are over a dozen indices, but the most common fit indices and interpretation information are as follows: Comparative fit index: In this index, a higher value is better. Conventionally, a value of greater than 0.9 was considered an indicator of good model fit, but some might argue that a value of at least 0.95 is needed. This is relatively sample size insensitive. Root mean square error of approximation: A value of under 0.08 (smaller is better) is often considered necessary to achieve model fit. However, this fit measure is quite sample size sensitive, penalizing small sample studies. Tucker-Lewis index (Non-normed fit index): This is interpreted in a similar manner as the comparative fit index. Also, this is not very sample size sensitive. Standardized root mean square residual: In this index, a lower value is better. A value of 0.06 or less is considered needed for model fit. Also, this may penalize small samples. In the next section, we will show you how to actually fit SEM models in R and how to evaluate fit using fit measures. Using OpenMx and matrix specification of an SEM We went through the basic principles of SEM and discussed the basic computational approach by which this can be achieved. SEM remains an active area of research (with an entire journal devoted to it, Structural Equation Modeling), so there are many additional peculiarities, but rather than delving into all of them, we will start by delving into actually fitting an SEM model in R. OpenMx is not in the CRAN repository, but it is easily obtainable from the OpenMx website, by typing the following in R: source('http://openmx.psyc.virginia.edu/getOpenMx.R')" Summarizing the OpenMx approach In this example, we will use OpenMx by specifying matrices as mentioned earlier. To fit an OpenMx model, we need to first specify the model and then tell the software to attempt to fit the model. Model specification involves four components: Specifying the model matrices; this has two parts: Declare starting values for the estimation Declaring which values can be estimated and which are fixed Telling OpenMx the algebraic relationship of the matrices that should produce an implied covariance matrix Giving an instruction for the model fitting criterion Providing a source of data The R commands that correspond to each of these steps are: mxMatrix mxAlgebra mxMLObjective mxData We will then pass the objects created with each of these commands to create an SEM model using mxModel. Explaining an entire example First, to make things simple, we will store the FALSE and TRUE logical values in single letter variables, which will be convenient when we have matrices full of TRUE and FALSE values as follows: F <- FALSE T <- TRUE Specifying the model matrices Specifying matrices is done with the mxMatrix function, which returns an MxMatrix object. (Note that the object starts with a capital "M" while the function starts with a lowercase "m.") Specifying an MxMatrix is much like specifying a regular R matrix, but MxMatrices has some additional components. The most notable difference is that there are actually two different matrices used to create an MxMatrix. The first is a matrix of starting values, and the second is a matrix that tells which starting values are free to be estimated and which are not. If a starting value is not freely estimable, then it is a fixed constant. Since the actual starting values that we choose do not really matter too much in this case, we will just pick one as a starting value for all parameters that we would like to be estimated. Let's take a look at the following example: mx.A <- mxMatrix( type = "Full", nrow=14, ncol=14, #Provide the Starting Values values = c(    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0 ), #Tell R which values are free to be estimated    free = c(    F, F, F, F, F, F, F, F, F, F, F, F, F, F,    F, F, F, F, F, F, F, F, F, F, F, F, T, F,    F, F, F, F, F, F, F, F, F, F, F, F, T, F,    F, F, F, F, F, F, F, F, F, F, F, F, T, F,    F, F, F, F, F, F, F, F, F, F, F, F, F, F,    F, F, F, F, F, F, F, F, F, F, F, F, F, T,    F, F, F, F, F, F, F, F, F, F, F, F, F, T,    F, F, F, F, F, F, F, F, F, F, F, F, F, T,    F, F, F, F, F, F, F, F, F, F, F, F, F, F,    F, F, F, F, F, F, F, F, F, F, F, T, F, F,    F, F, F, F, F, F, F, F, F, F, F, T, F, F,    F, F, F, F, F, F, F, F, F, F, F, F, F, F,    F, F, F, F, F, F, F, F, F, F, F, T, F, F,    F, F, F, F, F, F, F, F, F, F, F, T, T, F ), byrow=TRUE,   #Provide a matrix name that will be used in model fitting name="A", ) We will now apply this same technique to the S matrix. Here, we will create two S matrices, S1 and S2. They differ simply in the starting values that they supply. We will later try to fit an SEM model using one matrix, and then the other to address problems with the first one. The difference is that S1 uses starting variances of 1 in the diagonal, and S2 uses starting variances of 5. Here, we will use the "symm" matrix type, which is a symmetric matrix. We could use the "full" matrix type, but by using "symm", we are saved from typing all of the symmetric values in the upper half of the matrix. Let's take a look at the following matrix: mx.S1 <- mxMatrix("Symm", nrow=14, ncol=14, values = c(    1,    0, 1,    0, 0, 1,    0, 1, 0, 1,    1, 0, 0, 0, 1,    0, 1, 0, 0, 0, 1,    0, 0, 1, 0, 0, 0, 1,    0, 0, 0, 1, 0, 1, 0, 1,    0, 0, 0, 0, 0, 0, 0, 0, 1,    0, 0, 0, 0, 0, 0, 0, 0, 0, 1,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 ),      free = c(    T,    F, T,    F, F, T,    F, T, F, T,    T, F, F, F, T,    F, T, F, F, F, T,    F, F, T, F, F, F, T,    F, F, F, T, F, T, F, T,    F, F, F, F, F, F, F, F, T,    F, F, F, F, F, F, F, F, F, T,    F, F, F, F, F, F, F, F, F, F, T,    F, F, F, F, F, F, F, F, F, F, F, T,    F, F, F, F, F, F, F, F, F, F, F, F, T,    F, F, F, F, F, F, F, F, F, F, F, F, F, T ), byrow=TRUE, name="S" )   #The alternative, S2 matrix: mx.S2 <- mxMatrix("Symm", nrow=14, ncol=14, values = c(    5,    0, 5,    0, 0, 5,    0, 1, 0, 5,    1, 0, 0, 0, 5,    0, 1, 0, 0, 0, 5,    0, 0, 1, 0, 0, 0, 5,    0, 0, 0, 1, 0, 1, 0, 5,    0, 0, 0, 0, 0, 0, 0, 0, 5,    0, 0, 0, 0, 0, 0, 0, 0, 0, 5,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5 ),         free = c(    T,    F, T,    F, F, T,    F, T, F, T,    T, F, F, F, T,    F, T, F, F, F, T,    F, F, T, F, F, F, T,    F, F, F, T, F, T, F, T,    F, F, F, F, F, F, F, F, T,    F, F, F, F, F, F, F, F, F, T,    F, F, F, F, F, F, F, F, F, F, T,    F, F, F, F, F, F, F, F, F, F, F, T,    F, F, F, F, F, F, F, F, F, F, F, F, T,    F, F, F, F, F, F, F, F, F, F, F, F, F, T ), byrow=TRUE, name="S" ) mx.Filter <- mxMatrix("Full", nrow=11, ncol=14, values= c(        1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,      0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,        0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,        0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,        0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,        0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,        0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,        0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,        0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,        0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0    ),    free=FALSE,    name="Filter",    byrow = TRUE ) And finally, we will create our identity and filter matrices the same way, as follows: mx.I <- mxMatrix("Full", nrow=14, ncol=14,    values= c(        1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,        0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,        0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,        0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,        0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,        0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,        0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,        0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,        0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,        0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1    ),    free=FALSE,    byrow = TRUE,    name="I" ) Fitting the model Now, it is time to declare the model that we would like to fit using the mxModel command. This part includes steps 2 through step 4 mentioned earlier. Here, we will tell mxModel which matrices to use. We will then use the mxAlgegra command to tell R how the matrices should be combined to reproduce the implied covariance matrix. We will tell R to use ML estimation with the mxMLObjective command, and we will tell it to apply the estimation to a particular matrix algebra, which we named "C". This is simply the right-hand side of the McArdle McDonald equation. Finally, we will tell R where to get the data to use in model fitting using the following code: factorModel.1 <- mxModel("Political Democracy Model", #Model Matrices mx.A, mx.S1, mx.Filter, mx.I, #Model Fitting Instructions mxAlgebra(Filter %*% solve(I-A) %*% S %*% t(solve(I - A)) %*% t(Filter), name="C"),      mxMLObjective("C", dimnames = names(PoliticalDemocracy)),    #Data to fit mxData(cov(PoliticalDemocracy), type="cov", numObs=75) ) Now, let's tell R to fit the model and summarize the results using mxRun, as follows: summary(mxRun(factorModel.1)) Running Political Democracy Model Error in summary(mxRun(factorModel.1)) : error in evaluating the argument 'object' in selecting a method for function 'summary': Error: The job for model 'Political Democracy Model' exited abnormally with the error message: Expected covariance matrix is non-positive-definite. Uh oh! We got an error message telling us that the expected covariance matrix is not positive definite. Our observed covariance matrix is positive definite but the implied covariance matrix (at least at first) is not. This is an effect of the fact that if we multiply our starting value matrices together as specified by the McArdle McDonald equation, we get a starting implied covariance matrix. If we perform an eigenvalue decomposition of this starting implied covariance matrix, then we will find that the last eigenvalue is negative. This means a negative variance does not make much sense, and this is what "not positive definite" refers to. The good news is that this is simply our starting values, so we can fix this if we modify our starting values. In this case, we can choose values of five along the diagonal of the S matrix, and get a positive definite starting implied covariance matrix. We can rerun this using the mx.S2 matrix specified earlier and the software will proceed as follows: #Rerun with a positive definite matrix   factorModel.2 <- mxModel("Political Democracy Model", #Model Matrices mx.A, mx.S2, mx.Filter, mx.I, #Model Fitting Instructions mxAlgebra(Filter %*% solve(I-A) %*% S %*% t(solve(I - A)) %*% t(Filter), name="C"),    mxMLObjective("C", dimnames = names(PoliticalDemocracy)),    #Data to fit mxData(cov(PoliticalDemocracy), type="cov", numObs=75) )   summary(mxRun(factorModel.2)) This should provide a solution. As can be seen from the previous code, the parameters solved in the model are returned as matrix components. Just like we had to figure out how to go from paths to matrices, we now have to figure out how to go from matrices to paths (the reverse problem). In the following screenshot, we show just the first few free parameters: The preceding screenshot tells us that the parameter estimated in the position of the tenth row and twelfth column in the matrix A is 2.18. This corresponds to a path from the twelfth variable in the A matrix ind60, to the 10th variable in the matrix x2. Thus, the path coefficient from ind60 to x2 is 2.18. There are a few other pieces of information here. The first one tells us that the model has not converged but is "Mx status Green." This means that the model was still converging when it stopped running (that is, it did not converge), but an optimal solution was still found and therefore, the results are likely reliable. Model fit information is also provided suggesting a pretty good model fit with CFI of 0.99 and RMSEA of 0.032. This was a fair amount of work, and creating model matrices by hand from path diagrams can be quite tedious. For this reason, SEM fitting programs have generally adopted the ability to fit SEM by declaring paths rather than model matrices. OpenMx has the ability to allow declaration by paths, but applying model matrices has a few advantages. Principally, we get under the hood of SEM fitting. If we step back, we can see that OpenMx actually did very little for us that is specific to SEM. We told OpenMx how we wanted matrices multiplied together and which parameters of the matrix were free to be estimated. Instead of using the RAM specification, we could have passed the matrices of the LISREL or Bentler-Weeks models with the corresponding algebra methods to recreate an implied covariance matrix. This means that if we are trying to come up with our matrix specification, reproduce prior research, or apply a new SEM matrix specification method published in the literature, OpenMx gives us the power to do it. Also, for educators wishing to teach the underlying mathematical ideas of SEM, OpenMx is a very powerful tool. Fitting SEM models using lavaan If we were to describe OpenMx as the SEM equivalent of having a well-stocked pantry and full kitchen to create whatever you want, and you have the time and know how to do it, we might regard lavaan as a large freezer full of prepackaged microwavable dinners. It does not allow quite as much flexibility as OpenMx because it sweeps much of the work that we did by hand in OpenMx under the rug. Lavaan does use an internal matrix representation, but the user never has to see it. It is this sweeping under the rug that makes lavaan generally much easier to use. It is worth adding that the list of prepackaged features that are built into lavaan with minimal additional programming challenge many commercial SEM packages. The lavaan syntax The key to describing lavaan models is the model syntax, as follows: X =~ Y: Y is a manifestation of the latent variable X Y ~ X: Y is regressed on X Y ~~ X: The covariance between Y and X can be estimated Y ~ 1: This estimates the intercept for Y (implicitly requires mean structure) Y | a*t1 + b*t2: Y has two thresholds that is a and b Y ~ a * X: Y is regressed on X with coefficient a Y ~ start(a) * X: Y is regressed on X; the starting value used for estimation is a It may not be evident at first, but this model description language actually makes lavaan quite powerful. Wherever you have seen a or b in the previous examples, a variable or constant can be used in their place. The beauty of this is that multiple parameters can be constrained to be equal simply by assigning a single parameter name to them. Using lavaan, we can fit a factor analysis model to our physical functioning dataset with only a few lines of code: phys.func.data <- read.csv('phys_func.csv')[-1] names(phys.func.data) <- LETTERS[1:20] R has a built-in vector named LETTERS, which contains all of the capital letters of the English alphabet. The lower case vector letters contains the lowercase alphabet. We will then describe our model using the lavaan syntax. Here, we have a model of three latent variables, our factors, and each of them has manifest variables. Let's take a look at the following example: model.definition.1 <- ' #Factors    Cognitive =~ A + Q + R + S    Legs =~ B + C + D + H + I + J + M + N    Arms =~ E + F+ G + K +L + O + P + T    #Correlations Between Factors    Cognitive ~~ Legs    Cognitive ~~ Arms    Legs ~~ Arms ' We then tell lavaan to fit the model as follows: fit.phys.func <- cfa(model.definition.1, data=phys.func.data, ordered= c('A','B', 'C','D', 'E','F','G', 'H','I','J', 'K', 'L','M','N','O','P','Q','R', 'S', 'T')) In the previous code, we add an ordered = argument, which tells lavaan that some variables are ordinal in nature. In response, lavaan estimates polychoric correlations for these variables. Polychoric correlations assume that we binned a continuous variable into discrete categories, and attempts to explicitly model correlations assuming that there is some continuous underlying variable. Part of this requires finding thresholds (placed on an arbitrary scale) between each categorical response. (for example, threshold 1 falls between the response of 1 and 2, and so on). By telling lavaan to treat some variables as categorical, lavaan will also know to use a special estimation method. Lavaan will use diagonally weighted least squares, which does not assume normality and uses the diagonals of the polychoric correlation matrix for weights in the discrepancy function. With five response options, it is questionable as to whether polychoric correlations are truly needed. Some analysts might argue that with many response options, the data can be treated as continuous, but here we use this method to show off lavaan's capabilities. All SEM models in lavaan use the lavaan command. Here, we use the cfa command, which is one of a number of wrapper functions for the lavaan command. Others include sem and growth. These commands differ in the default options passed to the lavaan command. (For full details, see the package documentation.) Summarizing the data, we can see the loadings of each item on the factor as well as the factor intercorrelations. We can also see the thresholds between each category from the polychoric correlations as follows: summary(fit.phys.func) We can also assess things such as model fit using the fitMeasures command, which has most of the popularly used fit measures and even a few obscure ones. Here, we tell lavaan to simply extract three measures of model fit as follows: fitMeasures(fit.phys.func, c('rmsea', 'cfi', 'srmr')) Collectively, these measures suggest adequate model fit. It is worth noting here that the interpretation of fit measures largely comes from studies using maximum likelihood estimation, and there is some debate as to how well these generalize other fitting methods. The lavaan package also has the capability to use other estimators that treat the data as truly continuous in nature. For this, a particular dataset is far from multivariate normal distributed, so an estimator such as ML is appropriate to use. However, if we wanted to do so, the syntax would be as follows: fit.phys.func.ML <- cfa(model.definition.1, data=phys.func.data, estimator = 'ML') Comparing OpenMx to lavaan It can be seen that lavaan has a much simpler syntax that allows to rapidly model basic SEM models. However, we were a bit unfair to OpenMx because we used a path model specification for lavaan and a matrix specification for OpenMx. The truth is that OpenMx is still probably a bit wordier than lavaan, but let's apply a path model specification in each to do a fair head-to-head comparison. We will use the famous Holzinger-Swineford 1939 dataset here from the lavaan package to do our modeling, as follows: hs.dat <- HolzingerSwineford1939 We will create a new dataset with a shorter name so that we don't have to keep typing HozlingerSwineford1939. Explaining an example in lavaan We will learn to fit the Holzinger-Swineford model in this section. We will start by specifying the SEM model using the lavaan model syntax: hs.model.lavaan <- ' visual =~ x1 + x2 + x3 textual =~ x4 + x5 + x6 speed   =~ x7 + x8 + x9   visual ~~ textual visual ~~ speed textual ~~ speed '   fit.hs.lavaan <- cfa(hs.model.lavaan, data=hs.dat, std.lv = TRUE) summary(fit.hs.lavaan) Here, we add the std.lv argument to the fit function, which fixes the variance of the latent variables to 1. We do this instead of constraining the first factor loading on each variable to 1. Only the model coefficients are included for ease of viewing in this book. The result is shown in the following model: > summary(fit.hs.lavaan) …                      Estimate Std.err Z-value P(>|z|) Latent variables: visual =~    x1               0.900   0.081   11.127   0.000    x2               0.498   0.077   6.429   0.000    x3              0.656   0.074   8.817   0.000 textual =~    x4               0.990   0.057   17.474   0.000    x5               1.102   0.063   17.576   0.000    x6               0.917   0.054   17.082   0.000 speed =~    x7               0.619   0.070   8.903   0.000    x8               0.731   0.066   11.090   0.000    x9               0.670   0.065   10.305   0.000   Covariances: visual ~~    textual           0.459   0.064   7.189   0.000    speed             0.471   0.073   6.461   0.000 textual ~~    speed             0.283   0.069   4.117   0.000 Let's compare these results with a model fit in OpenMx using the same dataset and SEM model. Explaining an example in OpenMx The OpenMx syntax for path specification is substantially longer and more explicit. Let's take a look at the following model: hs.model.open.mx <- mxModel("Holzinger Swineford", type="RAM",      manifestVars = names(hs.dat)[7:15], latentVars = c('visual', 'textual', 'speed'),    # Create paths from latent to observed variables mxPath(        from = 'visual',        to = c('x1', 'x2', 'x3'),    free = c(TRUE, TRUE, TRUE),    values = 1          ), mxPath(        from = 'textual',        to = c('x4', 'x5', 'x6'),        free = c(TRUE, TRUE, TRUE),        values = 1      ), mxPath(    from = 'speed',    to = c('x7', 'x8', 'x9'),    free = c(TRUE, TRUE, TRUE),    values = 1      ), # Create covariances among latent variables mxPath(    from = 'visual',    to = 'textual',    arrows=2,    free=TRUE      ), mxPath(        from = 'visual',        to = 'speed',        arrows=2,        free=TRUE      ), mxPath(        from = 'textual',        to = 'speed',        arrows=2,        free=TRUE      ), #Create residual variance terms for the latent variables mxPath(    from= c('visual', 'textual', 'speed'),    arrows=2, #Here we are fixing the latent variances to 1 #These two lines are like st.lv = TRUE in lavaan    free=c(FALSE,FALSE,FALSE),    values=1 ), #Create residual variance terms mxPath( from= c('x1', 'x2', 'x3', 'x4', 'x5', 'x6', 'x7', 'x8', 'x9'),    arrows=2, ),    mxData(        observed=cov(hs.dat[,c(7:15)]),        type="cov",        numObs=301    ) )     fit.hs.open.mx <- mxRun(hs.model.open.mx) summary(fit.hs.open.mx) Here are the results of the OpenMx model fit, which look very similar to lavaan's. This gives a long output. For ease of viewing, only the most relevant parts of the output are included in the following model (the last column that R prints giving the standard error of estimates is also not shown here): > summary(fit.hs.open.mx) …   free parameters:                            name matrix     row     col Estimate Std.Error 1   Holzinger Swineford.A[1,10]     A     x1 visual 0.9011177 2   Holzinger Swineford.A[2,10]     A     x2 visual 0.4987688 3   Holzinger Swineford.A[3,10]     A     x3 visual 0.6572487 4   Holzinger Swineford.A[4,11]     A     x4 textual 0.9913408 5   Holzinger Swineford.A[5,11]     A     x5 textual 1.1034381 6   Holzinger Swineford.A[6,11]     A     x6 textual 0.9181265 7   Holzinger Swineford.A[7,12]     A     x7   speed 0.6205055 8   Holzinger Swineford.A[8,12]     A     x8 speed 0.7321655 9   Holzinger Swineford.A[9,12]     A     x9   speed 0.6710954 10   Holzinger Swineford.S[1,1]     S     x1     x1 0.5508846 11   Holzinger Swineford.S[2,2]     S     x2     x2 1.1376195 12   Holzinger Swineford.S[3,3]     S    x3     x3 0.8471385 13   Holzinger Swineford.S[4,4]     S     x4     x4 0.3724102 14   Holzinger Swineford.S[5,5]     S     x5     x5 0.4477426 15   Holzinger Swineford.S[6,6]     S     x6     x6 0.3573899 16   Holzinger Swineford.S[7,7]      S     x7     x7 0.8020562 17   Holzinger Swineford.S[8,8]     S     x8     x8 0.4893230 18   Holzinger Swineford.S[9,9]     S     x9     x9 0.5680182 19 Holzinger Swineford.S[10,11]     S visual textual 0.4585093 20 Holzinger Swineford.S[10,12]     S visual   speed 0.4705348 21 Holzinger Swineford.S[11,12]     S textual   speed 0.2829848 In summary, the results agree quite closely. For example, looking at the coefficient for the path going from the latent variable visual to the observed variable x1, lavaan gives an estimate of 0.900 while OpenMx computes a value of 0.901. Summary The lavaan package is user friendly, pretty powerful, and constantly adding new features. Alternatively, OpenMx has a steeper learning curve but tremendous flexibility in what it can do. Thus, lavaan is a bit like a large freezer full of prepackaged microwavable dinners, whereas OpenMx is like a well-stocked pantry with no prepared foods but a full kitchen that will let you prepare it if you have the time and the know-how. To run a quick analysis, it is tough to beat the simplicity of lavaan, especially given its wide range of capabilities. For large complex models, OpenMx may be a better choice. The methods covered here are useful to analyze statistical relationships when one has all of the data from events that have already occurred. Resources for Article: Further resources on this subject: Creating your first heat map in R [article] Going Viral [article] Introduction to S4 Classes [article]
Read more
  • 0
  • 0
  • 6841
article-image-9-recommended-blockchain-online-courses
Guest Contributor
27 Sep 2018
7 min read
Save for later

9 recommended blockchain online courses

Guest Contributor
27 Sep 2018
7 min read
Blockchain is reshaping the world as we know it. And we are not talking metaphorically because the new technology is really influencing everything from online security and data management to governance and smart contracting. Statistical reports support these claims. According to the study, the blockchain universe grows by over 40% annually, while almost 70% of banks are already experimenting with this technology. IT experts at the Editing AussieWritings.com Services claim that the potential in this field is almost limitless: “Blockchain offers a myriad of practical possibilities, so you definitely want to get acquainted with it more thoroughly.” Developers who are curious about blockchain can turn it into a lucrative career opportunity since it gives them the chance to master the art of cryptography, hierarchical distribution, growth metrics, transparent management, and many more. There were 5,743 mostly full-time job openings calling for blockchain skills in the last 12 months - representing the 320% increase - while the biggest freelancing website Upwork reported more than 6,000% year-over-year growth. In this post, we will recommend our 9 best blockchain online courses. Let’s take a look! Udemy Udemy offers users one of the most comprehensive blockchain learning sources. The target audience is people who have heard a little bit about the latest developments in this field, but want to understand more. This online course can help you to fully understand how the blockchain works, as well as get to grips with all that surrounds it. Udemy breaks down the course into several less complicated units, allowing you to figure out this complex system rather easily. It costs $19.99, but you can probably get it with a 40% discount. The one downside, however, is that content quality in terms of subject scope can vary depending on the instructor, but user reviews are a good way to gauge quality. Each tutorial lasts approximately 30 minutes, but it also depends on your own tempo and style of work. Pluralsight Pluralsight is an excellent beginner-level blockchain course. It comes in three versions: Blockchain Fundamentals, Surveying Blockchain Technologies for Enterprise, and Introduction to Bitcoin and Decentralized Technology. Course duration varies from 80 to 200 minutes depending on the package. The price of Pluralsight is $29 a month or $299 a year. Choosing one of these options, you are granted access to the entire library of documents, including course discussions, learning paths, channels, skill assessments, and other similar tools. Packt Publishing Packt Publishing has a wide portfolio of learning products on Blockchain for varying levels of experience in the field from beginners to experts. And what’s even more interesting is that you can choose your learning format from books, ebooks to videos, courses and live courses. Or you could simply subscribe to MAPT, their library to gain access to all products at a reasonable price of $29 monthly and $150 annually.  It offers several books and videos on the leading blockchain technology. You can purchase 5 blockchain titles at a discounted rate of $50. Here’s the list of top blockchain courses offered by Packt Publishing: Exploring Blockchain and Crypto-currencies: You will gain the foundational understanding of blockchain and crypto-currencies through various use-cases. Building Blockchain Projects: In this, you will be able to develop real-time practical DApps with Ethereum and JavaScript. Mastering Blockchain - Second Edition: You can learn about cryptography and cryptocurrencies, so you can build highly secure, decentralized applications and conduct trusted in-app transactions. Hands-On Blockchain with Hyperledger: This book will help you leverage the power of Hyperledger Fabric to develop Blockchain-based distributed ledgers with ease. Learning Blockchain Application Development [video ]: This interactive video will help you learn build smart contracts and DApps on Ethereum. Create Ethereum and Blockchain Applications using Solidity [video ]: This video will help you learn about Ethereum, Solidity, DAO, ICO, Bitcoin, Altcoin, Website Security, Ripple, Litecoin, Smart Contracts, and Apps. Cryptozombies Cryptozombies is an online blockchain course based on gamification elements. The tool teaches you to write smart contracts in Solidity through building your own crypto-collectibles game. It is entirely Ethereum-focused, but you don’t need any previous experience to understand how Solidity works. There is a step by step guide that explains to you even the smallest details, so you can quickly learn to create your own fully-functional blockchain-based game. The best thing about Cryptozombies is that you can test it for free and give up in case you don’t like it. Coursera The blockchain is the epicenter of the cryptocurrency world, so it’s necessary to study it if you want to deal with Bitcoin and other digital currencies. Coursera is the leading online resource in the field of virtual currencies, so you might want to check it out. After this course like Blockchain Specialization, you’ll know everything you need to be able to separate fact from fiction when reading claims about Bitcoin and other cryptocurrencies. You’ll have the conceptual foundations you need to engineer to secure software that interacts with the Bitcoin network. And you’ll be able to integrate ideas from Bitcoin in your own projects. The course is a 4-part course spanning a duration 4 weeks, but you can take each part separately. The price depends on the level and features you choose. LinkedIn Learning (formerly known as Lynda) LinkedIn Learning (what used to be Lynda) doesn't offer a specific blockchain course, but it does have a wide range of industry-related learning sources. A search for ‘blockchain’ will present you with almost 100 relevant video courses. You can find all sorts of lessons here, from beginner to expert levels. Lynda allows you to customize selection according to video duration, authors, software, subjects, etc. You can access the library for $15 a month. B9Lab B9Lab ETH-25 Certified Online Ethereum Developer Course is another course that promotes blockchain technology aimed at the Ethereum platform. It’s a 12-week in-depth learning solution that targets experienced programmers. B9Lab introduces everything there is to know about blockchain and how to build useful applications. Participants are taught about the Ethereum platform, the programming language Solidity, how to use web3 and the Truffle framework, and how to tie everything together. The price is €1450 or about $1700. IBM IBM made a self-paced blockchain course, titled Blockchain Essentials that lasts over two hours. The video lectures and lab in this course help you learn about blockchain for business and explore key use cases that demonstrate how the technology adds value. You can learn how to leverage blockchain benefits, transform your business with the new technology, and transfer assets. Besides that, you get a nice wrap-up and a quiz to test your knowledge upon completion. IBM’s course is free of charge. Khan Academy Khan Academy is the last, but certainly not the least important online course on our list. It gives users a comprehensive overview of blockchain-powered systems, particularly Bitcoin. Using this platform, you can learn more on cryptocurrency transactions, security, proof of work, etc. As an online education platform, Khan Academy won’t cost you a dime. [dropcap]B[/dropcap]lockchain is the groundbreaking technology that opens new boundaries in almost every field of business. It directly influences financial markets, data management, digital security, and a variety of other industries. In this post, we presented 9 best blockchain online courses you should try. These sources can teach you everything there is to know about the blockchain basics. Take some time to check them out and you won’t regret it! Author Bio: Olivia is a passionate blogger who writes on topics of digital marketing, career, and self-development. She constantly tries to learn something new and to share this experience on various websites. Connect with her on Facebook and Twitter. Google introduces Machine Learning courses for AI beginners Microsoft start AI School to teach Machine Learning and Artificial Intelligence.
Read more
  • 0
  • 0
  • 6810

article-image-php-data-objects-error-handling
Packt
22 Oct 2009
11 min read
Save for later

PHP Data Objects: Error Handling

Packt
22 Oct 2009
11 min read
In this article, we will extend our application so that we can edit existing records as well as add new records. As we will deal with user input supplied via web forms, we have to take care of its validation. Also, we may add error handling so that we can react to non-standard situations and present the user with a friendly message. Before we proceed, let's briefly examine the sources of errors mentioned above and see what error handling strategy should be applied in each case. Our error handling strategy will use exceptions, so you should be familiar with them. If you are not, you can refer to Appendix A, which will introduce you to the new object-oriented features of PHP5. We have consciously chosen to use exceptions, even though PDO can be instructed not to use them, because there is one situation where they cannot be avoided. The PDO constructors always throw an exception when the database object cannot be created, so we may as well use exceptions as our main error‑trapping method throughout the code. Sources of Errors To create an error handling strategy, we should first analyze where errors can happen. Errors can happen on every call to the database, and although this is rather unlikely, we will look at this scenario. But before doing so, let's check each of the possible error sources and define a strategy for dealing with them. This can happen on a really busy server, which cannot handle any more incoming connections. For example, there may be a lengthy update running in the background. The outcome is that we are unable to get any data from the database, so we should do the following. If the PDO constructor fails, we present a page displaying a message, which says that the user's request could not be fulfilled at this time and that they should try again later. Of course, we should also log this error because it may require immediate attention. (A good idea would be emailing the database administrator about the error.) The problem with this error is that, while it usually manifests itself before a connection is established with the database (in a call to PDO constructor), there is a small risk that it can happen after the connection has been established (on a call to a method of the PDO or PDO Statement object when the database server is being shutdown). In this case, our reaction will be the same—present the user with an error message asking them to try again later. Improper Configuration of the Application This error can only occur when we move the application across servers where database access details differ; this may be when we are uploading from a development server to production server, where database setups differ. This is not an error that can happen during normal execution of the application, but care should be taken while uploading as this may interrupt the site's operation. If this error occurs, we can display another error message like: "This site is under maintenance". In this scenario, the site maintainer should react immediately, as without correcting, the connection string the application cannot normally operate. Improper Validation of User Input This is an error which is closely related to SQL injection vulnerability. Every developer of database-driven applications must undertake proper measures to validate and filter all user inputs. This error may lead to two major consequences: Either the query will fail due to malformed SQL (so that nothing particularly bad happens), or an SQL injection may occur and application security may be compromised. While their consequences differ, both these problems can be prevented in the same way. Let's consider the following scenario. We accept some numeric value from a form and insert it into the database. To keep our example simple, assume that we want to update a book's year of publication. To achieve this, we can create a form that has two fields: A hidden field containing the book's ID, and a text field to enter the year. We will skip implementation details here, and see how using a poorly designed script to process this form could lead to errors and put the whole system at risk. The form processing script will examine two request variables:$_REQUEST['book'], which holds the book's ID and $_REQUEST['year'], which holds the year of publication. If there is no validation of these values, the final code will look similar to this: $book = $_REQUEST['book'];$year = $_REQUEST['year'];$sql = "UPDATE books SET year=$year WHERE id=$book";$conn->query($sql); Let's see what happens if the user leaves the book field empty. The final SQL would then look like: UPDATE books SET year= WHERE id=1; This SQL is malformed and will lead to a syntax error. Therefore, we should ensure that both variables are holding numeric values. If they don't, we should redisplay the form with an error message. Now, let's see how an attacker might exploit this to delete the contents of the entire table. To achieve this, they could just enter the following into the year field: 2007; DELETE FROM books; This turns a single query into three queries: UPDATE books SET year=2007; DELETE FROM books; WHERE book=1; Of course, the third query is malformed, but the first and second will execute, and the database server will report an error. To counter this problem, we could use simple validation to ensure that the year field contains four digits. However, if we have text fields, which can contain arbitrary characters, the field's values must be escaped prior to creating the SQL. Inserting a Record with a Duplicate Primary Key or Unique Index Value This problem may happen when the application is inserting a record with duplicate values for the primary key or a unique index. For example, in our database of authors and books, we might want to prevent the user from entering the same book twice by mistake. To do this, we can create a unique index of the ISBN column of the books table. As every book has a unique ISBN, any attempt to insert the same ISBN will generate an error. We can trap this error and react accordingly, by displaying an error message asking the user to correct the ISBN or cancel its addition. Syntax Errors in SQL Statements This error may occur if we haven't properly tested the application. A good application must not contain these errors, and it is the responsibility of the development team to test every possible situation and check that every SQL statement performs without syntax errors. If this type of an error occurs, then we trap it with exceptions and display a fatal error message. The developers must correct the situation at once. Now that we have learned a bit about possible sources of errors, let's examine how PDO handles errors. Types of Error Handling in PDO By default, PDO uses the silent error handling mode. This means that any error that arises when calling methods of the PDO or PDOStatement classes go unreported. With this mode, one would have to call PDO::errorInfo(), PDO::errorCode(), PDOStatement::errorInfo(), or PDOStatement::errorCode(), every time an error occurred to see if it really did occur. Note that this mode is similar to traditional database access—usually, the code calls mysql_errno(),and mysql_error() (or equivalent functions for other database systems) after calling functions that could cause an error, after connecting to a database and after issuing a query. Another mode is the warning mode. Here, PDO will act identical to the traditional database access. Any error that happens during communication with the database would raise an E_WARNING error. Depending on the configuration, an error message could be displayed or logged into a file. Finally, PDO introduces a modern way of handling database connection errors—by using exceptions. Every failed call to any of the PDO or PDOStatement methods will throw an exception. As we have previously noted, PDO uses the silent mode, by default. To switch to a desired error handling mode, we have to specify it by calling PDO::setAttribute() method. Each of the error handling modes is specified by the following constants, which are defined in the PDO class: PDO::ERRMODE_SILENT – the silent strategy. PDO::ERRMODE_WARNING – the warning strategy. PDO::ERRMODE_EXCEPTION – use exceptions. To set the desired error handling mode, we have to set the PDO::ATTR_ERRMODE attribute in the following way: $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); To see how PDO throws an exception, edit the common.inc.php file by adding the above statement after the line #46. If you want to test what will happen when PDO throws an exception, change the connection string to specify a nonexistent database. Now point your browser to the books listing page. You should see an output similar to: This is PHP's default reaction to uncaught exceptions—they are regarded as fatal errors and program execution stops. The error message reveals the class of the exception, PDOException, the error description, and some debug information, including name and line number of the statement that threw the exception. Note that if you want to test SQLite, specifying a non-existent database may not work as the database will get created if it does not exist already. To see that it does work for SQLite, change the $connStr variable on line 10 so that there is an illegal character in the database name: $connStr = 'sqlite:/path/to/pdo*.db'; Refresh your browser and you should see something like this: As you can see, a message similar to the previous example is displayed, specifying the cause and the location of the error in the source code. Defining an Error Handling Function If we know that a certain statement or block of code can throw an exception, we should wrap that code within the try…catch block to prevent the default error message being displayed and present a user-friendly error page. But before we proceed, let's create a function that will render an error message and exit the application. As we will be calling it from different script files, the best place for this function is, of course, the common.inc.php file. Our function, called showError(), will do the following: Render a heading saying "Error". Render the error message. We will escape the text with the htmlspecialchars() function and process it with the nl2br() function so that we can display multi-line messages. (This function will convert all line break characters to tags.) Call the showFooter() function to close the opening and tags. The function will assume that the application has already called the showHeader() function. (Otherwise, we will end up with broken HTML.) We will also have to modify the block that creates the connection object in common.inc.php to catch the possible exception. With all these changes, the new version of common.inc.php will look like this: <?php/*** This is a common include file* PDO Library Management example application* @author Dennis Popel*/// DB connection string and username/password$connStr = 'mysql:host=localhost;dbname=pdo';$user = 'root';$pass = 'root';/*** This function will render the header on every page,* including the opening html tag,* the head section and the opening body tag.* It should be called before any output of the/*** This function will 'close' the body and html* tags opened by the showHeader() function*/function showFooter(){?></body></html><?php}/*** This function will display an error message, call the* showFooter() function and terminate the application* @param string $message the error message*/function showError($message){echo "<h2>Error</h2>";echo nl2br(htmlspecialchars($message));showFooter();exit();}// Create the connection objecttry{$conn = new PDO($connStr, $user, $pass);$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);}catch(PDOException $e){showHeader('Error');showError("Sorry, an error has occurred. Please try your requestlatern" . $e->getMessage());} As you can see, the newly created function is pretty straightforward. The more interesting part is the try…catch block that we use to trap the exception. Now with these modifications we can test how a real exception will get processed. To do that, make sure your connection string is wrong (so that it specifies wrong databasename for MySQL or contains invalid file name for SQLite). Point your browser to books.php and you should see the following window:
Read more
  • 0
  • 0
  • 6809
Modal Close icon
Modal Close icon