Guidance for Blacksmiths

Before reading this page, you should probably read Setting up HabitRPG Locally and Contributing to HabitRPG (from the "Coders (Web & Mobile)" section onwards).

Git
Here is a small tutorial to explain how to fork habitrpg, create new feature branches, and perform pull requests:

1. Fork & Clone HabitRPG
The easiest way is to go on the habitrpg github page and click the Fork button. Then do a git clone of your own repository.

Another way is to

git

directly, then (and note you need to change "YourUsername" with your actual github.com username)

cd habitrpg git

Finally,

git push

to update your fork.

2. Setup Upstream Remote
Next step is to keep track of the upstream habitrpg repository, so we can easily update our fork with the latest changes. Because origin is the url to our fork, we will create another remote upstream for habitrpg:

Then, every time you want to get habitrpg's latest code, you can do:

git fetch upstream

3. Rebase Branch
When you want to update your local branch, you need to rebase it from the upstream branch. Let's update develop:

git checkout develop git fetch upstream git rebase upstream/develop git push # to update your fork

Note: If you want to work on a feature that was started by someone else, and not yet merged in upstream, you can use the same technique as shown in 2. & 3. to pull their commits:

Let's say the user 'Fandekasp' wrote a feature branch 'add_theme' which is awaiting a pull request into upstream/audio. You also have your fork of upstream/audio, and would like to get Fandekasp's changes right now.

git remote add fandekasp https://github.com/Fandekasp/habitrpg.git git fetch fandekasp git rebase fandekasp/add_themes

4. Create a New Feature
Now, you want to start a new task, and pull request it. First, you need to find which branch to start the feature from. Do a  to see the list of remote branches. Choose the appropriate branch depending on the task you want to work on.

Let's say you decide to start your task from the develop branch. To create your feature, do:

git checkout -b relevant_branch_name upstream/develop

Then you can  it in your fork.

5. Write Commits
When developing code, It is recommend that you do regular small commits, which are easier to review than a big one. It's also easier for developers to refuse or edit one specific commit that needs changes.

For example, you could have a commit dedicated to tests, followed by one with ui changes, one to write the logic, and one for the translations.

If your changes are done in several days, make sure you rebase your branch to properly update your code. If some conflicts appear, you'll often want to simply use the upstream version:

git rebase -Xours upstream/develop

6. Pull Request
When you're done with your code, or half-done but wish to do the pull request anyway (to get some feedback, for example), push your branch code in your fork.

You can then go to https://github.com/YourUsername/habitrpg.git, and you will see a Pull request button. When doing the pull request, verify that the merge will be done in the correct branch. It often defaults to upstream/develop even if you created your branch from another one.

Another way is to use the tool hub (install it with your package manager), then run  from your branch.

7. Issues
You tried to do a rebase and now you have many new and irrelevant commits in your branch.

No worries, there are several solutions to get out of that mess:

Cherry-pick
One solution is to keep a  window open, with all your commits hash available.

You can now perform  (if there is only one commit to remove, otherwise HEAD~nb_of_commits). If you delete too many commits, just  again from your fork. Then, copy the commit hash of the commits you want to re-add on top of your branch (from the git log window you left open before doing a reset). For example,

git cherry-pick ee1768b7e2c0bcee9eff1a45be1e3543fac0687b

This will re-add commit ee1768b7e2c0bcee9eff1a45be1e3543fac0687b on top of HEAD.

Stash
if your attempt to push failed because remote has a different HEAD than your local repo (this happens to people working together on the same branch and pushing on the same remote), don't pull! Instead, do

git reset --soft HEAD~n_local_commits_ahead git stash git pull git stash pop git commit -am "message"

8. Work with habitrpg-shared
NOTE!  habitrpg-shared has been deprecated. You no longer need -- or should be -- working in habitrpg-shared. The habitrpg-shared files have been moved to the /common subdirectory under habitrpg, and all actions you used to do in habitrpg-shared can be done there -- without needing to go through grunt for changes to be applied..

Ok, you started working on a task, and you realized you needed to modify some code that is present in node_modules/habitrpg-shared (for example, for translations).

You could do your modifications in the habitrpg repo to test then with grunt, then do those modifications again in the habitrpg-shared repo to commit and pull-request them, but that would be quite inefficient.

Here is a better way to do it. Let's assume you've already forked habitrpg-shared in your github account, but haven't cloned it yet:

cd habitrpg/ rm -rf node_modules/habitrpg-shared cd node_modules && git clone https://github.com/YourUsername/habitrpg-shared.git && cd .. cd public/bower_components/ && rm -rf habitrpg-shared && ln -s ../../node_modules/habitrpg-shared.

Note: Somehow, I couldn't get grunt working if I used a symlink in the node_modules directory

Now, when you need to commit and pull request, it is suggested that you keep the same branch names in both repositories. Also, you can pull request the habitrpg branch first, then copy the pull request url and paste it in the pull request from habitrpg-shared for cross-reference.

MongoDB
Quick tips for new developers to get the hang of using the database.

In the commands below, the $ sign indicates a Unix or Windows or Git Shell prompt and the > sign indicates a mongo shell prompt. Type only the text that appears after $ or >

Access a shell
First option is to start the mongo shell, then select the database:

$ mongo > show dbs > use habitrpg

Second option is to directly start the shell with the correct db:

$ mongo habitrpg > show collections

Use the shell
Within the shell, the first thing you probably want to do is find your test user and examine its data. From localhost settings, copy the user id, then run the following command:

> db.users.find({_id: '85b007a2-b5b9-4bb4-8b82-e4567edb4919'})[0]

Want to see the preferences only?

> db.users.find({_id: '85b007a2-b5b9-4bb4-8b82-e4567edb4919'})[0].preferences

If you want to update something for your user, use the update method. Here's an example to edit your profile blurb:

> db.users.update({_id: '1703b7fa-ba95-4d67-a006-7005ff67c1cb'}, {$set: {"profile.blurb":'test'}}) Other examples you can use for setting things with the update method:

Give a user 10 gems
{$set:{balance:10 }} ) Many of these are also available in the Debug menu displayed on the lower right side of the footer.

Migrations
When you work on migration scripts, you need to add your new script in migrations/. There are already plenty of scripts, so you can read them and use them as examples to write your own script.

Then you can test it as follows:

$ mongo habitrpg migrations/date_title.js

Get Admin Rights
Find your user id, then run in the mongo shell:

> db.users.update({_id: '85b007a2-b5b9-4bb4-8b82-e4567edb4919'}, {$set: {contributor: {"admin":1}}})

Reload your page, and you'll see some extra options in the Hall.

Data Binding, and Template Conditionals:
When working in the Jade templates, you may see element attributes such as ng-class, ng-show, or ng-if. These are bindings that are used to map model data to elements for display that are part of AngularJS. These bindings can be used to define styles or display based on conditionals.

You may also notice some other attributes such as bo-class or bo-if.... so what's the difference?

Bindings that begin with "ng" are part of Angular and are completely dynamic. This means that every time something changes with the app, Angular will test all of these bindings with conditionals again and take action again. Obviously this is a concern for performance.

Sometimes the data used to calculate these conditionals will not change often, if ever. When that is the case, we have an option for static binding, using a library called Bindonce. These are similar bindings, but they only are checked at the initial load of the application. If possible, this should be the preferred method of binding as long as the data will not change.

Bindonce directives should only be used if the following rule is true: the model data being used for the conditional will not change during a session, or will change infrequently enough that it's not unreasonable to expect the user to refresh the page. If not, stick with traditional binding. If you can't decide or have no idea what you just read, just use the ng- attributes.

Adding Translatable Strings
If you need to add a new translatable string in some template, it must be written in the jade file as follow:

env.t("stringLabel")

Then, in the  directory, edit the json files, adding the new string as follow:

'lastLabel': 'Add a comma at the end of this line', 'stringLabel': 'String Title' }

Do not update files in other directories under ; translations are managed in Transifex.

To test the string:


 * stop npm if it is running (Ctrl-C)
 * run  

Modifying Translatable Strings
Translatable strings appear in files in the  directory. Each string consists of a key and a piece of text, for example:

'clearAll': 'clear all items',

If you need to change the text of a translatable string, do not also change the key unless there is a very good reason to do so. For example, if you needed to change 'clear all items' to 'delete all items', you would not also change 'clearAll' to 'deleteAll'.

This is because the key ('clearAll') can be used in many places throughout HabitRPG's code, and so changing the key would require you to also make otherwise unnecessary changes to the code.

In addition, the keys that are used in the English translatable strings are also used throughout all the other languages directories under. When a piece of English text is changed without the key being changed, the other languages will keep using the existing translations for the original English text until the translators have had time to update the translations. Usually this is what you want because the existing translations are usually still good enough to be used. However if you were to change the key as well as the text, all the existing translations would no longer be used because the language files do not contain the new key. This would mean that people using languages other than English would see the new English text until the translators had time to provide strings for the new key.

Other
Various useful commands.

Search code
In the console, type:

grep -R "STRING" *

To search for STRING in all files in the current directory and all the directories within it.

If you want to make a search case insensitive (STRING or String or string etc), add -i

grep -iR "STRING" *

You'll often want to search all files containing some keyword, in order to determine what files need to be edited when adding/editing some feature.

The grep command also takes a regex as its search parameter: grep -R "REGEX" * Read more about grep and regex here.

Search and replace
Here is a perl command to run in the terminal:

perl -e "s/FROM/TO/g" -pi $(find . -name "*.js")

Replace FROM by the string you want to replace, and TO by the string to replace the first one with. Note that this example will replace that string only in javascript files (extension .js), but you can specify other filetypes if you want.

If you want to replace all strings, but not their plural or other words containing that string (e.g. replace weapon by TEST, but do not replace weapons, do:

perl -e 's/weapon\b/TEST/g' -pi *

If you want to remove all lines in the javascript files that contain some keyword, do:

perl -ni -e 'print unless /keyword/' -pi $(find . -name "*.js")

These commands are particularly useful with translation-related work.

Testing the Swagger API Interface Locally
HabitRPG has an API interface at https://habitrpg.com/static/api, which uses the production database and code. When you have made your own local install of HabitRPG, you can test the local version of that interface with these steps:


 * 1) Edit config.json to change the BASE_URL line to "BASE_URL":"http://localhost:3000" You do not need to change it back afterwards.
 * 2) Cancel and rerun the npm start command.
 * 3) Go to http://localhost:3000/static/api

Preference Settings
HabitRPG has preference settings for the users to customise the website's behaviour. You can add new settings if necessary. However please do not add settings that only a small proportion of users are likely to use, or settings for trivial customisations; instead choose a behaviour that the majority of users are likely to be happy with. Too many preferences will make the settings screen look bloated and cluttered and will increase the appearance of complexity. If you're uncertain about whether a preference setting is desirable, you can discuss it in the Aspiring Coders guild or in a relevant GitHub issue or pull request.